From b9ca06f5f4fd48453a34f42c727419f3c37e9d19 Mon Sep 17 00:00:00 2001 From: illuzen Date: Sat, 12 Sep 2026 16:18:56 +0800 Subject: [PATCH 01/10] Match airdrop snapshot addresses against wallet keys in the SDK. find_airdrop_matches derives every historical Dilithium hash and wormhole sponge scheme locally and intersects with the claim-server snapshot, so the app can show which rewards belong to this wallet without sending keys anywhere. Co-authored-by: Cursor --- quantus_sdk/lib/src/rust/api/airdrop.dart | 84 ++ quantus_sdk/lib/src/rust/api/crypto.dart | 50 +- quantus_sdk/lib/src/rust/api/multisig.dart | 11 +- quantus_sdk/lib/src/rust/api/ur.dart | 11 +- quantus_sdk/lib/src/rust/api/wormhole.dart | 63 +- quantus_sdk/lib/src/rust/frb_generated.dart | 798 ++++++++++++++---- .../lib/src/rust/frb_generated.io.dart | 117 ++- .../lib/src/rust/frb_generated.web.dart | 118 ++- quantus_sdk/rust/src/api/airdrop.rs | 464 ++++++++++ quantus_sdk/rust/src/api/mod.rs | 1 + quantus_sdk/rust/src/frb_generated.rs | 211 ++++- 11 files changed, 1678 insertions(+), 250 deletions(-) create mode 100644 quantus_sdk/lib/src/rust/api/airdrop.dart create mode 100644 quantus_sdk/rust/src/api/airdrop.rs diff --git a/quantus_sdk/lib/src/rust/api/airdrop.dart b/quantus_sdk/lib/src/rust/api/airdrop.dart new file mode 100644 index 000000000..093e915fd --- /dev/null +++ b/quantus_sdk/lib/src/rust/api/airdrop.dart @@ -0,0 +1,84 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.12.0. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import '../frb_generated.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; + +// These functions are ignored because they are not marked as `pub`: `compact8_decode`, `derive_dilithium`, `derive`, `hash_felts_rate4_pad10`, `hash_felts`, `hash_no_pad_v09`, `hash_padded_v09`, `hash_padded_v10`, `injective4`, `rehash` +// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `SecretEncoding`, `Sponge`, `WormholeSchemeDef` +// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `eq`, `fmt` + +/// Determine which snapshot addresses belong to this wallet. +/// +/// `snapshot_addresses` are the SS58 addresses from `GET /snapshot` (or the +/// miner-rewards CSV). Dilithium matches are checked against +/// `dilithium_public_key` under every historical hash. Wormhole matches are +/// checked for HD-derived secrets (`m/44'/189189189'/0'/{0,1}'/{0..=16}'`) +/// when `mnemonic` is given, plus any `extra_wormhole_secrets` (32 bytes +/// each). +Future> findAirdropMatches({ + required List snapshotAddresses, + Uint8List? dilithiumPublicKey, + String? mnemonic, + required List extraWormholeSecrets, +}) => RustLib.instance.api.crateApiAirdropFindAirdropMatches( + snapshotAddresses: snapshotAddresses, + dilithiumPublicKey: dilithiumPublicKey, + mnemonic: mnemonic, + extraWormholeSecrets: extraWormholeSecrets, +); + +/// A snapshot address this wallet can prove ownership of. +class AirdropMatch { + /// The address exactly as it appeared in `snapshot_addresses`. + final String address; + + /// "dilithium" or "wormhole". + final String kind; + + /// Derivation scheme id, e.g. "dilithium-v10-padded". + final String scheme; + + /// Whether the claim server currently accepts proofs for this scheme. + final bool claimable; + + /// Where the key came from: "dilithium public key", an HD path, or + /// "provided secret #N". + final String source; + + /// The 32-byte wormhole secret that produced the match (needed to build + /// the ownership proof). None for Dilithium matches. + final Uint8List? wormholeSecret; + + const AirdropMatch({ + required this.address, + required this.kind, + required this.scheme, + required this.claimable, + required this.source, + this.wormholeSecret, + }); + + @override + int get hashCode => + address.hashCode ^ + kind.hashCode ^ + scheme.hashCode ^ + claimable.hashCode ^ + source.hashCode ^ + wormholeSecret.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is AirdropMatch && + runtimeType == other.runtimeType && + address == other.address && + kind == other.kind && + scheme == other.scheme && + claimable == other.claimable && + source == other.source && + wormholeSecret == other.wormholeSecret; +} diff --git a/quantus_sdk/lib/src/rust/api/crypto.dart b/quantus_sdk/lib/src/rust/api/crypto.dart index 502c92c0f..09228ebeb 100644 --- a/quantus_sdk/lib/src/rust/api/crypto.dart +++ b/quantus_sdk/lib/src/rust/api/crypto.dart @@ -14,28 +14,44 @@ void setDefaultSs58Prefix({required int prefix}) => RustLib.instance.api.crateApiCryptoSetDefaultSs58Prefix(prefix: prefix); /// Convert public key to accountId32 in ss58check format -String toAccountId({required Keypair obj}) => RustLib.instance.api.crateApiCryptoToAccountId(obj: obj); +String toAccountId({required Keypair obj}) => + RustLib.instance.api.crateApiCryptoToAccountId(obj: obj); /// Convert key in ss58check format to accountId32 -Uint8List ss58ToAccountId({required String s}) => RustLib.instance.api.crateApiCryptoSs58ToAccountId(s: s); +Uint8List ss58ToAccountId({required String s}) => + RustLib.instance.api.crateApiCryptoSs58ToAccountId(s: s); /// Legacy non-HD ML-DSA-87 keypair straight from the mnemonic seed (early CLI and miner accounts). -Keypair generateKeypair({required String mnemonicStr}) => - RustLib.instance.api.crateApiCryptoGenerateKeypair(mnemonicStr: mnemonicStr); - -Keypair generateDerivedKeypair({required String mnemonicStr, required String path, required DilithiumScheme scheme}) => - RustLib.instance.api.crateApiCryptoGenerateDerivedKeypair(mnemonicStr: mnemonicStr, path: path, scheme: scheme); +Keypair generateKeypair({required String mnemonicStr}) => RustLib.instance.api + .crateApiCryptoGenerateKeypair(mnemonicStr: mnemonicStr); + +Keypair generateDerivedKeypair({ + required String mnemonicStr, + required String path, + required DilithiumScheme scheme, +}) => RustLib.instance.api.crateApiCryptoGenerateDerivedKeypair( + mnemonicStr: mnemonicStr, + path: path, + scheme: scheme, +); -WormholeResult deriveWormhole({required String mnemonicStr, required String path}) => - RustLib.instance.api.crateApiCryptoDeriveWormhole(mnemonicStr: mnemonicStr, path: path); +WormholeResult deriveWormhole({ + required String mnemonicStr, + required String path, +}) => RustLib.instance.api.crateApiCryptoDeriveWormhole( + mnemonicStr: mnemonicStr, + path: path, +); /// Convert a first_hash (rewards preimage) to its corresponding wormhole address. /// /// Mirrors how the chain and ZK circuit derive the address from the preimage: /// - Convert 32 bytes → 4 Poseidon field elements (8 bytes each) /// - Hash once without padding -String firstHashToAddress({required String firstHashHex}) => - RustLib.instance.api.crateApiCryptoFirstHashToAddress(firstHashHex: firstHashHex); +String firstHashToAddress({required String firstHashHex}) => RustLib + .instance + .api + .crateApiCryptoFirstHashToAddress(firstHashHex: firstHashHex); /// ML-DSA-87 keypair from a raw 32-byte seed (dev accounts). Keypair generateKeypairFromSeed({required List seed}) => @@ -106,7 +122,11 @@ class Keypair { final Uint8List secretKey; final DilithiumScheme scheme; - const Keypair({required this.publicKey, required this.secretKey, required this.scheme}); + const Keypair({ + required this.publicKey, + required this.secretKey, + required this.scheme, + }); @override int get hashCode => publicKey.hashCode ^ secretKey.hashCode ^ scheme.hashCode; @@ -138,7 +158,11 @@ class WormholeResult { final Uint8List firstHash; final Uint8List secret; - const WormholeResult({required this.address, required this.firstHash, required this.secret}); + const WormholeResult({ + required this.address, + required this.firstHash, + required this.secret, + }); @override int get hashCode => address.hashCode ^ firstHash.hashCode ^ secret.hashCode; diff --git a/quantus_sdk/lib/src/rust/api/multisig.dart b/quantus_sdk/lib/src/rust/api/multisig.dart index 8fcb105f9..cfec97749 100644 --- a/quantus_sdk/lib/src/rust/api/multisig.dart +++ b/quantus_sdk/lib/src/rust/api/multisig.dart @@ -11,5 +11,12 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; /// Predicts the on-chain multisig address for the given signers, threshold, and nonce. /// /// Address is computed as: hash(pallet_id || sorted_signers || threshold || nonce) -String predictMultisigAddress({required List signers, required int threshold, required BigInt nonce}) => - RustLib.instance.api.crateApiMultisigPredictMultisigAddress(signers: signers, threshold: threshold, nonce: nonce); +String predictMultisigAddress({ + required List signers, + required int threshold, + required BigInt nonce, +}) => RustLib.instance.api.crateApiMultisigPredictMultisigAddress( + signers: signers, + threshold: threshold, + nonce: nonce, +); diff --git a/quantus_sdk/lib/src/rust/api/ur.dart b/quantus_sdk/lib/src/rust/api/ur.dart index 7a82bc0f2..cb91f5267 100644 --- a/quantus_sdk/lib/src/rust/api/ur.dart +++ b/quantus_sdk/lib/src/rust/api/ur.dart @@ -12,9 +12,14 @@ int maxUrParts() => RustLib.instance.api.crateApiUrMaxUrParts(); int maxUrPartChars() => RustLib.instance.api.crateApiUrMaxUrPartChars(); -Uint8List decodeUr({required List urParts}) => RustLib.instance.api.crateApiUrDecodeUr(urParts: urParts); +Uint8List decodeUr({required List urParts}) => + RustLib.instance.api.crateApiUrDecodeUr(urParts: urParts); List encodeUr({required List data, int? maxFragmentLength}) => - RustLib.instance.api.crateApiUrEncodeUr(data: data, maxFragmentLength: maxFragmentLength); + RustLib.instance.api.crateApiUrEncodeUr( + data: data, + maxFragmentLength: maxFragmentLength, + ); -bool isCompleteUr({required List urParts}) => RustLib.instance.api.crateApiUrIsCompleteUr(urParts: urParts); +bool isCompleteUr({required List urParts}) => + RustLib.instance.api.crateApiUrIsCompleteUr(urParts: urParts); diff --git a/quantus_sdk/lib/src/rust/api/wormhole.dart b/quantus_sdk/lib/src/rust/api/wormhole.dart index b94bb0597..ff954fb24 100644 --- a/quantus_sdk/lib/src/rust/api/wormhole.dart +++ b/quantus_sdk/lib/src/rust/api/wormhole.dart @@ -8,28 +8,45 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; // These functions are ignored because they are not marked as `pub`: `all_required_files_exist`, `cleanup_stale_circuit_dirs`, `vec_to_32`, `vec_to_digest`, `versioned_bins_dir` -String zkCircuitsVersion() => RustLib.instance.api.crateApiWormholeZkCircuitsVersion(); - -String computeAddressHashHex({required List rawAddress}) => - RustLib.instance.api.crateApiWormholeComputeAddressHashHex(rawAddress: rawAddress); - -Uint8List computeNullifier({required List secret, required BigInt transferCount}) => - RustLib.instance.api.crateApiWormholeComputeNullifier(secret: secret, transferCount: transferCount); +String zkCircuitsVersion() => + RustLib.instance.api.crateApiWormholeZkCircuitsVersion(); + +String computeAddressHashHex({required List rawAddress}) => RustLib + .instance + .api + .crateApiWormholeComputeAddressHashHex(rawAddress: rawAddress); + +Uint8List computeNullifier({ + required List secret, + required BigInt transferCount, +}) => RustLib.instance.api.crateApiWormholeComputeNullifier( + secret: secret, + transferCount: transferCount, +); Uint8List computeWormholeAddress({required List secret}) => RustLib.instance.api.crateApiWormholeComputeWormholeAddress(secret: secret); -int wormholeComputeOutputAmount({required int inputAmount, required int feeBps}) => - RustLib.instance.api.crateApiWormholeWormholeComputeOutputAmount(inputAmount: inputAmount, feeBps: feeBps); +int wormholeComputeOutputAmount({ + required int inputAmount, + required int feeBps, +}) => RustLib.instance.api.crateApiWormholeWormholeComputeOutputAmount( + inputAmount: inputAmount, + feeBps: feeBps, +); int decodeLeafAmount({required List leafData}) => RustLib.instance.api.crateApiWormholeDecodeLeafAmount(leafData: leafData); -BigInt decodeLeafTransferCount({required List leafData}) => - RustLib.instance.api.crateApiWormholeDecodeLeafTransferCount(leafData: leafData); +BigInt decodeLeafTransferCount({required List leafData}) => RustLib + .instance + .api + .crateApiWormholeDecodeLeafTransferCount(leafData: leafData); -Uint8List decodeLeafToAccount({required List leafData}) => - RustLib.instance.api.crateApiWormholeDecodeLeafToAccount(leafData: leafData); +Uint8List decodeLeafToAccount({required List leafData}) => RustLib + .instance + .api + .crateApiWormholeDecodeLeafToAccount(leafData: leafData); MerkleProcessed computeMerklePositions({ required List unsortedSiblingsFlat, @@ -41,8 +58,10 @@ MerkleProcessed computeMerklePositions({ depth: depth, ); -Future ensureCircuitBinaries({required String binsDir}) => - RustLib.instance.api.crateApiWormholeEnsureCircuitBinaries(binsDir: binsDir); +Future ensureCircuitBinaries({required String binsDir}) => RustLib + .instance + .api + .crateApiWormholeEnsureCircuitBinaries(binsDir: binsDir); Future generateProof({ required ProofInput input, @@ -54,14 +73,22 @@ Future generateProof({ commonBinPath: commonBinPath, ); -Future aggregateProofs({required List proofBytesList, required String binsDir}) => - RustLib.instance.api.crateApiWormholeAggregateProofs(proofBytesList: proofBytesList, binsDir: binsDir); +Future aggregateProofs({ + required List proofBytesList, + required String binsDir, +}) => RustLib.instance.api.crateApiWormholeAggregateProofs( + proofBytesList: proofBytesList, + binsDir: binsDir, +); class MerkleProcessed { final Uint8List sortedSiblingsFlat; final Uint8List positions; - const MerkleProcessed({required this.sortedSiblingsFlat, required this.positions}); + const MerkleProcessed({ + required this.sortedSiblingsFlat, + required this.positions, + }); @override int get hashCode => sortedSiblingsFlat.hashCode ^ positions.hashCode; diff --git a/quantus_sdk/lib/src/rust/frb_generated.dart b/quantus_sdk/lib/src/rust/frb_generated.dart index d8c26a019..345f07aef 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.dart @@ -3,6 +3,7 @@ // ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field +import 'api/airdrop.dart'; import 'api/crypto.dart'; import 'api/multisig.dart'; import 'api/ur.dart'; @@ -10,7 +11,8 @@ import 'api/wormhole.dart'; import 'dart:async'; import 'dart:convert'; import 'frb_generated.dart'; -import 'frb_generated.io.dart' if (dart.library.js_interop) 'frb_generated.web.dart'; +import 'frb_generated.io.dart' + if (dart.library.js_interop) 'frb_generated.web.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; /// Main entrypoint of the Rust API @@ -48,10 +50,12 @@ class RustLib extends BaseEntrypoint { static void dispose() => instance.disposeImpl(); @override - ApiImplConstructor get apiImplConstructor => RustLibApiImpl.new; + ApiImplConstructor get apiImplConstructor => + RustLibApiImpl.new; @override - WireConstructor get wireConstructor => RustLibWire.fromExternalLibrary; + WireConstructor get wireConstructor => + RustLibWire.fromExternalLibrary; @override Future executeRustInitializers() async { @@ -59,24 +63,29 @@ class RustLib extends BaseEntrypoint { } @override - ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => kDefaultExternalLibraryLoaderConfig; + ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => + kDefaultExternalLibraryLoaderConfig; @override String get codegenVersion => '2.12.0'; @override - int get rustContentHash => 1684949517; - - static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( - stem: 'rust_lib_quantus_wallet', - ioDirectory: 'rust/target/release/', - webPrefix: 'pkg/', - wasmBindgenName: 'wasm_bindgen', - ); + int get rustContentHash => 231357703; + + static const kDefaultExternalLibraryLoaderConfig = + ExternalLibraryLoaderConfig( + stem: 'rust_lib_quantus_wallet', + ioDirectory: 'rust/target/release/', + webPrefix: 'pkg/', + wasmBindgenName: 'wasm_bindgen', + ); } abstract class RustLibApi extends BaseApi { - Future crateApiWormholeAggregateProofs({required List proofBytesList, required String binsDir}); + Future crateApiWormholeAggregateProofs({ + required List proofBytesList, + required String binsDir, + }); String crateApiWormholeComputeAddressHashHex({required List rawAddress}); @@ -86,7 +95,10 @@ abstract class RustLibApi extends BaseApi { required int depth, }); - Uint8List crateApiWormholeComputeNullifier({required List secret, required BigInt transferCount}); + Uint8List crateApiWormholeComputeNullifier({ + required List secret, + required BigInt transferCount, + }); Uint8List crateApiWormholeComputeWormholeAddress({required List secret}); @@ -104,11 +116,26 @@ abstract class RustLibApi extends BaseApi { Uint8List crateApiUrDecodeUr({required List urParts}); - WormholeResult crateApiCryptoDeriveWormhole({required String mnemonicStr, required String path}); + WormholeResult crateApiCryptoDeriveWormhole({ + required String mnemonicStr, + required String path, + }); + + List crateApiUrEncodeUr({ + required List data, + int? maxFragmentLength, + }); - List crateApiUrEncodeUr({required List data, int? maxFragmentLength}); + Future crateApiWormholeEnsureCircuitBinaries({ + required String binsDir, + }); - Future crateApiWormholeEnsureCircuitBinaries({required String binsDir}); + Future> crateApiAirdropFindAirdropMatches({ + required List snapshotAddresses, + Uint8List? dilithiumPublicKey, + String? mnemonic, + required List extraWormholeSecrets, + }); String crateApiCryptoFirstHashToAddress({required String firstHashHex}); @@ -175,15 +202,21 @@ abstract class RustLibApi extends BaseApi { required int specVersion, }); - int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps}); + int crateApiWormholeWormholeComputeOutputAmount({ + required int inputAmount, + required int feeBps, + }); String crateApiWormholeZkCircuitsVersion(); - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_HdLatticeError; + RustArcIncrementStrongCountFnType + get rust_arc_increment_strong_count_HdLatticeError; - RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_HdLatticeError; + RustArcDecrementStrongCountFnType + get rust_arc_decrement_strong_count_HdLatticeError; - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr; + CrossPlatformFinalizerArg + get rust_arc_decrement_strong_count_HdLatticeErrorPtr; } class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @@ -205,9 +238,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_list_prim_u_8_strict(proofBytesList, serializer); sse_encode_String(binsDir, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 1, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 1, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeAggregateProofsConstMeta, argValues: [proofBytesList, binsDir], apiImpl: this, @@ -216,10 +257,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeAggregateProofsConstMeta => - const TaskConstMeta(debugName: 'aggregate_proofs', argNames: ['proofBytesList', 'binsDir']); + const TaskConstMeta( + debugName: 'aggregate_proofs', + argNames: ['proofBytesList', 'binsDir'], + ); @override - String crateApiWormholeComputeAddressHashHex({required List rawAddress}) { + String crateApiWormholeComputeAddressHashHex({ + required List rawAddress, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -227,7 +273,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(rawAddress, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 2)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeComputeAddressHashHexConstMeta, argValues: [rawAddress], apiImpl: this, @@ -236,7 +285,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeAddressHashHexConstMeta => - const TaskConstMeta(debugName: 'compute_address_hash_hex', argNames: ['rawAddress']); + const TaskConstMeta( + debugName: 'compute_address_hash_hex', + argNames: ['rawAddress'], + ); @override MerkleProcessed crateApiWormholeComputeMerklePositions({ @@ -253,7 +305,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(depth, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_merkle_processed, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_merkle_processed, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeComputeMerklePositionsConstMeta, argValues: [unsortedSiblingsFlat, leafHash, depth], apiImpl: this, @@ -261,13 +316,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiWormholeComputeMerklePositionsConstMeta => const TaskConstMeta( - debugName: 'compute_merkle_positions', - argNames: ['unsortedSiblingsFlat', 'leafHash', 'depth'], - ); + TaskConstMeta get kCrateApiWormholeComputeMerklePositionsConstMeta => + const TaskConstMeta( + debugName: 'compute_merkle_positions', + argNames: ['unsortedSiblingsFlat', 'leafHash', 'depth'], + ); @override - Uint8List crateApiWormholeComputeNullifier({required List secret, required BigInt transferCount}) { + Uint8List crateApiWormholeComputeNullifier({ + required List secret, + required BigInt transferCount, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -276,7 +335,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_64(transferCount, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 4)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeComputeNullifierConstMeta, argValues: [secret, transferCount], apiImpl: this, @@ -285,10 +347,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeNullifierConstMeta => - const TaskConstMeta(debugName: 'compute_nullifier', argNames: ['secret', 'transferCount']); + const TaskConstMeta( + debugName: 'compute_nullifier', + argNames: ['secret', 'transferCount'], + ); @override - Uint8List crateApiWormholeComputeWormholeAddress({required List secret}) { + Uint8List crateApiWormholeComputeWormholeAddress({ + required List secret, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -296,7 +363,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(secret, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeComputeWormholeAddressConstMeta, argValues: [secret], apiImpl: this, @@ -305,7 +375,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeWormholeAddressConstMeta => - const TaskConstMeta(debugName: 'compute_wormhole_address', argNames: ['secret']); + const TaskConstMeta( + debugName: 'compute_wormhole_address', + argNames: ['secret'], + ); @override Keypair crateApiCryptoCrystalAlice() { @@ -315,7 +388,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_keypair, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoCrystalAliceConstMeta, argValues: [], apiImpl: this, @@ -334,7 +410,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_keypair, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoCrystalBobConstMeta, argValues: [], apiImpl: this, @@ -342,7 +421,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoCrystalBobConstMeta => const TaskConstMeta(debugName: 'crystal_bob', argNames: []); + TaskConstMeta get kCrateApiCryptoCrystalBobConstMeta => + const TaskConstMeta(debugName: 'crystal_bob', argNames: []); @override Keypair crateApiCryptoCrystalCharlie() { @@ -352,7 +432,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_keypair, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoCrystalCharlieConstMeta, argValues: [], apiImpl: this, @@ -372,7 +455,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeDecodeLeafAmountConstMeta, argValues: [leafData], apiImpl: this, @@ -381,7 +467,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafAmountConstMeta => - const TaskConstMeta(debugName: 'decode_leaf_amount', argNames: ['leafData']); + const TaskConstMeta( + debugName: 'decode_leaf_amount', + argNames: ['leafData'], + ); @override Uint8List crateApiWormholeDecodeLeafToAccount({required List leafData}) { @@ -392,7 +481,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeDecodeLeafToAccountConstMeta, argValues: [leafData], apiImpl: this, @@ -401,10 +493,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafToAccountConstMeta => - const TaskConstMeta(debugName: 'decode_leaf_to_account', argNames: ['leafData']); + const TaskConstMeta( + debugName: 'decode_leaf_to_account', + argNames: ['leafData'], + ); @override - BigInt crateApiWormholeDecodeLeafTransferCount({required List leafData}) { + BigInt crateApiWormholeDecodeLeafTransferCount({ + required List leafData, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -412,7 +509,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_64, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_u_64, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeDecodeLeafTransferCountConstMeta, argValues: [leafData], apiImpl: this, @@ -421,7 +521,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafTransferCountConstMeta => - const TaskConstMeta(debugName: 'decode_leaf_transfer_count', argNames: ['leafData']); + const TaskConstMeta( + debugName: 'decode_leaf_transfer_count', + argNames: ['leafData'], + ); @override Uint8List crateApiUrDecodeUr({required List urParts}) { @@ -432,7 +535,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(urParts, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiUrDecodeUrConstMeta, argValues: [urParts], apiImpl: this, @@ -440,10 +546,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrDecodeUrConstMeta => const TaskConstMeta(debugName: 'decode_ur', argNames: ['urParts']); + TaskConstMeta get kCrateApiUrDecodeUrConstMeta => + const TaskConstMeta(debugName: 'decode_ur', argNames: ['urParts']); @override - WormholeResult crateApiCryptoDeriveWormhole({required String mnemonicStr, required String path}) { + WormholeResult crateApiCryptoDeriveWormhole({ + required String mnemonicStr, + required String path, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -465,10 +575,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoDeriveWormholeConstMeta => - const TaskConstMeta(debugName: 'derive_wormhole', argNames: ['mnemonicStr', 'path']); + const TaskConstMeta( + debugName: 'derive_wormhole', + argNames: ['mnemonicStr', 'path'], + ); @override - List crateApiUrEncodeUr({required List data, int? maxFragmentLength}) { + List crateApiUrEncodeUr({ + required List data, + int? maxFragmentLength, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -477,7 +593,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(maxFragmentLength, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiUrEncodeUrConstMeta, argValues: [data, maxFragmentLength], apiImpl: this, @@ -485,19 +604,31 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrEncodeUrConstMeta => - const TaskConstMeta(debugName: 'encode_ur', argNames: ['data', 'maxFragmentLength']); + TaskConstMeta get kCrateApiUrEncodeUrConstMeta => const TaskConstMeta( + debugName: 'encode_ur', + argNames: ['data', 'maxFragmentLength'], + ); @override - Future crateApiWormholeEnsureCircuitBinaries({required String binsDir}) { + Future crateApiWormholeEnsureCircuitBinaries({ + required String binsDir, + }) { return handler.executeNormal( NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(binsDir, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 15, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeEnsureCircuitBinariesConstMeta, argValues: [binsDir], apiImpl: this, @@ -506,7 +637,62 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeEnsureCircuitBinariesConstMeta => - const TaskConstMeta(debugName: 'ensure_circuit_binaries', argNames: ['binsDir']); + const TaskConstMeta( + debugName: 'ensure_circuit_binaries', + argNames: ['binsDir'], + ); + + @override + Future> crateApiAirdropFindAirdropMatches({ + required List snapshotAddresses, + Uint8List? dilithiumPublicKey, + String? mnemonic, + required List extraWormholeSecrets, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_list_String(snapshotAddresses, serializer); + sse_encode_opt_list_prim_u_8_strict(dilithiumPublicKey, serializer); + sse_encode_opt_String(mnemonic, serializer); + sse_encode_list_list_prim_u_8_strict( + extraWormholeSecrets, + serializer, + ); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 16, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_airdrop_match, + decodeErrorData: sse_decode_String, + ), + constMeta: kCrateApiAirdropFindAirdropMatchesConstMeta, + argValues: [ + snapshotAddresses, + dilithiumPublicKey, + mnemonic, + extraWormholeSecrets, + ], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiAirdropFindAirdropMatchesConstMeta => + const TaskConstMeta( + debugName: 'find_airdrop_matches', + argNames: [ + 'snapshotAddresses', + 'dilithiumPublicKey', + 'mnemonic', + 'extraWormholeSecrets', + ], + ); @override String crateApiCryptoFirstHashToAddress({required String firstHashHex}) { @@ -515,9 +701,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(firstHashHex, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 16)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiCryptoFirstHashToAddressConstMeta, argValues: [firstHashHex], apiImpl: this, @@ -526,7 +715,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoFirstHashToAddressConstMeta => - const TaskConstMeta(debugName: 'first_hash_to_address', argNames: ['firstHashHex']); + const TaskConstMeta( + debugName: 'first_hash_to_address', + argNames: ['firstHashHex'], + ); @override Keypair crateApiCryptoGenerateDerivedKeypair({ @@ -541,7 +733,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(mnemonicStr, serializer); sse_encode_String(path, serializer); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -556,7 +748,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateDerivedKeypairConstMeta => - const TaskConstMeta(debugName: 'generate_derived_keypair', argNames: ['mnemonicStr', 'path', 'scheme']); + const TaskConstMeta( + debugName: 'generate_derived_keypair', + argNames: ['mnemonicStr', 'path', 'scheme'], + ); @override Keypair crateApiCryptoGenerateKeypair({required String mnemonicStr}) { @@ -565,7 +760,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(mnemonicStr, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -580,7 +775,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateKeypairConstMeta => - const TaskConstMeta(debugName: 'generate_keypair', argNames: ['mnemonicStr']); + const TaskConstMeta( + debugName: 'generate_keypair', + argNames: ['mnemonicStr'], + ); @override Keypair crateApiCryptoGenerateKeypairFromSeed({required List seed}) { @@ -589,9 +787,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(seed, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_keypair, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoGenerateKeypairFromSeedConstMeta, argValues: [seed], apiImpl: this, @@ -600,7 +801,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateKeypairFromSeedConstMeta => - const TaskConstMeta(debugName: 'generate_keypair_from_seed', argNames: ['seed']); + const TaskConstMeta( + debugName: 'generate_keypair_from_seed', + argNames: ['seed'], + ); @override Future crateApiWormholeGenerateProof({ @@ -615,9 +819,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_proof_input(input, serializer); sse_encode_String(proverBinPath, serializer); sse_encode_String(commonBinPath, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 21, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_proof_output, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_proof_output, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeGenerateProofConstMeta, argValues: [input, proverBinPath, commonBinPath], apiImpl: this, @@ -626,7 +838,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeGenerateProofConstMeta => - const TaskConstMeta(debugName: 'generate_proof', argNames: ['input', 'proverBinPath', 'commonBinPath']); + const TaskConstMeta( + debugName: 'generate_proof', + argNames: ['input', 'proverBinPath', 'commonBinPath'], + ); @override Future crateApiCryptoInitApp() { @@ -634,9 +849,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 21, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 22, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoInitAppConstMeta, argValues: [], apiImpl: this, @@ -644,7 +867,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoInitAppConstMeta => const TaskConstMeta(debugName: 'init_app', argNames: []); + TaskConstMeta get kCrateApiCryptoInitAppConstMeta => + const TaskConstMeta(debugName: 'init_app', argNames: []); @override bool crateApiUrIsCompleteUr({required List urParts}) { @@ -653,9 +877,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_String(urParts, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 22)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_bool, + decodeErrorData: null, + ), constMeta: kCrateApiUrIsCompleteUrConstMeta, argValues: [urParts], apiImpl: this, @@ -672,9 +899,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiUrMaxUrPartCharsConstMeta, argValues: [], apiImpl: this, @@ -691,9 +921,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiUrMaxUrPartsConstMeta, argValues: [], apiImpl: this, @@ -701,7 +934,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrMaxUrPartsConstMeta => const TaskConstMeta(debugName: 'max_ur_parts', argNames: []); + TaskConstMeta get kCrateApiUrMaxUrPartsConstMeta => + const TaskConstMeta(debugName: 'max_ur_parts', argNames: []); @override String crateApiMultisigPredictMultisigAddress({ @@ -716,9 +950,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(signers, serializer); sse_encode_u_32(threshold, serializer); sse_encode_u_64(nonce, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiMultisigPredictMultisigAddressConstMeta, argValues: [signers, threshold, nonce], apiImpl: this, @@ -727,7 +964,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiMultisigPredictMultisigAddressConstMeta => - const TaskConstMeta(debugName: 'predict_multisig_address', argNames: ['signers', 'threshold', 'nonce']); + const TaskConstMeta( + debugName: 'predict_multisig_address', + argNames: ['signers', 'threshold', 'nonce'], + ); @override int crateApiCryptoPublicKeyBytes({required DilithiumScheme scheme}) { @@ -736,9 +976,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoPublicKeyBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -756,9 +999,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSecretKeyBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -776,9 +1022,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_u_16(prefix, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSetDefaultSs58PrefixConstMeta, argValues: [prefix], apiImpl: this, @@ -787,7 +1036,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoSetDefaultSs58PrefixConstMeta => - const TaskConstMeta(debugName: 'set_default_ss58_prefix', argNames: ['prefix']); + const TaskConstMeta( + debugName: 'set_default_ss58_prefix', + argNames: ['prefix'], + ); @override Uint8List crateApiCryptoSignMessage({ @@ -804,9 +1056,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_opt_u_8_array_32(entropy, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSignMessageConstMeta, argValues: [keypair, message, entropy, specVersion], apiImpl: this, @@ -814,8 +1069,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoSignMessageConstMeta => - const TaskConstMeta(debugName: 'sign_message', argNames: ['keypair', 'message', 'entropy', 'specVersion']); + TaskConstMeta get kCrateApiCryptoSignMessageConstMeta => const TaskConstMeta( + debugName: 'sign_message', + argNames: ['keypair', 'message', 'entropy', 'specVersion'], + ); @override Uint8List crateApiCryptoSignMessageWithPubkey({ @@ -832,9 +1089,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_opt_u_8_array_32(entropy, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSignMessageWithPubkeyConstMeta, argValues: [keypair, message, entropy, specVersion], apiImpl: this, @@ -842,10 +1102,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta => const TaskConstMeta( - debugName: 'sign_message_with_pubkey', - argNames: ['keypair', 'message', 'entropy', 'specVersion'], - ); + TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta => + const TaskConstMeta( + debugName: 'sign_message_with_pubkey', + argNames: ['keypair', 'message', 'entropy', 'specVersion'], + ); @override int crateApiCryptoSignatureBytes({required DilithiumScheme scheme}) { @@ -854,9 +1115,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSignatureBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -874,9 +1138,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(s, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiCryptoSs58ToAccountIdConstMeta, argValues: [s], apiImpl: this, @@ -894,9 +1161,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_box_autoadd_keypair(obj, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoToAccountIdConstMeta, argValues: [obj], apiImpl: this, @@ -922,9 +1192,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_list_prim_u_8_loose(signature, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_bool, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoVerifyMessageConstMeta, argValues: [keypair, message, signature, specVersion], apiImpl: this, @@ -933,19 +1206,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoVerifyMessageConstMeta => - const TaskConstMeta(debugName: 'verify_message', argNames: ['keypair', 'message', 'signature', 'specVersion']); + const TaskConstMeta( + debugName: 'verify_message', + argNames: ['keypair', 'message', 'signature', 'specVersion'], + ); @override - int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps}) { + int crateApiWormholeWormholeComputeOutputAmount({ + required int inputAmount, + required int feeBps, + }) { return handler.executeSync( SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_u_32(inputAmount, serializer); sse_encode_u_32(feeBps, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeWormholeComputeOutputAmountConstMeta, argValues: [inputAmount, feeBps], apiImpl: this, @@ -954,7 +1236,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeWormholeComputeOutputAmountConstMeta => - const TaskConstMeta(debugName: 'wormhole_compute_output_amount', argNames: ['inputAmount', 'feeBps']); + const TaskConstMeta( + debugName: 'wormhole_compute_output_amount', + argNames: ['inputAmount', 'feeBps'], + ); @override String crateApiWormholeZkCircuitsVersion() { @@ -962,9 +1247,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: null, + ), constMeta: kCrateApiWormholeZkCircuitsVersionConstMeta, argValues: [], apiImpl: this, @@ -975,14 +1263,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiWormholeZkCircuitsVersionConstMeta => const TaskConstMeta(debugName: 'zk_circuits_version', argNames: []); - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_HdLatticeError => - wire.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + RustArcIncrementStrongCountFnType + get rust_arc_increment_strong_count_HdLatticeError => wire + .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; - RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_HdLatticeError => - wire.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + RustArcDecrementStrongCountFnType + get rust_arc_decrement_strong_count_HdLatticeError => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; @protected - HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -990,7 +1281,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw) { + HdLatticeError + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + dynamic raw, + ) { // Codec=Dco (DartCObject based), see doc to use other codecs return HdLatticeErrorImpl.frbInternalDcoDecode(raw as List); } @@ -1001,6 +1295,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as String; } + @protected + AirdropMatch dco_decode_airdrop_match(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 6) + throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + return AirdropMatch( + address: dco_decode_String(arr[0]), + kind: dco_decode_String(arr[1]), + scheme: dco_decode_String(arr[2]), + claimable: dco_decode_bool(arr[3]), + source: dco_decode_String(arr[4]), + wormholeSecret: dco_decode_opt_list_prim_u_8_strict(arr[5]), + ); + } + @protected bool dco_decode_bool(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1041,7 +1351,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { Keypair dco_decode_keypair(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 3) + throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); return Keypair( publicKey: dco_decode_list_prim_u_8_strict(arr[0]), secretKey: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1055,6 +1366,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (raw as List).map(dco_decode_String).toList(); } + @protected + List dco_decode_list_airdrop_match(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return (raw as List).map(dco_decode_airdrop_match).toList(); + } + @protected List dco_decode_list_list_prim_u_8_strict(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1077,19 +1394,32 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { MerkleProcessed dco_decode_merkle_processed(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return MerkleProcessed( sortedSiblingsFlat: dco_decode_list_prim_u_8_strict(arr[0]), positions: dco_decode_list_prim_u_8_strict(arr[1]), ); } + @protected + String? dco_decode_opt_String(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw == null ? null : dco_decode_String(raw); + } + @protected int? dco_decode_opt_box_autoadd_u_32(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return raw == null ? null : dco_decode_box_autoadd_u_32(raw); } + @protected + Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw == null ? null : dco_decode_list_prim_u_8_strict(raw); + } + @protected U8Array32? dco_decode_opt_u_8_array_32(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1100,7 +1430,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ProofInput dco_decode_proof_input(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 19) throw Exception('unexpected arr length: expect 19 but see ${arr.length}'); + if (arr.length != 19) + throw Exception('unexpected arr length: expect 19 but see ${arr.length}'); return ProofInput( secret: dco_decode_list_prim_u_8_strict(arr[0]), transferCount: dco_decode_u_64(arr[1]), @@ -1128,7 +1459,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ProofOutput dco_decode_proof_output(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return ProofOutput( proofBytes: dco_decode_list_prim_u_8_strict(arr[0]), nullifier: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1181,7 +1513,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { WormholeResult dco_decode_wormhole_result(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 3) + throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); return WormholeResult( address: dco_decode_String(arr[0]), firstHash: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1190,19 +1523,27 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - return HdLatticeErrorImpl.frbInternalSseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); + return HdLatticeErrorImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); } @protected - HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - return HdLatticeErrorImpl.frbInternalSseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); + return HdLatticeErrorImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); } @protected @@ -1212,6 +1553,25 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return utf8.decoder.convert(inner); } + @protected + AirdropMatch sse_decode_airdrop_match(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_address = sse_decode_String(deserializer); + var var_kind = sse_decode_String(deserializer); + var var_scheme = sse_decode_String(deserializer); + var var_claimable = sse_decode_bool(deserializer); + var var_source = sse_decode_String(deserializer); + var var_wormholeSecret = sse_decode_opt_list_prim_u_8_strict(deserializer); + return AirdropMatch( + address: var_address, + kind: var_kind, + scheme: var_scheme, + claimable: var_claimable, + source: var_source, + wormholeSecret: var_wormholeSecret, + ); + } + @protected bool sse_decode_bool(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1255,7 +1615,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_publicKey = sse_decode_list_prim_u_8_strict(deserializer); var var_secretKey = sse_decode_list_prim_u_8_strict(deserializer); var var_scheme = sse_decode_dilithium_scheme(deserializer); - return Keypair(publicKey: var_publicKey, secretKey: var_secretKey, scheme: var_scheme); + return Keypair( + publicKey: var_publicKey, + secretKey: var_secretKey, + scheme: var_scheme, + ); } @protected @@ -1271,7 +1635,23 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer) { + List sse_decode_list_airdrop_match( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + + var len_ = sse_decode_i_32(deserializer); + var ans_ = []; + for (var idx_ = 0; idx_ < len_; ++idx_) { + ans_.add(sse_decode_airdrop_match(deserializer)); + } + return ans_; + } + + @protected + List sse_decode_list_list_prim_u_8_strict( + SseDeserializer deserializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs var len_ = sse_decode_i_32(deserializer); @@ -1301,7 +1681,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs var var_sortedSiblingsFlat = sse_decode_list_prim_u_8_strict(deserializer); var var_positions = sse_decode_list_prim_u_8_strict(deserializer); - return MerkleProcessed(sortedSiblingsFlat: var_sortedSiblingsFlat, positions: var_positions); + return MerkleProcessed( + sortedSiblingsFlat: var_sortedSiblingsFlat, + positions: var_positions, + ); + } + + @protected + String? sse_decode_opt_String(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + if (sse_decode_bool(deserializer)) { + return (sse_decode_String(deserializer)); + } else { + return null; + } } @protected @@ -1315,6 +1709,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + if (sse_decode_bool(deserializer)) { + return (sse_decode_list_prim_u_8_strict(deserializer)); + } else { + return null; + } + } + @protected U8Array32? sse_decode_opt_u_8_array_32(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1427,25 +1832,37 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_address = sse_decode_String(deserializer); var var_firstHash = sse_decode_list_prim_u_8_strict(deserializer); var var_secret = sse_decode_list_prim_u_8_strict(deserializer); - return WormholeResult(address: var_address, firstHash: var_firstHash, secret: var_secret); + return WormholeResult( + address: var_address, + firstHash: var_firstHash, + secret: var_secret, + ); } @protected - void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize((self as HdLatticeErrorImpl).frbInternalSseEncode(move: true), serializer); + sse_encode_usize( + (self as HdLatticeErrorImpl).frbInternalSseEncode(move: true), + serializer, + ); } @protected - void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize((self as HdLatticeErrorImpl).frbInternalSseEncode(move: null), serializer); + sse_encode_usize( + (self as HdLatticeErrorImpl).frbInternalSseEncode(move: null), + serializer, + ); } @protected @@ -1454,6 +1871,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer); } + @protected + void sse_encode_airdrop_match(AirdropMatch self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.address, serializer); + sse_encode_String(self.kind, serializer); + sse_encode_String(self.scheme, serializer); + sse_encode_bool(self.claimable, serializer); + sse_encode_String(self.source, serializer); + sse_encode_opt_list_prim_u_8_strict(self.wormholeSecret, serializer); + } + @protected void sse_encode_bool(bool self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1467,7 +1895,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer) { + void sse_encode_box_autoadd_proof_input( + ProofInput self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_proof_input(self, serializer); } @@ -1479,7 +1910,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer) { + void sse_encode_dilithium_scheme( + DilithiumScheme self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.index, serializer); } @@ -1508,7 +1942,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer) { + void sse_encode_list_airdrop_match( + List self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + for (final item in self) { + sse_encode_airdrop_match(item, serializer); + } + } + + @protected + void sse_encode_list_list_prim_u_8_strict( + List self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); for (final item in self) { @@ -1517,26 +1966,47 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer) { + void sse_encode_list_prim_u_8_loose( + List self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); - serializer.buffer.putUint8List(self is Uint8List ? self : Uint8List.fromList(self)); + serializer.buffer.putUint8List( + self is Uint8List ? self : Uint8List.fromList(self), + ); } @protected - void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer) { + void sse_encode_list_prim_u_8_strict( + Uint8List self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); serializer.buffer.putUint8List(self); } @protected - void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer) { + void sse_encode_merkle_processed( + MerkleProcessed self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_list_prim_u_8_strict(self.sortedSiblingsFlat, serializer); sse_encode_list_prim_u_8_strict(self.positions, serializer); } + @protected + void sse_encode_opt_String(String? self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_String(self, serializer); + } + } + @protected void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1547,6 +2017,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + void sse_encode_opt_list_prim_u_8_strict( + Uint8List? self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_list_prim_u_8_strict(self, serializer); + } + } + @protected void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1630,7 +2113,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer) { + void sse_encode_wormhole_result( + WormholeResult self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.address, serializer); sse_encode_list_prim_u_8_strict(self.firstHash, serializer); @@ -1641,15 +2127,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @sealed class HdLatticeErrorImpl extends RustOpaque implements HdLatticeError { // Not to be used by end users - HdLatticeErrorImpl.frbInternalDcoDecode(List wire) : super.frbInternalDcoDecode(wire, _kStaticData); + HdLatticeErrorImpl.frbInternalDcoDecode(List wire) + : super.frbInternalDcoDecode(wire, _kStaticData); // Not to be used by end users HdLatticeErrorImpl.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative) : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); static final _kStaticData = RustArcStaticData( - rustArcIncrementStrongCount: RustLib.instance.api.rust_arc_increment_strong_count_HdLatticeError, - rustArcDecrementStrongCount: RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeError, - rustArcDecrementStrongCountPtr: RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeErrorPtr, + rustArcIncrementStrongCount: + RustLib.instance.api.rust_arc_increment_strong_count_HdLatticeError, + rustArcDecrementStrongCount: + RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeError, + rustArcDecrementStrongCountPtr: + RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeErrorPtr, ); } diff --git a/quantus_sdk/lib/src/rust/frb_generated.io.dart b/quantus_sdk/lib/src/rust/frb_generated.io.dart index c8cd57473..d02fb2049 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.io.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.io.dart @@ -3,6 +3,7 @@ // ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field +import 'api/airdrop.dart'; import 'api/crypto.dart'; import 'api/multisig.dart'; import 'api/ur.dart'; @@ -21,20 +22,28 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire + CrossPlatformFinalizerArg + get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeErrorPtr; @protected - HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ); @protected - HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw); + HdLatticeError + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + dynamic raw, + ); @protected String dco_decode_String(dynamic raw); + @protected + AirdropMatch dco_decode_airdrop_match(dynamic raw); + @protected bool dco_decode_bool(dynamic raw); @@ -59,6 +68,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List dco_decode_list_String(dynamic raw); + @protected + List dco_decode_list_airdrop_match(dynamic raw); + @protected List dco_decode_list_list_prim_u_8_strict(dynamic raw); @@ -71,9 +83,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected MerkleProcessed dco_decode_merkle_processed(dynamic raw); + @protected + String? dco_decode_opt_String(dynamic raw); + @protected int? dco_decode_opt_box_autoadd_u_32(dynamic raw); + @protected + Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw); + @protected U8Array32? dco_decode_opt_u_8_array_32(dynamic raw); @@ -108,18 +126,23 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult dco_decode_wormhole_result(dynamic raw); @protected - HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected - HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected String sse_decode_String(SseDeserializer deserializer); + @protected + AirdropMatch sse_decode_airdrop_match(SseDeserializer deserializer); + @protected bool sse_decode_bool(SseDeserializer deserializer); @@ -145,7 +168,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { List sse_decode_list_String(SseDeserializer deserializer); @protected - List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer); + List sse_decode_list_airdrop_match( + SseDeserializer deserializer, + ); + + @protected + List sse_decode_list_list_prim_u_8_strict( + SseDeserializer deserializer, + ); @protected List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer); @@ -156,9 +186,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected MerkleProcessed sse_decode_merkle_processed(SseDeserializer deserializer); + @protected + String? sse_decode_opt_String(SseDeserializer deserializer); + @protected int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer); + @protected + Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer); + @protected U8Array32? sse_decode_opt_u_8_array_32(SseDeserializer deserializer); @@ -193,13 +229,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @protected - void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @protected - void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @@ -207,6 +245,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_airdrop_match(AirdropMatch self, SseSerializer serializer); + @protected void sse_encode_bool(bool self, SseSerializer serializer); @@ -214,13 +255,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_keypair(Keypair self, SseSerializer serializer); @protected - void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer); + void sse_encode_box_autoadd_proof_input( + ProofInput self, + SseSerializer serializer, + ); @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); @protected - void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer); + void sse_encode_dilithium_scheme( + DilithiumScheme self, + SseSerializer serializer, + ); @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -232,20 +279,44 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_String(List self, SseSerializer serializer); @protected - void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer); + void sse_encode_list_airdrop_match( + List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_list_prim_u_8_strict( + List self, + SseSerializer serializer, + ); @protected void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); @protected - void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer); + void sse_encode_list_prim_u_8_strict( + Uint8List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_merkle_processed( + MerkleProcessed self, + SseSerializer serializer, + ); @protected - void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer); + void sse_encode_opt_String(String? self, SseSerializer serializer); @protected void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer); + @protected + void sse_encode_opt_list_prim_u_8_strict( + Uint8List? self, + SseSerializer serializer, + ); + @protected void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer); @@ -277,21 +348,28 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_usize(BigInt self, SseSerializer serializer); @protected - void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer); + void sse_encode_wormhole_result( + WormholeResult self, + SseSerializer serializer, + ); } // Section: wire_class class RustLibWire implements BaseWire { - factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => RustLibWire(lib.ffiDynamicLibrary); + factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => + RustLibWire(lib.ffiDynamicLibrary); /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) _lookup; + final ffi.Pointer Function(String symbolName) + _lookup; /// The symbols are looked up in [dynamicLibrary]. - RustLibWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; + RustLibWire(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; - void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ffi.Pointer ptr, ) { return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( @@ -307,7 +385,8 @@ class RustLibWire implements BaseWire { _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeErrorPtr .asFunction)>(); - void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ffi.Pointer ptr, ) { return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( diff --git a/quantus_sdk/lib/src/rust/frb_generated.web.dart b/quantus_sdk/lib/src/rust/frb_generated.web.dart index 9dbc5b342..952d92fb2 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.web.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.web.dart @@ -6,6 +6,7 @@ // Static analysis wrongly picks the IO variant, thus ignore this // ignore_for_file: argument_type_not_assignable +import 'api/airdrop.dart'; import 'api/crypto.dart'; import 'api/multisig.dart'; import 'api/ur.dart'; @@ -23,20 +24,28 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr => - wire.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + CrossPlatformFinalizerArg + get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; @protected - HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ); @protected - HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw); + HdLatticeError + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + dynamic raw, + ); @protected String dco_decode_String(dynamic raw); + @protected + AirdropMatch dco_decode_airdrop_match(dynamic raw); + @protected bool dco_decode_bool(dynamic raw); @@ -61,6 +70,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List dco_decode_list_String(dynamic raw); + @protected + List dco_decode_list_airdrop_match(dynamic raw); + @protected List dco_decode_list_list_prim_u_8_strict(dynamic raw); @@ -73,9 +85,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected MerkleProcessed dco_decode_merkle_processed(dynamic raw); + @protected + String? dco_decode_opt_String(dynamic raw); + @protected int? dco_decode_opt_box_autoadd_u_32(dynamic raw); + @protected + Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw); + @protected U8Array32? dco_decode_opt_u_8_array_32(dynamic raw); @@ -110,18 +128,23 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult dco_decode_wormhole_result(dynamic raw); @protected - HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected - HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected String sse_decode_String(SseDeserializer deserializer); + @protected + AirdropMatch sse_decode_airdrop_match(SseDeserializer deserializer); + @protected bool sse_decode_bool(SseDeserializer deserializer); @@ -147,7 +170,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { List sse_decode_list_String(SseDeserializer deserializer); @protected - List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer); + List sse_decode_list_airdrop_match( + SseDeserializer deserializer, + ); + + @protected + List sse_decode_list_list_prim_u_8_strict( + SseDeserializer deserializer, + ); @protected List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer); @@ -158,9 +188,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected MerkleProcessed sse_decode_merkle_processed(SseDeserializer deserializer); + @protected + String? sse_decode_opt_String(SseDeserializer deserializer); + @protected int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer); + @protected + Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer); + @protected U8Array32? sse_decode_opt_u_8_array_32(SseDeserializer deserializer); @@ -195,13 +231,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @protected - void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @protected - void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @@ -209,6 +247,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_airdrop_match(AirdropMatch self, SseSerializer serializer); + @protected void sse_encode_bool(bool self, SseSerializer serializer); @@ -216,13 +257,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_keypair(Keypair self, SseSerializer serializer); @protected - void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer); + void sse_encode_box_autoadd_proof_input( + ProofInput self, + SseSerializer serializer, + ); @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); @protected - void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer); + void sse_encode_dilithium_scheme( + DilithiumScheme self, + SseSerializer serializer, + ); @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -234,20 +281,44 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_String(List self, SseSerializer serializer); @protected - void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer); + void sse_encode_list_airdrop_match( + List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_list_prim_u_8_strict( + List self, + SseSerializer serializer, + ); @protected void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); @protected - void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer); + void sse_encode_list_prim_u_8_strict( + Uint8List self, + SseSerializer serializer, + ); @protected - void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer); + void sse_encode_merkle_processed( + MerkleProcessed self, + SseSerializer serializer, + ); + + @protected + void sse_encode_opt_String(String? self, SseSerializer serializer); @protected void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer); + @protected + void sse_encode_opt_list_prim_u_8_strict( + Uint8List? self, + SseSerializer serializer, + ); + @protected void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer); @@ -279,7 +350,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_usize(BigInt self, SseSerializer serializer); @protected - void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer); + void sse_encode_wormhole_result( + WormholeResult self, + SseSerializer serializer, + ); } // Section: wire_class @@ -287,14 +361,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { class RustLibWire implements BaseWire { RustLibWire.fromExternalLibrary(ExternalLibrary lib); - void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( int ptr, ) => wasmModule .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ptr, ); - void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( int ptr, ) => wasmModule .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( @@ -309,8 +385,12 @@ external RustLibWasmModule get wasmModule; @anonymous extension type RustLibWasmModule._(JSObject _) implements JSObject { external void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(int ptr); + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + int ptr, + ); external void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(int ptr); + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + int ptr, + ); } diff --git a/quantus_sdk/rust/src/api/airdrop.rs b/quantus_sdk/rust/src/api/airdrop.rs new file mode 100644 index 000000000..1844054a1 --- /dev/null +++ b/quantus_sdk/rust/src/api/airdrop.rs @@ -0,0 +1,464 @@ +//! Airdrop snapshot matching. Given the claim-server snapshot address list, +//! derive every historical address format from the wallet's keys locally and +//! return which snapshot rows belong to this wallet. Nothing here talks to a +//! server; secrets never leave the process. +//! +//! Testnet address history (the Poseidon2 permutation never changed, but the +//! sponge wrappers did): +//! - Resonance / early Schrödinger: rate 4, pad10 + domain block +//! - late Schrödinger / Dirac: rate 4, pad10 +//! - late Planck / current: rate 8, pad10 + +use qp_poseidon_core::{ + serialization::digest_to_bytes, Goldilocks, Poseidon2, POSEIDON2_OUTPUT, SPONGE_WIDTH, +}; +use qp_rusty_crystals_hdwallet::{derive_wormhole_from_mnemonic, QUANTUS_WORMHOLE_CHAIN_ID}; +use std::collections::HashMap; + +const RATE_4: usize = 4; +const WORMHOLE_SALT: &[u8] = b"wormhole"; +/// Only proofs for the current wormhole scheme are accepted by the claim server. +const CLAIMABLE_WORMHOLE_SCHEME: &str = "wormhole-rate8-compact"; +const HD_SCAN_INDEXES: u32 = 17; + +// --------------------------------------------------------------------------- +// Historical Poseidon2 sponges +// --------------------------------------------------------------------------- + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Sponge { + /// qp-poseidon 0.9.x `hash_no_pad`: rate 4, pad10, then a mandatory `[0,0,0,1]` block. + Rate4Pad10PlusDomain, + /// qp-poseidon 1.0.x–1.1.x: rate 4, pad10 only. + Rate4Pad10, + /// qp-poseidon 1.2.x+ (current): rate 8, pad10. + Rate8Pad10, +} + +impl Sponge { + fn hash_felts(self, input: &[Goldilocks]) -> [u8; 32] { + match self { + Self::Rate4Pad10PlusDomain => hash_no_pad_v09(input), + Self::Rate4Pad10 => hash_felts_rate4_pad10(input), + Self::Rate8Pad10 => qp_poseidon_core::hash_to_bytes(input), + } + } + + fn rehash(self, digest: &[u8; 32]) -> [u8; 32] { + self.hash_felts(&compact8_decode(digest)) + } +} + +/// 8-byte little-endian limbs, reduced into Goldilocks. +fn compact8_decode(bytes: &[u8; 32]) -> [Goldilocks; POSEIDON2_OUTPUT] { + qp_poseidon_core::serialization::bytes_to_digest_lossy(bytes) +} + +/// v0.9.5 injective 4-bytes/felt encoding (terminator in the last word or as an extra felt). +fn injective4(bytes: &[u8]) -> Vec { + if bytes.is_empty() { + return Vec::new(); + } + const N: usize = 4; + let mut out = Vec::new(); + let num_chunks = bytes.len().div_ceil(N); + let mut unpadded = false; + for (i, chunk) in bytes.chunks(N).enumerate() { + let mut word = [0u8; N]; + if i == num_chunks - 1 { + if chunk.len() < N { + word[chunk.len()] = 1; + } else { + unpadded = true; + } + } + word[..chunk.len()].copy_from_slice(chunk); + out.push(Goldilocks::from_u64(u32::from_le_bytes(word) as u64)); + } + if unpadded { + out.push(Goldilocks::from_u64(1)); + } + out +} + +fn hash_no_pad_v09(x: &[Goldilocks]) -> [u8; 32] { + let poseidon = Poseidon2::new(); + let mut state = [Goldilocks::ZERO; SPONGE_WIDTH]; + + if !x.is_empty() { + let num_chunks = x.chunks(RATE_4).len(); + let mut unpadded = false; + for (j, chunk) in x.chunks(RATE_4).enumerate() { + let mut block = [Goldilocks::ZERO; RATE_4]; + if j == num_chunks - 1 { + if chunk.len() < RATE_4 { + block[chunk.len()] = Goldilocks::ONE; + } else { + unpadded = true; + } + } + block[..chunk.len()].copy_from_slice(chunk); + for i in 0..RATE_4 { + state[i] += block[i]; + } + poseidon.permute_mut(&mut state); + } + if unpadded { + state[0] += Goldilocks::ONE; + poseidon.permute_mut(&mut state); + } + } + + state[3] += Goldilocks::ONE; + poseidon.permute_mut(&mut state); + + let digest: [Goldilocks; POSEIDON2_OUTPUT] = state[..POSEIDON2_OUTPUT] + .try_into() + .expect("width > output"); + digest_to_bytes(&digest) +} + +fn hash_felts_rate4_pad10(x: &[Goldilocks]) -> [u8; 32] { + let poseidon = Poseidon2::new(); + let mut state = [Goldilocks::ZERO; SPONGE_WIDTH]; + let mut buf = [Goldilocks::ZERO; RATE_4]; + let mut buf_len = 0usize; + + let absorb = |felt: Goldilocks, + state: &mut [Goldilocks; SPONGE_WIDTH], + buf: &mut [Goldilocks; RATE_4], + buf_len: &mut usize| { + buf[*buf_len] = felt; + *buf_len += 1; + if *buf_len == RATE_4 { + for i in 0..RATE_4 { + state[i] += buf[i]; + } + poseidon.permute_mut(state); + *buf = [Goldilocks::ZERO; RATE_4]; + *buf_len = 0; + } + }; + + for &felt in x { + absorb(felt, &mut state, &mut buf, &mut buf_len); + } + absorb(Goldilocks::ONE, &mut state, &mut buf, &mut buf_len); + while buf_len != 0 { + absorb(Goldilocks::ZERO, &mut state, &mut buf, &mut buf_len); + } + + let digest: [Goldilocks; POSEIDON2_OUTPUT] = state[..POSEIDON2_OUTPUT] + .try_into() + .expect("width > output"); + digest_to_bytes(&digest) +} + +/// Resonance-era Dilithium AccountId: injective bytes, length prefix, pad to 190, v0.9 sponge. +fn hash_padded_v09(bytes: &[u8]) -> [u8; 32] { + const MIN_FELTS: usize = 190; + let mut felts = injective4(bytes); + let len = felts.len(); + felts.insert(0, Goldilocks::from_u64(len as u64)); + if len < MIN_FELTS { + felts.resize(MIN_FELTS, Goldilocks::ZERO); + } + hash_no_pad_v09(&felts) +} + +/// qp-poseidon 1.0.x Dilithium AccountId: injective bytes, zero-pad to 189, rate-4 pad10. +fn hash_padded_v10(bytes: &[u8]) -> [u8; 32] { + const PAD: usize = 189; + let mut felts = injective4(bytes); + if felts.len() < PAD { + felts.resize(PAD, Goldilocks::ZERO); + } + hash_felts_rate4_pad10(&felts) +} + +// --------------------------------------------------------------------------- +// Derivation schemes +// --------------------------------------------------------------------------- + +#[derive(Clone, Copy)] +enum SecretEncoding { + Injective4, + Compact8, +} + +struct WormholeSchemeDef { + id: &'static str, + sponge: Sponge, + secret_encoding: SecretEncoding, +} + +const WORMHOLE_SCHEMES: &[WormholeSchemeDef] = &[ + WormholeSchemeDef { + id: "wormhole-v09-injective", + sponge: Sponge::Rate4Pad10PlusDomain, + secret_encoding: SecretEncoding::Injective4, + }, + WormholeSchemeDef { + id: "wormhole-rate4-compact", + sponge: Sponge::Rate4Pad10, + secret_encoding: SecretEncoding::Compact8, + }, + WormholeSchemeDef { + id: "wormhole-rate8-compact", + sponge: Sponge::Rate8Pad10, + secret_encoding: SecretEncoding::Compact8, + }, + WormholeSchemeDef { + id: "wormhole-rate4-injective", + sponge: Sponge::Rate4Pad10, + secret_encoding: SecretEncoding::Injective4, + }, + WormholeSchemeDef { + id: "wormhole-rate8-injective", + sponge: Sponge::Rate8Pad10, + secret_encoding: SecretEncoding::Injective4, + }, +]; + +impl WormholeSchemeDef { + fn derive(&self, secret: &[u8; 32]) -> [u8; 32] { + let mut preimage = injective4(WORMHOLE_SALT); + match self.secret_encoding { + SecretEncoding::Injective4 => preimage.extend(injective4(secret)), + SecretEncoding::Compact8 => preimage.extend(compact8_decode(secret)), + } + let first_hash = self.sponge.hash_felts(&preimage); + self.sponge.rehash(&first_hash) + } +} + +const DILITHIUM_SCHEMES: &[&str] = &[ + "dilithium-v09-padded", + "dilithium-v10-padded", + "dilithium-rate8-hash-bytes", +]; + +fn derive_dilithium(scheme: &str, public_key: &[u8]) -> [u8; 32] { + match scheme { + "dilithium-v09-padded" => hash_padded_v09(public_key), + "dilithium-v10-padded" => hash_padded_v10(public_key), + "dilithium-rate8-hash-bytes" => qp_poseidon_core::hash_bytes(public_key), + _ => unreachable!(), + } +} + +// --------------------------------------------------------------------------- +// FFI surface +// --------------------------------------------------------------------------- + +/// A snapshot address this wallet can prove ownership of. +#[flutter_rust_bridge::frb(sync)] +pub struct AirdropMatch { + /// The address exactly as it appeared in `snapshot_addresses`. + pub address: String, + /// "dilithium" or "wormhole". + pub kind: String, + /// Derivation scheme id, e.g. "dilithium-v10-padded". + pub scheme: String, + /// Whether the claim server currently accepts proofs for this scheme. + pub claimable: bool, + /// Where the key came from: "dilithium public key", an HD path, or + /// "provided secret #N". + pub source: String, + /// The 32-byte wormhole secret that produced the match (needed to build + /// the ownership proof). None for Dilithium matches. + pub wormhole_secret: Option>, +} + +/// Determine which snapshot addresses belong to this wallet. +/// +/// `snapshot_addresses` are the SS58 addresses from `GET /snapshot` (or the +/// miner-rewards CSV). Dilithium matches are checked against +/// `dilithium_public_key` under every historical hash. Wormhole matches are +/// checked for HD-derived secrets (`m/44'/189189189'/0'/{0,1}'/{0..=16}'`) +/// when `mnemonic` is given, plus any `extra_wormhole_secrets` (32 bytes +/// each). +pub fn find_airdrop_matches( + snapshot_addresses: Vec, + dilithium_public_key: Option>, + mnemonic: Option, + extra_wormhole_secrets: Vec>, +) -> Result, String> { + let mut by_account: HashMap<[u8; 32], String> = HashMap::new(); + for address in snapshot_addresses { + let account: [u8; 32] = super::crypto::ss58_to_account_id(&address)? + .try_into() + .map_err(|_| format!("address {address} did not decode to 32 bytes"))?; + by_account.insert(account, address); + } + + let mut matches = Vec::new(); + + if let Some(public_key) = dilithium_public_key { + if !public_key.is_empty() { + for scheme in DILITHIUM_SCHEMES { + let account = derive_dilithium(scheme, &public_key); + if let Some(address) = by_account.get(&account) { + matches.push(AirdropMatch { + address: address.clone(), + kind: "dilithium".into(), + scheme: (*scheme).into(), + claimable: true, + source: "dilithium public key".into(), + wormhole_secret: None, + }); + } + } + } + } + + let mut secrets: Vec<([u8; 32], String)> = Vec::new(); + if let Some(mnemonic) = mnemonic { + for change in 0..=1u32 { + for index in 0..HD_SCAN_INDEXES { + let path = format!( + "m/44'/{}/0'/{}'/{}'", + QUANTUS_WORMHOLE_CHAIN_ID, change, index + ); + let pair = derive_wormhole_from_mnemonic(&mnemonic, None, &path) + .map_err(|e| format!("HD derivation failed at {path}: {e:?}"))?; + secrets.push((*pair.secret().as_bytes(), path)); + } + } + } + for (i, secret) in extra_wormhole_secrets.iter().enumerate() { + let secret: [u8; 32] = secret + .as_slice() + .try_into() + .map_err(|_| format!("wormhole secret #{i} must be 32 bytes"))?; + secrets.push((secret, format!("provided secret #{i}"))); + } + + let mut seen: Vec<([u8; 32], &'static str)> = Vec::new(); + for (secret, source) in &secrets { + for scheme in WORMHOLE_SCHEMES { + let account = scheme.derive(secret); + if let Some(address) = by_account.get(&account) { + if seen.contains(&(account, scheme.id)) { + continue; + } + seen.push((account, scheme.id)); + matches.push(AirdropMatch { + address: address.clone(), + kind: "wormhole".into(), + scheme: scheme.id.into(), + claimable: scheme.id == CLAIMABLE_WORMHOLE_SCHEME, + source: source.clone(), + wormhole_secret: Some(secret.to_vec()), + }); + } + } + } + + Ok(matches) +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::crypto::{AccountId32, Ss58Codec}; + + fn to_ss58(account: &[u8; 32]) -> String { + AccountId32::new(*account) + .to_ss58check_with_version(sp_core::crypto::Ss58AddressFormat::custom(189)) + } + + fn hex32(s: &str) -> [u8; 32] { + hex::decode(s).unwrap().try_into().unwrap() + } + + #[test] + fn current_wormhole_scheme_matches_hdwallet_golden() { + let secret = hex32("30051cfa3abd462d3bc26da2d660e90ba8af6080b7fe95d9fd3f3b37c7d9ce4b"); + let scheme = WORMHOLE_SCHEMES + .iter() + .find(|s| s.id == CLAIMABLE_WORMHOLE_SCHEME) + .unwrap(); + assert_eq!( + scheme.derive(&secret), + hex32("6a2f0d3abe4390e0b05f6dea4ba10670676cda7c00d49526ddde59f16c85269f") + ); + } + + #[test] + fn historical_wormhole_schemes_disagree() { + let secret = [9u8; 32]; + let addrs: Vec<_> = WORMHOLE_SCHEMES.iter().map(|s| s.derive(&secret)).collect(); + assert!(addrs.iter().any(|a| *a != addrs[0])); + } + + #[test] + fn dilithium_rate8_matches_current_hash_bytes() { + let pk = [7u8; 2592]; + assert_eq!( + derive_dilithium("dilithium-rate8-hash-bytes", &pk), + qp_poseidon_core::hash_bytes(&pk) + ); + } + + #[test] + fn matches_wormhole_secret_against_snapshot() { + let secret = [42u8; 32]; + let current = WORMHOLE_SCHEMES + .iter() + .find(|s| s.id == CLAIMABLE_WORMHOLE_SCHEME) + .unwrap() + .derive(&secret); + let snapshot = vec![to_ss58(¤t), to_ss58(&[1u8; 32])]; + + let matches = + find_airdrop_matches(snapshot.clone(), None, None, vec![secret.to_vec()]).unwrap(); + assert_eq!(matches.len(), 1); + assert_eq!(matches[0].address, snapshot[0]); + assert_eq!(matches[0].kind, "wormhole"); + assert_eq!(matches[0].scheme, CLAIMABLE_WORMHOLE_SCHEME); + assert!(matches[0].claimable); + assert_eq!(matches[0].wormhole_secret.as_deref(), Some(&secret[..])); + } + + #[test] + fn matches_dilithium_public_key_against_snapshot() { + let pk = vec![5u8; 2592]; + let dirac_era = derive_dilithium("dilithium-v10-padded", &pk); + let snapshot = vec![to_ss58(&dirac_era)]; + + let matches = find_airdrop_matches(snapshot.clone(), Some(pk), None, vec![]).unwrap(); + assert_eq!(matches.len(), 1); + assert_eq!(matches[0].address, snapshot[0]); + assert_eq!(matches[0].scheme, "dilithium-v10-padded"); + assert!(matches[0].claimable); + assert!(matches[0].wormhole_secret.is_none()); + } + + #[test] + fn rejects_non_quantus_addresses() { + // Prefix 42 (generic Substrate) must be rejected. + let generic = AccountId32::new([1u8; 32]) + .to_ss58check_with_version(sp_core::crypto::Ss58AddressFormat::custom(42)); + assert!(find_airdrop_matches(vec![generic], None, None, vec![]).is_err()); + } + + #[test] + fn hd_scan_finds_wallet_wormhole_address() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; + let path = format!("m/44'/{}/0'/0'/3'", QUANTUS_WORMHOLE_CHAIN_ID); + let pair = derive_wormhole_from_mnemonic(mnemonic, None, &path).unwrap(); + let current = WORMHOLE_SCHEMES + .iter() + .find(|s| s.id == CLAIMABLE_WORMHOLE_SCHEME) + .unwrap() + .derive(pair.secret().as_bytes()); + let snapshot = vec![to_ss58(¤t)]; + + let matches = + find_airdrop_matches(snapshot.clone(), None, Some(mnemonic.into()), vec![]).unwrap(); + assert_eq!(matches.len(), 1); + assert_eq!(matches[0].source, path); + assert!(matches[0].claimable); + } +} diff --git a/quantus_sdk/rust/src/api/mod.rs b/quantus_sdk/rust/src/api/mod.rs index a03eae7f8..47a4a587e 100644 --- a/quantus_sdk/rust/src/api/mod.rs +++ b/quantus_sdk/rust/src/api/mod.rs @@ -1,3 +1,4 @@ +pub mod airdrop; pub mod crypto; pub mod multisig; pub mod ur; diff --git a/quantus_sdk/rust/src/frb_generated.rs b/quantus_sdk/rust/src/frb_generated.rs index b09af1211..8ff4b863f 100644 --- a/quantus_sdk/rust/src/frb_generated.rs +++ b/quantus_sdk/rust/src/frb_generated.rs @@ -39,7 +39,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1684949517; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 231357703; // Section: executor @@ -512,6 +512,47 @@ fn wire__crate__api__wormhole__ensure_circuit_binaries_impl( }, ) } +fn wire__crate__api__airdrop__find_airdrop_matches_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "find_airdrop_matches", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_snapshot_addresses = >::sse_decode(&mut deserializer); + let api_dilithium_public_key = >>::sse_decode(&mut deserializer); + let api_mnemonic = >::sse_decode(&mut deserializer); + let api_extra_wormhole_secrets = >>::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, String>((move || { + let output_ok = crate::api::airdrop::find_airdrop_matches( + api_snapshot_addresses, + api_dilithium_public_key, + api_mnemonic, + api_extra_wormhole_secrets, + )?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__crypto__first_hash_to_address_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -1235,6 +1276,26 @@ impl SseDecode for String { } } +impl SseDecode for crate::api::airdrop::AirdropMatch { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_address = ::sse_decode(deserializer); + let mut var_kind = ::sse_decode(deserializer); + let mut var_scheme = ::sse_decode(deserializer); + let mut var_claimable = ::sse_decode(deserializer); + let mut var_source = ::sse_decode(deserializer); + let mut var_wormholeSecret = >>::sse_decode(deserializer); + return crate::api::airdrop::AirdropMatch { + address: var_address, + kind: var_kind, + scheme: var_scheme, + claimable: var_claimable, + source: var_source, + wormhole_secret: var_wormholeSecret, + }; + } +} + impl SseDecode for bool { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1287,6 +1348,20 @@ impl SseDecode for Vec { } } +impl SseDecode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = Vec::with_capacity(len_ as usize); + for idx_ in 0..len_ { + ans_.push(::sse_decode( + deserializer, + )); + } + return ans_; + } +} + impl SseDecode for Vec> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1323,6 +1398,17 @@ impl SseDecode for crate::api::wormhole::MerkleProcessed { } } +impl SseDecode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + impl SseDecode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1334,6 +1420,17 @@ impl SseDecode for Option { } } +impl SseDecode for Option> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(>::sse_decode(deserializer)); + } else { + return None; + } + } +} + impl SseDecode for Option<[u8; 32]> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1481,8 +1578,11 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 20 => wire__crate__api__wormhole__generate_proof_impl(port, ptr, rust_vec_len, data_len), - 21 => wire__crate__api__crypto__init_app_impl(port, ptr, rust_vec_len, data_len), + 16 => { + wire__crate__api__airdrop__find_airdrop_matches_impl(port, ptr, rust_vec_len, data_len) + } + 21 => wire__crate__api__wormhole__generate_proof_impl(port, ptr, rust_vec_len, data_len), + 22 => wire__crate__api__crypto__init_app_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -1510,33 +1610,33 @@ fn pde_ffi_dispatcher_sync_impl( 12 => wire__crate__api__ur__decode_ur_impl(ptr, rust_vec_len, data_len), 13 => wire__crate__api__crypto__derive_wormhole_impl(ptr, rust_vec_len, data_len), 14 => wire__crate__api__ur__encode_ur_impl(ptr, rust_vec_len, data_len), - 16 => wire__crate__api__crypto__first_hash_to_address_impl(ptr, rust_vec_len, data_len), - 17 => wire__crate__api__crypto__generate_derived_keypair_impl(ptr, rust_vec_len, data_len), - 18 => wire__crate__api__crypto__generate_keypair_impl(ptr, rust_vec_len, data_len), - 19 => { + 17 => wire__crate__api__crypto__first_hash_to_address_impl(ptr, rust_vec_len, data_len), + 18 => wire__crate__api__crypto__generate_derived_keypair_impl(ptr, rust_vec_len, data_len), + 19 => wire__crate__api__crypto__generate_keypair_impl(ptr, rust_vec_len, data_len), + 20 => { wire__crate__api__crypto__generate_keypair_from_seed_impl(ptr, rust_vec_len, data_len) } - 22 => wire__crate__api__ur__is_complete_ur_impl(ptr, rust_vec_len, data_len), - 23 => wire__crate__api__ur__max_ur_part_chars_impl(ptr, rust_vec_len, data_len), - 24 => wire__crate__api__ur__max_ur_parts_impl(ptr, rust_vec_len, data_len), - 25 => { + 23 => wire__crate__api__ur__is_complete_ur_impl(ptr, rust_vec_len, data_len), + 24 => wire__crate__api__ur__max_ur_part_chars_impl(ptr, rust_vec_len, data_len), + 25 => wire__crate__api__ur__max_ur_parts_impl(ptr, rust_vec_len, data_len), + 26 => { wire__crate__api__multisig__predict_multisig_address_impl(ptr, rust_vec_len, data_len) } - 26 => wire__crate__api__crypto__public_key_bytes_impl(ptr, rust_vec_len, data_len), - 27 => wire__crate__api__crypto__secret_key_bytes_impl(ptr, rust_vec_len, data_len), - 28 => wire__crate__api__crypto__set_default_ss58_prefix_impl(ptr, rust_vec_len, data_len), - 29 => wire__crate__api__crypto__sign_message_impl(ptr, rust_vec_len, data_len), - 30 => wire__crate__api__crypto__sign_message_with_pubkey_impl(ptr, rust_vec_len, data_len), - 31 => wire__crate__api__crypto__signature_bytes_impl(ptr, rust_vec_len, data_len), - 32 => wire__crate__api__crypto__ss58_to_account_id_impl(ptr, rust_vec_len, data_len), - 33 => wire__crate__api__crypto__to_account_id_impl(ptr, rust_vec_len, data_len), - 34 => wire__crate__api__crypto__verify_message_impl(ptr, rust_vec_len, data_len), - 35 => wire__crate__api__wormhole__wormhole_compute_output_amount_impl( + 27 => wire__crate__api__crypto__public_key_bytes_impl(ptr, rust_vec_len, data_len), + 28 => wire__crate__api__crypto__secret_key_bytes_impl(ptr, rust_vec_len, data_len), + 29 => wire__crate__api__crypto__set_default_ss58_prefix_impl(ptr, rust_vec_len, data_len), + 30 => wire__crate__api__crypto__sign_message_impl(ptr, rust_vec_len, data_len), + 31 => wire__crate__api__crypto__sign_message_with_pubkey_impl(ptr, rust_vec_len, data_len), + 32 => wire__crate__api__crypto__signature_bytes_impl(ptr, rust_vec_len, data_len), + 33 => wire__crate__api__crypto__ss58_to_account_id_impl(ptr, rust_vec_len, data_len), + 34 => wire__crate__api__crypto__to_account_id_impl(ptr, rust_vec_len, data_len), + 35 => wire__crate__api__crypto__verify_message_impl(ptr, rust_vec_len, data_len), + 36 => wire__crate__api__wormhole__wormhole_compute_output_amount_impl( ptr, rust_vec_len, data_len, ), - 36 => wire__crate__api__wormhole__zk_circuits_version_impl(ptr, rust_vec_len, data_len), + 37 => wire__crate__api__wormhole__zk_circuits_version_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -1558,6 +1658,31 @@ impl flutter_rust_bridge::IntoIntoDart> for HDLattice } } +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::airdrop::AirdropMatch { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.address.into_into_dart().into_dart(), + self.kind.into_into_dart().into_dart(), + self.scheme.into_into_dart().into_dart(), + self.claimable.into_into_dart().into_dart(), + self.source.into_into_dart().into_dart(), + self.wormhole_secret.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::api::airdrop::AirdropMatch +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::api::airdrop::AirdropMatch +{ + fn into_into_dart(self) -> crate::api::airdrop::AirdropMatch { + self + } +} // Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::api::crypto::DilithiumScheme { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { @@ -1726,6 +1851,18 @@ impl SseEncode for String { } } +impl SseEncode for crate::api::airdrop::AirdropMatch { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.address, serializer); + ::sse_encode(self.kind, serializer); + ::sse_encode(self.scheme, serializer); + ::sse_encode(self.claimable, serializer); + ::sse_encode(self.source, serializer); + >>::sse_encode(self.wormhole_secret, serializer); + } +} + impl SseEncode for bool { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1775,6 +1912,16 @@ impl SseEncode for Vec { } } +impl SseEncode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + impl SseEncode for Vec> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1803,6 +1950,16 @@ impl SseEncode for crate::api::wormhole::MerkleProcessed { } } +impl SseEncode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + impl SseEncode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1813,6 +1970,16 @@ impl SseEncode for Option { } } +impl SseEncode for Option> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + >::sse_encode(value, serializer); + } + } +} + impl SseEncode for Option<[u8; 32]> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { From c9d9ed3751c124e538abb161aed68e2bb18b5ddc Mon Sep 17 00:00:00 2001 From: illuzen Date: Sat, 12 Sep 2026 16:23:48 +0800 Subject: [PATCH 02/10] Build airdrop claim proofs in the SDK. build_airdrop_dilithium_claim signs address || claim_account || expiry under the qp-airdrop-claim-v1 context; prove_airdrop_wormhole generates the rate-8 ownership proof in-process. Ownership crates come from the qp-zk-circuits v4.4.0 tag so the existing wormhole 4.3.0 stack is unchanged. Co-authored-by: Cursor --- quantus_sdk/lib/src/rust/api/airdrop.dart | 89 +++++- quantus_sdk/lib/src/rust/frb_generated.dart | 258 ++++++++++++++--- .../lib/src/rust/frb_generated.io.dart | 37 +++ .../lib/src/rust/frb_generated.web.dart | 37 +++ quantus_sdk/rust/Cargo.lock | 96 +++++- quantus_sdk/rust/Cargo.toml | 3 + quantus_sdk/rust/src/api/airdrop.rs | 165 +++++++++++ quantus_sdk/rust/src/frb_generated.rs | 274 +++++++++++++++--- 8 files changed, 870 insertions(+), 89 deletions(-) diff --git a/quantus_sdk/lib/src/rust/api/airdrop.dart b/quantus_sdk/lib/src/rust/api/airdrop.dart index 093e915fd..f376fb16b 100644 --- a/quantus_sdk/lib/src/rust/api/airdrop.dart +++ b/quantus_sdk/lib/src/rust/api/airdrop.dart @@ -4,11 +4,12 @@ // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import import '../frb_generated.dart'; +import 'crypto.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; -// These functions are ignored because they are not marked as `pub`: `compact8_decode`, `derive_dilithium`, `derive`, `hash_felts_rate4_pad10`, `hash_felts`, `hash_no_pad_v09`, `hash_padded_v09`, `hash_padded_v10`, `injective4`, `rehash` +// These functions are ignored because they are not marked as `pub`: `compact8_decode`, `decode_account`, `derive_dilithium`, `derive`, `hash_felts_rate4_pad10`, `hash_felts`, `hash_no_pad_v09`, `hash_padded_v09`, `hash_padded_v10`, `injective4`, `now_unix`, `rehash` // These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `SecretEncoding`, `Sponge`, `WormholeSchemeDef` -// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `eq`, `fmt` +// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `eq`, `fmt`, `fmt` /// Determine which snapshot addresses belong to this wallet. /// @@ -30,6 +31,31 @@ Future> findAirdropMatches({ extraWormholeSecrets: extraWormholeSecrets, ); +/// Sign an airdrop claim for a Dilithium-era snapshot `address`, paying out to +/// `claim_account`. The wallet must be ML-DSA-87 (miner keys are). Submit the +/// result as `{"kind": "dilithium", ...fields}` within ~10 minutes. +Future buildAirdropDilithiumClaim({ + required Keypair keypair, + required String address, + required String claimAccount, +}) => RustLib.instance.api.crateApiAirdropBuildAirdropDilithiumClaim( + keypair: keypair, + address: address, + claimAccount: claimAccount, +); + +/// Prove ownership of the wormhole address derived from `wormhole_secret` +/// (current rate-8 scheme), binding the payout to `claim_account`. Builds the +/// circuit in-process: expect tens of seconds of CPU on first call. Submit as +/// `{"kind": "wormhole", ...fields}`. +Future proveAirdropWormhole({ + required List wormholeSecret, + required String claimAccount, +}) => RustLib.instance.api.crateApiAirdropProveAirdropWormhole( + wormholeSecret: wormholeSecret, + claimAccount: claimAccount, +); + /// A snapshot address this wallet can prove ownership of. class AirdropMatch { /// The address exactly as it appeared in `snapshot_addresses`. @@ -82,3 +108,62 @@ class AirdropMatch { source == other.source && wormholeSecret == other.wormholeSecret; } + +/// The `POST /claim` body fields for a Dilithium claim. +class DilithiumClaimBody { + final String scheme; + final String address; + final String claimAccount; + final String publicKeyHex; + final String signatureHex; + final PlatformInt64 expiryUnix; + + const DilithiumClaimBody({ + required this.scheme, + required this.address, + required this.claimAccount, + required this.publicKeyHex, + required this.signatureHex, + required this.expiryUnix, + }); + + @override + int get hashCode => + scheme.hashCode ^ + address.hashCode ^ + claimAccount.hashCode ^ + publicKeyHex.hashCode ^ + signatureHex.hashCode ^ + expiryUnix.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is DilithiumClaimBody && + runtimeType == other.runtimeType && + scheme == other.scheme && + address == other.address && + claimAccount == other.claimAccount && + publicKeyHex == other.publicKeyHex && + signatureHex == other.signatureHex && + expiryUnix == other.expiryUnix; +} + +/// The `POST /claim` body fields for a wormhole claim. +class WormholeClaimBody { + final String proofKind; + final String proofHex; + + const WormholeClaimBody({required this.proofKind, required this.proofHex}); + + @override + int get hashCode => proofKind.hashCode ^ proofHex.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is WormholeClaimBody && + runtimeType == other.runtimeType && + proofKind == other.proofKind && + proofHex == other.proofHex; +} diff --git a/quantus_sdk/lib/src/rust/frb_generated.dart b/quantus_sdk/lib/src/rust/frb_generated.dart index 345f07aef..a921216eb 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.dart @@ -70,7 +70,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.12.0'; @override - int get rustContentHash => 231357703; + int get rustContentHash => 272316986; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -87,6 +87,12 @@ abstract class RustLibApi extends BaseApi { required String binsDir, }); + Future crateApiAirdropBuildAirdropDilithiumClaim({ + required Keypair keypair, + required String address, + required String claimAccount, + }); + String crateApiWormholeComputeAddressHashHex({required List rawAddress}); MerkleProcessed crateApiWormholeComputeMerklePositions({ @@ -169,6 +175,11 @@ abstract class RustLibApi extends BaseApi { required BigInt nonce, }); + Future crateApiAirdropProveAirdropWormhole({ + required List wormholeSecret, + required String claimAccount, + }); + int crateApiCryptoPublicKeyBytes({required DilithiumScheme scheme}); int crateApiCryptoSecretKeyBytes({required DilithiumScheme scheme}); @@ -262,6 +273,43 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ['proofBytesList', 'binsDir'], ); + @override + Future crateApiAirdropBuildAirdropDilithiumClaim({ + required Keypair keypair, + required String address, + required String claimAccount, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_keypair(keypair, serializer); + sse_encode_String(address, serializer); + sse_encode_String(claimAccount, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 2, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_dilithium_claim_body, + decodeErrorData: sse_decode_String, + ), + constMeta: kCrateApiAirdropBuildAirdropDilithiumClaimConstMeta, + argValues: [keypair, address, claimAccount], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiAirdropBuildAirdropDilithiumClaimConstMeta => + const TaskConstMeta( + debugName: 'build_airdrop_dilithium_claim', + argNames: ['keypair', 'address', 'claimAccount'], + ); + @override String crateApiWormholeComputeAddressHashHex({ required List rawAddress, @@ -271,7 +319,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(rawAddress, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 2)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -303,7 +351,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(unsortedSiblingsFlat, serializer); sse_encode_list_prim_u_8_loose(leafHash, serializer); sse_encode_u_32(depth, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 4)!; }, codec: SseCodec( decodeSuccessData: sse_decode_merkle_processed, @@ -333,7 +381,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(secret, serializer); sse_encode_u_64(transferCount, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 4)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_u_8_strict, @@ -361,7 +409,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(secret, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_u_8_strict, @@ -386,7 +434,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -408,7 +456,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -430,7 +478,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -453,7 +501,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(leafData, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; }, codec: SseCodec( decodeSuccessData: sse_decode_u_32, @@ -479,7 +527,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(leafData, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_u_8_strict, @@ -507,7 +555,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(leafData, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; }, codec: SseCodec( decodeSuccessData: sse_decode_u_64, @@ -533,7 +581,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_String(urParts, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13)!; }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_u_8_strict, @@ -560,7 +608,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(mnemonicStr, serializer); sse_encode_String(path, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; }, codec: SseCodec( decodeSuccessData: sse_decode_wormhole_result, @@ -591,7 +639,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(data, serializer); sse_encode_opt_box_autoadd_u_32(maxFragmentLength, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15)!; }, codec: SseCodec( decodeSuccessData: sse_decode_list_String, @@ -621,7 +669,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 15, + funcId: 16, port: port_, ); }, @@ -663,7 +711,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 16, + funcId: 17, port: port_, ); }, @@ -701,7 +749,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(firstHashHex, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -733,7 +781,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(mnemonicStr, serializer); sse_encode_String(path, serializer); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -760,7 +808,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(mnemonicStr, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -787,7 +835,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(seed, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 21)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -822,7 +870,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 21, + funcId: 22, port: port_, ); }, @@ -852,7 +900,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 22, + funcId: 23, port: port_, ); }, @@ -877,7 +925,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_String(urParts, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24)!; }, codec: SseCodec( decodeSuccessData: sse_decode_bool, @@ -899,7 +947,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25)!; }, codec: SseCodec( decodeSuccessData: sse_decode_u_32, @@ -921,7 +969,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; }, codec: SseCodec( decodeSuccessData: sse_decode_u_32, @@ -950,7 +998,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(signers, serializer); sse_encode_u_32(threshold, serializer); sse_encode_u_64(nonce, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -969,6 +1017,41 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ['signers', 'threshold', 'nonce'], ); + @override + Future crateApiAirdropProveAirdropWormhole({ + required List wormholeSecret, + required String claimAccount, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_list_prim_u_8_loose(wormholeSecret, serializer); + sse_encode_String(claimAccount, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 28, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_wormhole_claim_body, + decodeErrorData: sse_decode_String, + ), + constMeta: kCrateApiAirdropProveAirdropWormholeConstMeta, + argValues: [wormholeSecret, claimAccount], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiAirdropProveAirdropWormholeConstMeta => + const TaskConstMeta( + debugName: 'prove_airdrop_wormhole', + argNames: ['wormholeSecret', 'claimAccount'], + ); + @override int crateApiCryptoPublicKeyBytes({required DilithiumScheme scheme}) { return handler.executeSync( @@ -976,7 +1059,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29)!; }, codec: SseCodec( decodeSuccessData: sse_decode_u_32, @@ -999,7 +1082,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30)!; }, codec: SseCodec( decodeSuccessData: sse_decode_u_32, @@ -1022,7 +1105,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_u_16(prefix, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -1056,7 +1139,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_opt_u_8_array_32(entropy, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_u_8_strict, @@ -1089,7 +1172,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_opt_u_8_array_32(entropy, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_u_8_strict, @@ -1115,7 +1198,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; }, codec: SseCodec( decodeSuccessData: sse_decode_u_32, @@ -1138,7 +1221,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(s, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35)!; }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_u_8_strict, @@ -1161,7 +1244,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_box_autoadd_keypair(obj, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -1192,7 +1275,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_list_prim_u_8_loose(signature, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!; }, codec: SseCodec( decodeSuccessData: sse_decode_bool, @@ -1222,7 +1305,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_u_32(inputAmount, serializer); sse_encode_u_32(feeBps, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 38)!; }, codec: SseCodec( decodeSuccessData: sse_decode_u_32, @@ -1247,7 +1330,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 39)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -1335,6 +1418,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as int; } + @protected + DilithiumClaimBody dco_decode_dilithium_claim_body(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 6) + throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + return DilithiumClaimBody( + scheme: dco_decode_String(arr[0]), + address: dco_decode_String(arr[1]), + claimAccount: dco_decode_String(arr[2]), + publicKeyHex: dco_decode_String(arr[3]), + signatureHex: dco_decode_String(arr[4]), + expiryUnix: dco_decode_i_64(arr[5]), + ); + } + @protected DilithiumScheme dco_decode_dilithium_scheme(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1347,6 +1446,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as int; } + @protected + PlatformInt64 dco_decode_i_64(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dcoDecodeI64(raw); + } + @protected Keypair dco_decode_keypair(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1509,6 +1614,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dcoDecodeU64(raw); } + @protected + WormholeClaimBody dco_decode_wormhole_claim_body(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return WormholeClaimBody( + proofKind: dco_decode_String(arr[0]), + proofHex: dco_decode_String(arr[1]), + ); + } + @protected WormholeResult dco_decode_wormhole_result(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1596,6 +1713,27 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (sse_decode_u_32(deserializer)); } + @protected + DilithiumClaimBody sse_decode_dilithium_claim_body( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_scheme = sse_decode_String(deserializer); + var var_address = sse_decode_String(deserializer); + var var_claimAccount = sse_decode_String(deserializer); + var var_publicKeyHex = sse_decode_String(deserializer); + var var_signatureHex = sse_decode_String(deserializer); + var var_expiryUnix = sse_decode_i_64(deserializer); + return DilithiumClaimBody( + scheme: var_scheme, + address: var_address, + claimAccount: var_claimAccount, + publicKeyHex: var_publicKeyHex, + signatureHex: var_signatureHex, + expiryUnix: var_expiryUnix, + ); + } + @protected DilithiumScheme sse_decode_dilithium_scheme(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1609,6 +1747,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getInt32(); } + @protected + PlatformInt64 sse_decode_i_64(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getPlatformInt64(); + } + @protected Keypair sse_decode_keypair(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1826,6 +1970,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getBigUint64(); } + @protected + WormholeClaimBody sse_decode_wormhole_claim_body( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_proofKind = sse_decode_String(deserializer); + var var_proofHex = sse_decode_String(deserializer); + return WormholeClaimBody(proofKind: var_proofKind, proofHex: var_proofHex); + } + @protected WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1909,6 +2063,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(self, serializer); } + @protected + void sse_encode_dilithium_claim_body( + DilithiumClaimBody self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.scheme, serializer); + sse_encode_String(self.address, serializer); + sse_encode_String(self.claimAccount, serializer); + sse_encode_String(self.publicKeyHex, serializer); + sse_encode_String(self.signatureHex, serializer); + sse_encode_i_64(self.expiryUnix, serializer); + } + @protected void sse_encode_dilithium_scheme( DilithiumScheme self, @@ -1924,6 +2092,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putInt32(self); } + @protected + void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putPlatformInt64(self); + } + @protected void sse_encode_keypair(Keypair self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -2112,6 +2286,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putBigUint64(self); } + @protected + void sse_encode_wormhole_claim_body( + WormholeClaimBody self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.proofKind, serializer); + sse_encode_String(self.proofHex, serializer); + } + @protected void sse_encode_wormhole_result( WormholeResult self, diff --git a/quantus_sdk/lib/src/rust/frb_generated.io.dart b/quantus_sdk/lib/src/rust/frb_generated.io.dart index d02fb2049..2d9794ca9 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.io.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.io.dart @@ -56,12 +56,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected int dco_decode_box_autoadd_u_32(dynamic raw); + @protected + DilithiumClaimBody dco_decode_dilithium_claim_body(dynamic raw); + @protected DilithiumScheme dco_decode_dilithium_scheme(dynamic raw); @protected int dco_decode_i_32(dynamic raw); + @protected + PlatformInt64 dco_decode_i_64(dynamic raw); + @protected Keypair dco_decode_keypair(dynamic raw); @@ -122,6 +128,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt dco_decode_usize(dynamic raw); + @protected + WormholeClaimBody dco_decode_wormhole_claim_body(dynamic raw); + @protected WormholeResult dco_decode_wormhole_result(dynamic raw); @@ -155,12 +164,20 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected int sse_decode_box_autoadd_u_32(SseDeserializer deserializer); + @protected + DilithiumClaimBody sse_decode_dilithium_claim_body( + SseDeserializer deserializer, + ); + @protected DilithiumScheme sse_decode_dilithium_scheme(SseDeserializer deserializer); @protected int sse_decode_i_32(SseDeserializer deserializer); + @protected + PlatformInt64 sse_decode_i_64(SseDeserializer deserializer); + @protected Keypair sse_decode_keypair(SseDeserializer deserializer); @@ -225,6 +242,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt sse_decode_usize(SseDeserializer deserializer); + @protected + WormholeClaimBody sse_decode_wormhole_claim_body( + SseDeserializer deserializer, + ); + @protected WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @@ -263,6 +285,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); + @protected + void sse_encode_dilithium_claim_body( + DilithiumClaimBody self, + SseSerializer serializer, + ); + @protected void sse_encode_dilithium_scheme( DilithiumScheme self, @@ -272,6 +300,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_i_32(int self, SseSerializer serializer); + @protected + void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer); + @protected void sse_encode_keypair(Keypair self, SseSerializer serializer); @@ -347,6 +378,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_usize(BigInt self, SseSerializer serializer); + @protected + void sse_encode_wormhole_claim_body( + WormholeClaimBody self, + SseSerializer serializer, + ); + @protected void sse_encode_wormhole_result( WormholeResult self, diff --git a/quantus_sdk/lib/src/rust/frb_generated.web.dart b/quantus_sdk/lib/src/rust/frb_generated.web.dart index 952d92fb2..eb7d47ef3 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.web.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.web.dart @@ -58,12 +58,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected int dco_decode_box_autoadd_u_32(dynamic raw); + @protected + DilithiumClaimBody dco_decode_dilithium_claim_body(dynamic raw); + @protected DilithiumScheme dco_decode_dilithium_scheme(dynamic raw); @protected int dco_decode_i_32(dynamic raw); + @protected + PlatformInt64 dco_decode_i_64(dynamic raw); + @protected Keypair dco_decode_keypair(dynamic raw); @@ -124,6 +130,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt dco_decode_usize(dynamic raw); + @protected + WormholeClaimBody dco_decode_wormhole_claim_body(dynamic raw); + @protected WormholeResult dco_decode_wormhole_result(dynamic raw); @@ -157,12 +166,20 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected int sse_decode_box_autoadd_u_32(SseDeserializer deserializer); + @protected + DilithiumClaimBody sse_decode_dilithium_claim_body( + SseDeserializer deserializer, + ); + @protected DilithiumScheme sse_decode_dilithium_scheme(SseDeserializer deserializer); @protected int sse_decode_i_32(SseDeserializer deserializer); + @protected + PlatformInt64 sse_decode_i_64(SseDeserializer deserializer); + @protected Keypair sse_decode_keypair(SseDeserializer deserializer); @@ -227,6 +244,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt sse_decode_usize(SseDeserializer deserializer); + @protected + WormholeClaimBody sse_decode_wormhole_claim_body( + SseDeserializer deserializer, + ); + @protected WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @@ -265,6 +287,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); + @protected + void sse_encode_dilithium_claim_body( + DilithiumClaimBody self, + SseSerializer serializer, + ); + @protected void sse_encode_dilithium_scheme( DilithiumScheme self, @@ -274,6 +302,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_i_32(int self, SseSerializer serializer); + @protected + void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer); + @protected void sse_encode_keypair(Keypair self, SseSerializer serializer); @@ -349,6 +380,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_usize(BigInt self, SseSerializer serializer); + @protected + void sse_encode_wormhole_claim_body( + WormholeClaimBody self, + SseSerializer serializer, + ); + @protected void sse_encode_wormhole_result( WormholeResult self, diff --git a/quantus_sdk/rust/Cargo.lock b/quantus_sdk/rust/Cargo.lock index 7f893213c..fe166980b 100644 --- a/quantus_sdk/rust/Cargo.lock +++ b/quantus_sdk/rust/Cargo.lock @@ -2736,6 +2736,39 @@ dependencies = [ "prost", ] +[[package]] +name = "qp-ownership-circuit" +version = "4.4.0" +source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +dependencies = [ + "anyhow", + "qp-ownership-inputs", + "qp-plonky2", + "qp-wormhole-circuit 4.4.0", + "qp-zk-circuits-common 4.4.0", +] + +[[package]] +name = "qp-ownership-inputs" +version = "4.4.0" +source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +dependencies = [ + "anyhow", + "qp-wormhole-inputs 4.4.0", +] + +[[package]] +name = "qp-ownership-prover" +version = "4.4.0" +source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +dependencies = [ + "anyhow", + "qp-ownership-circuit", + "qp-plonky2", + "qp-wormhole-circuit 4.4.0", + "qp-zk-circuits-common 4.4.0", +] + [[package]] name = "qp-plonky2" version = "1.5.5" @@ -2870,10 +2903,10 @@ dependencies = [ "anyhow", "hex", "qp-plonky2", - "qp-wormhole-circuit", - "qp-wormhole-inputs", + "qp-wormhole-circuit 4.3.0", + "qp-wormhole-inputs 4.3.0", "qp-wormhole-prover", - "qp-zk-circuits-common", + "qp-zk-circuits-common 4.3.0", "rand 0.8.6", "serde", "serde_json", @@ -2888,8 +2921,21 @@ dependencies = [ "anyhow", "hex", "qp-plonky2", - "qp-wormhole-inputs", - "qp-zk-circuits-common", + "qp-wormhole-inputs 4.3.0", + "qp-zk-circuits-common 4.3.0", + "zeroize", +] + +[[package]] +name = "qp-wormhole-circuit" +version = "4.4.0" +source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +dependencies = [ + "anyhow", + "hex", + "qp-plonky2", + "qp-wormhole-inputs 4.4.0", + "qp-zk-circuits-common 4.4.0", "zeroize", ] @@ -2903,8 +2949,8 @@ dependencies = [ "clap", "qp-plonky2", "qp-wormhole-aggregator", - "qp-wormhole-circuit", - "qp-zk-circuits-common", + "qp-wormhole-circuit 4.3.0", + "qp-zk-circuits-common 4.3.0", "rand 0.8.6", ] @@ -2917,6 +2963,14 @@ dependencies = [ "anyhow", ] +[[package]] +name = "qp-wormhole-inputs" +version = "4.4.0" +source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +dependencies = [ + "anyhow", +] + [[package]] name = "qp-wormhole-prover" version = "4.3.0" @@ -2925,8 +2979,8 @@ checksum = "7203e454113812e1ca85aee7687dc5f270f7c297d113a647a3a00b6d43c5a4f6" dependencies = [ "anyhow", "qp-plonky2", - "qp-wormhole-circuit", - "qp-zk-circuits-common", + "qp-wormhole-circuit 4.3.0", + "qp-zk-circuits-common 4.3.0", ] [[package]] @@ -2938,7 +2992,21 @@ dependencies = [ "anyhow", "qp-plonky2", "qp-poseidon-core", - "qp-wormhole-inputs", + "qp-wormhole-inputs 4.3.0", + "rand 0.8.6", + "serde", + "serde_json", +] + +[[package]] +name = "qp-zk-circuits-common" +version = "4.4.0" +source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +dependencies = [ + "anyhow", + "qp-plonky2", + "qp-poseidon-core", + "qp-wormhole-inputs 4.4.0", "rand 0.8.6", "serde", "serde_json", @@ -3141,16 +3209,18 @@ dependencies = [ "flutter_rust_bridge", "hex", "parity-scale-codec", + "qp-ownership-circuit", + "qp-ownership-prover", "qp-plonky2", "qp-poseidon-core", "qp-rusty-crystals-dilithium", "qp-rusty-crystals-hdwallet", "qp-wormhole-aggregator", - "qp-wormhole-circuit", + "qp-wormhole-circuit 4.3.0", "qp-wormhole-circuit-builder", - "qp-wormhole-inputs", + "qp-wormhole-inputs 4.3.0", "qp-wormhole-prover", - "qp-zk-circuits-common", + "qp-zk-circuits-common 4.3.0", "quantus_ur", "sp-core", "sp-runtime", diff --git a/quantus_sdk/rust/Cargo.toml b/quantus_sdk/rust/Cargo.toml index d661db037..ed88ea052 100644 --- a/quantus_sdk/rust/Cargo.toml +++ b/quantus_sdk/rust/Cargo.toml @@ -22,6 +22,9 @@ quantus_ur = { git = "https://github.com/Quantus-Network/quantus_ur.git", tag = # ZK proof generation (aligned with quantus-cli / chain) anyhow = "1.0" blake3 = "1.8" +# Airdrop ownership proofs. Git source so the wormhole 4.3.0 crates below stay untouched. +qp-ownership-circuit = { version = "4.4.0", git = "https://github.com/Quantus-Network/qp-zk-circuits", tag = "v4.4.0", default-features = false, features = ["std"] } +qp-ownership-prover = { version = "4.4.0", git = "https://github.com/Quantus-Network/qp-zk-circuits", tag = "v4.4.0", default-features = false, features = ["std"] } qp-plonky2 = { version = "1.5.5", default-features = false, features = ["rand", "std"] } qp-wormhole-circuit = { version = "4.3.0", default-features = false, features = ["std"] } qp-wormhole-prover = { version = "4.3.0", default-features = false, features = ["std"] } diff --git a/quantus_sdk/rust/src/api/airdrop.rs b/quantus_sdk/rust/src/api/airdrop.rs index 1844054a1..0a83e3621 100644 --- a/quantus_sdk/rust/src/api/airdrop.rs +++ b/quantus_sdk/rust/src/api/airdrop.rs @@ -9,9 +9,11 @@ //! - late Schrödinger / Dirac: rate 4, pad10 //! - late Planck / current: rate 8, pad10 +use qp_ownership_circuit::{BytesDigest, CircuitInputs, Secret}; use qp_poseidon_core::{ serialization::digest_to_bytes, Goldilocks, Poseidon2, POSEIDON2_OUTPUT, SPONGE_WIDTH, }; +use qp_rusty_crystals_dilithium::ml_dsa_87; use qp_rusty_crystals_hdwallet::{derive_wormhole_from_mnemonic, QUANTUS_WORMHOLE_CHAIN_ID}; use std::collections::HashMap; @@ -20,6 +22,10 @@ const WORMHOLE_SALT: &[u8] = b"wormhole"; /// Only proofs for the current wormhole scheme are accepted by the claim server. const CLAIMABLE_WORMHOLE_SCHEME: &str = "wormhole-rate8-compact"; const HD_SCAN_INDEXES: u32 = 17; +/// FIPS 204 context for airdrop claim signatures; must match the claim server. +const CLAIM_CONTEXT: &[u8] = b"qp-airdrop-claim-v1"; +/// Claim expiry horizon. The server rejects expiries more than 15 min out. +const CLAIM_TTL_SECS: i64 = 10 * 60; // --------------------------------------------------------------------------- // Historical Poseidon2 sponges @@ -358,6 +364,109 @@ pub fn find_airdrop_matches( Ok(matches) } +fn decode_account(label: &str, address: &str) -> Result<[u8; 32], String> { + super::crypto::ss58_to_account_id(address)? + .try_into() + .map_err(|_| format!("{label} {address} did not decode to 32 bytes")) +} + +fn now_unix() -> Result { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs() as i64) + .map_err(|e| format!("system clock before epoch: {e}")) +} + +/// The `POST /claim` body fields for a Dilithium claim. +#[derive(Debug)] +#[flutter_rust_bridge::frb(sync)] +pub struct DilithiumClaimBody { + pub scheme: String, + pub address: String, + pub claim_account: String, + pub public_key_hex: String, + pub signature_hex: String, + pub expiry_unix: i64, +} + +/// Sign an airdrop claim for a Dilithium-era snapshot `address`, paying out to +/// `claim_account`. The wallet must be ML-DSA-87 (miner keys are). Submit the +/// result as `{"kind": "dilithium", ...fields}` within ~10 minutes. +pub fn build_airdrop_dilithium_claim( + keypair: &super::crypto::Keypair, + address: String, + claim_account: String, +) -> Result { + if keypair.scheme != super::crypto::DilithiumScheme::MlDsa87 { + return Err("airdrop claims require an ML-DSA-87 keypair".into()); + } + let address_bytes = decode_account("address", &address)?; + let claim_bytes = decode_account("claim account", &claim_account)?; + + let scheme = DILITHIUM_SCHEMES + .iter() + .find(|s| derive_dilithium(s, &keypair.public_key) == address_bytes) + .ok_or("public key does not derive this address under any known Dilithium scheme")?; + + let expiry_unix = now_unix()?.saturating_add(CLAIM_TTL_SECS); + let mut msg = [0u8; 72]; + msg[..32].copy_from_slice(&address_bytes); + msg[32..64].copy_from_slice(&claim_bytes); + msg[64..].copy_from_slice(&expiry_unix.to_be_bytes()); + + let secret = ml_dsa_87::SecretKey::from_bytes(&keypair.secret_key) + .map_err(|_| "invalid ML-DSA-87 secret key")?; + let signature = secret + .sign(&msg, Some(CLAIM_CONTEXT), None) + .map_err(|e| format!("ML-DSA sign failed: {e}"))?; + + Ok(DilithiumClaimBody { + scheme: (*scheme).into(), + address, + claim_account, + public_key_hex: hex::encode(&keypair.public_key), + signature_hex: hex::encode(signature), + expiry_unix, + }) +} + +/// The `POST /claim` body fields for a wormhole claim. +#[flutter_rust_bridge::frb(sync)] +pub struct WormholeClaimBody { + pub proof_kind: String, + pub proof_hex: String, +} + +/// Prove ownership of the wormhole address derived from `wormhole_secret` +/// (current rate-8 scheme), binding the payout to `claim_account`. Builds the +/// circuit in-process: expect tens of seconds of CPU on first call. Submit as +/// `{"kind": "wormhole", ...fields}`. +pub fn prove_airdrop_wormhole( + wormhole_secret: Vec, + claim_account: String, +) -> Result { + let secret_bytes: [u8; 32] = wormhole_secret + .try_into() + .map_err(|_| "wormhole secret must be 32 bytes")?; + let secret = + Secret::try_from(secret_bytes).map_err(|e| format!("invalid wormhole secret: {e:?}"))?; + let claim_bytes = decode_account("claim account", &claim_account)?; + let claim = BytesDigest::try_from(claim_bytes.as_slice()) + .map_err(|e| format!("invalid claim account: {e:?}"))?; + + let inputs = CircuitInputs::from_secret(secret, claim); + let proof = qp_ownership_prover::build_fresh() + .commit(&inputs) + .map_err(|e| format!("ownership prover commit failed: {e}"))? + .prove() + .map_err(|e| format!("ownership proof failed: {e}"))?; + + Ok(WormholeClaimBody { + proof_kind: "wormhole_rate8".into(), + proof_hex: hex::encode(proof.to_bytes()), + }) +} + #[cfg(test)] mod tests { use super::*; @@ -443,6 +552,62 @@ mod tests { assert!(find_airdrop_matches(vec![generic], None, None, vec![]).is_err()); } + #[test] + fn dilithium_claim_signature_verifies_with_context() { + let mut seed = [7u8; 32]; + let keys = ml_dsa_87::Keypair::generate( + &mut qp_rusty_crystals_dilithium::SensitiveBytes32::new(&mut seed), + ); + let keypair = super::super::crypto::Keypair { + public_key: keys.public().to_bytes().to_vec(), + secret_key: keys.secret().to_bytes().to_vec(), + scheme: super::super::crypto::DilithiumScheme::MlDsa87, + }; + let address_bytes = derive_dilithium("dilithium-v10-padded", &keypair.public_key); + let claim_bytes = [9u8; 32]; + + let body = build_airdrop_dilithium_claim( + &keypair, + to_ss58(&address_bytes), + to_ss58(&claim_bytes), + ) + .unwrap(); + assert_eq!(body.scheme, "dilithium-v10-padded"); + + let mut msg = [0u8; 72]; + msg[..32].copy_from_slice(&address_bytes); + msg[32..64].copy_from_slice(&claim_bytes); + msg[64..].copy_from_slice(&body.expiry_unix.to_be_bytes()); + let sig = hex::decode(&body.signature_hex).unwrap(); + assert!(keys.public().verify(&msg, &sig, Some(CLAIM_CONTEXT))); + // Context is part of the signature: verification without it must fail. + assert!(!keys.public().verify(&msg, &sig, None)); + } + + #[test] + fn dilithium_claim_rejects_foreign_address() { + let mut seed = [8u8; 32]; + let keys = ml_dsa_87::Keypair::generate( + &mut qp_rusty_crystals_dilithium::SensitiveBytes32::new(&mut seed), + ); + let keypair = super::super::crypto::Keypair { + public_key: keys.public().to_bytes().to_vec(), + secret_key: keys.secret().to_bytes().to_vec(), + scheme: super::super::crypto::DilithiumScheme::MlDsa87, + }; + let err = build_airdrop_dilithium_claim(&keypair, to_ss58(&[3u8; 32]), to_ss58(&[9u8; 32])) + .unwrap_err(); + assert!(err.contains("does not derive")); + } + + #[test] + #[ignore = "builds the ownership circuit; run with --release"] + fn wormhole_proof_generates() { + let body = prove_airdrop_wormhole([42u8; 32].to_vec(), to_ss58(&[9u8; 32])).unwrap(); + assert_eq!(body.proof_kind, "wormhole_rate8"); + assert!(!body.proof_hex.is_empty()); + } + #[test] fn hd_scan_finds_wallet_wormhole_address() { let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; diff --git a/quantus_sdk/rust/src/frb_generated.rs b/quantus_sdk/rust/src/frb_generated.rs index 8ff4b863f..542f0484d 100644 --- a/quantus_sdk/rust/src/frb_generated.rs +++ b/quantus_sdk/rust/src/frb_generated.rs @@ -39,7 +39,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 231357703; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 272316986; // Section: executor @@ -82,6 +82,45 @@ fn wire__crate__api__wormhole__aggregate_proofs_impl( }, ) } +fn wire__crate__api__airdrop__build_airdrop_dilithium_claim_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "build_airdrop_dilithium_claim", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_keypair = ::sse_decode(&mut deserializer); + let api_address = ::sse_decode(&mut deserializer); + let api_claim_account = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, String>((move || { + let output_ok = crate::api::airdrop::build_airdrop_dilithium_claim( + &api_keypair, + api_address, + api_claim_account, + )?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__wormhole__compute_address_hash_hex_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -877,6 +916,43 @@ fn wire__crate__api__multisig__predict_multisig_address_impl( }, ) } +fn wire__crate__api__airdrop__prove_airdrop_wormhole_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "prove_airdrop_wormhole", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_wormhole_secret = >::sse_decode(&mut deserializer); + let api_claim_account = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, String>((move || { + let output_ok = crate::api::airdrop::prove_airdrop_wormhole( + api_wormhole_secret, + api_claim_account, + )?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__crypto__public_key_bytes_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -1303,6 +1379,26 @@ impl SseDecode for bool { } } +impl SseDecode for crate::api::airdrop::DilithiumClaimBody { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_scheme = ::sse_decode(deserializer); + let mut var_address = ::sse_decode(deserializer); + let mut var_claimAccount = ::sse_decode(deserializer); + let mut var_publicKeyHex = ::sse_decode(deserializer); + let mut var_signatureHex = ::sse_decode(deserializer); + let mut var_expiryUnix = ::sse_decode(deserializer); + return crate::api::airdrop::DilithiumClaimBody { + scheme: var_scheme, + address: var_address, + claim_account: var_claimAccount, + public_key_hex: var_publicKeyHex, + signature_hex: var_signatureHex, + expiry_unix: var_expiryUnix, + }; + } +} + impl SseDecode for crate::api::crypto::DilithiumScheme { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1322,6 +1418,13 @@ impl SseDecode for i32 { } } +impl SseDecode for i64 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_i64::().unwrap() + } +} + impl SseDecode for crate::api::crypto::Keypair { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1548,6 +1651,18 @@ impl SseDecode for usize { } } +impl SseDecode for crate::api::airdrop::WormholeClaimBody { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_proofKind = ::sse_decode(deserializer); + let mut var_proofHex = ::sse_decode(deserializer); + return crate::api::airdrop::WormholeClaimBody { + proof_kind: var_proofKind, + proof_hex: var_proofHex, + }; + } +} + impl SseDecode for crate::api::crypto::WormholeResult { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1572,17 +1687,29 @@ fn pde_ffi_dispatcher_primary_impl( // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { 1 => wire__crate__api__wormhole__aggregate_proofs_impl(port, ptr, rust_vec_len, data_len), - 15 => wire__crate__api__wormhole__ensure_circuit_binaries_impl( + 2 => wire__crate__api__airdrop__build_airdrop_dilithium_claim_impl( port, ptr, rust_vec_len, data_len, ), - 16 => { + 16 => wire__crate__api__wormhole__ensure_circuit_binaries_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 17 => { wire__crate__api__airdrop__find_airdrop_matches_impl(port, ptr, rust_vec_len, data_len) } - 21 => wire__crate__api__wormhole__generate_proof_impl(port, ptr, rust_vec_len, data_len), - 22 => wire__crate__api__crypto__init_app_impl(port, ptr, rust_vec_len, data_len), + 22 => wire__crate__api__wormhole__generate_proof_impl(port, ptr, rust_vec_len, data_len), + 23 => wire__crate__api__crypto__init_app_impl(port, ptr, rust_vec_len, data_len), + 28 => wire__crate__api__airdrop__prove_airdrop_wormhole_impl( + port, + ptr, + rust_vec_len, + data_len, + ), _ => unreachable!(), } } @@ -1595,48 +1722,48 @@ fn pde_ffi_dispatcher_sync_impl( ) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 2 => wire__crate__api__wormhole__compute_address_hash_hex_impl(ptr, rust_vec_len, data_len), - 3 => wire__crate__api__wormhole__compute_merkle_positions_impl(ptr, rust_vec_len, data_len), - 4 => wire__crate__api__wormhole__compute_nullifier_impl(ptr, rust_vec_len, data_len), - 5 => wire__crate__api__wormhole__compute_wormhole_address_impl(ptr, rust_vec_len, data_len), - 6 => wire__crate__api__crypto__crystal_alice_impl(ptr, rust_vec_len, data_len), - 7 => wire__crate__api__crypto__crystal_bob_impl(ptr, rust_vec_len, data_len), - 8 => wire__crate__api__crypto__crystal_charlie_impl(ptr, rust_vec_len, data_len), - 9 => wire__crate__api__wormhole__decode_leaf_amount_impl(ptr, rust_vec_len, data_len), - 10 => wire__crate__api__wormhole__decode_leaf_to_account_impl(ptr, rust_vec_len, data_len), - 11 => { + 3 => wire__crate__api__wormhole__compute_address_hash_hex_impl(ptr, rust_vec_len, data_len), + 4 => wire__crate__api__wormhole__compute_merkle_positions_impl(ptr, rust_vec_len, data_len), + 5 => wire__crate__api__wormhole__compute_nullifier_impl(ptr, rust_vec_len, data_len), + 6 => wire__crate__api__wormhole__compute_wormhole_address_impl(ptr, rust_vec_len, data_len), + 7 => wire__crate__api__crypto__crystal_alice_impl(ptr, rust_vec_len, data_len), + 8 => wire__crate__api__crypto__crystal_bob_impl(ptr, rust_vec_len, data_len), + 9 => wire__crate__api__crypto__crystal_charlie_impl(ptr, rust_vec_len, data_len), + 10 => wire__crate__api__wormhole__decode_leaf_amount_impl(ptr, rust_vec_len, data_len), + 11 => wire__crate__api__wormhole__decode_leaf_to_account_impl(ptr, rust_vec_len, data_len), + 12 => { wire__crate__api__wormhole__decode_leaf_transfer_count_impl(ptr, rust_vec_len, data_len) } - 12 => wire__crate__api__ur__decode_ur_impl(ptr, rust_vec_len, data_len), - 13 => wire__crate__api__crypto__derive_wormhole_impl(ptr, rust_vec_len, data_len), - 14 => wire__crate__api__ur__encode_ur_impl(ptr, rust_vec_len, data_len), - 17 => wire__crate__api__crypto__first_hash_to_address_impl(ptr, rust_vec_len, data_len), - 18 => wire__crate__api__crypto__generate_derived_keypair_impl(ptr, rust_vec_len, data_len), - 19 => wire__crate__api__crypto__generate_keypair_impl(ptr, rust_vec_len, data_len), - 20 => { + 13 => wire__crate__api__ur__decode_ur_impl(ptr, rust_vec_len, data_len), + 14 => wire__crate__api__crypto__derive_wormhole_impl(ptr, rust_vec_len, data_len), + 15 => wire__crate__api__ur__encode_ur_impl(ptr, rust_vec_len, data_len), + 18 => wire__crate__api__crypto__first_hash_to_address_impl(ptr, rust_vec_len, data_len), + 19 => wire__crate__api__crypto__generate_derived_keypair_impl(ptr, rust_vec_len, data_len), + 20 => wire__crate__api__crypto__generate_keypair_impl(ptr, rust_vec_len, data_len), + 21 => { wire__crate__api__crypto__generate_keypair_from_seed_impl(ptr, rust_vec_len, data_len) } - 23 => wire__crate__api__ur__is_complete_ur_impl(ptr, rust_vec_len, data_len), - 24 => wire__crate__api__ur__max_ur_part_chars_impl(ptr, rust_vec_len, data_len), - 25 => wire__crate__api__ur__max_ur_parts_impl(ptr, rust_vec_len, data_len), - 26 => { + 24 => wire__crate__api__ur__is_complete_ur_impl(ptr, rust_vec_len, data_len), + 25 => wire__crate__api__ur__max_ur_part_chars_impl(ptr, rust_vec_len, data_len), + 26 => wire__crate__api__ur__max_ur_parts_impl(ptr, rust_vec_len, data_len), + 27 => { wire__crate__api__multisig__predict_multisig_address_impl(ptr, rust_vec_len, data_len) } - 27 => wire__crate__api__crypto__public_key_bytes_impl(ptr, rust_vec_len, data_len), - 28 => wire__crate__api__crypto__secret_key_bytes_impl(ptr, rust_vec_len, data_len), - 29 => wire__crate__api__crypto__set_default_ss58_prefix_impl(ptr, rust_vec_len, data_len), - 30 => wire__crate__api__crypto__sign_message_impl(ptr, rust_vec_len, data_len), - 31 => wire__crate__api__crypto__sign_message_with_pubkey_impl(ptr, rust_vec_len, data_len), - 32 => wire__crate__api__crypto__signature_bytes_impl(ptr, rust_vec_len, data_len), - 33 => wire__crate__api__crypto__ss58_to_account_id_impl(ptr, rust_vec_len, data_len), - 34 => wire__crate__api__crypto__to_account_id_impl(ptr, rust_vec_len, data_len), - 35 => wire__crate__api__crypto__verify_message_impl(ptr, rust_vec_len, data_len), - 36 => wire__crate__api__wormhole__wormhole_compute_output_amount_impl( + 29 => wire__crate__api__crypto__public_key_bytes_impl(ptr, rust_vec_len, data_len), + 30 => wire__crate__api__crypto__secret_key_bytes_impl(ptr, rust_vec_len, data_len), + 31 => wire__crate__api__crypto__set_default_ss58_prefix_impl(ptr, rust_vec_len, data_len), + 32 => wire__crate__api__crypto__sign_message_impl(ptr, rust_vec_len, data_len), + 33 => wire__crate__api__crypto__sign_message_with_pubkey_impl(ptr, rust_vec_len, data_len), + 34 => wire__crate__api__crypto__signature_bytes_impl(ptr, rust_vec_len, data_len), + 35 => wire__crate__api__crypto__ss58_to_account_id_impl(ptr, rust_vec_len, data_len), + 36 => wire__crate__api__crypto__to_account_id_impl(ptr, rust_vec_len, data_len), + 37 => wire__crate__api__crypto__verify_message_impl(ptr, rust_vec_len, data_len), + 38 => wire__crate__api__wormhole__wormhole_compute_output_amount_impl( ptr, rust_vec_len, data_len, ), - 37 => wire__crate__api__wormhole__zk_circuits_version_impl(ptr, rust_vec_len, data_len), + 39 => wire__crate__api__wormhole__zk_circuits_version_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -1684,6 +1811,31 @@ impl flutter_rust_bridge::IntoIntoDart } } // Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::airdrop::DilithiumClaimBody { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.scheme.into_into_dart().into_dart(), + self.address.into_into_dart().into_dart(), + self.claim_account.into_into_dart().into_dart(), + self.public_key_hex.into_into_dart().into_dart(), + self.signature_hex.into_into_dart().into_dart(), + self.expiry_unix.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::api::airdrop::DilithiumClaimBody +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::api::airdrop::DilithiumClaimBody +{ + fn into_into_dart(self) -> crate::api::airdrop::DilithiumClaimBody { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::api::crypto::DilithiumScheme { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { match self { @@ -1804,6 +1956,27 @@ impl flutter_rust_bridge::IntoIntoDart } } // Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::airdrop::WormholeClaimBody { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.proof_kind.into_into_dart().into_dart(), + self.proof_hex.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::api::airdrop::WormholeClaimBody +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::api::airdrop::WormholeClaimBody +{ + fn into_into_dart(self) -> crate::api::airdrop::WormholeClaimBody { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::api::crypto::WormholeResult { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [ @@ -1870,6 +2043,18 @@ impl SseEncode for bool { } } +impl SseEncode for crate::api::airdrop::DilithiumClaimBody { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.scheme, serializer); + ::sse_encode(self.address, serializer); + ::sse_encode(self.claim_account, serializer); + ::sse_encode(self.public_key_hex, serializer); + ::sse_encode(self.signature_hex, serializer); + ::sse_encode(self.expiry_unix, serializer); + } +} + impl SseEncode for crate::api::crypto::DilithiumScheme { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1893,6 +2078,13 @@ impl SseEncode for i32 { } } +impl SseEncode for i64 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_i64::(self).unwrap(); + } +} + impl SseEncode for crate::api::crypto::Keypair { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -2079,6 +2271,14 @@ impl SseEncode for usize { } } +impl SseEncode for crate::api::airdrop::WormholeClaimBody { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.proof_kind, serializer); + ::sse_encode(self.proof_hex, serializer); + } +} + impl SseEncode for crate::api::crypto::WormholeResult { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { From 90f6517aee84f2d66d289de180eb6984fd532351 Mon Sep 17 00:00:00 2001 From: illuzen Date: Sat, 12 Sep 2026 16:29:55 +0800 Subject: [PATCH 03/10] cargo +nightly fmt Co-authored-by: Cursor --- quantus_sdk/rust/src/api/airdrop.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/quantus_sdk/rust/src/api/airdrop.rs b/quantus_sdk/rust/src/api/airdrop.rs index 0a83e3621..c75622206 100644 --- a/quantus_sdk/rust/src/api/airdrop.rs +++ b/quantus_sdk/rust/src/api/airdrop.rs @@ -566,12 +566,9 @@ mod tests { let address_bytes = derive_dilithium("dilithium-v10-padded", &keypair.public_key); let claim_bytes = [9u8; 32]; - let body = build_airdrop_dilithium_claim( - &keypair, - to_ss58(&address_bytes), - to_ss58(&claim_bytes), - ) - .unwrap(); + let body = + build_airdrop_dilithium_claim(&keypair, to_ss58(&address_bytes), to_ss58(&claim_bytes)) + .unwrap(); assert_eq!(body.scheme, "dilithium-v10-padded"); let mut msg = [0u8; 72]; From e0337779df4259501c7a5ff5d7036f0f9578bd56 Mon Sep 17 00:00:00 2001 From: illuzen Date: Mon, 14 Sep 2026 09:39:37 +0800 Subject: [PATCH 04/10] Fix v0.9 address derivation, export airdrop API, apply repo formatting The v0.9.x qp-poseidon permutation used different round constants (ChaCha8, seed 0x189189189189189) than the current one, so reconstructing its sponge on the current permutation produced wrong addresses. Depend on the real qp-poseidon-core 0.9.5 crate for the v09 schemes and pin them with vectors from the original implementation. Also export the airdrop API from quantus_sdk.dart with a public-import smoke test, and format the regenerated Dart files at 120 columns. Co-authored-by: Cursor --- quantus_sdk/lib/quantus_sdk.dart | 1 + quantus_sdk/lib/src/rust/api/airdrop.dart | 12 +- quantus_sdk/lib/src/rust/api/crypto.dart | 50 +- quantus_sdk/lib/src/rust/api/multisig.dart | 11 +- quantus_sdk/lib/src/rust/api/ur.dart | 11 +- quantus_sdk/lib/src/rust/api/wormhole.dart | 63 +- quantus_sdk/lib/src/rust/frb_generated.dart | 679 ++++-------------- .../lib/src/rust/frb_generated.io.dart | 104 +-- .../lib/src/rust/frb_generated.web.dart | 105 +-- quantus_sdk/rust/Cargo.lock | 197 ++++- quantus_sdk/rust/Cargo.toml | 3 + quantus_sdk/rust/src/api/airdrop.rs | 112 ++- quantus_sdk/test/public_exports_test.dart | 12 + 13 files changed, 509 insertions(+), 851 deletions(-) create mode 100644 quantus_sdk/test/public_exports_test.dart diff --git a/quantus_sdk/lib/quantus_sdk.dart b/quantus_sdk/lib/quantus_sdk.dart index 6659a8219..8e11c11f4 100644 --- a/quantus_sdk/lib/quantus_sdk.dart +++ b/quantus_sdk/lib/quantus_sdk.dart @@ -54,6 +54,7 @@ export 'src/models/raid_stats.dart'; export 'src/rust/api/crypto.dart' hide crystalAlice, crystalCharlie, crystalBob; export 'src/rust/api/ur.dart'; export 'src/utils/ur_qr.dart'; +export 'src/rust/api/airdrop.dart'; export 'src/rust/api/wormhole.dart'; export 'src/services/account_discovery_service.dart'; export 'src/services/accounts_service.dart'; diff --git a/quantus_sdk/lib/src/rust/api/airdrop.dart b/quantus_sdk/lib/src/rust/api/airdrop.dart index f376fb16b..74b30f233 100644 --- a/quantus_sdk/lib/src/rust/api/airdrop.dart +++ b/quantus_sdk/lib/src/rust/api/airdrop.dart @@ -48,13 +48,11 @@ Future buildAirdropDilithiumClaim({ /// (current rate-8 scheme), binding the payout to `claim_account`. Builds the /// circuit in-process: expect tens of seconds of CPU on first call. Submit as /// `{"kind": "wormhole", ...fields}`. -Future proveAirdropWormhole({ - required List wormholeSecret, - required String claimAccount, -}) => RustLib.instance.api.crateApiAirdropProveAirdropWormhole( - wormholeSecret: wormholeSecret, - claimAccount: claimAccount, -); +Future proveAirdropWormhole({required List wormholeSecret, required String claimAccount}) => + RustLib.instance.api.crateApiAirdropProveAirdropWormhole( + wormholeSecret: wormholeSecret, + claimAccount: claimAccount, + ); /// A snapshot address this wallet can prove ownership of. class AirdropMatch { diff --git a/quantus_sdk/lib/src/rust/api/crypto.dart b/quantus_sdk/lib/src/rust/api/crypto.dart index 09228ebeb..502c92c0f 100644 --- a/quantus_sdk/lib/src/rust/api/crypto.dart +++ b/quantus_sdk/lib/src/rust/api/crypto.dart @@ -14,44 +14,28 @@ void setDefaultSs58Prefix({required int prefix}) => RustLib.instance.api.crateApiCryptoSetDefaultSs58Prefix(prefix: prefix); /// Convert public key to accountId32 in ss58check format -String toAccountId({required Keypair obj}) => - RustLib.instance.api.crateApiCryptoToAccountId(obj: obj); +String toAccountId({required Keypair obj}) => RustLib.instance.api.crateApiCryptoToAccountId(obj: obj); /// Convert key in ss58check format to accountId32 -Uint8List ss58ToAccountId({required String s}) => - RustLib.instance.api.crateApiCryptoSs58ToAccountId(s: s); +Uint8List ss58ToAccountId({required String s}) => RustLib.instance.api.crateApiCryptoSs58ToAccountId(s: s); /// Legacy non-HD ML-DSA-87 keypair straight from the mnemonic seed (early CLI and miner accounts). -Keypair generateKeypair({required String mnemonicStr}) => RustLib.instance.api - .crateApiCryptoGenerateKeypair(mnemonicStr: mnemonicStr); - -Keypair generateDerivedKeypair({ - required String mnemonicStr, - required String path, - required DilithiumScheme scheme, -}) => RustLib.instance.api.crateApiCryptoGenerateDerivedKeypair( - mnemonicStr: mnemonicStr, - path: path, - scheme: scheme, -); +Keypair generateKeypair({required String mnemonicStr}) => + RustLib.instance.api.crateApiCryptoGenerateKeypair(mnemonicStr: mnemonicStr); -WormholeResult deriveWormhole({ - required String mnemonicStr, - required String path, -}) => RustLib.instance.api.crateApiCryptoDeriveWormhole( - mnemonicStr: mnemonicStr, - path: path, -); +Keypair generateDerivedKeypair({required String mnemonicStr, required String path, required DilithiumScheme scheme}) => + RustLib.instance.api.crateApiCryptoGenerateDerivedKeypair(mnemonicStr: mnemonicStr, path: path, scheme: scheme); + +WormholeResult deriveWormhole({required String mnemonicStr, required String path}) => + RustLib.instance.api.crateApiCryptoDeriveWormhole(mnemonicStr: mnemonicStr, path: path); /// Convert a first_hash (rewards preimage) to its corresponding wormhole address. /// /// Mirrors how the chain and ZK circuit derive the address from the preimage: /// - Convert 32 bytes → 4 Poseidon field elements (8 bytes each) /// - Hash once without padding -String firstHashToAddress({required String firstHashHex}) => RustLib - .instance - .api - .crateApiCryptoFirstHashToAddress(firstHashHex: firstHashHex); +String firstHashToAddress({required String firstHashHex}) => + RustLib.instance.api.crateApiCryptoFirstHashToAddress(firstHashHex: firstHashHex); /// ML-DSA-87 keypair from a raw 32-byte seed (dev accounts). Keypair generateKeypairFromSeed({required List seed}) => @@ -122,11 +106,7 @@ class Keypair { final Uint8List secretKey; final DilithiumScheme scheme; - const Keypair({ - required this.publicKey, - required this.secretKey, - required this.scheme, - }); + const Keypair({required this.publicKey, required this.secretKey, required this.scheme}); @override int get hashCode => publicKey.hashCode ^ secretKey.hashCode ^ scheme.hashCode; @@ -158,11 +138,7 @@ class WormholeResult { final Uint8List firstHash; final Uint8List secret; - const WormholeResult({ - required this.address, - required this.firstHash, - required this.secret, - }); + const WormholeResult({required this.address, required this.firstHash, required this.secret}); @override int get hashCode => address.hashCode ^ firstHash.hashCode ^ secret.hashCode; diff --git a/quantus_sdk/lib/src/rust/api/multisig.dart b/quantus_sdk/lib/src/rust/api/multisig.dart index cfec97749..8fcb105f9 100644 --- a/quantus_sdk/lib/src/rust/api/multisig.dart +++ b/quantus_sdk/lib/src/rust/api/multisig.dart @@ -11,12 +11,5 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; /// Predicts the on-chain multisig address for the given signers, threshold, and nonce. /// /// Address is computed as: hash(pallet_id || sorted_signers || threshold || nonce) -String predictMultisigAddress({ - required List signers, - required int threshold, - required BigInt nonce, -}) => RustLib.instance.api.crateApiMultisigPredictMultisigAddress( - signers: signers, - threshold: threshold, - nonce: nonce, -); +String predictMultisigAddress({required List signers, required int threshold, required BigInt nonce}) => + RustLib.instance.api.crateApiMultisigPredictMultisigAddress(signers: signers, threshold: threshold, nonce: nonce); diff --git a/quantus_sdk/lib/src/rust/api/ur.dart b/quantus_sdk/lib/src/rust/api/ur.dart index cb91f5267..7a82bc0f2 100644 --- a/quantus_sdk/lib/src/rust/api/ur.dart +++ b/quantus_sdk/lib/src/rust/api/ur.dart @@ -12,14 +12,9 @@ int maxUrParts() => RustLib.instance.api.crateApiUrMaxUrParts(); int maxUrPartChars() => RustLib.instance.api.crateApiUrMaxUrPartChars(); -Uint8List decodeUr({required List urParts}) => - RustLib.instance.api.crateApiUrDecodeUr(urParts: urParts); +Uint8List decodeUr({required List urParts}) => RustLib.instance.api.crateApiUrDecodeUr(urParts: urParts); List encodeUr({required List data, int? maxFragmentLength}) => - RustLib.instance.api.crateApiUrEncodeUr( - data: data, - maxFragmentLength: maxFragmentLength, - ); + RustLib.instance.api.crateApiUrEncodeUr(data: data, maxFragmentLength: maxFragmentLength); -bool isCompleteUr({required List urParts}) => - RustLib.instance.api.crateApiUrIsCompleteUr(urParts: urParts); +bool isCompleteUr({required List urParts}) => RustLib.instance.api.crateApiUrIsCompleteUr(urParts: urParts); diff --git a/quantus_sdk/lib/src/rust/api/wormhole.dart b/quantus_sdk/lib/src/rust/api/wormhole.dart index ff954fb24..b94bb0597 100644 --- a/quantus_sdk/lib/src/rust/api/wormhole.dart +++ b/quantus_sdk/lib/src/rust/api/wormhole.dart @@ -8,45 +8,28 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; // These functions are ignored because they are not marked as `pub`: `all_required_files_exist`, `cleanup_stale_circuit_dirs`, `vec_to_32`, `vec_to_digest`, `versioned_bins_dir` -String zkCircuitsVersion() => - RustLib.instance.api.crateApiWormholeZkCircuitsVersion(); - -String computeAddressHashHex({required List rawAddress}) => RustLib - .instance - .api - .crateApiWormholeComputeAddressHashHex(rawAddress: rawAddress); - -Uint8List computeNullifier({ - required List secret, - required BigInt transferCount, -}) => RustLib.instance.api.crateApiWormholeComputeNullifier( - secret: secret, - transferCount: transferCount, -); +String zkCircuitsVersion() => RustLib.instance.api.crateApiWormholeZkCircuitsVersion(); + +String computeAddressHashHex({required List rawAddress}) => + RustLib.instance.api.crateApiWormholeComputeAddressHashHex(rawAddress: rawAddress); + +Uint8List computeNullifier({required List secret, required BigInt transferCount}) => + RustLib.instance.api.crateApiWormholeComputeNullifier(secret: secret, transferCount: transferCount); Uint8List computeWormholeAddress({required List secret}) => RustLib.instance.api.crateApiWormholeComputeWormholeAddress(secret: secret); -int wormholeComputeOutputAmount({ - required int inputAmount, - required int feeBps, -}) => RustLib.instance.api.crateApiWormholeWormholeComputeOutputAmount( - inputAmount: inputAmount, - feeBps: feeBps, -); +int wormholeComputeOutputAmount({required int inputAmount, required int feeBps}) => + RustLib.instance.api.crateApiWormholeWormholeComputeOutputAmount(inputAmount: inputAmount, feeBps: feeBps); int decodeLeafAmount({required List leafData}) => RustLib.instance.api.crateApiWormholeDecodeLeafAmount(leafData: leafData); -BigInt decodeLeafTransferCount({required List leafData}) => RustLib - .instance - .api - .crateApiWormholeDecodeLeafTransferCount(leafData: leafData); +BigInt decodeLeafTransferCount({required List leafData}) => + RustLib.instance.api.crateApiWormholeDecodeLeafTransferCount(leafData: leafData); -Uint8List decodeLeafToAccount({required List leafData}) => RustLib - .instance - .api - .crateApiWormholeDecodeLeafToAccount(leafData: leafData); +Uint8List decodeLeafToAccount({required List leafData}) => + RustLib.instance.api.crateApiWormholeDecodeLeafToAccount(leafData: leafData); MerkleProcessed computeMerklePositions({ required List unsortedSiblingsFlat, @@ -58,10 +41,8 @@ MerkleProcessed computeMerklePositions({ depth: depth, ); -Future ensureCircuitBinaries({required String binsDir}) => RustLib - .instance - .api - .crateApiWormholeEnsureCircuitBinaries(binsDir: binsDir); +Future ensureCircuitBinaries({required String binsDir}) => + RustLib.instance.api.crateApiWormholeEnsureCircuitBinaries(binsDir: binsDir); Future generateProof({ required ProofInput input, @@ -73,22 +54,14 @@ Future generateProof({ commonBinPath: commonBinPath, ); -Future aggregateProofs({ - required List proofBytesList, - required String binsDir, -}) => RustLib.instance.api.crateApiWormholeAggregateProofs( - proofBytesList: proofBytesList, - binsDir: binsDir, -); +Future aggregateProofs({required List proofBytesList, required String binsDir}) => + RustLib.instance.api.crateApiWormholeAggregateProofs(proofBytesList: proofBytesList, binsDir: binsDir); class MerkleProcessed { final Uint8List sortedSiblingsFlat; final Uint8List positions; - const MerkleProcessed({ - required this.sortedSiblingsFlat, - required this.positions, - }); + const MerkleProcessed({required this.sortedSiblingsFlat, required this.positions}); @override int get hashCode => sortedSiblingsFlat.hashCode ^ positions.hashCode; diff --git a/quantus_sdk/lib/src/rust/frb_generated.dart b/quantus_sdk/lib/src/rust/frb_generated.dart index a921216eb..4787c55f3 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.dart @@ -11,8 +11,7 @@ import 'api/wormhole.dart'; import 'dart:async'; import 'dart:convert'; import 'frb_generated.dart'; -import 'frb_generated.io.dart' - if (dart.library.js_interop) 'frb_generated.web.dart'; +import 'frb_generated.io.dart' if (dart.library.js_interop) 'frb_generated.web.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; /// Main entrypoint of the Rust API @@ -50,12 +49,10 @@ class RustLib extends BaseEntrypoint { static void dispose() => instance.disposeImpl(); @override - ApiImplConstructor get apiImplConstructor => - RustLibApiImpl.new; + ApiImplConstructor get apiImplConstructor => RustLibApiImpl.new; @override - WireConstructor get wireConstructor => - RustLibWire.fromExternalLibrary; + WireConstructor get wireConstructor => RustLibWire.fromExternalLibrary; @override Future executeRustInitializers() async { @@ -63,8 +60,7 @@ class RustLib extends BaseEntrypoint { } @override - ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => - kDefaultExternalLibraryLoaderConfig; + ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => kDefaultExternalLibraryLoaderConfig; @override String get codegenVersion => '2.12.0'; @@ -72,20 +68,16 @@ class RustLib extends BaseEntrypoint { @override int get rustContentHash => 272316986; - static const kDefaultExternalLibraryLoaderConfig = - ExternalLibraryLoaderConfig( - stem: 'rust_lib_quantus_wallet', - ioDirectory: 'rust/target/release/', - webPrefix: 'pkg/', - wasmBindgenName: 'wasm_bindgen', - ); + static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( + stem: 'rust_lib_quantus_wallet', + ioDirectory: 'rust/target/release/', + webPrefix: 'pkg/', + wasmBindgenName: 'wasm_bindgen', + ); } abstract class RustLibApi extends BaseApi { - Future crateApiWormholeAggregateProofs({ - required List proofBytesList, - required String binsDir, - }); + Future crateApiWormholeAggregateProofs({required List proofBytesList, required String binsDir}); Future crateApiAirdropBuildAirdropDilithiumClaim({ required Keypair keypair, @@ -101,10 +93,7 @@ abstract class RustLibApi extends BaseApi { required int depth, }); - Uint8List crateApiWormholeComputeNullifier({ - required List secret, - required BigInt transferCount, - }); + Uint8List crateApiWormholeComputeNullifier({required List secret, required BigInt transferCount}); Uint8List crateApiWormholeComputeWormholeAddress({required List secret}); @@ -122,19 +111,11 @@ abstract class RustLibApi extends BaseApi { Uint8List crateApiUrDecodeUr({required List urParts}); - WormholeResult crateApiCryptoDeriveWormhole({ - required String mnemonicStr, - required String path, - }); + WormholeResult crateApiCryptoDeriveWormhole({required String mnemonicStr, required String path}); - List crateApiUrEncodeUr({ - required List data, - int? maxFragmentLength, - }); + List crateApiUrEncodeUr({required List data, int? maxFragmentLength}); - Future crateApiWormholeEnsureCircuitBinaries({ - required String binsDir, - }); + Future crateApiWormholeEnsureCircuitBinaries({required String binsDir}); Future> crateApiAirdropFindAirdropMatches({ required List snapshotAddresses, @@ -213,21 +194,15 @@ abstract class RustLibApi extends BaseApi { required int specVersion, }); - int crateApiWormholeWormholeComputeOutputAmount({ - required int inputAmount, - required int feeBps, - }); + int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps}); String crateApiWormholeZkCircuitsVersion(); - RustArcIncrementStrongCountFnType - get rust_arc_increment_strong_count_HdLatticeError; + RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_HdLatticeError; - RustArcDecrementStrongCountFnType - get rust_arc_decrement_strong_count_HdLatticeError; + RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_HdLatticeError; - CrossPlatformFinalizerArg - get rust_arc_decrement_strong_count_HdLatticeErrorPtr; + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr; } class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @@ -249,17 +224,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_list_prim_u_8_strict(proofBytesList, serializer); sse_encode_String(binsDir, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 1, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 1, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeAggregateProofsConstMeta, argValues: [proofBytesList, binsDir], apiImpl: this, @@ -268,10 +235,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeAggregateProofsConstMeta => - const TaskConstMeta( - debugName: 'aggregate_proofs', - argNames: ['proofBytesList', 'binsDir'], - ); + const TaskConstMeta(debugName: 'aggregate_proofs', argNames: ['proofBytesList', 'binsDir']); @override Future crateApiAirdropBuildAirdropDilithiumClaim({ @@ -286,17 +250,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_keypair(keypair, serializer); sse_encode_String(address, serializer); sse_encode_String(claimAccount, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 2, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 2, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_dilithium_claim_body, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_dilithium_claim_body, decodeErrorData: sse_decode_String), constMeta: kCrateApiAirdropBuildAirdropDilithiumClaimConstMeta, argValues: [keypair, address, claimAccount], apiImpl: this, @@ -305,15 +261,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiAirdropBuildAirdropDilithiumClaimConstMeta => - const TaskConstMeta( - debugName: 'build_airdrop_dilithium_claim', - argNames: ['keypair', 'address', 'claimAccount'], - ); + const TaskConstMeta(debugName: 'build_airdrop_dilithium_claim', argNames: ['keypair', 'address', 'claimAccount']); @override - String crateApiWormholeComputeAddressHashHex({ - required List rawAddress, - }) { + String crateApiWormholeComputeAddressHashHex({required List rawAddress}) { return handler.executeSync( SyncTask( callFfi: () { @@ -321,10 +272,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(rawAddress, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeAddressHashHexConstMeta, argValues: [rawAddress], apiImpl: this, @@ -333,10 +281,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeAddressHashHexConstMeta => - const TaskConstMeta( - debugName: 'compute_address_hash_hex', - argNames: ['rawAddress'], - ); + const TaskConstMeta(debugName: 'compute_address_hash_hex', argNames: ['rawAddress']); @override MerkleProcessed crateApiWormholeComputeMerklePositions({ @@ -353,10 +298,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(depth, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 4)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_merkle_processed, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_merkle_processed, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeMerklePositionsConstMeta, argValues: [unsortedSiblingsFlat, leafHash, depth], apiImpl: this, @@ -364,17 +306,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiWormholeComputeMerklePositionsConstMeta => - const TaskConstMeta( - debugName: 'compute_merkle_positions', - argNames: ['unsortedSiblingsFlat', 'leafHash', 'depth'], - ); + TaskConstMeta get kCrateApiWormholeComputeMerklePositionsConstMeta => const TaskConstMeta( + debugName: 'compute_merkle_positions', + argNames: ['unsortedSiblingsFlat', 'leafHash', 'depth'], + ); @override - Uint8List crateApiWormholeComputeNullifier({ - required List secret, - required BigInt transferCount, - }) { + Uint8List crateApiWormholeComputeNullifier({required List secret, required BigInt transferCount}) { return handler.executeSync( SyncTask( callFfi: () { @@ -383,10 +321,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_64(transferCount, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeNullifierConstMeta, argValues: [secret, transferCount], apiImpl: this, @@ -395,15 +330,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeNullifierConstMeta => - const TaskConstMeta( - debugName: 'compute_nullifier', - argNames: ['secret', 'transferCount'], - ); + const TaskConstMeta(debugName: 'compute_nullifier', argNames: ['secret', 'transferCount']); @override - Uint8List crateApiWormholeComputeWormholeAddress({ - required List secret, - }) { + Uint8List crateApiWormholeComputeWormholeAddress({required List secret}) { return handler.executeSync( SyncTask( callFfi: () { @@ -411,10 +341,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(secret, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeWormholeAddressConstMeta, argValues: [secret], apiImpl: this, @@ -423,10 +350,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeWormholeAddressConstMeta => - const TaskConstMeta( - debugName: 'compute_wormhole_address', - argNames: ['secret'], - ); + const TaskConstMeta(debugName: 'compute_wormhole_address', argNames: ['secret']); @override Keypair crateApiCryptoCrystalAlice() { @@ -436,10 +360,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_keypair, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoCrystalAliceConstMeta, argValues: [], apiImpl: this, @@ -458,10 +379,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_keypair, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoCrystalBobConstMeta, argValues: [], apiImpl: this, @@ -469,8 +387,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoCrystalBobConstMeta => - const TaskConstMeta(debugName: 'crystal_bob', argNames: []); + TaskConstMeta get kCrateApiCryptoCrystalBobConstMeta => const TaskConstMeta(debugName: 'crystal_bob', argNames: []); @override Keypair crateApiCryptoCrystalCharlie() { @@ -480,10 +397,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_keypair, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoCrystalCharlieConstMeta, argValues: [], apiImpl: this, @@ -503,10 +417,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeDecodeLeafAmountConstMeta, argValues: [leafData], apiImpl: this, @@ -515,10 +426,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafAmountConstMeta => - const TaskConstMeta( - debugName: 'decode_leaf_amount', - argNames: ['leafData'], - ); + const TaskConstMeta(debugName: 'decode_leaf_amount', argNames: ['leafData']); @override Uint8List crateApiWormholeDecodeLeafToAccount({required List leafData}) { @@ -529,10 +437,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeDecodeLeafToAccountConstMeta, argValues: [leafData], apiImpl: this, @@ -541,15 +446,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafToAccountConstMeta => - const TaskConstMeta( - debugName: 'decode_leaf_to_account', - argNames: ['leafData'], - ); + const TaskConstMeta(debugName: 'decode_leaf_to_account', argNames: ['leafData']); @override - BigInt crateApiWormholeDecodeLeafTransferCount({ - required List leafData, - }) { + BigInt crateApiWormholeDecodeLeafTransferCount({required List leafData}) { return handler.executeSync( SyncTask( callFfi: () { @@ -557,10 +457,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_64, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_64, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeDecodeLeafTransferCountConstMeta, argValues: [leafData], apiImpl: this, @@ -569,10 +466,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafTransferCountConstMeta => - const TaskConstMeta( - debugName: 'decode_leaf_transfer_count', - argNames: ['leafData'], - ); + const TaskConstMeta(debugName: 'decode_leaf_transfer_count', argNames: ['leafData']); @override Uint8List crateApiUrDecodeUr({required List urParts}) { @@ -583,10 +477,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(urParts, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiUrDecodeUrConstMeta, argValues: [urParts], apiImpl: this, @@ -594,14 +485,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrDecodeUrConstMeta => - const TaskConstMeta(debugName: 'decode_ur', argNames: ['urParts']); + TaskConstMeta get kCrateApiUrDecodeUrConstMeta => const TaskConstMeta(debugName: 'decode_ur', argNames: ['urParts']); @override - WormholeResult crateApiCryptoDeriveWormhole({ - required String mnemonicStr, - required String path, - }) { + WormholeResult crateApiCryptoDeriveWormhole({required String mnemonicStr, required String path}) { return handler.executeSync( SyncTask( callFfi: () { @@ -623,16 +510,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoDeriveWormholeConstMeta => - const TaskConstMeta( - debugName: 'derive_wormhole', - argNames: ['mnemonicStr', 'path'], - ); + const TaskConstMeta(debugName: 'derive_wormhole', argNames: ['mnemonicStr', 'path']); @override - List crateApiUrEncodeUr({ - required List data, - int? maxFragmentLength, - }) { + List crateApiUrEncodeUr({required List data, int? maxFragmentLength}) { return handler.executeSync( SyncTask( callFfi: () { @@ -641,10 +522,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(maxFragmentLength, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiUrEncodeUrConstMeta, argValues: [data, maxFragmentLength], apiImpl: this, @@ -652,31 +530,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrEncodeUrConstMeta => const TaskConstMeta( - debugName: 'encode_ur', - argNames: ['data', 'maxFragmentLength'], - ); + TaskConstMeta get kCrateApiUrEncodeUrConstMeta => + const TaskConstMeta(debugName: 'encode_ur', argNames: ['data', 'maxFragmentLength']); @override - Future crateApiWormholeEnsureCircuitBinaries({ - required String binsDir, - }) { + Future crateApiWormholeEnsureCircuitBinaries({required String binsDir}) { return handler.executeNormal( NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(binsDir, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 16, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 16, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeEnsureCircuitBinariesConstMeta, argValues: [binsDir], apiImpl: this, @@ -685,10 +551,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeEnsureCircuitBinariesConstMeta => - const TaskConstMeta( - debugName: 'ensure_circuit_binaries', - argNames: ['binsDir'], - ); + const TaskConstMeta(debugName: 'ensure_circuit_binaries', argNames: ['binsDir']); @override Future> crateApiAirdropFindAirdropMatches({ @@ -704,43 +567,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(snapshotAddresses, serializer); sse_encode_opt_list_prim_u_8_strict(dilithiumPublicKey, serializer); sse_encode_opt_String(mnemonic, serializer); - sse_encode_list_list_prim_u_8_strict( - extraWormholeSecrets, - serializer, - ); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 17, - port: port_, - ); + sse_encode_list_list_prim_u_8_strict(extraWormholeSecrets, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_airdrop_match, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_airdrop_match, decodeErrorData: sse_decode_String), constMeta: kCrateApiAirdropFindAirdropMatchesConstMeta, - argValues: [ - snapshotAddresses, - dilithiumPublicKey, - mnemonic, - extraWormholeSecrets, - ], + argValues: [snapshotAddresses, dilithiumPublicKey, mnemonic, extraWormholeSecrets], apiImpl: this, ), ); } - TaskConstMeta get kCrateApiAirdropFindAirdropMatchesConstMeta => - const TaskConstMeta( - debugName: 'find_airdrop_matches', - argNames: [ - 'snapshotAddresses', - 'dilithiumPublicKey', - 'mnemonic', - 'extraWormholeSecrets', - ], - ); + TaskConstMeta get kCrateApiAirdropFindAirdropMatchesConstMeta => const TaskConstMeta( + debugName: 'find_airdrop_matches', + argNames: ['snapshotAddresses', 'dilithiumPublicKey', 'mnemonic', 'extraWormholeSecrets'], + ); @override String crateApiCryptoFirstHashToAddress({required String firstHashHex}) { @@ -751,10 +592,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(firstHashHex, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiCryptoFirstHashToAddressConstMeta, argValues: [firstHashHex], apiImpl: this, @@ -763,10 +601,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoFirstHashToAddressConstMeta => - const TaskConstMeta( - debugName: 'first_hash_to_address', - argNames: ['firstHashHex'], - ); + const TaskConstMeta(debugName: 'first_hash_to_address', argNames: ['firstHashHex']); @override Keypair crateApiCryptoGenerateDerivedKeypair({ @@ -796,10 +631,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateDerivedKeypairConstMeta => - const TaskConstMeta( - debugName: 'generate_derived_keypair', - argNames: ['mnemonicStr', 'path', 'scheme'], - ); + const TaskConstMeta(debugName: 'generate_derived_keypair', argNames: ['mnemonicStr', 'path', 'scheme']); @override Keypair crateApiCryptoGenerateKeypair({required String mnemonicStr}) { @@ -823,10 +655,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateKeypairConstMeta => - const TaskConstMeta( - debugName: 'generate_keypair', - argNames: ['mnemonicStr'], - ); + const TaskConstMeta(debugName: 'generate_keypair', argNames: ['mnemonicStr']); @override Keypair crateApiCryptoGenerateKeypairFromSeed({required List seed}) { @@ -837,10 +666,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(seed, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 21)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_keypair, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoGenerateKeypairFromSeedConstMeta, argValues: [seed], apiImpl: this, @@ -849,10 +675,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateKeypairFromSeedConstMeta => - const TaskConstMeta( - debugName: 'generate_keypair_from_seed', - argNames: ['seed'], - ); + const TaskConstMeta(debugName: 'generate_keypair_from_seed', argNames: ['seed']); @override Future crateApiWormholeGenerateProof({ @@ -867,17 +690,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_proof_input(input, serializer); sse_encode_String(proverBinPath, serializer); sse_encode_String(commonBinPath, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 22, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 22, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_proof_output, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_proof_output, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeGenerateProofConstMeta, argValues: [input, proverBinPath, commonBinPath], apiImpl: this, @@ -886,10 +701,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeGenerateProofConstMeta => - const TaskConstMeta( - debugName: 'generate_proof', - argNames: ['input', 'proverBinPath', 'commonBinPath'], - ); + const TaskConstMeta(debugName: 'generate_proof', argNames: ['input', 'proverBinPath', 'commonBinPath']); @override Future crateApiCryptoInitApp() { @@ -897,17 +709,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 23, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), constMeta: kCrateApiCryptoInitAppConstMeta, argValues: [], apiImpl: this, @@ -915,8 +719,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoInitAppConstMeta => - const TaskConstMeta(debugName: 'init_app', argNames: []); + TaskConstMeta get kCrateApiCryptoInitAppConstMeta => const TaskConstMeta(debugName: 'init_app', argNames: []); @override bool crateApiUrIsCompleteUr({required List urParts}) { @@ -927,10 +730,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(urParts, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_bool, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), constMeta: kCrateApiUrIsCompleteUrConstMeta, argValues: [urParts], apiImpl: this, @@ -949,10 +749,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiUrMaxUrPartCharsConstMeta, argValues: [], apiImpl: this, @@ -971,10 +768,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiUrMaxUrPartsConstMeta, argValues: [], apiImpl: this, @@ -982,8 +776,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrMaxUrPartsConstMeta => - const TaskConstMeta(debugName: 'max_ur_parts', argNames: []); + TaskConstMeta get kCrateApiUrMaxUrPartsConstMeta => const TaskConstMeta(debugName: 'max_ur_parts', argNames: []); @override String crateApiMultisigPredictMultisigAddress({ @@ -1000,10 +793,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_64(nonce, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiMultisigPredictMultisigAddressConstMeta, argValues: [signers, threshold, nonce], apiImpl: this, @@ -1012,10 +802,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiMultisigPredictMultisigAddressConstMeta => - const TaskConstMeta( - debugName: 'predict_multisig_address', - argNames: ['signers', 'threshold', 'nonce'], - ); + const TaskConstMeta(debugName: 'predict_multisig_address', argNames: ['signers', 'threshold', 'nonce']); @override Future crateApiAirdropProveAirdropWormhole({ @@ -1028,17 +815,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(wormholeSecret, serializer); sse_encode_String(claimAccount, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 28, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_wormhole_claim_body, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_wormhole_claim_body, decodeErrorData: sse_decode_String), constMeta: kCrateApiAirdropProveAirdropWormholeConstMeta, argValues: [wormholeSecret, claimAccount], apiImpl: this, @@ -1047,10 +826,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiAirdropProveAirdropWormholeConstMeta => - const TaskConstMeta( - debugName: 'prove_airdrop_wormhole', - argNames: ['wormholeSecret', 'claimAccount'], - ); + const TaskConstMeta(debugName: 'prove_airdrop_wormhole', argNames: ['wormholeSecret', 'claimAccount']); @override int crateApiCryptoPublicKeyBytes({required DilithiumScheme scheme}) { @@ -1061,10 +837,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_dilithium_scheme(scheme, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiCryptoPublicKeyBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -1084,10 +857,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_dilithium_scheme(scheme, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiCryptoSecretKeyBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -1107,10 +877,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_16(prefix, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), constMeta: kCrateApiCryptoSetDefaultSs58PrefixConstMeta, argValues: [prefix], apiImpl: this, @@ -1119,10 +886,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoSetDefaultSs58PrefixConstMeta => - const TaskConstMeta( - debugName: 'set_default_ss58_prefix', - argNames: ['prefix'], - ); + const TaskConstMeta(debugName: 'set_default_ss58_prefix', argNames: ['prefix']); @override Uint8List crateApiCryptoSignMessage({ @@ -1141,10 +905,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(specVersion, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), constMeta: kCrateApiCryptoSignMessageConstMeta, argValues: [keypair, message, entropy, specVersion], apiImpl: this, @@ -1152,10 +913,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoSignMessageConstMeta => const TaskConstMeta( - debugName: 'sign_message', - argNames: ['keypair', 'message', 'entropy', 'specVersion'], - ); + TaskConstMeta get kCrateApiCryptoSignMessageConstMeta => + const TaskConstMeta(debugName: 'sign_message', argNames: ['keypair', 'message', 'entropy', 'specVersion']); @override Uint8List crateApiCryptoSignMessageWithPubkey({ @@ -1174,10 +933,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(specVersion, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), constMeta: kCrateApiCryptoSignMessageWithPubkeyConstMeta, argValues: [keypair, message, entropy, specVersion], apiImpl: this, @@ -1185,11 +941,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta => - const TaskConstMeta( - debugName: 'sign_message_with_pubkey', - argNames: ['keypair', 'message', 'entropy', 'specVersion'], - ); + TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta => const TaskConstMeta( + debugName: 'sign_message_with_pubkey', + argNames: ['keypair', 'message', 'entropy', 'specVersion'], + ); @override int crateApiCryptoSignatureBytes({required DilithiumScheme scheme}) { @@ -1200,10 +955,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_dilithium_scheme(scheme, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiCryptoSignatureBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -1223,10 +975,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(s, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiCryptoSs58ToAccountIdConstMeta, argValues: [s], apiImpl: this, @@ -1246,10 +995,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_keypair(obj, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), constMeta: kCrateApiCryptoToAccountIdConstMeta, argValues: [obj], apiImpl: this, @@ -1277,10 +1023,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(specVersion, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_bool, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), constMeta: kCrateApiCryptoVerifyMessageConstMeta, argValues: [keypair, message, signature, specVersion], apiImpl: this, @@ -1289,16 +1032,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoVerifyMessageConstMeta => - const TaskConstMeta( - debugName: 'verify_message', - argNames: ['keypair', 'message', 'signature', 'specVersion'], - ); + const TaskConstMeta(debugName: 'verify_message', argNames: ['keypair', 'message', 'signature', 'specVersion']); @override - int crateApiWormholeWormholeComputeOutputAmount({ - required int inputAmount, - required int feeBps, - }) { + int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps}) { return handler.executeSync( SyncTask( callFfi: () { @@ -1307,10 +1044,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(feeBps, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 38)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeWormholeComputeOutputAmountConstMeta, argValues: [inputAmount, feeBps], apiImpl: this, @@ -1319,10 +1053,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeWormholeComputeOutputAmountConstMeta => - const TaskConstMeta( - debugName: 'wormhole_compute_output_amount', - argNames: ['inputAmount', 'feeBps'], - ); + const TaskConstMeta(debugName: 'wormhole_compute_output_amount', argNames: ['inputAmount', 'feeBps']); @override String crateApiWormholeZkCircuitsVersion() { @@ -1332,10 +1063,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 39)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), constMeta: kCrateApiWormholeZkCircuitsVersionConstMeta, argValues: [], apiImpl: this, @@ -1346,17 +1074,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiWormholeZkCircuitsVersionConstMeta => const TaskConstMeta(debugName: 'zk_circuits_version', argNames: []); - RustArcIncrementStrongCountFnType - get rust_arc_increment_strong_count_HdLatticeError => wire - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_HdLatticeError => + wire.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; - RustArcDecrementStrongCountFnType - get rust_arc_decrement_strong_count_HdLatticeError => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_HdLatticeError => + wire.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; @protected - HdLatticeError - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1364,10 +1089,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - HdLatticeError - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - dynamic raw, - ) { + HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return HdLatticeErrorImpl.frbInternalDcoDecode(raw as List); } @@ -1382,8 +1104,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { AirdropMatch dco_decode_airdrop_match(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 6) - throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); return AirdropMatch( address: dco_decode_String(arr[0]), kind: dco_decode_String(arr[1]), @@ -1422,8 +1143,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { DilithiumClaimBody dco_decode_dilithium_claim_body(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 6) - throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); return DilithiumClaimBody( scheme: dco_decode_String(arr[0]), address: dco_decode_String(arr[1]), @@ -1456,8 +1176,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { Keypair dco_decode_keypair(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 3) - throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); return Keypair( publicKey: dco_decode_list_prim_u_8_strict(arr[0]), secretKey: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1499,8 +1218,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { MerkleProcessed dco_decode_merkle_processed(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) - throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return MerkleProcessed( sortedSiblingsFlat: dco_decode_list_prim_u_8_strict(arr[0]), positions: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1535,8 +1253,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ProofInput dco_decode_proof_input(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 19) - throw Exception('unexpected arr length: expect 19 but see ${arr.length}'); + if (arr.length != 19) throw Exception('unexpected arr length: expect 19 but see ${arr.length}'); return ProofInput( secret: dco_decode_list_prim_u_8_strict(arr[0]), transferCount: dco_decode_u_64(arr[1]), @@ -1564,8 +1281,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ProofOutput dco_decode_proof_output(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) - throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return ProofOutput( proofBytes: dco_decode_list_prim_u_8_strict(arr[0]), nullifier: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1618,20 +1334,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { WormholeClaimBody dco_decode_wormhole_claim_body(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) - throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); - return WormholeClaimBody( - proofKind: dco_decode_String(arr[0]), - proofHex: dco_decode_String(arr[1]), - ); + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return WormholeClaimBody(proofKind: dco_decode_String(arr[0]), proofHex: dco_decode_String(arr[1])); } @protected WormholeResult dco_decode_wormhole_result(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 3) - throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); return WormholeResult( address: dco_decode_String(arr[0]), firstHash: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1640,27 +1351,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - HdLatticeError - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - return HdLatticeErrorImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); + return HdLatticeErrorImpl.frbInternalSseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); } @protected - HdLatticeError - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - return HdLatticeErrorImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); + return HdLatticeErrorImpl.frbInternalSseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); } @protected @@ -1714,9 +1417,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - DilithiumClaimBody sse_decode_dilithium_claim_body( - SseDeserializer deserializer, - ) { + DilithiumClaimBody sse_decode_dilithium_claim_body(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var var_scheme = sse_decode_String(deserializer); var var_address = sse_decode_String(deserializer); @@ -1759,11 +1460,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_publicKey = sse_decode_list_prim_u_8_strict(deserializer); var var_secretKey = sse_decode_list_prim_u_8_strict(deserializer); var var_scheme = sse_decode_dilithium_scheme(deserializer); - return Keypair( - publicKey: var_publicKey, - secretKey: var_secretKey, - scheme: var_scheme, - ); + return Keypair(publicKey: var_publicKey, secretKey: var_secretKey, scheme: var_scheme); } @protected @@ -1779,9 +1476,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - List sse_decode_list_airdrop_match( - SseDeserializer deserializer, - ) { + List sse_decode_list_airdrop_match(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var len_ = sse_decode_i_32(deserializer); @@ -1793,9 +1488,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - List sse_decode_list_list_prim_u_8_strict( - SseDeserializer deserializer, - ) { + List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var len_ = sse_decode_i_32(deserializer); @@ -1825,10 +1518,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs var var_sortedSiblingsFlat = sse_decode_list_prim_u_8_strict(deserializer); var var_positions = sse_decode_list_prim_u_8_strict(deserializer); - return MerkleProcessed( - sortedSiblingsFlat: var_sortedSiblingsFlat, - positions: var_positions, - ); + return MerkleProcessed(sortedSiblingsFlat: var_sortedSiblingsFlat, positions: var_positions); } @protected @@ -1971,9 +1661,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - WormholeClaimBody sse_decode_wormhole_claim_body( - SseDeserializer deserializer, - ) { + WormholeClaimBody sse_decode_wormhole_claim_body(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var var_proofKind = sse_decode_String(deserializer); var var_proofHex = sse_decode_String(deserializer); @@ -1986,37 +1674,25 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_address = sse_decode_String(deserializer); var var_firstHash = sse_decode_list_prim_u_8_strict(deserializer); var var_secret = sse_decode_list_prim_u_8_strict(deserializer); - return WormholeResult( - address: var_address, - firstHash: var_firstHash, - secret: var_secret, - ); + return WormholeResult(address: var_address, firstHash: var_firstHash, secret: var_secret); } @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as HdLatticeErrorImpl).frbInternalSseEncode(move: true), - serializer, - ); + sse_encode_usize((self as HdLatticeErrorImpl).frbInternalSseEncode(move: true), serializer); } @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as HdLatticeErrorImpl).frbInternalSseEncode(move: null), - serializer, - ); + sse_encode_usize((self as HdLatticeErrorImpl).frbInternalSseEncode(move: null), serializer); } @protected @@ -2049,10 +1725,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_box_autoadd_proof_input( - ProofInput self, - SseSerializer serializer, - ) { + void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_proof_input(self, serializer); } @@ -2064,10 +1737,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_dilithium_claim_body( - DilithiumClaimBody self, - SseSerializer serializer, - ) { + void sse_encode_dilithium_claim_body(DilithiumClaimBody self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.scheme, serializer); sse_encode_String(self.address, serializer); @@ -2078,10 +1748,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_dilithium_scheme( - DilithiumScheme self, - SseSerializer serializer, - ) { + void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.index, serializer); } @@ -2116,10 +1783,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_airdrop_match( - List self, - SseSerializer serializer, - ) { + void sse_encode_list_airdrop_match(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); for (final item in self) { @@ -2128,10 +1792,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_list_prim_u_8_strict( - List self, - SseSerializer serializer, - ) { + void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); for (final item in self) { @@ -2140,32 +1801,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_prim_u_8_loose( - List self, - SseSerializer serializer, - ) { + void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); - serializer.buffer.putUint8List( - self is Uint8List ? self : Uint8List.fromList(self), - ); + serializer.buffer.putUint8List(self is Uint8List ? self : Uint8List.fromList(self)); } @protected - void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ) { + void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); serializer.buffer.putUint8List(self); } @protected - void sse_encode_merkle_processed( - MerkleProcessed self, - SseSerializer serializer, - ) { + void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_list_prim_u_8_strict(self.sortedSiblingsFlat, serializer); sse_encode_list_prim_u_8_strict(self.positions, serializer); @@ -2192,10 +1842,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_opt_list_prim_u_8_strict( - Uint8List? self, - SseSerializer serializer, - ) { + void sse_encode_opt_list_prim_u_8_strict(Uint8List? self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_bool(self != null, serializer); @@ -2287,20 +1934,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_wormhole_claim_body( - WormholeClaimBody self, - SseSerializer serializer, - ) { + void sse_encode_wormhole_claim_body(WormholeClaimBody self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.proofKind, serializer); sse_encode_String(self.proofHex, serializer); } @protected - void sse_encode_wormhole_result( - WormholeResult self, - SseSerializer serializer, - ) { + void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.address, serializer); sse_encode_list_prim_u_8_strict(self.firstHash, serializer); @@ -2311,19 +1952,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @sealed class HdLatticeErrorImpl extends RustOpaque implements HdLatticeError { // Not to be used by end users - HdLatticeErrorImpl.frbInternalDcoDecode(List wire) - : super.frbInternalDcoDecode(wire, _kStaticData); + HdLatticeErrorImpl.frbInternalDcoDecode(List wire) : super.frbInternalDcoDecode(wire, _kStaticData); // Not to be used by end users HdLatticeErrorImpl.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative) : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); static final _kStaticData = RustArcStaticData( - rustArcIncrementStrongCount: - RustLib.instance.api.rust_arc_increment_strong_count_HdLatticeError, - rustArcDecrementStrongCount: - RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeError, - rustArcDecrementStrongCountPtr: - RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeErrorPtr, + rustArcIncrementStrongCount: RustLib.instance.api.rust_arc_increment_strong_count_HdLatticeError, + rustArcDecrementStrongCount: RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeError, + rustArcDecrementStrongCountPtr: RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeErrorPtr, ); } diff --git a/quantus_sdk/lib/src/rust/frb_generated.io.dart b/quantus_sdk/lib/src/rust/frb_generated.io.dart index 2d9794ca9..b115cd87f 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.io.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.io.dart @@ -22,21 +22,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg - get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeErrorPtr; @protected - HdLatticeError - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ); @protected - HdLatticeError - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - dynamic raw, - ); + HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw); @protected String dco_decode_String(dynamic raw); @@ -135,14 +130,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult dco_decode_wormhole_result(dynamic raw); @protected - HdLatticeError - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected - HdLatticeError - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @@ -165,9 +158,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { int sse_decode_box_autoadd_u_32(SseDeserializer deserializer); @protected - DilithiumClaimBody sse_decode_dilithium_claim_body( - SseDeserializer deserializer, - ); + DilithiumClaimBody sse_decode_dilithium_claim_body(SseDeserializer deserializer); @protected DilithiumScheme sse_decode_dilithium_scheme(SseDeserializer deserializer); @@ -185,14 +176,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { List sse_decode_list_String(SseDeserializer deserializer); @protected - List sse_decode_list_airdrop_match( - SseDeserializer deserializer, - ); + List sse_decode_list_airdrop_match(SseDeserializer deserializer); @protected - List sse_decode_list_list_prim_u_8_strict( - SseDeserializer deserializer, - ); + List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer); @protected List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer); @@ -243,23 +230,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BigInt sse_decode_usize(SseDeserializer deserializer); @protected - WormholeClaimBody sse_decode_wormhole_claim_body( - SseDeserializer deserializer, - ); + WormholeClaimBody sse_decode_wormhole_claim_body(SseDeserializer deserializer); @protected WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @@ -277,25 +260,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_keypair(Keypair self, SseSerializer serializer); @protected - void sse_encode_box_autoadd_proof_input( - ProofInput self, - SseSerializer serializer, - ); + void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer); @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); @protected - void sse_encode_dilithium_claim_body( - DilithiumClaimBody self, - SseSerializer serializer, - ); + void sse_encode_dilithium_claim_body(DilithiumClaimBody self, SseSerializer serializer); @protected - void sse_encode_dilithium_scheme( - DilithiumScheme self, - SseSerializer serializer, - ); + void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer); @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -310,31 +284,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_String(List self, SseSerializer serializer); @protected - void sse_encode_list_airdrop_match( - List self, - SseSerializer serializer, - ); + void sse_encode_list_airdrop_match(List self, SseSerializer serializer); @protected - void sse_encode_list_list_prim_u_8_strict( - List self, - SseSerializer serializer, - ); + void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer); @protected void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); @protected - void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ); + void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer); @protected - void sse_encode_merkle_processed( - MerkleProcessed self, - SseSerializer serializer, - ); + void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer); @protected void sse_encode_opt_String(String? self, SseSerializer serializer); @@ -343,10 +305,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer); @protected - void sse_encode_opt_list_prim_u_8_strict( - Uint8List? self, - SseSerializer serializer, - ); + void sse_encode_opt_list_prim_u_8_strict(Uint8List? self, SseSerializer serializer); @protected void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer); @@ -379,34 +338,24 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_usize(BigInt self, SseSerializer serializer); @protected - void sse_encode_wormhole_claim_body( - WormholeClaimBody self, - SseSerializer serializer, - ); + void sse_encode_wormhole_claim_body(WormholeClaimBody self, SseSerializer serializer); @protected - void sse_encode_wormhole_result( - WormholeResult self, - SseSerializer serializer, - ); + void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer); } // Section: wire_class class RustLibWire implements BaseWire { - factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => - RustLibWire(lib.ffiDynamicLibrary); + factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => RustLibWire(lib.ffiDynamicLibrary); /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; + final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. - RustLibWire(ffi.DynamicLibrary dynamicLibrary) - : _lookup = dynamicLibrary.lookup; + RustLibWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ffi.Pointer ptr, ) { return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( @@ -422,8 +371,7 @@ class RustLibWire implements BaseWire { _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeErrorPtr .asFunction)>(); - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ffi.Pointer ptr, ) { return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( diff --git a/quantus_sdk/lib/src/rust/frb_generated.web.dart b/quantus_sdk/lib/src/rust/frb_generated.web.dart index eb7d47ef3..f8299322b 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.web.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.web.dart @@ -24,21 +24,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg - get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr => + wire.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; @protected - HdLatticeError - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ); @protected - HdLatticeError - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - dynamic raw, - ); + HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw); @protected String dco_decode_String(dynamic raw); @@ -137,14 +132,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult dco_decode_wormhole_result(dynamic raw); @protected - HdLatticeError - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected - HdLatticeError - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @@ -167,9 +160,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { int sse_decode_box_autoadd_u_32(SseDeserializer deserializer); @protected - DilithiumClaimBody sse_decode_dilithium_claim_body( - SseDeserializer deserializer, - ); + DilithiumClaimBody sse_decode_dilithium_claim_body(SseDeserializer deserializer); @protected DilithiumScheme sse_decode_dilithium_scheme(SseDeserializer deserializer); @@ -187,14 +178,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { List sse_decode_list_String(SseDeserializer deserializer); @protected - List sse_decode_list_airdrop_match( - SseDeserializer deserializer, - ); + List sse_decode_list_airdrop_match(SseDeserializer deserializer); @protected - List sse_decode_list_list_prim_u_8_strict( - SseDeserializer deserializer, - ); + List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer); @protected List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer); @@ -245,23 +232,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BigInt sse_decode_usize(SseDeserializer deserializer); @protected - WormholeClaimBody sse_decode_wormhole_claim_body( - SseDeserializer deserializer, - ); + WormholeClaimBody sse_decode_wormhole_claim_body(SseDeserializer deserializer); @protected WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @@ -279,25 +262,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_keypair(Keypair self, SseSerializer serializer); @protected - void sse_encode_box_autoadd_proof_input( - ProofInput self, - SseSerializer serializer, - ); + void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer); @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); @protected - void sse_encode_dilithium_claim_body( - DilithiumClaimBody self, - SseSerializer serializer, - ); + void sse_encode_dilithium_claim_body(DilithiumClaimBody self, SseSerializer serializer); @protected - void sse_encode_dilithium_scheme( - DilithiumScheme self, - SseSerializer serializer, - ); + void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer); @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -312,31 +286,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_String(List self, SseSerializer serializer); @protected - void sse_encode_list_airdrop_match( - List self, - SseSerializer serializer, - ); + void sse_encode_list_airdrop_match(List self, SseSerializer serializer); @protected - void sse_encode_list_list_prim_u_8_strict( - List self, - SseSerializer serializer, - ); + void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer); @protected void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); @protected - void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ); + void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer); @protected - void sse_encode_merkle_processed( - MerkleProcessed self, - SseSerializer serializer, - ); + void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer); @protected void sse_encode_opt_String(String? self, SseSerializer serializer); @@ -345,10 +307,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer); @protected - void sse_encode_opt_list_prim_u_8_strict( - Uint8List? self, - SseSerializer serializer, - ); + void sse_encode_opt_list_prim_u_8_strict(Uint8List? self, SseSerializer serializer); @protected void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer); @@ -381,16 +340,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_usize(BigInt self, SseSerializer serializer); @protected - void sse_encode_wormhole_claim_body( - WormholeClaimBody self, - SseSerializer serializer, - ); + void sse_encode_wormhole_claim_body(WormholeClaimBody self, SseSerializer serializer); @protected - void sse_encode_wormhole_result( - WormholeResult self, - SseSerializer serializer, - ); + void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer); } // Section: wire_class @@ -398,16 +351,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { class RustLibWire implements BaseWire { RustLibWire.fromExternalLibrary(ExternalLibrary lib); - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( int ptr, ) => wasmModule .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ptr, ); - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( int ptr, ) => wasmModule .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( @@ -422,12 +373,8 @@ external RustLibWasmModule get wasmModule; @anonymous extension type RustLibWasmModule._(JSObject _) implements JSObject { external void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - int ptr, - ); + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(int ptr); external void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - int ptr, - ); + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(int ptr); } diff --git a/quantus_sdk/rust/Cargo.lock b/quantus_sdk/rust/Cargo.lock index fe166980b..d878d358c 100644 --- a/quantus_sdk/rust/Cargo.lock +++ b/quantus_sdk/rust/Cargo.lock @@ -1884,6 +1884,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -2357,6 +2366,122 @@ dependencies = [ "log", ] +[[package]] +name = "p3-dft" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b2764a3982d22d62aa933c8de6f9d71d8a474c9110b69e675dea1887bdeffc" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc13a73509fe09c67b339951ca8d4cc6e61c9bf08c130dbc90dda52452918cc2" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand 0.9.5", + "serde", + "tracing", +] + +[[package]] +name = "p3-goldilocks" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552849f6309ffde34af0d31aa9a2d0a549cb0ec138d9792bfbf4a17800742362" +dependencies = [ + "num-bigint", + "p3-dft", + "p3-field", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "p3-util", + "paste", + "rand 0.9.5", + "serde", +] + +[[package]] +name = "p3-matrix" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e1e9f69c2fe15768b3ceb2915edb88c47398aa22c485d8163deab2a47fe194" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand 0.9.5", + "serde", + "tracing", + "transpose", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f765046b763d046728b3246b690f81dfa7ccd7523b7a1582c74f616fbce6a0" + +[[package]] +name = "p3-mds" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c90541c6056712daf2ee69ec328db8b5605ae8dbafe60226c8eb75eaac0e1f9" +dependencies = [ + "p3-dft", + "p3-field", + "p3-symmetric", + "p3-util", + "rand 0.9.5", +] + +[[package]] +name = "p3-poseidon2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88e9f053f120a78ad27e9c1991a0ea547777328ca24025c42364d6ee2667d59a" +dependencies = [ + "p3-field", + "p3-mds", + "p3-symmetric", + "p3-util", + "rand 0.9.5", +] + +[[package]] +name = "p3-symmetric" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d5db8f05a26d706dfd8aaf7aa4272ca4f3e7a075db897ec7108f24fad78759" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfee67245d9ce78a15176728da2280032f0a84b5819a39a953e7ec03cfd9bd7" +dependencies = [ + "serde", +] + [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -2789,7 +2914,7 @@ dependencies = [ "qp-plonky2-core", "qp-plonky2-field", "qp-plonky2-verifier", - "qp-poseidon-core", + "qp-poseidon-core 3.1.0", "rand 0.10.1", "serde", "static_assertions", @@ -2853,12 +2978,26 @@ dependencies = [ "plonky2_util", "qp-plonky2-core", "qp-plonky2-field", - "qp-poseidon-core", + "qp-poseidon-core 3.1.0", "serde", "static_assertions", "unroll", ] +[[package]] +name = "qp-poseidon-core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec326fc2631a929de09a38af2613a3db5230882c12a2f68205693ec632751e8b" +dependencies = [ + "p3-field", + "p3-goldilocks", + "p3-poseidon2", + "p3-symmetric", + "rand 0.9.5", + "rand_chacha 0.9.0", +] + [[package]] name = "qp-poseidon-core" version = "3.1.0" @@ -2884,7 +3023,7 @@ dependencies = [ "getrandom 0.2.17", "hex", "hex-literal", - "qp-poseidon-core", + "qp-poseidon-core 3.1.0", "qp-rusty-crystals-dilithium", "serde", "serde_json", @@ -2991,7 +3130,7 @@ checksum = "dec45510701f160fdb730bdb622d6cdd0a62547e607c9b1ecb19558202797f81" dependencies = [ "anyhow", "qp-plonky2", - "qp-poseidon-core", + "qp-poseidon-core 3.1.0", "qp-wormhole-inputs 4.3.0", "rand 0.8.6", "serde", @@ -3005,7 +3144,7 @@ source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2cee dependencies = [ "anyhow", "qp-plonky2", - "qp-poseidon-core", + "qp-poseidon-core 3.1.0", "qp-wormhole-inputs 4.4.0", "rand 0.8.6", "serde", @@ -3057,10 +3196,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", - "rand_chacha", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_core 0.9.5", +] + [[package]] name = "rand" version = "0.10.1" @@ -3082,6 +3230,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -3091,6 +3249,12 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" + [[package]] name = "rand_core" version = "0.10.1" @@ -3212,7 +3376,8 @@ dependencies = [ "qp-ownership-circuit", "qp-ownership-prover", "qp-plonky2", - "qp-poseidon-core", + "qp-poseidon-core 0.9.5", + "qp-poseidon-core 3.1.0", "qp-rusty-crystals-dilithium", "qp-rusty-crystals-hdwallet", "qp-wormhole-aggregator", @@ -3928,6 +4093,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + [[package]] name = "strsim" version = "0.11.1" @@ -4346,6 +4517,16 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + [[package]] name = "trie-db" version = "0.31.0" @@ -4511,7 +4692,7 @@ dependencies = [ "arrayref", "digest 0.10.7", "rand 0.8.6", - "rand_chacha", + "rand_chacha 0.3.1", "rand_core 0.6.4", "sha2 0.10.9", "sha3", diff --git a/quantus_sdk/rust/Cargo.toml b/quantus_sdk/rust/Cargo.toml index ed88ea052..cc24f7c35 100644 --- a/quantus_sdk/rust/Cargo.toml +++ b/quantus_sdk/rust/Cargo.toml @@ -9,6 +9,9 @@ crate-type = ["cdylib", "staticlib", "rlib"] [dependencies] # NOTE: Quantus chain dependencies. qp-poseidon-core = "3.1.0" +# The Resonance-era (v0.9.x) Poseidon2 permutation used different round +# constants than the current one, so v09 airdrop addresses need the real crate. +qp-poseidon-core-v09 = { package = "qp-poseidon-core", version = "=0.9.5" } qp-rusty-crystals-dilithium = { version = "4.1.1", default-features = false, features = ["ml-dsa-65", "ml-dsa-87"] } qp-rusty-crystals-hdwallet = { version = "4.1.1", features = ["ml-dsa-65"] } diff --git a/quantus_sdk/rust/src/api/airdrop.rs b/quantus_sdk/rust/src/api/airdrop.rs index c75622206..6382e163f 100644 --- a/quantus_sdk/rust/src/api/airdrop.rs +++ b/quantus_sdk/rust/src/api/airdrop.rs @@ -3,16 +3,18 @@ //! return which snapshot rows belong to this wallet. Nothing here talks to a //! server; secrets never leave the process. //! -//! Testnet address history (the Poseidon2 permutation never changed, but the -//! sponge wrappers did): -//! - Resonance / early Schrödinger: rate 4, pad10 + domain block -//! - late Schrödinger / Dirac: rate 4, pad10 -//! - late Planck / current: rate 8, pad10 +//! Testnet address history: +//! - Resonance / early Schrödinger (qp-poseidon 0.9.x): different permutation +//! constants (ChaCha8, seed 0x189189189189189) AND a different sponge +//! (rate 4, pad10 + domain block). Handled by the real 0.9.5 crate. +//! - late Schrödinger / Dirac (1.0.x–1.1.x): current permutation, rate 4, pad10 +//! - late Planck / current (1.2.x+): current permutation, rate 8, pad10 use qp_ownership_circuit::{BytesDigest, CircuitInputs, Secret}; use qp_poseidon_core::{ serialization::digest_to_bytes, Goldilocks, Poseidon2, POSEIDON2_OUTPUT, SPONGE_WIDTH, }; +use qp_poseidon_core_v09 as v09; use qp_rusty_crystals_dilithium::ml_dsa_87; use qp_rusty_crystals_hdwallet::{derive_wormhole_from_mnemonic, QUANTUS_WORMHOLE_CHAIN_ID}; use std::collections::HashMap; @@ -33,8 +35,10 @@ const CLAIM_TTL_SECS: i64 = 10 * 60; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum Sponge { - /// qp-poseidon 0.9.x `hash_no_pad`: rate 4, pad10, then a mandatory `[0,0,0,1]` block. - Rate4Pad10PlusDomain, + /// qp-poseidon 0.9.x `hash_no_pad`: distinct permutation constants and a + /// rate-4 pad10 sponge with a mandatory `[0,0,0,1]` block. Uses the real + /// 0.9.5 crate; see `WormholeSchemeDef::derive`. + V09, /// qp-poseidon 1.0.x–1.1.x: rate 4, pad10 only. Rate4Pad10, /// qp-poseidon 1.2.x+ (current): rate 8, pad10. @@ -44,7 +48,7 @@ enum Sponge { impl Sponge { fn hash_felts(self, input: &[Goldilocks]) -> [u8; 32] { match self { - Self::Rate4Pad10PlusDomain => hash_no_pad_v09(input), + Self::V09 => unreachable!("v09 uses its own field type; see derive()"), Self::Rate4Pad10 => hash_felts_rate4_pad10(input), Self::Rate8Pad10 => qp_poseidon_core::hash_to_bytes(input), } @@ -87,43 +91,6 @@ fn injective4(bytes: &[u8]) -> Vec { out } -fn hash_no_pad_v09(x: &[Goldilocks]) -> [u8; 32] { - let poseidon = Poseidon2::new(); - let mut state = [Goldilocks::ZERO; SPONGE_WIDTH]; - - if !x.is_empty() { - let num_chunks = x.chunks(RATE_4).len(); - let mut unpadded = false; - for (j, chunk) in x.chunks(RATE_4).enumerate() { - let mut block = [Goldilocks::ZERO; RATE_4]; - if j == num_chunks - 1 { - if chunk.len() < RATE_4 { - block[chunk.len()] = Goldilocks::ONE; - } else { - unpadded = true; - } - } - block[..chunk.len()].copy_from_slice(chunk); - for i in 0..RATE_4 { - state[i] += block[i]; - } - poseidon.permute_mut(&mut state); - } - if unpadded { - state[0] += Goldilocks::ONE; - poseidon.permute_mut(&mut state); - } - } - - state[3] += Goldilocks::ONE; - poseidon.permute_mut(&mut state); - - let digest: [Goldilocks; POSEIDON2_OUTPUT] = state[..POSEIDON2_OUTPUT] - .try_into() - .expect("width > output"); - digest_to_bytes(&digest) -} - fn hash_felts_rate4_pad10(x: &[Goldilocks]) -> [u8; 32] { let poseidon = Poseidon2::new(); let mut state = [Goldilocks::ZERO; SPONGE_WIDTH]; @@ -160,18 +127,6 @@ fn hash_felts_rate4_pad10(x: &[Goldilocks]) -> [u8; 32] { digest_to_bytes(&digest) } -/// Resonance-era Dilithium AccountId: injective bytes, length prefix, pad to 190, v0.9 sponge. -fn hash_padded_v09(bytes: &[u8]) -> [u8; 32] { - const MIN_FELTS: usize = 190; - let mut felts = injective4(bytes); - let len = felts.len(); - felts.insert(0, Goldilocks::from_u64(len as u64)); - if len < MIN_FELTS { - felts.resize(MIN_FELTS, Goldilocks::ZERO); - } - hash_no_pad_v09(&felts) -} - /// qp-poseidon 1.0.x Dilithium AccountId: injective bytes, zero-pad to 189, rate-4 pad10. fn hash_padded_v10(bytes: &[u8]) -> [u8; 32] { const PAD: usize = 189; @@ -201,7 +156,7 @@ struct WormholeSchemeDef { const WORMHOLE_SCHEMES: &[WormholeSchemeDef] = &[ WormholeSchemeDef { id: "wormhole-v09-injective", - sponge: Sponge::Rate4Pad10PlusDomain, + sponge: Sponge::V09, secret_encoding: SecretEncoding::Injective4, }, WormholeSchemeDef { @@ -228,6 +183,18 @@ const WORMHOLE_SCHEMES: &[WormholeSchemeDef] = &[ impl WormholeSchemeDef { fn derive(&self, secret: &[u8; 32]) -> [u8; 32] { + if self.sponge == Sponge::V09 { + let core = v09::Poseidon2Core::new(); + let mut preimage = v09::injective_bytes_to_felts(WORMHOLE_SALT); + match self.secret_encoding { + SecretEncoding::Injective4 => { + preimage.extend(v09::injective_bytes_to_felts(secret)) + } + SecretEncoding::Compact8 => preimage.extend(v09::digest_bytes_to_felts(secret)), + } + let first_hash = core.hash_no_pad(preimage); + return core.hash_no_pad(v09::digest_bytes_to_felts(&first_hash)); + } let mut preimage = injective4(WORMHOLE_SALT); match self.secret_encoding { SecretEncoding::Injective4 => preimage.extend(injective4(secret)), @@ -246,7 +213,7 @@ const DILITHIUM_SCHEMES: &[&str] = &[ fn derive_dilithium(scheme: &str, public_key: &[u8]) -> [u8; 32] { match scheme { - "dilithium-v09-padded" => hash_padded_v09(public_key), + "dilithium-v09-padded" => v09::Poseidon2Core::new().hash_padded(public_key), "dilithium-v10-padded" => hash_padded_v10(public_key), "dilithium-rate8-hash-bytes" => qp_poseidon_core::hash_bytes(public_key), _ => unreachable!(), @@ -494,6 +461,33 @@ mod tests { ); } + /// Vectors computed with qp-poseidon-core 0.9.5 (git tag v0.9.5); the + /// `[0]` digest was also independently reproduced in review. These pin the + /// legacy permutation constants, not just the sponge shape. + #[test] + fn v09_dilithium_matches_original_crate_vectors() { + assert_eq!( + derive_dilithium("dilithium-v09-padded", &[0u8]), + hex32("b17b423096da9ebd57af5038b490257d9c492e64059c0ccff23f44e6293213d4") + ); + assert_eq!( + derive_dilithium("dilithium-v09-padded", &[5u8; 2592]), + hex32("90d32b1ed817ec85479f70af5db84a07fb8b6c314d12d3470fc046cf873a54ac") + ); + } + + #[test] + fn v09_wormhole_matches_original_crate_vector() { + let scheme = WORMHOLE_SCHEMES + .iter() + .find(|s| s.id == "wormhole-v09-injective") + .unwrap(); + assert_eq!( + scheme.derive(&[42u8; 32]), + hex32("f4e231ede747e9ca2da9528add147ee488651a0df72b00313b0a8e6b76388fea") + ); + } + #[test] fn historical_wormhole_schemes_disagree() { let secret = [9u8; 32]; diff --git a/quantus_sdk/test/public_exports_test.dart b/quantus_sdk/test/public_exports_test.dart new file mode 100644 index 000000000..684774336 --- /dev/null +++ b/quantus_sdk/test/public_exports_test.dart @@ -0,0 +1,12 @@ +// Compile-time check that the airdrop API is reachable through the public +// library, without needing the native library loaded. +import 'package:flutter_test/flutter_test.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; + +void main() { + test('airdrop API is exported from quantus_sdk.dart', () { + expect(findAirdropMatches, isA()); + expect(buildAirdropDilithiumClaim, isA()); + expect(proveAirdropWormhole, isA()); + }); +} From 7057e96de4cecfef80d714738cf289ed14b09ec0 Mon Sep 17 00:00:00 2001 From: illuzen Date: Mon, 14 Sep 2026 14:00:47 +0800 Subject: [PATCH 05/10] Add pre-0.9.5 Resonance Dilithium schemes Shipped Resonance chains predate qp-poseidon 0.9.5: chain tag v0.0.12-resonance-alpha pinned poseidon-resonance 0.8.0 (legacy plonky2 Poseidon, 8-byte limbs zero-padded to 73 felts) and chain rev e9fc9b9 pinned qp-poseidon 0.9.1 (same permutation, 4-byte limbs padded to 188). Early miners' snapshot addresses use these hashes, so match and claim under dilithium-v08-padded and dilithium-v091-padded, which the claim server now also accepts. The legacy Poseidon round constants are unchanged in today's qp-plonky2 (verified against the fork revs both chains pinned), so both schemes hash through the crate already in the tree; golden vectors were generated with the exact historical crates. Co-authored-by: Cursor --- quantus_sdk/rust/src/api/airdrop.rs | 126 +++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/quantus_sdk/rust/src/api/airdrop.rs b/quantus_sdk/rust/src/api/airdrop.rs index 6382e163f..173c6abe1 100644 --- a/quantus_sdk/rust/src/api/airdrop.rs +++ b/quantus_sdk/rust/src/api/airdrop.rs @@ -4,7 +4,11 @@ //! server; secrets never leave the process. //! //! Testnet address history: -//! - Resonance / early Schrödinger (qp-poseidon 0.9.x): different permutation +//! - early Resonance (poseidon-resonance 0.8.0): legacy plonky2 Poseidon, +//! 8-byte limbs zero-padded to 73 felts +//! - mid Resonance (qp-poseidon 0.9.1): legacy plonky2 Poseidon, 4-byte limbs +//! zero-padded to 188 felts +//! - Resonance / early Schrödinger (qp-poseidon 0.9.5): different permutation //! constants (ChaCha8, seed 0x189189189189189) AND a different sponge //! (rate 4, pad10 + domain block). Handled by the real 0.9.5 crate. //! - late Schrödinger / Dirac (1.0.x–1.1.x): current permutation, rate 4, pad10 @@ -127,6 +131,32 @@ fn hash_felts_rate4_pad10(x: &[Goldilocks]) -> [u8; 32] { digest_to_bytes(&digest) } +/// Pre-0.9.5 Resonance AccountId hash: legacy plonky2 Poseidon (unchanged in +/// today's qp-plonky2) over little-endian limbs, zero-padded to a fixed +/// preimage length. poseidon-resonance 0.8.0 used 8-byte limbs and 73 felts; +/// qp-poseidon 0.9.1 used 4-byte limbs and 188 felts. +fn hash_padded_legacy(bytes: &[u8], bytes_per_felt: usize, pad_to: usize) -> [u8; 32] { + use plonky2::field::goldilocks_field::GoldilocksField; + use plonky2::field::types::Field; + use plonky2::plonk::config::{GenericHashOut, Hasher}; + + let mut felts: Vec = bytes + .chunks(bytes_per_felt) + .map(|chunk| { + let mut word = [0u8; 8]; + word[..chunk.len()].copy_from_slice(chunk); + GoldilocksField::from_noncanonical_u64(u64::from_le_bytes(word)) + }) + .collect(); + if felts.len() < pad_to { + felts.resize(pad_to, GoldilocksField::ZERO); + } + plonky2::hash::poseidon::PoseidonHash::hash_no_pad(&felts) + .to_bytes() + .try_into() + .expect("poseidon output is 32 bytes") +} + /// qp-poseidon 1.0.x Dilithium AccountId: injective bytes, zero-pad to 189, rate-4 pad10. fn hash_padded_v10(bytes: &[u8]) -> [u8; 32] { const PAD: usize = 189; @@ -206,6 +236,8 @@ impl WormholeSchemeDef { } const DILITHIUM_SCHEMES: &[&str] = &[ + "dilithium-v08-padded", + "dilithium-v091-padded", "dilithium-v09-padded", "dilithium-v10-padded", "dilithium-rate8-hash-bytes", @@ -213,6 +245,8 @@ const DILITHIUM_SCHEMES: &[&str] = &[ fn derive_dilithium(scheme: &str, public_key: &[u8]) -> [u8; 32] { match scheme { + "dilithium-v08-padded" => hash_padded_legacy(public_key, 8, 73), + "dilithium-v091-padded" => hash_padded_legacy(public_key, 4, 188), "dilithium-v09-padded" => v09::Poseidon2Core::new().hash_padded(public_key), "dilithium-v10-padded" => hash_padded_v10(public_key), "dilithium-rate8-hash-bytes" => qp_poseidon_core::hash_bytes(public_key), @@ -461,6 +495,96 @@ mod tests { ); } + /// Vectors computed with the exact crates the shipped Resonance chains + /// pinned: poseidon-resonance 0.8.0 (rev fcb49a7, plonky2 fork rev + /// 80a1000, per chain tag v0.0.12-resonance-alpha) and crates.io + /// qp-poseidon 0.9.1 (per chain rev e9fc9b9). The 2592-byte inputs are + /// ML-DSA-87 public key sized. + #[test] + fn pre_v095_dilithium_matches_original_crate_vectors() { + let square_pattern: Vec = (0..2592u32).map(|i| (i * i % 251) as u8).collect(); + let vectors: &[(&str, &[u8], &str)] = &[ + ( + "dilithium-v08-padded", + &[0u8], + "fdf0715f178bfb2381d3804961bda8c679990d6318ff53f7a6475e1bef1982ca", + ), + ( + "dilithium-v08-padded", + &[42u8; 32], + "832c0ecb43187d773d7e54865c5148be7a2f9c9b86d8ad630196b0ff9a8037b5", + ), + ( + "dilithium-v08-padded", + &[5u8; 2592], + "9c69917b10f0228a0beed1d78ce34026b4778dc04a49a401dd5e692a51f44207", + ), + ( + "dilithium-v08-padded", + &square_pattern, + "3222344f6b35748d59d5983410b1baaba0bb680af7fecfdd7cd54de1a848977e", + ), + ( + "dilithium-v091-padded", + &[0u8], + "c4f1020767625056e669e3653f190b7763c6c398a45f1dc20db0d7ed32b14ff7", + ), + ( + "dilithium-v091-padded", + &[42u8; 32], + "19be0e79d925f42481cb5b30fb703903c866395373671728bb1594b01d850f6e", + ), + ( + "dilithium-v091-padded", + &[5u8; 2592], + "8ba4f919664c796aa811f552eaff5975570d56ed6812728944f60fe7d28d3c74", + ), + ( + "dilithium-v091-padded", + &square_pattern, + "61d1490430a295ad2d33ed55948ece058cce09b43994f35c7528f164a482a5e5", + ), + ]; + for (scheme, input, expected) in vectors { + assert_eq!( + derive_dilithium(scheme, input), + hex32(expected), + "{scheme} input len {}", + input.len() + ); + } + } + + #[test] + fn all_dilithium_schemes_disagree_on_same_public_key() { + let pk = [5u8; 2592]; + let addrs: Vec<_> = DILITHIUM_SCHEMES + .iter() + .map(|s| derive_dilithium(s, &pk)) + .collect(); + for i in 0..addrs.len() { + for j in (i + 1)..addrs.len() { + assert_ne!( + addrs[i], addrs[j], + "{} and {} collide", + DILITHIUM_SCHEMES[i], DILITHIUM_SCHEMES[j] + ); + } + } + } + + #[test] + fn matches_v08_dilithium_address_against_snapshot() { + let pk = vec![5u8; 2592]; + let resonance_era = derive_dilithium("dilithium-v08-padded", &pk); + let snapshot = vec![to_ss58(&resonance_era)]; + + let matches = find_airdrop_matches(snapshot.clone(), Some(pk), None, vec![]).unwrap(); + assert_eq!(matches.len(), 1); + assert_eq!(matches[0].scheme, "dilithium-v08-padded"); + assert!(matches[0].claimable); + } + /// Vectors computed with qp-poseidon-core 0.9.5 (git tag v0.9.5); the /// `[0]` digest was also independently reproduced in review. These pin the /// legacy permutation constants, not just the sponge shape. From 1056462ff45f8d2278ee5eaa49c98082eab58206 Mon Sep 17 00:00:00 2001 From: illuzen Date: Mon, 14 Sep 2026 16:20:25 +0800 Subject: [PATCH 06/10] Scan all HD wormhole branches and rounds when matching airdrop addresses The middle HD path component is not always a change branch: the app uses 0 (external) and 1 (dedicated change branch since July 2026), but the CLI's 'wormhole multiround' uses it as a round counter, deriving m/44'/189189189'/0'/round'/index' with a default of 2 rounds. Scanning only {0,1} missed every multiround address from round 2 on. Scan branches/rounds 0..=8 for each index, and stretch the BIP39 seed once instead of per path, which also makes the wider scan faster and zeroizes the mnemonic after stretching. Co-authored-by: Cursor --- quantus_sdk/lib/src/rust/api/airdrop.dart | 9 ++-- quantus_sdk/rust/src/api/airdrop.rs | 57 ++++++++++++++++------- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/quantus_sdk/lib/src/rust/api/airdrop.dart b/quantus_sdk/lib/src/rust/api/airdrop.dart index 74b30f233..68742e470 100644 --- a/quantus_sdk/lib/src/rust/api/airdrop.dart +++ b/quantus_sdk/lib/src/rust/api/airdrop.dart @@ -7,7 +7,7 @@ import '../frb_generated.dart'; import 'crypto.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; -// These functions are ignored because they are not marked as `pub`: `compact8_decode`, `decode_account`, `derive_dilithium`, `derive`, `hash_felts_rate4_pad10`, `hash_felts`, `hash_no_pad_v09`, `hash_padded_v09`, `hash_padded_v10`, `injective4`, `now_unix`, `rehash` +// These functions are ignored because they are not marked as `pub`: `compact8_decode`, `decode_account`, `derive_dilithium`, `derive`, `hash_felts_rate4_pad10`, `hash_felts`, `hash_padded_legacy`, `hash_padded_v10`, `injective4`, `now_unix`, `rehash` // These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `SecretEncoding`, `Sponge`, `WormholeSchemeDef` // These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `eq`, `fmt`, `fmt` @@ -16,9 +16,10 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; /// `snapshot_addresses` are the SS58 addresses from `GET /snapshot` (or the /// miner-rewards CSV). Dilithium matches are checked against /// `dilithium_public_key` under every historical hash. Wormhole matches are -/// checked for HD-derived secrets (`m/44'/189189189'/0'/{0,1}'/{0..=16}'`) -/// when `mnemonic` is given, plus any `extra_wormhole_secrets` (32 bytes -/// each). +/// checked for HD-derived secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=16}'`, +/// covering the app's external/change branches and the CLI's multiround +/// rounds) when `mnemonic` is given, plus any `extra_wormhole_secrets` +/// (32 bytes each). Future> findAirdropMatches({ required List snapshotAddresses, Uint8List? dilithiumPublicKey, diff --git a/quantus_sdk/rust/src/api/airdrop.rs b/quantus_sdk/rust/src/api/airdrop.rs index 173c6abe1..13a54afe9 100644 --- a/quantus_sdk/rust/src/api/airdrop.rs +++ b/quantus_sdk/rust/src/api/airdrop.rs @@ -20,7 +20,9 @@ use qp_poseidon_core::{ }; use qp_poseidon_core_v09 as v09; use qp_rusty_crystals_dilithium::ml_dsa_87; -use qp_rusty_crystals_hdwallet::{derive_wormhole_from_mnemonic, QUANTUS_WORMHOLE_CHAIN_ID}; +use qp_rusty_crystals_hdwallet::{ + generate_wormhole_from_seed, mnemonic_to_seed, SensitiveBytes64, QUANTUS_WORMHOLE_CHAIN_ID, +}; use std::collections::HashMap; const RATE_4: usize = 4; @@ -28,6 +30,10 @@ const WORMHOLE_SALT: &[u8] = b"wormhole"; /// Only proofs for the current wormhole scheme are accepted by the claim server. const CLAIMABLE_WORMHOLE_SCHEME: &str = "wormhole-rate8-compact"; const HD_SCAN_INDEXES: u32 = 17; +/// Middle HD path component. The app uses 0 (external) and 1 (dedicated +/// change branch since July 2026); the CLI's wormhole multiround flow uses it +/// as a round counter (default 2 rounds), so scan several rounds beyond that. +const HD_SCAN_BRANCHES: u32 = 9; /// FIPS 204 context for airdrop claim signatures; must match the claim server. const CLAIM_CONTEXT: &[u8] = b"qp-airdrop-claim-v1"; /// Claim expiry horizon. The server rejects expiries more than 15 min out. @@ -282,9 +288,10 @@ pub struct AirdropMatch { /// `snapshot_addresses` are the SS58 addresses from `GET /snapshot` (or the /// miner-rewards CSV). Dilithium matches are checked against /// `dilithium_public_key` under every historical hash. Wormhole matches are -/// checked for HD-derived secrets (`m/44'/189189189'/0'/{0,1}'/{0..=16}'`) -/// when `mnemonic` is given, plus any `extra_wormhole_secrets` (32 bytes -/// each). +/// checked for HD-derived secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=16}'`, +/// covering the app's external/change branches and the CLI's multiround +/// rounds) when `mnemonic` is given, plus any `extra_wormhole_secrets` +/// (32 bytes each). pub fn find_airdrop_matches( snapshot_addresses: Vec, dilithium_public_key: Option>, @@ -321,13 +328,18 @@ pub fn find_airdrop_matches( let mut secrets: Vec<([u8; 32], String)> = Vec::new(); if let Some(mnemonic) = mnemonic { - for change in 0..=1u32 { + // Stretch the BIP39 seed once, then walk the HD tree per path. + // `mnemonic_to_seed` consumes and zeroizes the mnemonic string. + let mut seed = SensitiveBytes64::zeroed(); + mnemonic_to_seed(mnemonic, None, &mut seed) + .map_err(|e| format!("invalid mnemonic: {e:?}"))?; + for branch in 0..HD_SCAN_BRANCHES { for index in 0..HD_SCAN_INDEXES { let path = format!( "m/44'/{}/0'/{}'/{}'", - QUANTUS_WORMHOLE_CHAIN_ID, change, index + QUANTUS_WORMHOLE_CHAIN_ID, branch, index ); - let pair = derive_wormhole_from_mnemonic(&mnemonic, None, &path) + let pair = generate_wormhole_from_seed(&seed, &path) .map_err(|e| format!("HD derivation failed at {path}: {e:?}"))?; secrets.push((*pair.secret().as_bytes(), path)); } @@ -726,19 +738,32 @@ mod tests { #[test] fn hd_scan_finds_wallet_wormhole_address() { let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let path = format!("m/44'/{}/0'/0'/3'", QUANTUS_WORMHOLE_CHAIN_ID); - let pair = derive_wormhole_from_mnemonic(mnemonic, None, &path).unwrap(); - let current = WORMHOLE_SCHEMES + // App external branch, plus a CLI multiround round-2 address: the + // middle component is a round counter there, not a change branch. + let paths = [ + format!("m/44'/{}/0'/0'/3'", QUANTUS_WORMHOLE_CHAIN_ID), + format!("m/44'/{}/0'/2'/1'", QUANTUS_WORMHOLE_CHAIN_ID), + ]; + let scheme = WORMHOLE_SCHEMES .iter() .find(|s| s.id == CLAIMABLE_WORMHOLE_SCHEME) - .unwrap() - .derive(pair.secret().as_bytes()); - let snapshot = vec![to_ss58(¤t)]; + .unwrap(); + let snapshot: Vec = paths + .iter() + .map(|path| { + let pair = + qp_rusty_crystals_hdwallet::derive_wormhole_from_mnemonic(mnemonic, None, path) + .unwrap(); + to_ss58(&scheme.derive(pair.secret().as_bytes())) + }) + .collect(); let matches = find_airdrop_matches(snapshot.clone(), None, Some(mnemonic.into()), vec![]).unwrap(); - assert_eq!(matches.len(), 1); - assert_eq!(matches[0].source, path); - assert!(matches[0].claimable); + assert_eq!(matches.len(), 2); + let sources: Vec<_> = matches.iter().map(|m| m.source.as_str()).collect(); + assert!(sources.contains(&paths[0].as_str())); + assert!(sources.contains(&paths[1].as_str())); + assert!(matches.iter().all(|m| m.claimable)); } } From 62bbd932a9e31bbcff6591057894b84877f80649 Mon Sep 17 00:00:00 2001 From: illuzen Date: Mon, 14 Sep 2026 17:08:31 +0800 Subject: [PATCH 07/10] Use published qp-ownership crates; align zk family on 4.4.0 The ownership crates are on crates.io as of the v4.4.0 release, so the git-tag workaround is gone. Registry semver unification pulls the qp-wormhole-* family to 4.4.0; the wormhole circuit sources are identical between 4.3.0 and 4.4.0 (only additive config and aggregator prover reuse), so proofs stay chain-compatible. Co-authored-by: Cursor --- quantus_sdk/rust/Cargo.lock | 104 +++++++++++++----------------------- quantus_sdk/rust/Cargo.toml | 20 +++---- 2 files changed, 47 insertions(+), 77 deletions(-) diff --git a/quantus_sdk/rust/Cargo.lock b/quantus_sdk/rust/Cargo.lock index d878d358c..13cf6eb69 100644 --- a/quantus_sdk/rust/Cargo.lock +++ b/quantus_sdk/rust/Cargo.lock @@ -2864,34 +2864,37 @@ dependencies = [ [[package]] name = "qp-ownership-circuit" version = "4.4.0" -source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6dfd3d8f8fba2a5ebc966bb7d53f8eaf0e4582eb58663a549f9a8beb745dbb4" dependencies = [ "anyhow", "qp-ownership-inputs", "qp-plonky2", - "qp-wormhole-circuit 4.4.0", - "qp-zk-circuits-common 4.4.0", + "qp-wormhole-circuit", + "qp-zk-circuits-common", ] [[package]] name = "qp-ownership-inputs" version = "4.4.0" -source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8b25b2a264b401240b011f6748306191478122b7b1fc15e45aad735ed05510" dependencies = [ "anyhow", - "qp-wormhole-inputs 4.4.0", + "qp-wormhole-inputs", ] [[package]] name = "qp-ownership-prover" version = "4.4.0" -source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04ea262e2d923cb1314cfe32b6289da3b3afe3f284e8a749a6ea5eafded3b50b" dependencies = [ "anyhow", "qp-ownership-circuit", "qp-plonky2", - "qp-wormhole-circuit 4.4.0", - "qp-zk-circuits-common 4.4.0", + "qp-wormhole-circuit", + "qp-zk-circuits-common", ] [[package]] @@ -3035,117 +3038,82 @@ dependencies = [ [[package]] name = "qp-wormhole-aggregator" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6604a9d9248e3270d330155fc27a4a84b382849b2aecb1f92a110511314fe07b" +checksum = "9f32f9fbbd49a0655b267a151afe4e6fd38edcad13bce79e487a949073672d46" dependencies = [ "anyhow", "hex", "qp-plonky2", - "qp-wormhole-circuit 4.3.0", - "qp-wormhole-inputs 4.3.0", + "qp-wormhole-circuit", + "qp-wormhole-inputs", "qp-wormhole-prover", - "qp-zk-circuits-common 4.3.0", + "qp-zk-circuits-common", "rand 0.8.6", "serde", "serde_json", ] -[[package]] -name = "qp-wormhole-circuit" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55167daf965a3616b74184171148b94c64b3a48771d6433a1a43377a6e46c6e8" -dependencies = [ - "anyhow", - "hex", - "qp-plonky2", - "qp-wormhole-inputs 4.3.0", - "qp-zk-circuits-common 4.3.0", - "zeroize", -] - [[package]] name = "qp-wormhole-circuit" version = "4.4.0" -source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "997bce0db5f628ff973b9983b167f8499ab5e0aa2b07848d97d6be5e7b0e921a" dependencies = [ "anyhow", "hex", "qp-plonky2", - "qp-wormhole-inputs 4.4.0", - "qp-zk-circuits-common 4.4.0", + "qp-wormhole-inputs", + "qp-zk-circuits-common", "zeroize", ] [[package]] name = "qp-wormhole-circuit-builder" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fe35e0d8ad019e37e447e092da348fe4c52ae691d84dfd919fc9b58e7effe5" +checksum = "372d30af09b9b9fcde24549d40749219ecf4098a48ef7c0cae2cc414141b4435" dependencies = [ "anyhow", "clap", "qp-plonky2", "qp-wormhole-aggregator", - "qp-wormhole-circuit 4.3.0", - "qp-zk-circuits-common 4.3.0", + "qp-wormhole-circuit", + "qp-zk-circuits-common", "rand 0.8.6", ] -[[package]] -name = "qp-wormhole-inputs" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c07863a2211a17b46319289c5ef22e5670a3fb8653386bb366c418193254793" -dependencies = [ - "anyhow", -] - [[package]] name = "qp-wormhole-inputs" version = "4.4.0" -source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" -dependencies = [ - "anyhow", -] - -[[package]] -name = "qp-wormhole-prover" -version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7203e454113812e1ca85aee7687dc5f270f7c297d113a647a3a00b6d43c5a4f6" +checksum = "536c7358f87bb209d3113e89763a1bc7a45a2cd4fc82968869f1be3cdd59a21a" dependencies = [ "anyhow", - "qp-plonky2", - "qp-wormhole-circuit 4.3.0", - "qp-zk-circuits-common 4.3.0", ] [[package]] -name = "qp-zk-circuits-common" -version = "4.3.0" +name = "qp-wormhole-prover" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec45510701f160fdb730bdb622d6cdd0a62547e607c9b1ecb19558202797f81" +checksum = "ee1fd5f90cfca2e5f01d7e36fe35d417b541443d7e6548d99b7e73b0c3ce75c4" dependencies = [ "anyhow", "qp-plonky2", - "qp-poseidon-core 3.1.0", - "qp-wormhole-inputs 4.3.0", - "rand 0.8.6", - "serde", - "serde_json", + "qp-wormhole-circuit", + "qp-zk-circuits-common", ] [[package]] name = "qp-zk-circuits-common" version = "4.4.0" -source = "git+https://github.com/Quantus-Network/qp-zk-circuits?tag=v4.4.0#c2ceefacd28fc800f0f1a2e4236d5d695f4239d2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10135f3f253f33423341f97c71de9eeaf39fe7f1d791e1cfab026c14fdf172bd" dependencies = [ "anyhow", "qp-plonky2", "qp-poseidon-core 3.1.0", - "qp-wormhole-inputs 4.4.0", + "qp-wormhole-inputs", "rand 0.8.6", "serde", "serde_json", @@ -3381,11 +3349,11 @@ dependencies = [ "qp-rusty-crystals-dilithium", "qp-rusty-crystals-hdwallet", "qp-wormhole-aggregator", - "qp-wormhole-circuit 4.3.0", + "qp-wormhole-circuit", "qp-wormhole-circuit-builder", - "qp-wormhole-inputs 4.3.0", + "qp-wormhole-inputs", "qp-wormhole-prover", - "qp-zk-circuits-common 4.3.0", + "qp-zk-circuits-common", "quantus_ur", "sp-core", "sp-runtime", diff --git a/quantus_sdk/rust/Cargo.toml b/quantus_sdk/rust/Cargo.toml index cc24f7c35..3c6c98c22 100644 --- a/quantus_sdk/rust/Cargo.toml +++ b/quantus_sdk/rust/Cargo.toml @@ -25,16 +25,18 @@ quantus_ur = { git = "https://github.com/Quantus-Network/quantus_ur.git", tag = # ZK proof generation (aligned with quantus-cli / chain) anyhow = "1.0" blake3 = "1.8" -# Airdrop ownership proofs. Git source so the wormhole 4.3.0 crates below stay untouched. -qp-ownership-circuit = { version = "4.4.0", git = "https://github.com/Quantus-Network/qp-zk-circuits", tag = "v4.4.0", default-features = false, features = ["std"] } -qp-ownership-prover = { version = "4.4.0", git = "https://github.com/Quantus-Network/qp-zk-circuits", tag = "v4.4.0", default-features = false, features = ["std"] } +# Airdrop ownership proofs (published on crates.io as of the v4.4.0 release). +# The wormhole circuit sources are identical between 4.3.0 and 4.4.0, so the +# family-wide bump keeps wormhole proofs chain-compatible. +qp-ownership-circuit = { version = "4.4.0", default-features = false, features = ["std"] } +qp-ownership-prover = { version = "4.4.0", default-features = false, features = ["std"] } qp-plonky2 = { version = "1.5.5", default-features = false, features = ["rand", "std"] } -qp-wormhole-circuit = { version = "4.3.0", default-features = false, features = ["std"] } -qp-wormhole-prover = { version = "4.3.0", default-features = false, features = ["std"] } -qp-wormhole-aggregator = { version = "4.3.0", default-features = false, features = ["std"] } -qp-wormhole-inputs = { version = "4.3.0", default-features = false, features = ["std"] } -qp-zk-circuits-common = { version = "4.3.0", default-features = false, features = ["std"] } -qp-wormhole-circuit-builder = { version = "4.3.0" } +qp-wormhole-circuit = { version = "4.4.0", default-features = false, features = ["std"] } +qp-wormhole-prover = { version = "4.4.0", default-features = false, features = ["std"] } +qp-wormhole-aggregator = { version = "4.4.0", default-features = false, features = ["std"] } +qp-wormhole-inputs = { version = "4.4.0", default-features = false, features = ["std"] } +qp-zk-circuits-common = { version = "4.4.0", default-features = false, features = ["std"] } +qp-wormhole-circuit-builder = { version = "4.4.0" } [dev-dependencies] tempfile = "3" From f5ef9390e1c7d3392eae1cc57d2b085c2046ec2d Mon Sep 17 00:00:00 2001 From: illuzen Date: Mon, 14 Sep 2026 17:24:52 +0800 Subject: [PATCH 08/10] Heap zeroization for airdrop matching and proving Port the quantus-cli secret-hygiene fixes: preimages are built in pre-sized zeroize-on-drop SensitiveFelts buffers; the v0.9.5 sponge is rebuilt locally from public crates and hashes secrets from borrowed slices (the historical crate's hash_no_pad consumes and frees its Vec unscrubbed); the rate-4 sponge wipes its stack state; the prover boundary uses Secret::new and scrubs the FFI-owned Vec; the HD scan buffer is wiped before it is freed. An allocator-based regression test scans every freed block for the secret in raw-byte and felt encodings with no exemptions; golden vectors pin the local sponge to the historical hasher. Co-authored-by: Cursor --- quantus_sdk/rust/Cargo.lock | 4 + quantus_sdk/rust/Cargo.toml | 7 + quantus_sdk/rust/src/api/airdrop.rs | 320 ++++++++++++++++++++++++++-- 3 files changed, 310 insertions(+), 21 deletions(-) diff --git a/quantus_sdk/rust/Cargo.lock b/quantus_sdk/rust/Cargo.lock index 13cf6eb69..e2ed7931a 100644 --- a/quantus_sdk/rust/Cargo.lock +++ b/quantus_sdk/rust/Cargo.lock @@ -3340,6 +3340,9 @@ dependencies = [ "blake3", "flutter_rust_bridge", "hex", + "p3-field", + "p3-goldilocks", + "p3-symmetric", "parity-scale-codec", "qp-ownership-circuit", "qp-ownership-prover", @@ -3355,6 +3358,7 @@ dependencies = [ "qp-wormhole-prover", "qp-zk-circuits-common", "quantus_ur", + "rand_chacha 0.9.0", "sp-core", "sp-runtime", "tempfile", diff --git a/quantus_sdk/rust/Cargo.toml b/quantus_sdk/rust/Cargo.toml index 3c6c98c22..36cd56410 100644 --- a/quantus_sdk/rust/Cargo.toml +++ b/quantus_sdk/rust/Cargo.toml @@ -30,6 +30,13 @@ blake3 = "1.8" # family-wide bump keeps wormhole proofs chain-compatible. qp-ownership-circuit = { version = "4.4.0", default-features = false, features = ["std"] } qp-ownership-prover = { version = "4.4.0", default-features = false, features = ["std"] } +# Building blocks of qp-poseidon-core 0.9.5's hasher. The local v09 sponge in +# api/airdrop.rs hashes secret preimages from borrowed slices because the +# 0.9.5 crate's hash_no_pad consumes its Vec and frees it unscrubbed. +p3-field = { version = "0.3.0", default-features = false } +p3-goldilocks = { version = "0.3.0", default-features = false } +p3-symmetric = { version = "0.3.0", default-features = false } +rand_chacha = { version = "0.9", default-features = false } qp-plonky2 = { version = "1.5.5", default-features = false, features = ["rand", "std"] } qp-wormhole-circuit = { version = "4.4.0", default-features = false, features = ["std"] } qp-wormhole-prover = { version = "4.4.0", default-features = false, features = ["std"] } diff --git a/quantus_sdk/rust/src/api/airdrop.rs b/quantus_sdk/rust/src/api/airdrop.rs index 13a54afe9..d6636772d 100644 --- a/quantus_sdk/rust/src/api/airdrop.rs +++ b/quantus_sdk/rust/src/api/airdrop.rs @@ -74,6 +74,142 @@ fn compact8_decode(bytes: &[u8; 32]) -> [Goldilocks; POSEIDON2_OUTPUT] { qp_poseidon_core::serialization::bytes_to_digest_lossy(bytes) } +/// Zero field elements, resistant to dead-store elimination: `black_box` +/// makes the compiler assume the zeros are observed, so the fill cannot be +/// elided (same construction as qp-poseidon-core's internal state wipe). +fn wipe_felts(felts: &mut [Goldilocks]) { + felts.fill(Goldilocks::ZERO); + core::hint::black_box(felts); +} + +/// Zero bytes, resistant to dead-store elimination. +fn wipe_bytes(bytes: &mut [u8]) { + bytes.fill(0); + core::hint::black_box(bytes); +} + +/// Heap buffer for secret-bearing field elements. The full capacity must be +/// reserved before secret material is written (a growing `Vec` frees its old +/// block unscrubbed); limbs are wiped on drop. Generic so both the current +/// qp-poseidon-core felts and the v09 p3-goldilocks felts are covered. +struct SensitiveFelts { + felts: Vec, + zero: F, +} + +impl SensitiveFelts { + fn with_capacity(zero: F, capacity: usize) -> Self { + Self { + felts: Vec::with_capacity(capacity), + zero, + } + } + + fn push(&mut self, felt: F) { + debug_assert!( + self.felts.len() < self.felts.capacity(), + "SensitiveFelts must be pre-sized" + ); + self.felts.push(felt); + } + + fn as_slice(&self) -> &[F] { + &self.felts + } +} + +impl Drop for SensitiveFelts { + fn drop(&mut self) { + // Same dead-store-resistant wipe as `wipe_felts`. + self.felts.fill(self.zero); + core::hint::black_box(self.felts.as_mut_slice()); + } +} + +/// The injective 4-bytes-per-felt encoding of a 32-byte secret, as canonical +/// limb values: eight little-endian u32 words plus the `1` terminator +/// (32 % 4 == 0, so the terminator is always appended). Matches both +/// `injective4` and v0.9.5's `injective_bytes_to_felts` without materializing +/// an intermediate felt buffer. +fn injective4_secret_words(secret: &[u8; 32]) -> impl Iterator + '_ { + secret + .chunks(4) + .map(|chunk| u32::from_le_bytes(chunk.try_into().expect("4-byte chunk")) as u64) + .chain([1u64]) +} + +/// Field element of qp-poseidon-core 0.9.5 (plonky3 Goldilocks 0.3.0). +type V09Felt = p3_goldilocks::Goldilocks; + +/// The v0.9.5 Poseidon2 permutation, rebuilt from the same public crates the +/// historical qp-poseidon-core used: ChaCha8-derived constants with seed +/// 0x189189189189189 over `Poseidon2Goldilocks<12>`. Equality with +/// `Poseidon2Core::new()` is pinned by the wormhole-v09-injective golden +/// vector test. +fn v09_permutation() -> &'static p3_goldilocks::Poseidon2Goldilocks<12> { + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + static PERMUTATION: std::sync::OnceLock> = + std::sync::OnceLock::new(); + PERMUTATION.get_or_init(|| { + const V09_POSEIDON2_SEED: u64 = 0x189189189189189; + let mut rng = ChaCha8Rng::seed_from_u64(V09_POSEIDON2_SEED); + p3_goldilocks::Poseidon2Goldilocks::<12>::new_from_rng_128(&mut rng) + }) +} + +/// qp-poseidon-core 0.9.5's `hash_no_pad` sponge (rate 4, terminator felt in +/// the last short block, `[1,0,0,0]` block after a full final chunk, then an +/// unconditional `[0,0,0,1]` domain block), reimplemented over a borrowed +/// slice with stack state. The 0.9.5 crate's own `hash_no_pad` takes its +/// preimage `Vec` by value and frees it unscrubbed, so it must never see a +/// secret. +fn v09_hash_no_pad(input: &[V09Felt]) -> [u8; 32] { + use p3_field::{PrimeCharacteristicRing, PrimeField64}; + use p3_symmetric::Permutation; + const WIDTH: usize = 12; + const RATE: usize = 4; + + let wipe = |felts: &mut [V09Felt]| { + felts.fill(V09Felt::ZERO); + core::hint::black_box(felts); + }; + + let poseidon2 = v09_permutation(); + let mut state = [V09Felt::ZERO; WIDTH]; + let mut block = [V09Felt::ZERO; RATE]; + let num_chunks = input.chunks(RATE).len(); + let mut unpadded = false; + for (j, chunk) in input.chunks(RATE).enumerate() { + block.fill(V09Felt::ZERO); + if j == num_chunks - 1 { + if chunk.len() < RATE { + block[chunk.len()] = V09Felt::ONE; + } else { + unpadded = true; + } + } + block[..chunk.len()].copy_from_slice(chunk); + for i in 0..RATE { + state[i] += block[i]; + } + poseidon2.permute_mut(&mut state); + } + if unpadded { + state[0] += V09Felt::ONE; + poseidon2.permute_mut(&mut state); + } + state[RATE - 1] += V09Felt::ONE; + poseidon2.permute_mut(&mut state); + + let mut out = [0u8; 32]; + for (i, felt) in state[..RATE].iter().enumerate() { + out[i * 8..(i + 1) * 8].copy_from_slice(&felt.as_canonical_u64().to_le_bytes()); + } + wipe(&mut state); + wipe(&mut block); + out +} + /// v0.9.5 injective 4-bytes/felt encoding (terminator in the last word or as an extra felt). fn injective4(bytes: &[u8]) -> Vec { if bytes.is_empty() { @@ -134,6 +270,10 @@ fn hash_felts_rate4_pad10(x: &[Goldilocks]) -> [u8; 32] { let digest: [Goldilocks; POSEIDON2_OUTPUT] = state[..POSEIDON2_OUTPUT] .try_into() .expect("width > output"); + // The absorb buffer holds raw preimage felts and the state is the + // permuted secret; wipe both before returning. + wipe_felts(&mut state); + wipe_felts(&mut buf); digest_to_bytes(&digest) } @@ -218,25 +358,55 @@ const WORMHOLE_SCHEMES: &[WormholeSchemeDef] = &[ ]; impl WormholeSchemeDef { + /// All preimages holding the secret are built in pre-sized + /// zeroize-on-drop buffers and hashed from borrowed slices, so no heap + /// allocation containing the secret is ever freed unscrubbed (verified by + /// the allocator test in `heap_zeroization_tests`). fn derive(&self, secret: &[u8; 32]) -> [u8; 32] { + use p3_field::{integers::QuotientMap, PrimeCharacteristicRing}; if self.sponge == Sponge::V09 { - let core = v09::Poseidon2Core::new(); - let mut preimage = v09::injective_bytes_to_felts(WORMHOLE_SALT); + let salt = v09::injective_bytes_to_felts(WORMHOLE_SALT); + let mut preimage = SensitiveFelts::with_capacity(V09Felt::ZERO, salt.len() + 9); + for felt in salt { + preimage.push(felt); + } match self.secret_encoding { SecretEncoding::Injective4 => { - preimage.extend(v09::injective_bytes_to_felts(secret)) + for word in injective4_secret_words(secret) { + preimage.push(V09Felt::from_int(word)); + } + } + SecretEncoding::Compact8 => { + for chunk in secret.chunks(8) { + let word = u64::from_le_bytes(chunk.try_into().expect("8-byte chunk")); + preimage.push(V09Felt::from_int(word)); + } } - SecretEncoding::Compact8 => preimage.extend(v09::digest_bytes_to_felts(secret)), } - let first_hash = core.hash_no_pad(preimage); - return core.hash_no_pad(v09::digest_bytes_to_felts(&first_hash)); + let first_hash = v09_hash_no_pad(preimage.as_slice()); + return v09_hash_no_pad(&v09::digest_bytes_to_felts(&first_hash)); + } + let salt = injective4(WORMHOLE_SALT); + // injective4 of a 32-byte secret is exactly 9 felts; compact8 is 4. + let mut preimage = SensitiveFelts::with_capacity(Goldilocks::ZERO, salt.len() + 9); + for felt in salt { + preimage.push(felt); } - let mut preimage = injective4(WORMHOLE_SALT); match self.secret_encoding { - SecretEncoding::Injective4 => preimage.extend(injective4(secret)), - SecretEncoding::Compact8 => preimage.extend(compact8_decode(secret)), + SecretEncoding::Injective4 => { + for word in injective4_secret_words(secret) { + preimage.push(Goldilocks::from_u64(word)); + } + } + SecretEncoding::Compact8 => { + let mut digest = compact8_decode(secret); + for felt in digest { + preimage.push(felt); + } + wipe_felts(&mut digest); + } } - let first_hash = self.sponge.hash_felts(&preimage); + let first_hash = self.sponge.hash_felts(preimage.as_slice()); self.sponge.rehash(&first_hash) } } @@ -345,12 +515,12 @@ pub fn find_airdrop_matches( } } } - for (i, secret) in extra_wormhole_secrets.iter().enumerate() { - let secret: [u8; 32] = secret - .as_slice() - .try_into() - .map_err(|_| format!("wormhole secret #{i} must be 32 bytes"))?; - secrets.push((secret, format!("provided secret #{i}"))); + let mut extra_wormhole_secrets = extra_wormhole_secrets; + for (i, secret) in extra_wormhole_secrets.iter_mut().enumerate() { + let copy: Result<[u8; 32], _> = secret.as_slice().try_into(); + wipe_bytes(secret); + let copy = copy.map_err(|_| format!("wormhole secret #{i} must be 32 bytes"))?; + secrets.push((copy, format!("provided secret #{i}"))); } let mut seen: Vec<([u8; 32], &'static str)> = Vec::new(); @@ -374,6 +544,13 @@ pub fn find_airdrop_matches( } } + // The scan buffer holds every candidate spend secret; wipe it before the + // backing allocation is freed. (Matched secrets intentionally survive in + // the returned `AirdropMatch`es — the caller needs them to prove.) + for (secret, _) in secrets.iter_mut() { + wipe_bytes(secret); + } + Ok(matches) } @@ -455,14 +632,23 @@ pub struct WormholeClaimBody { /// circuit in-process: expect tens of seconds of CPU on first call. Submit as /// `{"kind": "wormhole", ...fields}`. pub fn prove_airdrop_wormhole( - wormhole_secret: Vec, + mut wormhole_secret: Vec, claim_account: String, ) -> Result { - let secret_bytes: [u8; 32] = wormhole_secret - .try_into() - .map_err(|_| "wormhole secret must be 32 bytes")?; + // Copy into a fixed buffer and scrub both the FFI-owned Vec and (via + // `Secret::new`, which zeroizes its source) the copy, so no unscrubbed + // Rust-side copy of the secret outlives this call. + let valid_len = wormhole_secret.len() == 32; + let mut secret_bytes = [0u8; 32]; + if valid_len { + secret_bytes.copy_from_slice(&wormhole_secret); + } + wipe_bytes(&mut wormhole_secret); + if !valid_len { + return Err("wormhole secret must be 32 bytes".into()); + } let secret = - Secret::try_from(secret_bytes).map_err(|e| format!("invalid wormhole secret: {e:?}"))?; + Secret::new(&mut secret_bytes).map_err(|e| format!("invalid wormhole secret: {e:?}"))?; let claim_bytes = decode_account("claim account", &claim_account)?; let claim = BytesDigest::try_from(claim_bytes.as_slice()) .map_err(|e| format!("invalid claim account: {e:?}"))?; @@ -767,3 +953,95 @@ mod tests { assert!(matches.iter().all(|m| m.claimable)); } } + +/// Regression test (security review): wormhole address matching must never +/// free heap memory that still contains the spend secret. +/// +/// Mirroring `heap_zeroization.rs` in qp-zk-circuits, a global allocator +/// scans every freed block for the secret at `dealloc` time (the block is +/// still valid inside the hook). Two byte images are searched, because the +/// secret appears in two encodings: the raw 32 bytes (also the in-memory +/// image of the compact8 felt encoding — every 8-byte limb of the ASCII +/// pattern is canonical), and the injective4 felt image (4 secret bytes then +/// 4 zero bytes per limb). No exemptions: the v09 scheme hashes through the +/// local borrowed-slice sponge precisely so that no allocation holding the +/// secret is ever freed, by anyone. +/// +/// The scanner only reacts to blocks containing the distinctive pattern, so +/// unrelated tests running in the same binary cannot trip it. +#[cfg(test)] +mod heap_zeroization_tests { + use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + use std::{ + alloc::{GlobalAlloc, Layout, System}, + sync::OnceLock, + }; + + use super::{injective4_secret_words, WORMHOLE_SCHEMES}; + + /// Distinctive all-ASCII 32-byte pattern; see module docs for why ASCII + /// makes the compact8 felt image identical to the raw bytes. + const SECRET_PATTERN: [u8; 32] = *b"quantus-sdk-airdrop-zeroize-pat!"; + + static SCANNING: AtomicBool = AtomicBool::new(false); + static LEAKED_BLOCK_SIZE: AtomicUsize = AtomicUsize::new(0); + /// Injective4 felt image of the pattern (precomputed: the dealloc hook + /// should not allocate). + static INJECTIVE4_IMAGE: OnceLock> = OnceLock::new(); + + fn contains(haystack: &[u8], needle: &[u8]) -> bool { + haystack.windows(needle.len()).any(|w| w == needle) + } + + struct SecretScanningAllocator; + + unsafe impl GlobalAlloc for SecretScanningAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + unsafe { System.alloc(layout) } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + if SCANNING.load(Ordering::SeqCst) && layout.size() >= SECRET_PATTERN.len() { + let block = unsafe { core::slice::from_raw_parts(ptr, layout.size()) }; + let hit = contains(block, &SECRET_PATTERN) + || INJECTIVE4_IMAGE + .get() + .is_some_and(|img| contains(block, img)); + if hit { + LEAKED_BLOCK_SIZE.store(layout.size(), Ordering::SeqCst); + } + } + unsafe { System.dealloc(ptr, layout) } + } + } + + #[global_allocator] + static ALLOCATOR: SecretScanningAllocator = SecretScanningAllocator; + + fn injective4_image() -> Vec { + injective4_secret_words(&SECRET_PATTERN) + .take(8) // the terminator limb is not secret material + .flat_map(u64::to_le_bytes) + .collect() + } + + #[test] + fn matching_never_frees_heap_memory_containing_the_secret() { + INJECTIVE4_IMAGE.set(injective4_image()).expect("set once"); + + LEAKED_BLOCK_SIZE.store(0, Ordering::SeqCst); + SCANNING.store(true, Ordering::SeqCst); + for scheme in WORMHOLE_SCHEMES { + let address = scheme.derive(&SECRET_PATTERN); + core::hint::black_box(address); + } + SCANNING.store(false, Ordering::SeqCst); + + let leaked = LEAKED_BLOCK_SIZE.load(Ordering::SeqCst); + assert_eq!( + leaked, 0, + "a heap block of {leaked} bytes still containing the spend secret was freed \ + unscrubbed; check SensitiveFelts pre-sizing and drop in api/airdrop.rs" + ); + } +} From 152297a761f32fd6756e10dc64cf0b1697f0f5bb Mon Sep 17 00:00:00 2001 From: illuzen Date: Mon, 14 Sep 2026 18:18:54 +0800 Subject: [PATCH 09/10] Match Dilithium keys from every historical keygen era, wormhole from Bitcoin-seed tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The address-hash schemes covered how a public key became an AccountId, but the ML-DSA-87 public key for the same mnemonic changed four times: 1. pre Nov 2025: SHAKE256(seed[..32]) expansion, non-HD or soft HD paths (m/44'/189189'/N'/0/0) under the "Bitcoin seed" BIP32 master 2. Nov 2025 (dilithium 2.0.0): FIPS 204 expansion absorbing the whole seed 3. Feb 2026 (CLI): hardened default path and --no-derivation account child 4. Mar 2026 (hdwallet 2.1.0): "Dilithium seed" master, hardened-only — which also changed every wormhole HD entropy find_airdrop_matches now derives candidate keypairs for every era (both seed expansions are rebuilt locally from the current crate's public primitives, in wipeable buffers — the historical crates' keygens free seed-bearing heap copies unscrubbed) and scans wormhole paths under both BIP32 masters plus the legacy master-node secret. Matches carry a dilithium_keygen id; the new build_airdrop_dilithium_claim_from_mnemonic re-derives that era's key and signs with the current crate. Golden vectors are pinned against the shipped dilithium 1.0.3 / 2.0.0 and hdwallet 1.0.0 crates (pk and sk sha256 per era, BIP32 entropies). Co-authored-by: Cursor --- quantus_sdk/lib/src/rust/api/airdrop.dart | 51 +- quantus_sdk/lib/src/rust/frb_generated.dart | 119 +++-- quantus_sdk/rust/Cargo.lock | 22 + quantus_sdk/rust/Cargo.toml | 6 + quantus_sdk/rust/src/api/airdrop.rs | 486 +++++++++++++++++++- quantus_sdk/rust/src/frb_generated.rs | 128 ++++-- quantus_sdk/test/public_exports_test.dart | 1 + 7 files changed, 712 insertions(+), 101 deletions(-) diff --git a/quantus_sdk/lib/src/rust/api/airdrop.dart b/quantus_sdk/lib/src/rust/api/airdrop.dart index 68742e470..bb4d2cd98 100644 --- a/quantus_sdk/lib/src/rust/api/airdrop.dart +++ b/quantus_sdk/lib/src/rust/api/airdrop.dart @@ -7,19 +7,21 @@ import '../frb_generated.dart'; import 'crypto.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; -// These functions are ignored because they are not marked as `pub`: `compact8_decode`, `decode_account`, `derive_dilithium`, `derive`, `hash_felts_rate4_pad10`, `hash_felts`, `hash_padded_legacy`, `hash_padded_v10`, `injective4`, `now_unix`, `rehash` -// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `SecretEncoding`, `Sponge`, `WormholeSchemeDef` -// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `eq`, `fmt`, `fmt` +// These functions are ignored because they are not marked as `pub`: `as_slice`, `compact8_decode`, `decode_account`, `derive_dilithium`, `derive_historical_dilithium`, `derive`, `dilithium_keygen_ids`, `hash_felts_rate4_pad10`, `hash_felts`, `hash_padded_legacy`, `hash_padded_v10`, `injective4_secret_words`, `injective4`, `mldsa87_keypair`, `now_unix`, `push`, `rehash`, `sign_dilithium_claim`, `v09_hash_no_pad`, `v09_permutation`, `wipe_bytes`, `wipe_felts`, `with_capacity` +// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `HistoricalKeypair`, `SecretEncoding`, `SecretKeyBytes`, `SensitiveFelts`, `Sponge`, `WormholeSchemeDef` +// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `drop`, `drop`, `eq`, `fmt`, `fmt` /// Determine which snapshot addresses belong to this wallet. /// /// `snapshot_addresses` are the SS58 addresses from `GET /snapshot` (or the /// miner-rewards CSV). Dilithium matches are checked against -/// `dilithium_public_key` under every historical hash. Wormhole matches are -/// checked for HD-derived secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=16}'`, -/// covering the app's external/change branches and the CLI's multiround -/// rounds) when `mnemonic` is given, plus any `extra_wormhole_secrets` -/// (32 bytes each). +/// `dilithium_public_key` under every historical hash, and — when `mnemonic` +/// is given — against keypairs re-derived under every historical keygen era +/// (see `dilithium_keygen_ids`). Wormhole matches are checked for HD-derived +/// secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=16}'` under both the current +/// "Dilithium seed" and the pre-2.1.0 "Bitcoin seed" BIP32 masters, plus the +/// legacy master-node secret) when `mnemonic` is given, plus any +/// `extra_wormhole_secrets` (32 bytes each). Future> findAirdropMatches({ required List snapshotAddresses, Uint8List? dilithiumPublicKey, @@ -45,6 +47,22 @@ Future buildAirdropDilithiumClaim({ claimAccount: claimAccount, ); +/// Sign an airdrop claim with a key from a historical keygen era: pass the +/// `dilithium_keygen` id from the `AirdropMatch`. Re-derives the era's +/// keypair from the mnemonic, so the wallet does not need to store it. +/// Submit like `build_airdrop_dilithium_claim`'s result. +Future buildAirdropDilithiumClaimFromMnemonic({ + required String mnemonic, + required String dilithiumKeygen, + required String address, + required String claimAccount, +}) => RustLib.instance.api.crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic( + mnemonic: mnemonic, + dilithiumKeygen: dilithiumKeygen, + address: address, + claimAccount: claimAccount, +); + /// Prove ownership of the wormhole address derived from `wormhole_secret` /// (current rate-8 scheme), binding the payout to `claim_account`. Builds the /// circuit in-process: expect tens of seconds of CPU on first call. Submit as @@ -69,14 +87,20 @@ class AirdropMatch { /// Whether the claim server currently accepts proofs for this scheme. final bool claimable; - /// Where the key came from: "dilithium public key", an HD path, or - /// "provided secret #N". + /// Where the key came from: "dilithium public key", an HD path, + /// "provided secret #N", or a historical keygen description. final String source; /// The 32-byte wormhole secret that produced the match (needed to build /// the ownership proof). None for Dilithium matches. final Uint8List? wormholeSecret; + /// For Dilithium matches found through a historical keygen scheme: the + /// keygen id to pass to `build_airdrop_dilithium_claim_from_mnemonic`. + /// None when the match came from the provided current public key (claim + /// with `build_airdrop_dilithium_claim`). + final String? dilithiumKeygen; + const AirdropMatch({ required this.address, required this.kind, @@ -84,6 +108,7 @@ class AirdropMatch { required this.claimable, required this.source, this.wormholeSecret, + this.dilithiumKeygen, }); @override @@ -93,7 +118,8 @@ class AirdropMatch { scheme.hashCode ^ claimable.hashCode ^ source.hashCode ^ - wormholeSecret.hashCode; + wormholeSecret.hashCode ^ + dilithiumKeygen.hashCode; @override bool operator ==(Object other) => @@ -105,7 +131,8 @@ class AirdropMatch { scheme == other.scheme && claimable == other.claimable && source == other.source && - wormholeSecret == other.wormholeSecret; + wormholeSecret == other.wormholeSecret && + dilithiumKeygen == other.dilithiumKeygen; } /// The `POST /claim` body fields for a Dilithium claim. diff --git a/quantus_sdk/lib/src/rust/frb_generated.dart b/quantus_sdk/lib/src/rust/frb_generated.dart index 4787c55f3..60c08e8dc 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.dart @@ -66,7 +66,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.12.0'; @override - int get rustContentHash => 272316986; + int get rustContentHash => -1962506674; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( stem: 'rust_lib_quantus_wallet', @@ -85,6 +85,13 @@ abstract class RustLibApi extends BaseApi { required String claimAccount, }); + Future crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ + required String mnemonic, + required String dilithiumKeygen, + required String address, + required String claimAccount, + }); + String crateApiWormholeComputeAddressHashHex({required List rawAddress}); MerkleProcessed crateApiWormholeComputeMerklePositions({ @@ -263,6 +270,36 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiAirdropBuildAirdropDilithiumClaimConstMeta => const TaskConstMeta(debugName: 'build_airdrop_dilithium_claim', argNames: ['keypair', 'address', 'claimAccount']); + @override + Future crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ + required String mnemonic, + required String dilithiumKeygen, + required String address, + required String claimAccount, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(mnemonic, serializer); + sse_encode_String(dilithiumKeygen, serializer); + sse_encode_String(address, serializer); + sse_encode_String(claimAccount, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3, port: port_); + }, + codec: SseCodec(decodeSuccessData: sse_decode_dilithium_claim_body, decodeErrorData: sse_decode_String), + constMeta: kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta, + argValues: [mnemonic, dilithiumKeygen, address, claimAccount], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta => const TaskConstMeta( + debugName: 'build_airdrop_dilithium_claim_from_mnemonic', + argNames: ['mnemonic', 'dilithiumKeygen', 'address', 'claimAccount'], + ); + @override String crateApiWormholeComputeAddressHashHex({required List rawAddress}) { return handler.executeSync( @@ -270,7 +307,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(rawAddress, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 4)!; }, codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeAddressHashHexConstMeta, @@ -296,7 +333,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(unsortedSiblingsFlat, serializer); sse_encode_list_prim_u_8_loose(leafHash, serializer); sse_encode_u_32(depth, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 4)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; }, codec: SseCodec(decodeSuccessData: sse_decode_merkle_processed, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeMerklePositionsConstMeta, @@ -319,7 +356,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(secret, serializer); sse_encode_u_64(transferCount, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; }, codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeNullifierConstMeta, @@ -339,7 +376,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(secret, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; }, codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeWormholeAddressConstMeta, @@ -358,7 +395,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; }, codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoCrystalAliceConstMeta, @@ -377,7 +414,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; }, codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoCrystalBobConstMeta, @@ -395,7 +432,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; }, codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoCrystalCharlieConstMeta, @@ -415,7 +452,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(leafData, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; }, codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeDecodeLeafAmountConstMeta, @@ -435,7 +472,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(leafData, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; }, codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeDecodeLeafToAccountConstMeta, @@ -455,7 +492,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(leafData, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13)!; }, codec: SseCodec(decodeSuccessData: sse_decode_u_64, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeDecodeLeafTransferCountConstMeta, @@ -475,7 +512,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_String(urParts, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; }, codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiUrDecodeUrConstMeta, @@ -495,7 +532,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(mnemonicStr, serializer); sse_encode_String(path, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15)!; }, codec: SseCodec( decodeSuccessData: sse_decode_wormhole_result, @@ -520,7 +557,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(data, serializer); sse_encode_opt_box_autoadd_u_32(maxFragmentLength, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 16)!; }, codec: SseCodec(decodeSuccessData: sse_decode_list_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiUrEncodeUrConstMeta, @@ -540,7 +577,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(binsDir, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 16, port: port_); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17, port: port_); }, codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeEnsureCircuitBinariesConstMeta, @@ -568,7 +605,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_list_prim_u_8_strict(dilithiumPublicKey, serializer); sse_encode_opt_String(mnemonic, serializer); sse_encode_list_list_prim_u_8_strict(extraWormholeSecrets, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17, port: port_); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18, port: port_); }, codec: SseCodec(decodeSuccessData: sse_decode_list_airdrop_match, decodeErrorData: sse_decode_String), constMeta: kCrateApiAirdropFindAirdropMatchesConstMeta, @@ -590,7 +627,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(firstHashHex, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19)!; }, codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiCryptoFirstHashToAddressConstMeta, @@ -616,7 +653,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(mnemonicStr, serializer); sse_encode_String(path, serializer); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -640,7 +677,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(mnemonicStr, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 21)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -664,7 +701,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(seed, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 21)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 22)!; }, codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoGenerateKeypairFromSeedConstMeta, @@ -690,7 +727,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_proof_input(input, serializer); sse_encode_String(proverBinPath, serializer); sse_encode_String(commonBinPath, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 22, port: port_); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23, port: port_); }, codec: SseCodec(decodeSuccessData: sse_decode_proof_output, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeGenerateProofConstMeta, @@ -709,7 +746,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23, port: port_); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24, port: port_); }, codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), constMeta: kCrateApiCryptoInitAppConstMeta, @@ -728,7 +765,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_String(urParts, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25)!; }, codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), constMeta: kCrateApiUrIsCompleteUrConstMeta, @@ -747,7 +784,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; }, codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiUrMaxUrPartCharsConstMeta, @@ -766,7 +803,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; }, codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiUrMaxUrPartsConstMeta, @@ -791,7 +828,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(signers, serializer); sse_encode_u_32(threshold, serializer); sse_encode_u_64(nonce, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28)!; }, codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiMultisigPredictMultisigAddressConstMeta, @@ -815,7 +852,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(wormholeSecret, serializer); sse_encode_String(claimAccount, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28, port: port_); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29, port: port_); }, codec: SseCodec(decodeSuccessData: sse_decode_wormhole_claim_body, decodeErrorData: sse_decode_String), constMeta: kCrateApiAirdropProveAirdropWormholeConstMeta, @@ -835,7 +872,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30)!; }, codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiCryptoPublicKeyBytesConstMeta, @@ -855,7 +892,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; }, codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiCryptoSecretKeyBytesConstMeta, @@ -875,7 +912,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_u_16(prefix, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; }, codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), constMeta: kCrateApiCryptoSetDefaultSs58PrefixConstMeta, @@ -903,7 +940,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_opt_u_8_array_32(entropy, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; }, codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), constMeta: kCrateApiCryptoSignMessageConstMeta, @@ -931,7 +968,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_opt_u_8_array_32(entropy, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; }, codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), constMeta: kCrateApiCryptoSignMessageWithPubkeyConstMeta, @@ -953,7 +990,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35)!; }, codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiCryptoSignatureBytesConstMeta, @@ -973,7 +1010,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(s, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; }, codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiCryptoSs58ToAccountIdConstMeta, @@ -993,7 +1030,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_box_autoadd_keypair(obj, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!; }, codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), constMeta: kCrateApiCryptoToAccountIdConstMeta, @@ -1021,7 +1058,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_list_prim_u_8_loose(signature, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 38)!; }, codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), constMeta: kCrateApiCryptoVerifyMessageConstMeta, @@ -1042,7 +1079,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_u_32(inputAmount, serializer); sse_encode_u_32(feeBps, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 38)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 39)!; }, codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeWormholeComputeOutputAmountConstMeta, @@ -1061,7 +1098,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 39)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 40)!; }, codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), constMeta: kCrateApiWormholeZkCircuitsVersionConstMeta, @@ -1104,7 +1141,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { AirdropMatch dco_decode_airdrop_match(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); return AirdropMatch( address: dco_decode_String(arr[0]), kind: dco_decode_String(arr[1]), @@ -1112,6 +1149,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { claimable: dco_decode_bool(arr[3]), source: dco_decode_String(arr[4]), wormholeSecret: dco_decode_opt_list_prim_u_8_strict(arr[5]), + dilithiumKeygen: dco_decode_opt_String(arr[6]), ); } @@ -1382,6 +1420,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_claimable = sse_decode_bool(deserializer); var var_source = sse_decode_String(deserializer); var var_wormholeSecret = sse_decode_opt_list_prim_u_8_strict(deserializer); + var var_dilithiumKeygen = sse_decode_opt_String(deserializer); return AirdropMatch( address: var_address, kind: var_kind, @@ -1389,6 +1428,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { claimable: var_claimable, source: var_source, wormholeSecret: var_wormholeSecret, + dilithiumKeygen: var_dilithiumKeygen, ); } @@ -1710,6 +1750,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_bool(self.claimable, serializer); sse_encode_String(self.source, serializer); sse_encode_opt_list_prim_u_8_strict(self.wormholeSecret, serializer); + sse_encode_opt_String(self.dilithiumKeygen, serializer); } @protected diff --git a/quantus_sdk/rust/Cargo.lock b/quantus_sdk/rust/Cargo.lock index e2ed7931a..5a1daca99 100644 --- a/quantus_sdk/rust/Cargo.lock +++ b/quantus_sdk/rust/Cargo.lock @@ -511,6 +511,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + [[package]] name = "base64" version = "0.22.1" @@ -1950,6 +1956,7 @@ dependencies = [ "once_cell", "serdect", "sha2 0.10.9", + "signature", ] [[package]] @@ -2202,6 +2209,19 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "nam-tiny-hderive" +version = "0.3.1-nam.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cd44792ed5cd84dc9dedc3d572242ac00e76c244e85eb4bf34da2c6239ce30" +dependencies = [ + "base58", + "hmac", + "k256", + "sha2 0.10.9", + "zeroize", +] + [[package]] name = "no_std_io2" version = "0.9.4" @@ -3340,6 +3360,7 @@ dependencies = [ "blake3", "flutter_rust_bridge", "hex", + "nam-tiny-hderive", "p3-field", "p3-goldilocks", "p3-symmetric", @@ -3359,6 +3380,7 @@ dependencies = [ "qp-zk-circuits-common", "quantus_ur", "rand_chacha 0.9.0", + "sha2 0.10.9", "sp-core", "sp-runtime", "tempfile", diff --git a/quantus_sdk/rust/Cargo.toml b/quantus_sdk/rust/Cargo.toml index 36cd56410..882f2e64f 100644 --- a/quantus_sdk/rust/Cargo.toml +++ b/quantus_sdk/rust/Cargo.toml @@ -14,6 +14,11 @@ qp-poseidon-core = "3.1.0" qp-poseidon-core-v09 = { package = "qp-poseidon-core", version = "=0.9.5" } qp-rusty-crystals-dilithium = { version = "4.1.1", default-features = false, features = ["ml-dsa-65", "ml-dsa-87"] } qp-rusty-crystals-hdwallet = { version = "4.1.1", features = ["ml-dsa-65"] } +# The pre-March-2026 BIP32 tree ("Bitcoin seed" master HMAC key, soft child +# support): historical Dilithium accounts and wormhole entropies in +# api/airdrop.rs derive through it. Same crate qp-rusty-crystals-hdwallet +# < 2.1.0 used internally. +nam-tiny-hderive = { version = "=0.3.1-nam.1", default-features = false } flutter_rust_bridge = "=2.12.0" hex = "0.4.3" @@ -46,6 +51,7 @@ qp-zk-circuits-common = { version = "4.4.0", default-features = false, features qp-wormhole-circuit-builder = { version = "4.4.0" } [dev-dependencies] +sha2 = "0.10" tempfile = "3" # Circuit generation is CPU-intensive; without this it takes ~10min instead of ~30s. diff --git a/quantus_sdk/rust/src/api/airdrop.rs b/quantus_sdk/rust/src/api/airdrop.rs index d6636772d..b8223b022 100644 --- a/quantus_sdk/rust/src/api/airdrop.rs +++ b/quantus_sdk/rust/src/api/airdrop.rs @@ -14,12 +14,13 @@ //! - late Schrödinger / Dirac (1.0.x–1.1.x): current permutation, rate 4, pad10 //! - late Planck / current (1.2.x+): current permutation, rate 8, pad10 +use nam_tiny_hderive::bip32::ExtendedPrivKey; use qp_ownership_circuit::{BytesDigest, CircuitInputs, Secret}; use qp_poseidon_core::{ serialization::digest_to_bytes, Goldilocks, Poseidon2, POSEIDON2_OUTPUT, SPONGE_WIDTH, }; use qp_poseidon_core_v09 as v09; -use qp_rusty_crystals_dilithium::ml_dsa_87; +use qp_rusty_crystals_dilithium::{fips202, ml_dsa_87, packing, params, poly, polyvec}; use qp_rusty_crystals_hdwallet::{ generate_wormhole_from_seed, mnemonic_to_seed, SensitiveBytes64, QUANTUS_WORMHOLE_CHAIN_ID, }; @@ -430,6 +431,206 @@ fn derive_dilithium(scheme: &str, public_key: &[u8]) -> [u8; 32] { } } +// --------------------------------------------------------------------------- +// Historical ML-DSA-87 keygens +// --------------------------------------------------------------------------- +// +// The address-hash schemes above cover how a *public key* became an +// AccountId. The public key produced from the same mnemonic changed too: +// +// 1. pre Nov 2025 (qp-rusty-crystals-dilithium < 2.0.0): keygen expanded +// SHAKE256(seed[..32]) with no domain separation. Wallets were non-HD +// (`Keypair::generate(seed64)`, which absorbed only the first 32 bytes) +// or, after the multiple-accounts feature, HD at m/44'/189189'/N'/0/0 +// under the BIP32 master HMAC key "Bitcoin seed" with a soft tail. +// 2. Nov 2025 (dilithium 2.0.0): FIPS 204 keygen, SHAKE256(seed ‖ K ‖ L) +// absorbing the whole input. Same "Bitcoin seed" paths; the non-HD +// account went from an effective 32-byte input to the full 64-byte seed. +// 3. Feb 2026 (CLI only): default path hardened to m/44'/189189'/0'/0'/0'; +// --no-derivation wallets became the BIP32 child at m/44'/189189'/0'. +// 4. Mar 2026 (hdwallet 2.1.0): the BIP32 master HMAC key became +// "Dilithium seed" (hardened-only), changing every HD key again; the +// non-HD legacy account became FIPS keygen over seed64[..32]. This is +// the current scheme. +// +// Matching therefore derives candidate keypairs for every era and hashes +// each candidate public key under every address scheme. + +/// BIP44 coin type for Dilithium keys (the wormhole coin type is 189189189'). +const DILITHIUM_CHAIN_ID: &str = "189189'"; +/// Account indexes scanned per HD keygen family, same breadth as the +/// wormhole branch scan. +const DILITHIUM_SCAN_ACCOUNTS: u32 = 9; + +/// Secret-key bytes that wipe themselves on drop. +struct SecretKeyBytes(Vec); + +impl Drop for SecretKeyBytes { + fn drop(&mut self) { + wipe_bytes(&mut self.0); + } +} + +struct HistoricalKeypair { + public: Vec, + secret: SecretKeyBytes, +} + +/// Every mnemonic→ML-DSA-87-keypair scheme a snapshot key may have used. +/// Ids are opaque to callers ([`derive_historical_dilithium`] parses them): +/// `v1`/`fips` selects the seed expansion, and the source is `seed` (the +/// 64-byte BIP39 seed), `seed32` (its first half, the chain's `from_seed`), +/// `bip32:` ("Bitcoin seed" BIP32 entropy), or `hd:` (current +/// "Dilithium seed" derivation). +fn dilithium_keygen_ids() -> Vec { + let mut ids = vec![ + // Era 1 non-HD (absorbs seed64[..32]); era 2 non-HD; era 4 legacy. + "v1:seed".to_string(), + "fips:seed".to_string(), + "fips:seed32".to_string(), + ]; + for account in 0..DILITHIUM_SCAN_ACCOUNTS { + // Era 1 and era 2 app/CLI accounts (soft tail), era 3 CLI hardened + // default and --no-derivation account child, era 4 current HD. + ids.push(format!( + "v1:bip32:m/44'/{DILITHIUM_CHAIN_ID}/{account}'/0/0" + )); + ids.push(format!( + "fips:bip32:m/44'/{DILITHIUM_CHAIN_ID}/{account}'/0/0" + )); + ids.push(format!( + "fips:bip32:m/44'/{DILITHIUM_CHAIN_ID}/{account}'/0'/0'" + )); + ids.push(format!("fips:bip32:m/44'/{DILITHIUM_CHAIN_ID}/{account}'")); + ids.push(format!( + "fips:hd:m/44'/{DILITHIUM_CHAIN_ID}/{account}'/0'/0'" + )); + } + ids +} + +/// Derive the ML-DSA-87 keypair for a historical keygen id from the BIP39 +/// seed. The secret key comes back in a self-wiping buffer; intermediate +/// entropy buffers are wiped before returning. +fn derive_historical_dilithium( + seed: &SensitiveBytes64, + id: &str, +) -> Result { + let (expansion, source) = id + .split_once(':') + .ok_or_else(|| format!("malformed keygen id {id:?}"))?; + let v1 = match expansion { + "v1" => true, + "fips" => false, + _ => return Err(format!("unknown keygen era in {id:?}")), + }; + if source == "seed" { + return Ok(mldsa87_keypair(seed.as_bytes(), v1)); + } + if source == "seed32" { + return Ok(mldsa87_keypair(&seed.as_bytes()[..32], v1)); + } + if let Some(path) = source.strip_prefix("bip32:") { + let mut entropy = ExtendedPrivKey::derive(seed.as_bytes(), path) + .map_err(|e| format!("BIP32 derivation failed at {path}: {e:?}"))? + .secret(); + let keypair = mldsa87_keypair(&entropy, v1); + wipe_bytes(&mut entropy); + return Ok(keypair); + } + if let Some(path) = source.strip_prefix("hd:") { + if v1 { + return Err(format!( + "keygen id {id:?} is inconsistent: no v1-era wallet used the Dilithium-seed tree" + )); + } + let keypair = qp_rusty_crystals_hdwallet::ml_dsa_87::derive_key_from_seed(seed, path) + .map_err(|e| format!("HD derivation failed at {path}: {e:?}"))?; + // `to_bytes` returns a `Zeroizing` buffer, wiped when it drops here. + let secret = SecretKeyBytes(keypair.secret().to_bytes().to_vec()); + return Ok(HistoricalKeypair { + public: keypair.public().to_bytes().to_vec(), + secret, + }); + } + Err(format!("unknown keygen source in {id:?}")) +} + +/// ML-DSA-87 key generation with a selectable seed expansion, built from the +/// current crate's public primitives (its `keypair_var` is not public, and +/// the historical crates' own keygens copy the seed into heap buffers they +/// free unscrubbed). `v1` selects the pre-FIPS expansion — SHAKE256 over the +/// first 32 seed bytes with no `K ‖ L` domain suffix; otherwise the FIPS 204 +/// expansion absorbs the whole seed plus the suffix. Byte-equality of both +/// keypairs with the shipped dilithium 1.0.3 / 2.0.0 keygens is pinned by +/// golden vectors in the tests. +fn mldsa87_keypair(seed: &[u8], v1: bool) -> HistoricalKeypair { + use params::ml_dsa_87::{ETA, K, L, PUBLICKEYBYTES, SECRETKEYBYTES}; + use params::{CRHBYTES, SEEDBYTES, TR_BYTES}; + use polyvec::Polyvec; + + debug_assert!(seed.len() == 32 || seed.len() == 64); + let mut seedbuf = [0u8; 2 * SEEDBYTES + CRHBYTES]; + if v1 { + // The v1 expansion reads exactly SEEDBYTES from its input. + fips202::shake256(&mut seedbuf, &seed[..SEEDBYTES]); + } else { + let mut preimage = [0u8; 64 + 2]; + preimage[..seed.len()].copy_from_slice(seed); + preimage[seed.len()] = K as u8; + preimage[seed.len() + 1] = L as u8; + fips202::shake256(&mut seedbuf, &preimage[..seed.len() + 2]); + wipe_bytes(&mut preimage); + } + + let mut rho = [0u8; SEEDBYTES]; + rho.copy_from_slice(&seedbuf[..SEEDBYTES]); + let mut rhoprime = [0u8; CRHBYTES]; + rhoprime.copy_from_slice(&seedbuf[SEEDBYTES..SEEDBYTES + CRHBYTES]); + let mut key = [0u8; SEEDBYTES]; + key.copy_from_slice(&seedbuf[SEEDBYTES + CRHBYTES..]); + wipe_bytes(&mut seedbuf); + + let mut s1 = Polyvec::::default(); + for (i, p) in s1.vec.iter_mut().enumerate() { + poly::uniform_eta::(p, &rhoprime, i as u16); + } + let mut s2 = Polyvec::::default(); + for (i, p) in s2.vec.iter_mut().enumerate() { + poly::uniform_eta::(p, &rhoprime, (L + i) as u16); + } + wipe_bytes(&mut rhoprime); + + let mut s1hat = s1.clone(); + polyvec::ntt(&mut s1hat); + let mut t1 = Polyvec::::default(); + polyvec::matrix_pointwise_montgomery_streamed(&mut t1, &rho, &s1hat); + polyvec::reduce(&mut t1); + polyvec::invntt_tomont(&mut t1); + polyvec::add(&mut t1, &s2); + polyvec::caddq(&mut t1); + let mut t0 = Polyvec::::default(); + polyvec::power2round(&mut t1, &mut t0); + + let mut pk = [0u8; PUBLICKEYBYTES]; + packing::pack_pk::(&mut pk, &rho, &t1); + let mut tr = [0u8; TR_BYTES]; + fips202::shake256(&mut tr, &pk); + let mut sk = [0u8; SECRETKEYBYTES]; + packing::pack_sk::(&mut sk, &rho, &tr, &key, &t0, &s1, &s2); + wipe_bytes(&mut key); + + // s1, s2, t0, and s1hat wipe themselves on drop (Polyvec is + // ZeroizeOnDrop); the packed sk moves into a self-wiping buffer and its + // stack copy is scrubbed here. + let secret = SecretKeyBytes(sk.to_vec()); + wipe_bytes(&mut sk); + HistoricalKeypair { + public: pk.to_vec(), + secret, + } +} + // --------------------------------------------------------------------------- // FFI surface // --------------------------------------------------------------------------- @@ -445,23 +646,30 @@ pub struct AirdropMatch { pub scheme: String, /// Whether the claim server currently accepts proofs for this scheme. pub claimable: bool, - /// Where the key came from: "dilithium public key", an HD path, or - /// "provided secret #N". + /// Where the key came from: "dilithium public key", an HD path, + /// "provided secret #N", or a historical keygen description. pub source: String, /// The 32-byte wormhole secret that produced the match (needed to build /// the ownership proof). None for Dilithium matches. pub wormhole_secret: Option>, + /// For Dilithium matches found through a historical keygen scheme: the + /// keygen id to pass to `build_airdrop_dilithium_claim_from_mnemonic`. + /// None when the match came from the provided current public key (claim + /// with `build_airdrop_dilithium_claim`). + pub dilithium_keygen: Option, } /// Determine which snapshot addresses belong to this wallet. /// /// `snapshot_addresses` are the SS58 addresses from `GET /snapshot` (or the /// miner-rewards CSV). Dilithium matches are checked against -/// `dilithium_public_key` under every historical hash. Wormhole matches are -/// checked for HD-derived secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=16}'`, -/// covering the app's external/change branches and the CLI's multiround -/// rounds) when `mnemonic` is given, plus any `extra_wormhole_secrets` -/// (32 bytes each). +/// `dilithium_public_key` under every historical hash, and — when `mnemonic` +/// is given — against keypairs re-derived under every historical keygen era +/// (see `dilithium_keygen_ids`). Wormhole matches are checked for HD-derived +/// secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=16}'` under both the current +/// "Dilithium seed" and the pre-2.1.0 "Bitcoin seed" BIP32 masters, plus the +/// legacy master-node secret) when `mnemonic` is given, plus any +/// `extra_wormhole_secrets` (32 bytes each). pub fn find_airdrop_matches( snapshot_addresses: Vec, dilithium_public_key: Option>, @@ -477,12 +685,14 @@ pub fn find_airdrop_matches( } let mut matches = Vec::new(); + let mut seen_dilithium: Vec<([u8; 32], &str)> = Vec::new(); if let Some(public_key) = dilithium_public_key { if !public_key.is_empty() { for scheme in DILITHIUM_SCHEMES { let account = derive_dilithium(scheme, &public_key); if let Some(address) = by_account.get(&account) { + seen_dilithium.push((account, scheme)); matches.push(AirdropMatch { address: address.clone(), kind: "dilithium".into(), @@ -490,6 +700,7 @@ pub fn find_airdrop_matches( claimable: true, source: "dilithium public key".into(), wormhole_secret: None, + dilithium_keygen: None, }); } } @@ -503,6 +714,34 @@ pub fn find_airdrop_matches( let mut seed = SensitiveBytes64::zeroed(); mnemonic_to_seed(mnemonic, None, &mut seed) .map_err(|e| format!("invalid mnemonic: {e:?}"))?; + + // Dilithium keys under every historical keygen era. The current-era + // candidates can duplicate a match already found through the provided + // public key; the (account, scheme) dedupe keeps the pubkey-based + // match, whose claim path needs no mnemonic. + for id in dilithium_keygen_ids() { + let keypair = derive_historical_dilithium(&seed, &id)?; + for scheme in DILITHIUM_SCHEMES { + let account = derive_dilithium(scheme, &keypair.public); + if let Some(address) = by_account.get(&account) { + if seen_dilithium.contains(&(account, scheme)) { + continue; + } + seen_dilithium.push((account, scheme)); + matches.push(AirdropMatch { + address: address.clone(), + kind: "dilithium".into(), + scheme: (*scheme).into(), + claimable: true, + source: format!("mnemonic keygen {id}"), + wormhole_secret: None, + dilithium_keygen: Some(id.clone()), + }); + } + } + } + + // Wormhole secrets under the current "Dilithium seed" tree. for branch in 0..HD_SCAN_BRANCHES { for index in 0..HD_SCAN_INDEXES { let path = format!( @@ -514,6 +753,26 @@ pub fn find_airdrop_matches( secrets.push((*pair.secret().as_bytes(), path)); } } + + // Wormhole secrets under the pre-March-2026 "Bitcoin seed" tree: + // same paths, different master HMAC key, so every entropy differs. + // The app's first wormhole address also used the master node's own + // key (hdwallet 1.0.0 `generate_wormhole_pair`), hence path "m". + let mut legacy_paths = vec!["m".to_string()]; + for branch in 0..HD_SCAN_BRANCHES { + for index in 0..HD_SCAN_INDEXES { + legacy_paths.push(format!( + "m/44'/{}/0'/{}'/{}'", + QUANTUS_WORMHOLE_CHAIN_ID, branch, index + )); + } + } + for path in legacy_paths { + let entropy = ExtendedPrivKey::derive(seed.as_bytes(), path.as_str()) + .map_err(|e| format!("BIP32 derivation failed at {path}: {e:?}"))? + .secret(); + secrets.push((entropy, format!("bitcoin-seed {path}"))); + } } let mut extra_wormhole_secrets = extra_wormhole_secrets; for (i, secret) in extra_wormhole_secrets.iter_mut().enumerate() { @@ -539,6 +798,7 @@ pub fn find_airdrop_matches( claimable: scheme.id == CLAIMABLE_WORMHOLE_SCHEME, source: source.clone(), wormhole_secret: Some(secret.to_vec()), + dilithium_keygen: None, }); } } @@ -590,12 +850,43 @@ pub fn build_airdrop_dilithium_claim( if keypair.scheme != super::crypto::DilithiumScheme::MlDsa87 { return Err("airdrop claims require an ML-DSA-87 keypair".into()); } + sign_dilithium_claim( + &keypair.public_key, + &keypair.secret_key, + address, + claim_account, + ) +} + +/// Sign an airdrop claim with a key from a historical keygen era: pass the +/// `dilithium_keygen` id from the `AirdropMatch`. Re-derives the era's +/// keypair from the mnemonic, so the wallet does not need to store it. +/// Submit like `build_airdrop_dilithium_claim`'s result. +pub fn build_airdrop_dilithium_claim_from_mnemonic( + mnemonic: String, + dilithium_keygen: String, + address: String, + claim_account: String, +) -> Result { + // `mnemonic_to_seed` consumes and zeroizes the mnemonic string. + let mut seed = SensitiveBytes64::zeroed(); + mnemonic_to_seed(mnemonic, None, &mut seed).map_err(|e| format!("invalid mnemonic: {e:?}"))?; + let keypair = derive_historical_dilithium(&seed, &dilithium_keygen)?; + sign_dilithium_claim(&keypair.public, &keypair.secret.0, address, claim_account) +} + +fn sign_dilithium_claim( + public_key: &[u8], + secret_key: &[u8], + address: String, + claim_account: String, +) -> Result { let address_bytes = decode_account("address", &address)?; let claim_bytes = decode_account("claim account", &claim_account)?; let scheme = DILITHIUM_SCHEMES .iter() - .find(|s| derive_dilithium(s, &keypair.public_key) == address_bytes) + .find(|s| derive_dilithium(s, public_key) == address_bytes) .ok_or("public key does not derive this address under any known Dilithium scheme")?; let expiry_unix = now_unix()?.saturating_add(CLAIM_TTL_SECS); @@ -604,8 +895,8 @@ pub fn build_airdrop_dilithium_claim( msg[32..64].copy_from_slice(&claim_bytes); msg[64..].copy_from_slice(&expiry_unix.to_be_bytes()); - let secret = ml_dsa_87::SecretKey::from_bytes(&keypair.secret_key) - .map_err(|_| "invalid ML-DSA-87 secret key")?; + let secret = + ml_dsa_87::SecretKey::from_bytes(secret_key).map_err(|_| "invalid ML-DSA-87 secret key")?; let signature = secret .sign(&msg, Some(CLAIM_CONTEXT), None) .map_err(|e| format!("ML-DSA sign failed: {e}"))?; @@ -614,7 +905,7 @@ pub fn build_airdrop_dilithium_claim( scheme: (*scheme).into(), address, claim_account, - public_key_hex: hex::encode(&keypair.public_key), + public_key_hex: hex::encode(public_key), signature_hex: hex::encode(signature), expiry_unix, }) @@ -669,6 +960,7 @@ pub fn prove_airdrop_wormhole( #[cfg(test)] mod tests { use super::*; + use sha2::Digest; use sp_core::crypto::{AccountId32, Ss58Codec}; fn to_ss58(account: &[u8; 32]) -> String { @@ -921,6 +1213,176 @@ mod tests { assert!(!body.proof_hex.is_empty()); } + const TEST_MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; + + fn test_seed() -> SensitiveBytes64 { + let mut seed = SensitiveBytes64::zeroed(); + mnemonic_to_seed(TEST_MNEMONIC.into(), None, &mut seed).unwrap(); + seed + } + + /// sha256(pk) vectors computed with the exact shipped crates + /// (`.probe-keygen`): qp-rusty-crystals-dilithium 1.0.3 (pre-FIPS + /// expansion), 2.0.0 (FIPS expansion), qp-rusty-crystals-hdwallet 1.0.0 + /// ("Bitcoin seed" BIP32), and the current 4.1.1. These pin the local + /// keygen reimplementation and the BIP32 tree to the historical bytes. + #[test] + fn historical_dilithium_keygens_match_shipped_crate_vectors() { + let seed = test_seed(); + let ids = dilithium_keygen_ids(); + let vectors = [ + ( + "v1:seed", + "77993f1dafc02c9162925807f825f611bab071d121d6a42250bc4957c9149562", + ), + ( + "v1:bip32:m/44'/189189'/0'/0/0", + "57eafbd7c902c02686aff7f39c692beb9f3c057383dc6c954defc381e2c59f7d", + ), + ( + "fips:seed", + "1819feeaba63629813f1266de3d135de22ec505f1e014e669011cd9399bacb6f", + ), + ( + "fips:bip32:m/44'/189189'/0'/0/0", + "08fffe331b888d215c335e82712aa41ef680edd57e4634a89cca81b87e021e24", + ), + ( + "fips:bip32:m/44'/189189'/0'/0'/0'", + "7aeb9126559a7f750bf90941f632cf1f2835a57500cb1be74d9d3d15007d7e8d", + ), + ( + "fips:bip32:m/44'/189189'/0'", + "a49dac7c3537f61476626d491016abb2ca0e355be017c8cbbabc5a705d1cb5d7", + ), + ( + "fips:seed32", + "2af97815f11fb93d64d0e93fecff2b0e7af88882ed9fda813b5cd6f421799ab2", + ), + ( + "fips:hd:m/44'/189189'/0'/0'/0'", + "aa46cca1014fa42d40388298b33a7537d6a313b401f5be259cc59797cf4307e7", + ), + ]; + for (id, expected_pk_sha) in vectors { + assert!(ids.iter().any(|i| i == id), "{id} missing from scan list"); + let keypair = derive_historical_dilithium(&seed, id).unwrap(); + assert_eq!( + hex::encode(sha2::Sha256::digest(&keypair.public)), + expected_pk_sha, + "{id}" + ); + } + // Secret keys too, for the two locally reimplemented expansions: the + // packed sk must be byte-identical to the historical crates' output. + let v1 = derive_historical_dilithium(&seed, "v1:seed").unwrap(); + assert_eq!( + hex::encode(sha2::Sha256::digest(&v1.secret.0)), + "fbcb8f8db649111054baddc24f3eaab314c120950d76fdb8df5c1cd6a4102aa9" + ); + let fips = derive_historical_dilithium(&seed, "fips:seed").unwrap(); + assert_eq!( + hex::encode(sha2::Sha256::digest(&fips.secret.0)), + "fbe63db6ccf71badbb5a4aea59bead046637065fca38270b1a6361644ecf20d3" + ); + } + + /// An address minted by an era-1 wallet (pre-FIPS keygen, soft HD path, + /// v0.8 address hash) is found from the mnemonic alone, and the claim + /// built from its keygen id signs with the era's key. + #[test] + fn hd_scan_finds_historical_dilithium_address_and_claims() { + let keygen = "v1:bip32:m/44'/189189'/1'/0/0"; + let keypair = derive_historical_dilithium(&test_seed(), keygen).unwrap(); + let address_bytes = derive_dilithium("dilithium-v08-padded", &keypair.public); + let snapshot = vec![to_ss58(&address_bytes)]; + + let matches = + find_airdrop_matches(snapshot.clone(), None, Some(TEST_MNEMONIC.into()), vec![]) + .unwrap(); + assert_eq!(matches.len(), 1); + assert_eq!(matches[0].kind, "dilithium"); + assert_eq!(matches[0].scheme, "dilithium-v08-padded"); + assert_eq!(matches[0].dilithium_keygen.as_deref(), Some(keygen)); + assert!(matches[0].claimable); + + let claim_bytes = [9u8; 32]; + let body = build_airdrop_dilithium_claim_from_mnemonic( + TEST_MNEMONIC.into(), + keygen.into(), + matches[0].address.clone(), + to_ss58(&claim_bytes), + ) + .unwrap(); + assert_eq!(body.scheme, "dilithium-v08-padded"); + assert_eq!(body.public_key_hex, hex::encode(&keypair.public)); + + // The signature must verify under the current crate — that is what + // the claim server runs. + let mut msg = [0u8; 72]; + msg[..32].copy_from_slice(&address_bytes); + msg[32..64].copy_from_slice(&claim_bytes); + msg[64..].copy_from_slice(&body.expiry_unix.to_be_bytes()); + let public = ml_dsa_87::PublicKey::from_bytes(&keypair.public).unwrap(); + let sig = hex::decode(&body.signature_hex).unwrap(); + assert!(public.verify(&msg, &sig, Some(CLAIM_CONTEXT))); + } + + /// The current wallet's own key must not be reported twice when both the + /// provided public key and the era-4 HD keygen derive it. + #[test] + fn current_key_is_not_double_reported() { + let keygen = "fips:hd:m/44'/189189'/0'/0'/0'"; + let keypair = derive_historical_dilithium(&test_seed(), keygen).unwrap(); + let address = derive_dilithium("dilithium-rate8-hash-bytes", &keypair.public); + let snapshot = vec![to_ss58(&address)]; + + let matches = find_airdrop_matches( + snapshot, + Some(keypair.public.clone()), + Some(TEST_MNEMONIC.into()), + vec![], + ) + .unwrap(); + assert_eq!(matches.len(), 1); + // The pubkey-based match wins: its claim path needs no mnemonic. + assert_eq!(matches[0].source, "dilithium public key"); + assert!(matches[0].dilithium_keygen.is_none()); + } + + /// Wormhole entropies derived before hdwallet 2.1.0 used the "Bitcoin + /// seed" BIP32 master. Secrets pinned by the `.probe-keygen` run of + /// hdwallet 1.0.0: the master-node secret (`generate_wormhole_pair`) and + /// a path-derived one (`generate_wormhole_pair_from_path`). + #[test] + fn hd_scan_finds_bitcoin_seed_wormhole_addresses() { + let master_secret = + hex32("1837c1be8e2995ec11cda2b066151be2cfb48adf9e47b151d46adab3a21cdf67"); + let path_secret = hex32("87b3000325d7058a64b01b93f199b3d54ba5d9b2e036cee672199e7da326538c"); + let rate4 = WORMHOLE_SCHEMES + .iter() + .find(|s| s.id == "wormhole-rate4-compact") + .unwrap(); + let snapshot = vec![ + to_ss58(&rate4.derive(&master_secret)), + to_ss58(&rate4.derive(&path_secret)), + ]; + + let matches = + find_airdrop_matches(snapshot.clone(), None, Some(TEST_MNEMONIC.into()), vec![]) + .unwrap(); + assert_eq!(matches.len(), 2); + let sources: Vec<_> = matches.iter().map(|m| m.source.as_str()).collect(); + assert!(sources.contains(&"bitcoin-seed m")); + assert!(sources.contains( + &format!("bitcoin-seed m/44'/{QUANTUS_WORMHOLE_CHAIN_ID}/0'/0'/0'").as_str() + )); + for m in &matches { + assert_eq!(m.kind, "wormhole"); + assert!(m.wormhole_secret.is_some()); + } + } + #[test] fn hd_scan_finds_wallet_wormhole_address() { let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; diff --git a/quantus_sdk/rust/src/frb_generated.rs b/quantus_sdk/rust/src/frb_generated.rs index 542f0484d..10d93ed92 100644 --- a/quantus_sdk/rust/src/frb_generated.rs +++ b/quantus_sdk/rust/src/frb_generated.rs @@ -39,7 +39,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 272316986; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1962506674; // Section: executor @@ -121,6 +121,48 @@ fn wire__crate__api__airdrop__build_airdrop_dilithium_claim_impl( }, ) } +fn wire__crate__api__airdrop__build_airdrop_dilithium_claim_from_mnemonic_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "build_airdrop_dilithium_claim_from_mnemonic", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_mnemonic = ::sse_decode(&mut deserializer); + let api_dilithium_keygen = ::sse_decode(&mut deserializer); + let api_address = ::sse_decode(&mut deserializer); + let api_claim_account = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, String>((move || { + let output_ok = + crate::api::airdrop::build_airdrop_dilithium_claim_from_mnemonic( + api_mnemonic, + api_dilithium_keygen, + api_address, + api_claim_account, + )?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__wormhole__compute_address_hash_hex_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -1361,6 +1403,7 @@ impl SseDecode for crate::api::airdrop::AirdropMatch { let mut var_claimable = ::sse_decode(deserializer); let mut var_source = ::sse_decode(deserializer); let mut var_wormholeSecret = >>::sse_decode(deserializer); + let mut var_dilithiumKeygen = >::sse_decode(deserializer); return crate::api::airdrop::AirdropMatch { address: var_address, kind: var_kind, @@ -1368,6 +1411,7 @@ impl SseDecode for crate::api::airdrop::AirdropMatch { claimable: var_claimable, source: var_source, wormhole_secret: var_wormholeSecret, + dilithium_keygen: var_dilithiumKeygen, }; } } @@ -1693,18 +1737,24 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 16 => wire__crate__api__wormhole__ensure_circuit_binaries_impl( + 3 => wire__crate__api__airdrop__build_airdrop_dilithium_claim_from_mnemonic_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 17 => wire__crate__api__wormhole__ensure_circuit_binaries_impl( port, ptr, rust_vec_len, data_len, ), - 17 => { + 18 => { wire__crate__api__airdrop__find_airdrop_matches_impl(port, ptr, rust_vec_len, data_len) } - 22 => wire__crate__api__wormhole__generate_proof_impl(port, ptr, rust_vec_len, data_len), - 23 => wire__crate__api__crypto__init_app_impl(port, ptr, rust_vec_len, data_len), - 28 => wire__crate__api__airdrop__prove_airdrop_wormhole_impl( + 23 => wire__crate__api__wormhole__generate_proof_impl(port, ptr, rust_vec_len, data_len), + 24 => wire__crate__api__crypto__init_app_impl(port, ptr, rust_vec_len, data_len), + 29 => wire__crate__api__airdrop__prove_airdrop_wormhole_impl( port, ptr, rust_vec_len, @@ -1722,48 +1772,48 @@ fn pde_ffi_dispatcher_sync_impl( ) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 3 => wire__crate__api__wormhole__compute_address_hash_hex_impl(ptr, rust_vec_len, data_len), - 4 => wire__crate__api__wormhole__compute_merkle_positions_impl(ptr, rust_vec_len, data_len), - 5 => wire__crate__api__wormhole__compute_nullifier_impl(ptr, rust_vec_len, data_len), - 6 => wire__crate__api__wormhole__compute_wormhole_address_impl(ptr, rust_vec_len, data_len), - 7 => wire__crate__api__crypto__crystal_alice_impl(ptr, rust_vec_len, data_len), - 8 => wire__crate__api__crypto__crystal_bob_impl(ptr, rust_vec_len, data_len), - 9 => wire__crate__api__crypto__crystal_charlie_impl(ptr, rust_vec_len, data_len), - 10 => wire__crate__api__wormhole__decode_leaf_amount_impl(ptr, rust_vec_len, data_len), - 11 => wire__crate__api__wormhole__decode_leaf_to_account_impl(ptr, rust_vec_len, data_len), - 12 => { + 4 => wire__crate__api__wormhole__compute_address_hash_hex_impl(ptr, rust_vec_len, data_len), + 5 => wire__crate__api__wormhole__compute_merkle_positions_impl(ptr, rust_vec_len, data_len), + 6 => wire__crate__api__wormhole__compute_nullifier_impl(ptr, rust_vec_len, data_len), + 7 => wire__crate__api__wormhole__compute_wormhole_address_impl(ptr, rust_vec_len, data_len), + 8 => wire__crate__api__crypto__crystal_alice_impl(ptr, rust_vec_len, data_len), + 9 => wire__crate__api__crypto__crystal_bob_impl(ptr, rust_vec_len, data_len), + 10 => wire__crate__api__crypto__crystal_charlie_impl(ptr, rust_vec_len, data_len), + 11 => wire__crate__api__wormhole__decode_leaf_amount_impl(ptr, rust_vec_len, data_len), + 12 => wire__crate__api__wormhole__decode_leaf_to_account_impl(ptr, rust_vec_len, data_len), + 13 => { wire__crate__api__wormhole__decode_leaf_transfer_count_impl(ptr, rust_vec_len, data_len) } - 13 => wire__crate__api__ur__decode_ur_impl(ptr, rust_vec_len, data_len), - 14 => wire__crate__api__crypto__derive_wormhole_impl(ptr, rust_vec_len, data_len), - 15 => wire__crate__api__ur__encode_ur_impl(ptr, rust_vec_len, data_len), - 18 => wire__crate__api__crypto__first_hash_to_address_impl(ptr, rust_vec_len, data_len), - 19 => wire__crate__api__crypto__generate_derived_keypair_impl(ptr, rust_vec_len, data_len), - 20 => wire__crate__api__crypto__generate_keypair_impl(ptr, rust_vec_len, data_len), - 21 => { + 14 => wire__crate__api__ur__decode_ur_impl(ptr, rust_vec_len, data_len), + 15 => wire__crate__api__crypto__derive_wormhole_impl(ptr, rust_vec_len, data_len), + 16 => wire__crate__api__ur__encode_ur_impl(ptr, rust_vec_len, data_len), + 19 => wire__crate__api__crypto__first_hash_to_address_impl(ptr, rust_vec_len, data_len), + 20 => wire__crate__api__crypto__generate_derived_keypair_impl(ptr, rust_vec_len, data_len), + 21 => wire__crate__api__crypto__generate_keypair_impl(ptr, rust_vec_len, data_len), + 22 => { wire__crate__api__crypto__generate_keypair_from_seed_impl(ptr, rust_vec_len, data_len) } - 24 => wire__crate__api__ur__is_complete_ur_impl(ptr, rust_vec_len, data_len), - 25 => wire__crate__api__ur__max_ur_part_chars_impl(ptr, rust_vec_len, data_len), - 26 => wire__crate__api__ur__max_ur_parts_impl(ptr, rust_vec_len, data_len), - 27 => { + 25 => wire__crate__api__ur__is_complete_ur_impl(ptr, rust_vec_len, data_len), + 26 => wire__crate__api__ur__max_ur_part_chars_impl(ptr, rust_vec_len, data_len), + 27 => wire__crate__api__ur__max_ur_parts_impl(ptr, rust_vec_len, data_len), + 28 => { wire__crate__api__multisig__predict_multisig_address_impl(ptr, rust_vec_len, data_len) } - 29 => wire__crate__api__crypto__public_key_bytes_impl(ptr, rust_vec_len, data_len), - 30 => wire__crate__api__crypto__secret_key_bytes_impl(ptr, rust_vec_len, data_len), - 31 => wire__crate__api__crypto__set_default_ss58_prefix_impl(ptr, rust_vec_len, data_len), - 32 => wire__crate__api__crypto__sign_message_impl(ptr, rust_vec_len, data_len), - 33 => wire__crate__api__crypto__sign_message_with_pubkey_impl(ptr, rust_vec_len, data_len), - 34 => wire__crate__api__crypto__signature_bytes_impl(ptr, rust_vec_len, data_len), - 35 => wire__crate__api__crypto__ss58_to_account_id_impl(ptr, rust_vec_len, data_len), - 36 => wire__crate__api__crypto__to_account_id_impl(ptr, rust_vec_len, data_len), - 37 => wire__crate__api__crypto__verify_message_impl(ptr, rust_vec_len, data_len), - 38 => wire__crate__api__wormhole__wormhole_compute_output_amount_impl( + 30 => wire__crate__api__crypto__public_key_bytes_impl(ptr, rust_vec_len, data_len), + 31 => wire__crate__api__crypto__secret_key_bytes_impl(ptr, rust_vec_len, data_len), + 32 => wire__crate__api__crypto__set_default_ss58_prefix_impl(ptr, rust_vec_len, data_len), + 33 => wire__crate__api__crypto__sign_message_impl(ptr, rust_vec_len, data_len), + 34 => wire__crate__api__crypto__sign_message_with_pubkey_impl(ptr, rust_vec_len, data_len), + 35 => wire__crate__api__crypto__signature_bytes_impl(ptr, rust_vec_len, data_len), + 36 => wire__crate__api__crypto__ss58_to_account_id_impl(ptr, rust_vec_len, data_len), + 37 => wire__crate__api__crypto__to_account_id_impl(ptr, rust_vec_len, data_len), + 38 => wire__crate__api__crypto__verify_message_impl(ptr, rust_vec_len, data_len), + 39 => wire__crate__api__wormhole__wormhole_compute_output_amount_impl( ptr, rust_vec_len, data_len, ), - 39 => wire__crate__api__wormhole__zk_circuits_version_impl(ptr, rust_vec_len, data_len), + 40 => wire__crate__api__wormhole__zk_circuits_version_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -1795,6 +1845,7 @@ impl flutter_rust_bridge::IntoDart for crate::api::airdrop::AirdropMatch { self.claimable.into_into_dart().into_dart(), self.source.into_into_dart().into_dart(), self.wormhole_secret.into_into_dart().into_dart(), + self.dilithium_keygen.into_into_dart().into_dart(), ] .into_dart() } @@ -2033,6 +2084,7 @@ impl SseEncode for crate::api::airdrop::AirdropMatch { ::sse_encode(self.claimable, serializer); ::sse_encode(self.source, serializer); >>::sse_encode(self.wormhole_secret, serializer); + >::sse_encode(self.dilithium_keygen, serializer); } } diff --git a/quantus_sdk/test/public_exports_test.dart b/quantus_sdk/test/public_exports_test.dart index 684774336..bd368762f 100644 --- a/quantus_sdk/test/public_exports_test.dart +++ b/quantus_sdk/test/public_exports_test.dart @@ -7,6 +7,7 @@ void main() { test('airdrop API is exported from quantus_sdk.dart', () { expect(findAirdropMatches, isA()); expect(buildAirdropDilithiumClaim, isA()); + expect(buildAirdropDilithiumClaimFromMnemonic, isA()); expect(proveAirdropWormhole, isA()); }); } From 2ddd6e303c50659092a46d610a7faea3277661f8 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Tue, 15 Sep 2026 13:28:08 +0800 Subject: [PATCH 10/10] Scan HD accounts and wormhole indexes up to 100; wipe the scan buffer by RAII - DILITHIUM_SCAN_ACCOUNTS and HD_SCAN_INDEXES go from 9 / 17 to 100. The shipped multiple-accounts wallet never capped the account index, so a miner on account 9 (or a wormhole address at index 17) got no match and no keygen id. The new test pins the first previously excluded index and the last index in range for both. - New crate-root `sensitive` module: one `Wipe` trait and `SensitiveVec` replace SensitiveFelts, SecretKeyBytes and the manual wipe loops. The wormhole scan buffer is pre-sized for both HD trees plus the provided secrets and wipes on drop, so growth, early error returns and panics no longer free secret-bearing blocks. The FFI-provided extra secrets are wrapped the same way, so an invalid length no longer leaves the later inputs unwiped. - The allocator regression test now drives find_airdrop_matches on the success path (nine secrets, forcing reallocation) and the error path. - Regenerated the Dart doc comment for the new scan range. --- quantus_sdk/lib/src/rust/api/airdrop.dart | 8 +- quantus_sdk/rust/src/api/airdrop.rs | 280 +++++++++++----------- quantus_sdk/rust/src/lib.rs | 1 + quantus_sdk/rust/src/sensitive.rs | 85 +++++++ 4 files changed, 230 insertions(+), 144 deletions(-) create mode 100644 quantus_sdk/rust/src/sensitive.rs diff --git a/quantus_sdk/lib/src/rust/api/airdrop.dart b/quantus_sdk/lib/src/rust/api/airdrop.dart index bb4d2cd98..187942698 100644 --- a/quantus_sdk/lib/src/rust/api/airdrop.dart +++ b/quantus_sdk/lib/src/rust/api/airdrop.dart @@ -7,9 +7,9 @@ import '../frb_generated.dart'; import 'crypto.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; -// These functions are ignored because they are not marked as `pub`: `as_slice`, `compact8_decode`, `decode_account`, `derive_dilithium`, `derive_historical_dilithium`, `derive`, `dilithium_keygen_ids`, `hash_felts_rate4_pad10`, `hash_felts`, `hash_padded_legacy`, `hash_padded_v10`, `injective4_secret_words`, `injective4`, `mldsa87_keypair`, `now_unix`, `push`, `rehash`, `sign_dilithium_claim`, `v09_hash_no_pad`, `v09_permutation`, `wipe_bytes`, `wipe_felts`, `with_capacity` -// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `HistoricalKeypair`, `SecretEncoding`, `SecretKeyBytes`, `SensitiveFelts`, `Sponge`, `WormholeSchemeDef` -// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `drop`, `drop`, `eq`, `fmt`, `fmt` +// These functions are ignored because they are not marked as `pub`: `compact8_decode`, `decode_account`, `derive_dilithium`, `derive_historical_dilithium`, `derive`, `dilithium_keygen_ids`, `hash_felts_rate4_pad10`, `hash_felts`, `hash_padded_legacy`, `hash_padded_v10`, `injective4_secret_words`, `injective4`, `mldsa87_keypair`, `now_unix`, `rehash`, `sign_dilithium_claim`, `v09_hash_no_pad`, `v09_permutation`, `wormhole_scan_paths` +// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `HistoricalKeypair`, `SecretEncoding`, `Sponge`, `WormholeSchemeDef` +// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `eq`, `fmt`, `fmt` /// Determine which snapshot addresses belong to this wallet. /// @@ -18,7 +18,7 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; /// `dilithium_public_key` under every historical hash, and — when `mnemonic` /// is given — against keypairs re-derived under every historical keygen era /// (see `dilithium_keygen_ids`). Wormhole matches are checked for HD-derived -/// secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=16}'` under both the current +/// secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=99}'` under both the current /// "Dilithium seed" and the pre-2.1.0 "Bitcoin seed" BIP32 masters, plus the /// legacy master-node secret) when `mnemonic` is given, plus any /// `extra_wormhole_secrets` (32 bytes each). diff --git a/quantus_sdk/rust/src/api/airdrop.rs b/quantus_sdk/rust/src/api/airdrop.rs index b8223b022..778223a20 100644 --- a/quantus_sdk/rust/src/api/airdrop.rs +++ b/quantus_sdk/rust/src/api/airdrop.rs @@ -26,11 +26,15 @@ use qp_rusty_crystals_hdwallet::{ }; use std::collections::HashMap; +use crate::sensitive::{wipe, SensitiveVec}; + const RATE_4: usize = 4; const WORMHOLE_SALT: &[u8] = b"wormhole"; /// Only proofs for the current wormhole scheme are accepted by the claim server. const CLAIMABLE_WORMHOLE_SCHEME: &str = "wormhole-rate8-compact"; -const HD_SCAN_INDEXES: u32 = 17; +/// Address indexes scanned per wormhole HD branch. The shipped wallets never +/// capped the index, so scan well past any realistic count. +const HD_SCAN_INDEXES: u32 = 100; /// Middle HD path component. The app uses 0 (external) and 1 (dedicated /// change branch since July 2026); the CLI's wormhole multiround flow uses it /// as a round counter (default 2 rounds), so scan several rounds beyond that. @@ -75,57 +79,8 @@ fn compact8_decode(bytes: &[u8; 32]) -> [Goldilocks; POSEIDON2_OUTPUT] { qp_poseidon_core::serialization::bytes_to_digest_lossy(bytes) } -/// Zero field elements, resistant to dead-store elimination: `black_box` -/// makes the compiler assume the zeros are observed, so the fill cannot be -/// elided (same construction as qp-poseidon-core's internal state wipe). -fn wipe_felts(felts: &mut [Goldilocks]) { - felts.fill(Goldilocks::ZERO); - core::hint::black_box(felts); -} - -/// Zero bytes, resistant to dead-store elimination. -fn wipe_bytes(bytes: &mut [u8]) { - bytes.fill(0); - core::hint::black_box(bytes); -} - -/// Heap buffer for secret-bearing field elements. The full capacity must be -/// reserved before secret material is written (a growing `Vec` frees its old -/// block unscrubbed); limbs are wiped on drop. Generic so both the current -/// qp-poseidon-core felts and the v09 p3-goldilocks felts are covered. -struct SensitiveFelts { - felts: Vec, - zero: F, -} - -impl SensitiveFelts { - fn with_capacity(zero: F, capacity: usize) -> Self { - Self { - felts: Vec::with_capacity(capacity), - zero, - } - } - - fn push(&mut self, felt: F) { - debug_assert!( - self.felts.len() < self.felts.capacity(), - "SensitiveFelts must be pre-sized" - ); - self.felts.push(felt); - } - - fn as_slice(&self) -> &[F] { - &self.felts - } -} - -impl Drop for SensitiveFelts { - fn drop(&mut self) { - // Same dead-store-resistant wipe as `wipe_felts`. - self.felts.fill(self.zero); - core::hint::black_box(self.felts.as_mut_slice()); - } -} +/// Field element of qp-poseidon-core 0.9.5 (plonky3 Goldilocks 0.3.0). +type V09Felt = p3_goldilocks::Goldilocks; /// The injective 4-bytes-per-felt encoding of a 32-byte secret, as canonical /// limb values: eight little-endian u32 words plus the `1` terminator @@ -139,9 +94,6 @@ fn injective4_secret_words(secret: &[u8; 32]) -> impl Iterator + '_ .chain([1u64]) } -/// Field element of qp-poseidon-core 0.9.5 (plonky3 Goldilocks 0.3.0). -type V09Felt = p3_goldilocks::Goldilocks; - /// The v0.9.5 Poseidon2 permutation, rebuilt from the same public crates the /// historical qp-poseidon-core used: ChaCha8-derived constants with seed /// 0x189189189189189 over `Poseidon2Goldilocks<12>`. Equality with @@ -170,11 +122,6 @@ fn v09_hash_no_pad(input: &[V09Felt]) -> [u8; 32] { const WIDTH: usize = 12; const RATE: usize = 4; - let wipe = |felts: &mut [V09Felt]| { - felts.fill(V09Felt::ZERO); - core::hint::black_box(felts); - }; - let poseidon2 = v09_permutation(); let mut state = [V09Felt::ZERO; WIDTH]; let mut block = [V09Felt::ZERO; RATE]; @@ -273,8 +220,8 @@ fn hash_felts_rate4_pad10(x: &[Goldilocks]) -> [u8; 32] { .expect("width > output"); // The absorb buffer holds raw preimage felts and the state is the // permuted secret; wipe both before returning. - wipe_felts(&mut state); - wipe_felts(&mut buf); + wipe(&mut state); + wipe(&mut buf); digest_to_bytes(&digest) } @@ -364,10 +311,10 @@ impl WormholeSchemeDef { /// allocation containing the secret is ever freed unscrubbed (verified by /// the allocator test in `heap_zeroization_tests`). fn derive(&self, secret: &[u8; 32]) -> [u8; 32] { - use p3_field::{integers::QuotientMap, PrimeCharacteristicRing}; + use p3_field::integers::QuotientMap; if self.sponge == Sponge::V09 { let salt = v09::injective_bytes_to_felts(WORMHOLE_SALT); - let mut preimage = SensitiveFelts::with_capacity(V09Felt::ZERO, salt.len() + 9); + let mut preimage = SensitiveVec::with_capacity(salt.len() + 9); for felt in salt { preimage.push(felt); } @@ -384,12 +331,12 @@ impl WormholeSchemeDef { } } } - let first_hash = v09_hash_no_pad(preimage.as_slice()); + let first_hash = v09_hash_no_pad(&preimage); return v09_hash_no_pad(&v09::digest_bytes_to_felts(&first_hash)); } let salt = injective4(WORMHOLE_SALT); // injective4 of a 32-byte secret is exactly 9 felts; compact8 is 4. - let mut preimage = SensitiveFelts::with_capacity(Goldilocks::ZERO, salt.len() + 9); + let mut preimage = SensitiveVec::with_capacity(salt.len() + 9); for felt in salt { preimage.push(felt); } @@ -404,10 +351,10 @@ impl WormholeSchemeDef { for felt in digest { preimage.push(felt); } - wipe_felts(&mut digest); + wipe(&mut digest); } } - let first_hash = self.sponge.hash_felts(preimage.as_slice()); + let first_hash = self.sponge.hash_felts(&preimage); self.sponge.rehash(&first_hash) } } @@ -458,22 +405,14 @@ fn derive_dilithium(scheme: &str, public_key: &[u8]) -> [u8; 32] { /// BIP44 coin type for Dilithium keys (the wormhole coin type is 189189189'). const DILITHIUM_CHAIN_ID: &str = "189189'"; -/// Account indexes scanned per HD keygen family, same breadth as the -/// wormhole branch scan. -const DILITHIUM_SCAN_ACCOUNTS: u32 = 9; - -/// Secret-key bytes that wipe themselves on drop. -struct SecretKeyBytes(Vec); - -impl Drop for SecretKeyBytes { - fn drop(&mut self) { - wipe_bytes(&mut self.0); - } -} +/// Account indexes scanned per HD keygen family. The multiple-accounts +/// wallet never capped the account index, so scan well past any realistic +/// count. +const DILITHIUM_SCAN_ACCOUNTS: u32 = 100; struct HistoricalKeypair { public: Vec, - secret: SecretKeyBytes, + secret: SensitiveVec, } /// Every mnemonic→ML-DSA-87-keypair scheme a snapshot key may have used. @@ -535,7 +474,7 @@ fn derive_historical_dilithium( .map_err(|e| format!("BIP32 derivation failed at {path}: {e:?}"))? .secret(); let keypair = mldsa87_keypair(&entropy, v1); - wipe_bytes(&mut entropy); + wipe(&mut entropy); return Ok(keypair); } if let Some(path) = source.strip_prefix("hd:") { @@ -547,7 +486,7 @@ fn derive_historical_dilithium( let keypair = qp_rusty_crystals_hdwallet::ml_dsa_87::derive_key_from_seed(seed, path) .map_err(|e| format!("HD derivation failed at {path}: {e:?}"))?; // `to_bytes` returns a `Zeroizing` buffer, wiped when it drops here. - let secret = SecretKeyBytes(keypair.secret().to_bytes().to_vec()); + let secret = SensitiveVec(keypair.secret().to_bytes().to_vec()); return Ok(HistoricalKeypair { public: keypair.public().to_bytes().to_vec(), secret, @@ -580,7 +519,7 @@ fn mldsa87_keypair(seed: &[u8], v1: bool) -> HistoricalKeypair { preimage[seed.len()] = K as u8; preimage[seed.len() + 1] = L as u8; fips202::shake256(&mut seedbuf, &preimage[..seed.len() + 2]); - wipe_bytes(&mut preimage); + wipe(&mut preimage); } let mut rho = [0u8; SEEDBYTES]; @@ -589,7 +528,7 @@ fn mldsa87_keypair(seed: &[u8], v1: bool) -> HistoricalKeypair { rhoprime.copy_from_slice(&seedbuf[SEEDBYTES..SEEDBYTES + CRHBYTES]); let mut key = [0u8; SEEDBYTES]; key.copy_from_slice(&seedbuf[SEEDBYTES + CRHBYTES..]); - wipe_bytes(&mut seedbuf); + wipe(&mut seedbuf); let mut s1 = Polyvec::::default(); for (i, p) in s1.vec.iter_mut().enumerate() { @@ -599,7 +538,7 @@ fn mldsa87_keypair(seed: &[u8], v1: bool) -> HistoricalKeypair { for (i, p) in s2.vec.iter_mut().enumerate() { poly::uniform_eta::(p, &rhoprime, (L + i) as u16); } - wipe_bytes(&mut rhoprime); + wipe(&mut rhoprime); let mut s1hat = s1.clone(); polyvec::ntt(&mut s1hat); @@ -618,19 +557,32 @@ fn mldsa87_keypair(seed: &[u8], v1: bool) -> HistoricalKeypair { fips202::shake256(&mut tr, &pk); let mut sk = [0u8; SECRETKEYBYTES]; packing::pack_sk::(&mut sk, &rho, &tr, &key, &t0, &s1, &s2); - wipe_bytes(&mut key); + wipe(&mut key); // s1, s2, t0, and s1hat wipe themselves on drop (Polyvec is // ZeroizeOnDrop); the packed sk moves into a self-wiping buffer and its // stack copy is scrubbed here. - let secret = SecretKeyBytes(sk.to_vec()); - wipe_bytes(&mut sk); + let secret = SensitiveVec(sk.to_vec()); + wipe(&mut sk); HistoricalKeypair { public: pk.to_vec(), secret, } } +/// Wormhole HD paths the app and CLI may have used: account 0', every +/// scanned branch and address index. +fn wormhole_scan_paths() -> impl Iterator { + (0..HD_SCAN_BRANCHES).flat_map(|branch| { + (0..HD_SCAN_INDEXES) + .map(move |index| format!("m/44'/{QUANTUS_WORMHOLE_CHAIN_ID}/0'/{branch}'/{index}'")) + }) +} + +/// Scan-buffer entries one mnemonic contributes: the path grid under both +/// BIP32 masters plus the legacy master-node secret. +const WORMHOLE_SCAN_SECRETS: usize = 2 * (HD_SCAN_BRANCHES * HD_SCAN_INDEXES) as usize + 1; + // --------------------------------------------------------------------------- // FFI surface // --------------------------------------------------------------------------- @@ -666,7 +618,7 @@ pub struct AirdropMatch { /// `dilithium_public_key` under every historical hash, and — when `mnemonic` /// is given — against keypairs re-derived under every historical keygen era /// (see `dilithium_keygen_ids`). Wormhole matches are checked for HD-derived -/// secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=16}'` under both the current +/// secrets (`m/44'/189189189'/0'/{0..=8}'/{0..=99}'` under both the current /// "Dilithium seed" and the pre-2.1.0 "Bitcoin seed" BIP32 masters, plus the /// legacy master-node secret) when `mnemonic` is given, plus any /// `extra_wormhole_secrets` (32 bytes each). @@ -707,7 +659,12 @@ pub fn find_airdrop_matches( } } - let mut secrets: Vec<([u8; 32], String)> = Vec::new(); + let extra_wormhole_secrets = SensitiveVec(extra_wormhole_secrets); + // Every candidate spend secret goes into one pre-sized self-wiping + // buffer, so neither growth nor an early return frees it unscrubbed. + let mut secrets = SensitiveVec::with_capacity( + mnemonic.as_ref().map_or(0, |_| WORMHOLE_SCAN_SECRETS) + extra_wormhole_secrets.len(), + ); if let Some(mnemonic) = mnemonic { // Stretch the BIP39 seed once, then walk the HD tree per path. // `mnemonic_to_seed` consumes and zeroizes the mnemonic string. @@ -742,48 +699,31 @@ pub fn find_airdrop_matches( } // Wormhole secrets under the current "Dilithium seed" tree. - for branch in 0..HD_SCAN_BRANCHES { - for index in 0..HD_SCAN_INDEXES { - let path = format!( - "m/44'/{}/0'/{}'/{}'", - QUANTUS_WORMHOLE_CHAIN_ID, branch, index - ); - let pair = generate_wormhole_from_seed(&seed, &path) - .map_err(|e| format!("HD derivation failed at {path}: {e:?}"))?; - secrets.push((*pair.secret().as_bytes(), path)); - } + for path in wormhole_scan_paths() { + let pair = generate_wormhole_from_seed(&seed, &path) + .map_err(|e| format!("HD derivation failed at {path}: {e:?}"))?; + secrets.push((*pair.secret().as_bytes(), path)); } // Wormhole secrets under the pre-March-2026 "Bitcoin seed" tree: // same paths, different master HMAC key, so every entropy differs. // The app's first wormhole address also used the master node's own // key (hdwallet 1.0.0 `generate_wormhole_pair`), hence path "m". - let mut legacy_paths = vec!["m".to_string()]; - for branch in 0..HD_SCAN_BRANCHES { - for index in 0..HD_SCAN_INDEXES { - legacy_paths.push(format!( - "m/44'/{}/0'/{}'/{}'", - QUANTUS_WORMHOLE_CHAIN_ID, branch, index - )); - } - } - for path in legacy_paths { + for path in std::iter::once("m".to_string()).chain(wormhole_scan_paths()) { let entropy = ExtendedPrivKey::derive(seed.as_bytes(), path.as_str()) .map_err(|e| format!("BIP32 derivation failed at {path}: {e:?}"))? .secret(); secrets.push((entropy, format!("bitcoin-seed {path}"))); } } - let mut extra_wormhole_secrets = extra_wormhole_secrets; - for (i, secret) in extra_wormhole_secrets.iter_mut().enumerate() { - let copy: Result<[u8; 32], _> = secret.as_slice().try_into(); - wipe_bytes(secret); - let copy = copy.map_err(|_| format!("wormhole secret #{i} must be 32 bytes"))?; + for (i, secret) in extra_wormhole_secrets.iter().enumerate() { + let copy = <[u8; 32]>::try_from(secret.as_slice()) + .map_err(|_| format!("wormhole secret #{i} must be 32 bytes"))?; secrets.push((copy, format!("provided secret #{i}"))); } let mut seen: Vec<([u8; 32], &'static str)> = Vec::new(); - for (secret, source) in &secrets { + for (secret, source) in secrets.iter() { for scheme in WORMHOLE_SCHEMES { let account = scheme.derive(secret); if let Some(address) = by_account.get(&account) { @@ -804,13 +744,8 @@ pub fn find_airdrop_matches( } } - // The scan buffer holds every candidate spend secret; wipe it before the - // backing allocation is freed. (Matched secrets intentionally survive in - // the returned `AirdropMatch`es — the caller needs them to prove.) - for (secret, _) in secrets.iter_mut() { - wipe_bytes(secret); - } - + // `secrets` wipes itself on drop. Matched secrets intentionally survive + // in the returned `AirdropMatch`es — the caller needs them to prove. Ok(matches) } @@ -872,7 +807,7 @@ pub fn build_airdrop_dilithium_claim_from_mnemonic( let mut seed = SensitiveBytes64::zeroed(); mnemonic_to_seed(mnemonic, None, &mut seed).map_err(|e| format!("invalid mnemonic: {e:?}"))?; let keypair = derive_historical_dilithium(&seed, &dilithium_keygen)?; - sign_dilithium_claim(&keypair.public, &keypair.secret.0, address, claim_account) + sign_dilithium_claim(&keypair.public, &keypair.secret, address, claim_account) } fn sign_dilithium_claim( @@ -934,7 +869,7 @@ pub fn prove_airdrop_wormhole( if valid_len { secret_bytes.copy_from_slice(&wormhole_secret); } - wipe_bytes(&mut wormhole_secret); + wipe(&mut wormhole_secret); if !valid_len { return Err("wormhole secret must be 32 bytes".into()); } @@ -1277,12 +1212,12 @@ mod tests { // packed sk must be byte-identical to the historical crates' output. let v1 = derive_historical_dilithium(&seed, "v1:seed").unwrap(); assert_eq!( - hex::encode(sha2::Sha256::digest(&v1.secret.0)), + hex::encode(sha2::Sha256::digest(&v1.secret[..])), "fbcb8f8db649111054baddc24f3eaab314c120950d76fdb8df5c1cd6a4102aa9" ); let fips = derive_historical_dilithium(&seed, "fips:seed").unwrap(); assert_eq!( - hex::encode(sha2::Sha256::digest(&fips.secret.0)), + hex::encode(sha2::Sha256::digest(&fips.secret[..])), "fbe63db6ccf71badbb5a4aea59bead046637065fca38270b1a6361644ecf20d3" ); } @@ -1383,6 +1318,50 @@ mod tests { } } + /// The shipped wallets never capped the HD account or address index, so + /// the scan must reach past the old bounds (accounts 0..9, indexes 0..17) + /// and cover the whole configured range. + #[test] + fn hd_scan_reaches_high_account_and_address_indexes() { + let seed = test_seed(); + let keygens: Vec = [9, DILITHIUM_SCAN_ACCOUNTS - 1] + .iter() + .map(|account| format!("fips:hd:m/44'/{DILITHIUM_CHAIN_ID}/{account}'/0'/0'")) + .collect(); + let paths: Vec = [17, HD_SCAN_INDEXES - 1] + .iter() + .map(|index| format!("m/44'/{QUANTUS_WORMHOLE_CHAIN_ID}/0'/1'/{index}'")) + .collect(); + let wormhole = WORMHOLE_SCHEMES + .iter() + .find(|s| s.id == CLAIMABLE_WORMHOLE_SCHEME) + .unwrap(); + let mut snapshot = Vec::new(); + for keygen in &keygens { + let keypair = derive_historical_dilithium(&seed, keygen).unwrap(); + snapshot.push(to_ss58(&derive_dilithium( + "dilithium-rate8-hash-bytes", + &keypair.public, + ))); + } + for path in &paths { + let pair = generate_wormhole_from_seed(&seed, path).unwrap(); + snapshot.push(to_ss58(&wormhole.derive(pair.secret().as_bytes()))); + } + + let matches = + find_airdrop_matches(snapshot, None, Some(TEST_MNEMONIC.into()), vec![]).unwrap(); + let mut sources: Vec = matches.iter().map(|m| m.source.clone()).collect(); + sources.sort(); + let mut expected: Vec = keygens + .iter() + .map(|k| format!("mnemonic keygen {k}")) + .chain(paths.iter().cloned()) + .collect(); + expected.sort(); + assert_eq!(sources, expected); + } + #[test] fn hd_scan_finds_wallet_wormhole_address() { let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; @@ -1439,7 +1418,7 @@ mod heap_zeroization_tests { sync::OnceLock, }; - use super::{injective4_secret_words, WORMHOLE_SCHEMES}; + use super::{find_airdrop_matches, injective4_secret_words, WORMHOLE_SCHEMES}; /// Distinctive all-ASCII 32-byte pattern; see module docs for why ASCII /// makes the compact8 felt image identical to the raw bytes. @@ -1487,23 +1466,44 @@ mod heap_zeroization_tests { .collect() } - #[test] - fn matching_never_frees_heap_memory_containing_the_secret() { - INJECTIVE4_IMAGE.set(injective4_image()).expect("set once"); - + fn assert_no_secret_freed(what: &str, run: impl FnOnce()) { LEAKED_BLOCK_SIZE.store(0, Ordering::SeqCst); SCANNING.store(true, Ordering::SeqCst); - for scheme in WORMHOLE_SCHEMES { - let address = scheme.derive(&SECRET_PATTERN); - core::hint::black_box(address); - } + run(); SCANNING.store(false, Ordering::SeqCst); - let leaked = LEAKED_BLOCK_SIZE.load(Ordering::SeqCst); assert_eq!( leaked, 0, - "a heap block of {leaked} bytes still containing the spend secret was freed \ - unscrubbed; check SensitiveFelts pre-sizing and drop in api/airdrop.rs" + "{what}: a heap block of {leaked} bytes still containing the spend secret was \ + freed unscrubbed; see SensitiveVec pre-sizing (api/airdrop.rs) and Drop (sensitive.rs)" ); } + + #[test] + fn matching_never_frees_heap_memory_containing_the_secret() { + INJECTIVE4_IMAGE.set(injective4_image()).expect("set once"); + + assert_no_secret_freed("scheme derivation", || { + for scheme in WORMHOLE_SCHEMES { + core::hint::black_box(scheme.derive(&SECRET_PATTERN)); + } + }); + + // The public entry point: the scan buffer must not leak through Vec + // growth (nine secrets force several reallocations), an early error + // return (a bad length after a good secret, with an unvisited secret + // left in the input), or the final drop. + let nine = vec![SECRET_PATTERN.to_vec(); 9]; + assert_no_secret_freed("find_airdrop_matches", || { + assert!(find_airdrop_matches(vec![], None, None, nine).is_ok()); + }); + let bad_length = vec![ + SECRET_PATTERN.to_vec(), + vec![0u8; 31], + SECRET_PATTERN.to_vec(), + ]; + assert_no_secret_freed("find_airdrop_matches error path", || { + assert!(find_airdrop_matches(vec![], None, None, bad_length).is_err()); + }); + } } diff --git a/quantus_sdk/rust/src/lib.rs b/quantus_sdk/rust/src/lib.rs index fd84100b5..48631bed0 100644 --- a/quantus_sdk/rust/src/lib.rs +++ b/quantus_sdk/rust/src/lib.rs @@ -1,3 +1,4 @@ pub mod api; mod frb_generated; +mod sensitive; pub mod signing_context; diff --git a/quantus_sdk/rust/src/sensitive.rs b/quantus_sdk/rust/src/sensitive.rs new file mode 100644 index 000000000..a197e4f41 --- /dev/null +++ b/quantus_sdk/rust/src/sensitive.rs @@ -0,0 +1,85 @@ +//! Self-wiping buffers for secret material. Every wipe ends in `black_box`, +//! so the compiler must assume the zeros are observed and cannot elide the +//! stores (same construction as qp-poseidon-core's internal state wipe). + +/// Element types that can hold secret material. +pub(crate) trait Wipe { + fn wipe(&mut self); +} + +impl Wipe for u8 { + fn wipe(&mut self) { + *self = 0; + } +} + +impl Wipe for qp_poseidon_core::Goldilocks { + fn wipe(&mut self) { + *self = qp_poseidon_core::Goldilocks::ZERO; + } +} + +impl Wipe for p3_goldilocks::Goldilocks { + fn wipe(&mut self) { + *self = ::ZERO; + } +} + +impl Wipe for Vec { + fn wipe(&mut self) { + wipe(self.as_mut_slice()); + } +} + +impl Wipe for [T; N] { + fn wipe(&mut self) { + wipe(self.as_mut_slice()); + } +} + +/// A secret paired with its public source label. +impl Wipe for (T, String) { + fn wipe(&mut self) { + self.0.wipe(); + } +} + +/// Zero every element, resistant to dead-store elimination. +pub(crate) fn wipe(items: &mut [T]) { + for item in items.iter_mut() { + item.wipe(); + } + core::hint::black_box(items); +} + +/// Heap buffer of secret-bearing elements, wiped on drop (so success, error, +/// and panic paths all scrub). Wrap an existing `Vec` or reserve the full +/// capacity before pushing: a growing `Vec` frees its old block unscrubbed. +pub(crate) struct SensitiveVec(pub(crate) Vec); + +impl SensitiveVec { + pub(crate) fn with_capacity(capacity: usize) -> Self { + Self(Vec::with_capacity(capacity)) + } + + pub(crate) fn push(&mut self, item: T) { + debug_assert!( + self.0.len() < self.0.capacity(), + "SensitiveVec must be pre-sized" + ); + self.0.push(item); + } +} + +impl std::ops::Deref for SensitiveVec { + type Target = [T]; + fn deref(&self) -> &[T] { + &self.0 + } +} + +impl Drop for SensitiveVec { + fn drop(&mut self) { + wipe(&mut self.0); + } +}