Skip to content

Commit ebc89a4

Browse files
committed
fix: fix crash on wallet creation due to wallet struct change in epic v4
use cypherstack/epic-wallet to fix faulty sqlite usage
1 parent ed8cc16 commit ebc89a4

File tree

6 files changed

+158
-43
lines changed

6 files changed

+158
-43
lines changed

example/lib/wallet_info_view.dart

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:convert';
2+
13
import 'package:flutter/material.dart';
24
import 'package:flutter/services.dart';
35
import 'package:flutter_libepiccash/epic_cash.dart';
@@ -60,9 +62,19 @@ class _WalletInfoViewState extends State<WalletInfoView> {
6062
return;
6163
}
6264

65+
// Unwrap the JSON envelope.
66+
final envelope = jsonDecode(walletResult);
67+
if (envelope['ok'] != true) {
68+
setState(() {
69+
_resultMessage = 'Error opening wallet: ${envelope['error'] ?? 'Unknown error'}';
70+
});
71+
return;
72+
}
73+
final walletData = envelope['data'];
74+
6375
final epicboxConfig =
6476
await EpicboxConfig.getDefaultConfig(widget.walletName);
65-
final address = getAddressInfo(walletResult, 0, epicboxConfig);
77+
final address = getAddressInfo(walletData, 0, epicboxConfig);
6678
setState(() {
6779
_resultMessage = "Address Info: $address";
6880
});
@@ -271,9 +283,16 @@ class _WalletInfoViewState extends State<WalletInfoView> {
271283
return 'Error opening wallet: $walletResult';
272284
}
273285

286+
// Unwrap the JSON envelope.
287+
final envelope = jsonDecode(walletResult);
288+
if (envelope['ok'] != true) {
289+
return 'Error opening wallet: ${envelope['error'] ?? 'Unknown error'}';
290+
}
291+
final walletData = envelope['data'];
292+
274293
final epicboxConfig =
275294
await EpicboxConfig.getDefaultConfig(widget.walletName);
276-
final address = getAddressInfo(walletResult, 0, epicboxConfig);
295+
final address = getAddressInfo(walletData, 0, epicboxConfig);
277296
return address;
278297
} catch (e) {
279298
return 'Error getting address: $e';

example/pubspec.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,10 @@ packages:
187187
dependency: transitive
188188
description:
189189
name: meta
190-
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
190+
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
191191
url: "https://pub.dev"
192192
source: hosted
193-
version: "1.16.0"
193+
version: "1.17.0"
194194
mutex:
195195
dependency: transitive
196196
description:
@@ -336,10 +336,10 @@ packages:
336336
dependency: transitive
337337
description:
338338
name: test_api
339-
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
339+
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
340340
url: "https://pub.dev"
341341
source: hosted
342-
version: "0.7.6"
342+
version: "0.7.7"
343343
vector_math:
344344
dependency: transitive
345345
description:

lib/lib.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ abstract class LibEpiccash {
400400

401401
_checkForError(result);
402402

403-
return result;
403+
return epic_errors.unwrapOkData(result);
404404
} catch (e) {
405405
throw ("Error getting address info : ${e.toString()}");
406406
}

rust/Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,12 @@ epic_keychain = { git = "https://github.com/EpicCash/epic.git", branch = "master
3333
epic_util = { git = "https://github.com/EpicCash/epic.git", branch = "master" }
3434
epic_core = { git = "https://github.com/EpicCash/epic.git", branch = "master" }
3535

36-
# Wallet crates from official v4.0.0 tag
37-
epic_wallet_api = { git = "https://github.com/EpicCash/epic-wallet", tag = "v4.0.0-beta" }
38-
epic_wallet_impls = { git = "https://github.com/EpicCash/epic-wallet", tag = "v4.0.0-beta" }
39-
epic_wallet_libwallet = { git = "https://github.com/EpicCash/epic-wallet", tag = "v4.0.0-beta" }
40-
epic_wallet_config = { git = "https://github.com/EpicCash/epic-wallet", tag = "v4.0.0-beta" }
41-
epic_wallet_util = { git = "https://github.com/EpicCash/epic-wallet", tag = "v4.0.0-beta" }
42-
epic_wallet_controller = { git = "https://github.com/EpicCash/epic-wallet", tag = "v4.0.0-beta" }
36+
epic_wallet_api = { git = "https://github.com/cypherstack/epic-wallet", branch = "fix/sqlite-key-quoting" }
37+
epic_wallet_impls = { git = "https://github.com/cypherstack/epic-wallet", branch = "fix/sqlite-key-quoting" }
38+
epic_wallet_libwallet = { git = "https://github.com/cypherstack/epic-wallet", branch = "fix/sqlite-key-quoting" }
39+
epic_wallet_config = { git = "https://github.com/cypherstack/epic-wallet", branch = "fix/sqlite-key-quoting" }
40+
epic_wallet_util = { git = "https://github.com/cypherstack/epic-wallet", branch = "fix/sqlite-key-quoting" }
41+
epic_wallet_controller = { git = "https://github.com/cypherstack/epic-wallet", branch = "fix/sqlite-key-quoting" }
4342

4443
url = "2.1.0"
4544
futures = "0.3.15"
@@ -50,6 +49,7 @@ websocket = "0.21.1"
5049
ws = "0.9.2"
5150
ffi_helpers = "0.3.0"
5251
anyhow = "1.0.69"
52+
hex = "0.4.3"
5353

5454
# TODO: When we build for a windows target on an ubuntu runner, crunchy tries to
5555
# get the wrong path, update this when the workflow has been updated.

rust/src/ffi.rs

Lines changed: 82 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,59 @@ use crate::init_logger;
4242
use ffi_helpers::task::TaskHandle;
4343
use serde_json::json as serde_json_json;
4444

45+
/// Wrapper struct for wallet data to ensure consistent serialization.
46+
/// This replaces the tuple `(i64, Option<SecretKey>)` which can have
47+
/// inconsistent JSON serialization behavior across epic versions.
48+
/// SecretKey is serialized as a hex string to avoid version-specific serialization issues.
49+
#[derive(serde::Serialize, serde::Deserialize)]
50+
struct WalletData {
51+
wallet_ptr: i64,
52+
#[serde(
53+
serialize_with = "serialize_secret_key",
54+
deserialize_with = "deserialize_secret_key"
55+
)]
56+
keychain_mask: Option<SecretKey>,
57+
}
58+
59+
fn serialize_secret_key<S>(
60+
key: &Option<SecretKey>,
61+
serializer: S,
62+
) -> Result<S::Ok, S::Error>
63+
where
64+
S: serde::Serializer,
65+
{
66+
use epic_util::secp::constants::SECRET_KEY_SIZE;
67+
match key {
68+
Some(k) => {
69+
let bytes = &k.0[..SECRET_KEY_SIZE];
70+
let hex_string = hex::encode(bytes);
71+
serializer.serialize_some(&hex_string)
72+
}
73+
None => serializer.serialize_none(),
74+
}
75+
}
76+
77+
fn deserialize_secret_key<'de, D>(
78+
deserializer: D,
79+
) -> Result<Option<SecretKey>, D::Error>
80+
where
81+
D: serde::Deserializer<'de>,
82+
{
83+
use serde::Deserialize;
84+
use serde::de::Error;
85+
use epic_util::secp::Secp256k1;
86+
87+
let opt: Option<String> = Option::deserialize(deserializer)?;
88+
match opt {
89+
Some(hex_string) => {
90+
let bytes = hex::decode(&hex_string).map_err(D::Error::custom)?;
91+
let secp = Secp256k1::new();
92+
SecretKey::from_slice(&secp, &bytes).map_err(D::Error::custom).map(Some)
93+
}
94+
None => Ok(None),
95+
}
96+
}
97+
4598
fn is_error_str(s: &str) -> bool {
4699
s.starts_with("Error ") || s.to_uppercase().contains("ERROR")
47100
}
@@ -352,8 +405,12 @@ fn _open_wallet(
352405
let wlt = res.0;
353406
let sek_key = res.1;
354407
let wallet_int = Box::into_raw(Box::new(wlt)) as i64;
355-
let wallet_data = (wallet_int, sek_key);
408+
let wallet_data = WalletData {
409+
wallet_ptr: wallet_int,
410+
keychain_mask: sek_key,
411+
};
356412
let wallet_ptr = serde_json::to_string(&wallet_data).unwrap();
413+
println!("DEBUG: Serialized wallet data: {}", wallet_ptr);
357414
result.push_str(&wallet_ptr);
358415
}
359416
Err(err) => {
@@ -386,9 +443,9 @@ pub unsafe extern "C" fn rust_wallet_balances(
386443
};
387444

388445
let wallet_data = wallet_ptr.to_str().unwrap();
389-
let tuple_wallet_data: (i64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap();
390-
let wlt = tuple_wallet_data.0;
391-
let sek_key = tuple_wallet_data.1;
446+
let deserialized_wallet_data: WalletData = serde_json::from_str(wallet_data).unwrap();
447+
let wlt = deserialized_wallet_data.wallet_ptr;
448+
let sek_key = deserialized_wallet_data.keychain_mask;
392449

393450
ensure_wallet!(wlt, wallet);
394451

@@ -532,9 +589,9 @@ pub unsafe extern "C" fn rust_wallet_scan_outputs(
532589
let number_of_blocks: u64 = c_number_of_blocks.to_str().unwrap().to_string().parse().unwrap();
533590

534591
let wallet_data = wallet_ptr.to_str().unwrap();
535-
let tuple_wallet_data: (i64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap();
536-
let wlt = tuple_wallet_data.0;
537-
let sek_key = tuple_wallet_data.1;
592+
let deserialized_wallet_data: WalletData = serde_json::from_str(wallet_data).unwrap();
593+
let wlt = deserialized_wallet_data.wallet_ptr;
594+
let sek_key = deserialized_wallet_data.keychain_mask;
538595

539596
ensure_wallet!(wlt, wallet);
540597

@@ -616,7 +673,7 @@ pub unsafe extern "C" fn rust_create_tx(
616673
let key_index: u32 = CStr::from_ptr(secret_key_index).to_str().unwrap().parse().unwrap();
617674
let epicbox_config = CStr::from_ptr(epicbox_config).to_str().unwrap();
618675

619-
let tuple_wallet_data: (i64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap();
676+
let deserialized_wallet_data: WalletData = serde_json::from_str(wallet_data).unwrap();
620677

621678
let listen = Listener {
622679
wallet_ptr_str: wallet_data.to_string(),
@@ -627,8 +684,8 @@ pub unsafe extern "C" fn rust_create_tx(
627684
listener_cancel(handle);
628685
debug!("LISTENER CANCELLED IS {}", listener_cancelled(handle));
629686

630-
let wlt = tuple_wallet_data.0;
631-
let sek_key = tuple_wallet_data.1;
687+
let wlt = deserialized_wallet_data.wallet_ptr;
688+
let sek_key = deserialized_wallet_data.keychain_mask;
632689

633690
ensure_wallet!(wlt, wallet);
634691

@@ -714,9 +771,9 @@ pub unsafe extern "C" fn rust_txs_get(
714771
};
715772

716773
let wallet_data = c_wallet.to_str().unwrap();
717-
let tuple_wallet_data: (i64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap();
718-
let wlt = tuple_wallet_data.0;
719-
let sek_key = tuple_wallet_data.1;
774+
let deserialized_wallet_data: WalletData = serde_json::from_str(wallet_data).unwrap();
775+
let wlt = deserialized_wallet_data.wallet_ptr;
776+
let sek_key = deserialized_wallet_data.keychain_mask;
720777

721778
ensure_wallet!(wlt, wallet);
722779

@@ -776,9 +833,9 @@ pub unsafe extern "C" fn rust_tx_cancel(
776833
let uuid = Uuid::parse_str(tx_id).map_err(|e| EpicWalletControllerError::GenericError(e.to_string())).unwrap();
777834

778835
let wallet_data = wallet_ptr.to_str().unwrap();
779-
let tuple_wallet_data: (i64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap();
780-
let wlt = tuple_wallet_data.0;
781-
let sek_key = tuple_wallet_data.1;
836+
let deserialized_wallet_data: WalletData = serde_json::from_str(wallet_data).unwrap();
837+
let wlt = deserialized_wallet_data.wallet_ptr;
838+
let sek_key = deserialized_wallet_data.keychain_mask;
782839

783840
ensure_wallet!(wlt, wallet);
784841

@@ -933,9 +990,9 @@ pub unsafe extern "C" fn rust_tx_send_http(
933990
let str_address = c_address.to_str().unwrap();
934991

935992
let wallet_data = c_wallet.to_str().unwrap();
936-
let tuple_wallet_data: (i64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap();
937-
let wlt = tuple_wallet_data.0;
938-
let sek_key = tuple_wallet_data.1;
993+
let deserialized_wallet_data: WalletData = serde_json::from_str(wallet_data).unwrap();
994+
let wlt = deserialized_wallet_data.wallet_ptr;
995+
let sek_key = deserialized_wallet_data.keychain_mask;
939996
ensure_wallet!(wlt, wallet);
940997

941998
let result = match _tx_send_http(
@@ -1010,9 +1067,9 @@ pub unsafe extern "C" fn rust_get_wallet_address(
10101067
let index: u32 = index.to_str().unwrap().to_string().parse().unwrap();
10111068

10121069
let wallet_data = wallet_ptr.to_str().unwrap();
1013-
let tuple_wallet_data: (i64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap();
1014-
let wlt = tuple_wallet_data.0;
1015-
let sek_key = tuple_wallet_data.1;
1070+
let deserialized_wallet_data: WalletData = serde_json::from_str(wallet_data).unwrap();
1071+
let wlt = deserialized_wallet_data.wallet_ptr;
1072+
let sek_key = deserialized_wallet_data.keychain_mask;
10161073

10171074
ensure_wallet!(wlt, wallet);
10181075
let result = match _get_wallet_address(
@@ -1098,9 +1155,9 @@ pub unsafe extern "C" fn rust_get_tx_fees(
10981155
let amount: u64 = amount.to_str().unwrap().to_string().parse().unwrap();
10991156

11001157
let wallet_data = wallet_ptr.to_str().unwrap();
1101-
let tuple_wallet_data: (i64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap();
1102-
let wlt = tuple_wallet_data.0;
1103-
let sek_key = tuple_wallet_data.1;
1158+
let deserialized_wallet_data: WalletData = serde_json::from_str(wallet_data).unwrap();
1159+
let wlt = deserialized_wallet_data.wallet_ptr;
1160+
let sek_key = deserialized_wallet_data.keychain_mask;
11041161

11051162
ensure_wallet!(wlt, wallet);
11061163

rust/src/listener.rs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::sync::Arc;
2+
use anyhow::anyhow;
23
use ffi_helpers::{export_task, Task};
34
use ffi_helpers::task::CancellationToken;
45
use epic_util::Mutex;
@@ -7,6 +8,7 @@ use epic_wallet_config::{EpicboxConfig, TorConfig};
78
use epic_wallet_impls::EpicboxListenChannel;
89
use std::sync::atomic::AtomicBool;
910
use std::sync::Arc as StdArc;
11+
use serde::Deserialize;
1012

1113
use crate::wallet::Wallet;
1214

@@ -26,10 +28,7 @@ impl Task for Listener {
2628
let mut spins = 0;
2729

2830
let wallet_data_str = &self.wallet_ptr_str;
29-
// let wallet_data = wallet_ptr.to_str().unwrap();
30-
let tuple_wallet_data: (i64, Option<SecretKey>) = serde_json::from_str(wallet_data_str).unwrap();
31-
let wlt = tuple_wallet_data.0;
32-
let sek_key = tuple_wallet_data.1;
31+
let (wlt, sek_key) = parse_wallet_data(wallet_data_str)?;
3332

3433
// let wallet_data = &self.wallet_data;
3534
// let wlt = wallet_data.clone().0;
@@ -67,3 +66,43 @@ export_task! {
6766
handle_destroy: listener_handle_destroy;
6867
result_destroy: listener_result_destroy;
6968
}
69+
70+
#[derive(Deserialize)]
71+
struct WalletData {
72+
wallet_ptr: i64,
73+
#[serde(deserialize_with = "deserialize_secret_key")]
74+
keychain_mask: Option<SecretKey>,
75+
}
76+
77+
fn deserialize_secret_key<'de, D>(
78+
deserializer: D,
79+
) -> Result<Option<SecretKey>, D::Error>
80+
where
81+
D: serde::Deserializer<'de>,
82+
{
83+
let opt = Option::<String>::deserialize(deserializer)?;
84+
match opt {
85+
Some(hex_str) => {
86+
let bytes = hex::decode(hex_str).map_err(serde::de::Error::custom)?;
87+
let secp = epic_util::secp::Secp256k1::new();
88+
SecretKey::from_slice(&secp, bytes.as_slice())
89+
.map(Some)
90+
.map_err(serde::de::Error::custom)
91+
}
92+
None => Ok(None),
93+
}
94+
}
95+
96+
fn parse_wallet_data(wallet_data_str: &str) -> Result<(i64, Option<SecretKey>), anyhow::Error> {
97+
serde_json::from_str::<WalletData>(wallet_data_str)
98+
.map(|d| (d.wallet_ptr, d.keychain_mask))
99+
.or_else(|map_err| {
100+
serde_json::from_str::<(i64, Option<SecretKey>)>(wallet_data_str)
101+
.map(|t| (t.0, t.1))
102+
.map_err(|tuple_err| {
103+
anyhow!(
104+
"Failed to parse wallet data (map err: {map_err}; tuple err: {tuple_err})"
105+
)
106+
})
107+
})
108+
}

0 commit comments

Comments
 (0)