commit
b7bb468907
File diff suppressed because it is too large
Load Diff
|
@ -10,12 +10,12 @@ publish = false
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
android_logger = "0.9"
|
android_logger = "0.11"
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
hdwallet = "0.3.1"
|
hdwallet = "0.3.1"
|
||||||
hdwallet-bitcoin = "0.3"
|
hdwallet-bitcoin = "0.3"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
jni = { version = "0.17", default-features = false }
|
jni = { version = "0.19", default-features = false }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
log-panics = "2.0.0"
|
log-panics = "2.0.0"
|
||||||
schemer = "0.2"
|
schemer = "0.2"
|
||||||
|
@ -29,14 +29,14 @@ zcash_proofs = "0.7"
|
||||||
|
|
||||||
# Revision corresponds to the pending zcash_primitives 0.8.0.
|
# Revision corresponds to the pending zcash_primitives 0.8.0.
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
group = { git = "https://github.com/zkcrypto/group.git", rev = "a7f3ceb2373e9fe536996f7b4d55c797f3e667f0" }
|
orchard = { git = "https://github.com/zcash/orchard.git", rev = "4035a97d178f99bb889f4d7ed26c59378fa88961" }
|
||||||
orchard = { git = 'https://github.com/zcash/orchard.git', rev='f206b3f5d4e31bba75d03d9d03d5fa25825a9384' }
|
schemer = { git = "https://github.com/aschampion/schemer.git", rev = "6726b60f43f72c6e24a18d31be0ec7d42829e5e1" }
|
||||||
zcash_address = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
schemer-rusqlite = { git = "https://github.com/aschampion/schemer.git", rev = "6726b60f43f72c6e24a18d31be0ec7d42829e5e1" }
|
||||||
zcash_client_backend = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
zcash_address = { git = 'https://github.com/zcash/librustzcash.git', rev='b3dc323876e07d990392855c30a2958d37750e24' }
|
||||||
zcash_client_sqlite = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
zcash_client_backend = { git = 'https://github.com/zcash/librustzcash.git', rev='b3dc323876e07d990392855c30a2958d37750e24' }
|
||||||
zcash_note_encryption = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
zcash_client_sqlite = { git = 'https://github.com/zcash/librustzcash.git', rev='b3dc323876e07d990392855c30a2958d37750e24' }
|
||||||
zcash_primitives = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
zcash_primitives = { git = 'https://github.com/zcash/librustzcash.git', rev='b3dc323876e07d990392855c30a2958d37750e24' }
|
||||||
zcash_proofs = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
zcash_proofs = { git = 'https://github.com/zcash/librustzcash.git', rev='b3dc323876e07d990392855c30a2958d37750e24' }
|
||||||
|
|
||||||
## Uncomment this to test librustzcash changes locally
|
## Uncomment this to test librustzcash changes locally
|
||||||
#[patch.crates-io]
|
#[patch.crates-io]
|
||||||
|
|
|
@ -559,8 +559,15 @@ class CompactBlockProcessor internal constructor(
|
||||||
): Int = withContext(IO) {
|
): Int = withContext(IO) {
|
||||||
var skipped = 0
|
var skipped = 0
|
||||||
val aboveHeight = startHeight
|
val aboveHeight = startHeight
|
||||||
twig("Clearing utxos above height $aboveHeight", -1)
|
// TODO(str4d): We no longer clear UTXOs here, as rustBackend.putUtxo now uses an upsert instead of an insert.
|
||||||
rustBackend.clearUtxos(tAddress, aboveHeight)
|
// This means that now-spent UTXOs would previously have been deleted, but now are left in the database (like
|
||||||
|
// shielded notes). Due to the fact that the lightwalletd query only returns _current_ UTXOs, we don't learn
|
||||||
|
// about recently-spent UTXOs here, so the transparent balance does not get updated here. Instead, when a
|
||||||
|
// received shielded note is "enhanced" by downloading the full transaction, we mark any UTXOs spent in that
|
||||||
|
// transaction as spent in the database. This relies on two current properties: UTXOs are only ever spent in
|
||||||
|
// shielding transactions, and at least one shielded note from each shielding transaction is always enhanced.
|
||||||
|
// However, for greater reliability, we may want to alter the Data Access API to support "inferring spentness"
|
||||||
|
// from what is _not_ returned as a UTXO, or alternatively fetch TXOs from lightwalletd instead of just UTXOs.
|
||||||
twig("Checking for UTXOs above height $aboveHeight")
|
twig("Checking for UTXOs above height $aboveHeight")
|
||||||
result.forEach { utxo: Service.GetAddressUtxosReply ->
|
result.forEach { utxo: Service.GetAddressUtxosReply ->
|
||||||
twig("Found UTXO at height ${utxo.height.toInt()} with ${utxo.valueZat} zatoshi")
|
twig("Found UTXO at height ${utxo.height.toInt()} with ${utxo.valueZat} zatoshi")
|
||||||
|
|
|
@ -232,7 +232,6 @@ internal class RustBackend private constructor(
|
||||||
): Long = withContext(SdkDispatchers.DATABASE_IO) {
|
): Long = withContext(SdkDispatchers.DATABASE_IO) {
|
||||||
createToAddress(
|
createToAddress(
|
||||||
dataDbFile.absolutePath,
|
dataDbFile.absolutePath,
|
||||||
usk.account.value,
|
|
||||||
usk.copyBytes(),
|
usk.copyBytes(),
|
||||||
to,
|
to,
|
||||||
value,
|
value,
|
||||||
|
@ -251,7 +250,6 @@ internal class RustBackend private constructor(
|
||||||
return withContext(SdkDispatchers.DATABASE_IO) {
|
return withContext(SdkDispatchers.DATABASE_IO) {
|
||||||
shieldToAddress(
|
shieldToAddress(
|
||||||
dataDbFile.absolutePath,
|
dataDbFile.absolutePath,
|
||||||
usk.account.value,
|
|
||||||
usk.copyBytes(),
|
usk.copyBytes(),
|
||||||
memo ?: ByteArray(0),
|
memo ?: ByteArray(0),
|
||||||
"$pathParamsDir/$SPEND_PARAM_FILE_NAME",
|
"$pathParamsDir/$SPEND_PARAM_FILE_NAME",
|
||||||
|
@ -281,20 +279,6 @@ internal class RustBackend private constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun clearUtxos(
|
|
||||||
tAddress: String,
|
|
||||||
aboveHeightInclusive: BlockHeight
|
|
||||||
): Boolean = withContext(SdkDispatchers.DATABASE_IO) {
|
|
||||||
clearUtxos(
|
|
||||||
dataDbFile.absolutePath,
|
|
||||||
tAddress,
|
|
||||||
// The Kotlin API is inclusive, but the Rust API is exclusive.
|
|
||||||
// This can create invalid BlockHeights if the height is saplingActivationHeight.
|
|
||||||
aboveHeightInclusive.value - 1,
|
|
||||||
networkId = network.id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getDownloadedUtxoBalance(address: String): WalletBalance {
|
override suspend fun getDownloadedUtxoBalance(address: String): WalletBalance {
|
||||||
val verified = withContext(SdkDispatchers.DATABASE_IO) {
|
val verified = withContext(SdkDispatchers.DATABASE_IO) {
|
||||||
getVerifiedTransparentBalance(
|
getVerifiedTransparentBalance(
|
||||||
|
@ -514,7 +498,6 @@ internal class RustBackend private constructor(
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
private external fun createToAddress(
|
private external fun createToAddress(
|
||||||
dbDataPath: String,
|
dbDataPath: String,
|
||||||
account: Int,
|
|
||||||
usk: ByteArray,
|
usk: ByteArray,
|
||||||
to: String,
|
to: String,
|
||||||
value: Long,
|
value: Long,
|
||||||
|
@ -528,7 +511,6 @@ internal class RustBackend private constructor(
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
private external fun shieldToAddress(
|
private external fun shieldToAddress(
|
||||||
dbDataPath: String,
|
dbDataPath: String,
|
||||||
account: Int,
|
|
||||||
usk: ByteArray,
|
usk: ByteArray,
|
||||||
memo: ByteArray,
|
memo: ByteArray,
|
||||||
spendParamsPath: String,
|
spendParamsPath: String,
|
||||||
|
@ -555,14 +537,6 @@ internal class RustBackend private constructor(
|
||||||
networkId: Int
|
networkId: Int
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
private external fun clearUtxos(
|
|
||||||
dbDataPath: String,
|
|
||||||
tAddress: String,
|
|
||||||
aboveHeight: Long,
|
|
||||||
networkId: Int
|
|
||||||
): Boolean
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
private external fun getVerifiedTransparentBalance(
|
private external fun getVerifiedTransparentBalance(
|
||||||
pathDataDb: String,
|
pathDataDb: String,
|
||||||
|
|
|
@ -90,11 +90,6 @@ internal interface RustBackendWelding {
|
||||||
height: BlockHeight
|
height: BlockHeight
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
suspend fun clearUtxos(
|
|
||||||
tAddress: String,
|
|
||||||
aboveHeightInclusive: BlockHeight = BlockHeight(network.saplingActivationHeight.value)
|
|
||||||
): Boolean
|
|
||||||
|
|
||||||
suspend fun getDownloadedUtxoBalance(address: String): WalletBalance
|
suspend fun getDownloadedUtxoBalance(address: String): WalletBalance
|
||||||
|
|
||||||
// Implemented by `DerivationTool`
|
// Implemented by `DerivationTool`
|
||||||
|
|
|
@ -31,18 +31,17 @@ use zcash_client_backend::{
|
||||||
wallet::{
|
wallet::{
|
||||||
create_spend_to_address, decrypt_and_store_transaction, shield_transparent_funds,
|
create_spend_to_address, decrypt_and_store_transaction, shield_transparent_funds,
|
||||||
},
|
},
|
||||||
WalletRead, WalletReadTransparent, WalletWrite, WalletWriteTransparent,
|
WalletRead, WalletWrite,
|
||||||
},
|
},
|
||||||
encoding::AddressCodec,
|
encoding::AddressCodec,
|
||||||
keys::{Era, UnifiedFullViewingKey},
|
keys::{Era, UnifiedFullViewingKey},
|
||||||
wallet::{OvkPolicy, WalletTransparentOutput},
|
wallet::{OvkPolicy, WalletTransparentOutput},
|
||||||
};
|
};
|
||||||
use zcash_client_sqlite::wallet::init::WalletMigrationError;
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
use zcash_client_sqlite::wallet::{delete_utxos_above, get_rewind_height};
|
use zcash_client_sqlite::wallet::get_rewind_height;
|
||||||
use zcash_client_sqlite::{
|
use zcash_client_sqlite::{
|
||||||
error::SqliteClientError,
|
error::SqliteClientError,
|
||||||
wallet::init::{init_accounts_table, init_blocks_table, init_wallet_db},
|
wallet::init::{init_accounts_table, init_blocks_table, init_wallet_db, WalletMigrationError},
|
||||||
BlockDb, NoteId, WalletDb,
|
BlockDb, NoteId, WalletDb,
|
||||||
};
|
};
|
||||||
use zcash_primitives::consensus::Network::{MainNetwork, TestNetwork};
|
use zcash_primitives::consensus::Network::{MainNetwork, TestNetwork};
|
||||||
|
@ -714,7 +713,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getVerified
|
||||||
.map_err(|e| format_err!("Error while fetching verified balance: {}", e))
|
.map_err(|e| format_err!("Error while fetching verified balance: {}", e))
|
||||||
})?
|
})?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|utxo| utxo.txout.value)
|
.map(|utxo| utxo.txout().value)
|
||||||
.sum::<Option<Amount>>()
|
.sum::<Option<Amount>>()
|
||||||
.ok_or_else(|| format_err!("Balance overflowed MAX_MONEY."))?;
|
.ok_or_else(|| format_err!("Balance overflowed MAX_MONEY."))?;
|
||||||
|
|
||||||
|
@ -752,7 +751,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getTotalTra
|
||||||
.map_err(|e| format_err!("Error while fetching verified balance: {}", e))
|
.map_err(|e| format_err!("Error while fetching verified balance: {}", e))
|
||||||
})?
|
})?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|utxo| utxo.txout.value)
|
.map(|utxo| utxo.txout().value)
|
||||||
.sum::<Option<Amount>>()
|
.sum::<Option<Amount>>()
|
||||||
.ok_or_else(|| format_err!("Balance overflowed MAX_MONEY"))?;
|
.ok_or_else(|| format_err!("Balance overflowed MAX_MONEY"))?;
|
||||||
|
|
||||||
|
@ -994,14 +993,15 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_putUtxo(
|
||||||
let addr = utils::java_string_to_rust(&env, address);
|
let addr = utils::java_string_to_rust(&env, address);
|
||||||
let _address = TransparentAddress::decode(&network, &addr).unwrap();
|
let _address = TransparentAddress::decode(&network, &addr).unwrap();
|
||||||
|
|
||||||
let output = WalletTransparentOutput {
|
let output = WalletTransparentOutput::from_parts(
|
||||||
outpoint: OutPoint::new(txid, index as u32),
|
OutPoint::new(txid, index as u32),
|
||||||
txout: TxOut {
|
TxOut {
|
||||||
value: Amount::from_i64(value).unwrap(),
|
value: Amount::from_i64(value).unwrap(),
|
||||||
script_pubkey,
|
script_pubkey,
|
||||||
},
|
},
|
||||||
height: BlockHeight::from(height as u32),
|
BlockHeight::from(height as u32),
|
||||||
};
|
)
|
||||||
|
.ok_or_else(|| format_err!("UTXO is not P2PKH or P2SH"))?;
|
||||||
|
|
||||||
debug!("Storing UTXO in db_data");
|
debug!("Storing UTXO in db_data");
|
||||||
match db_data.put_received_transparent_utxo(&output) {
|
match db_data.put_received_transparent_utxo(&output) {
|
||||||
|
@ -1012,36 +1012,6 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_putUtxo(
|
||||||
unwrap_exc_or(&env, res, JNI_FALSE)
|
unwrap_exc_or(&env, res, JNI_FALSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_clearUtxos(
|
|
||||||
env: JNIEnv<'_>,
|
|
||||||
_: JClass<'_>,
|
|
||||||
db_data: JString<'_>,
|
|
||||||
taddress: JString<'_>,
|
|
||||||
above_height: jlong,
|
|
||||||
network_id: jint,
|
|
||||||
) -> jint {
|
|
||||||
#[allow(deprecated)]
|
|
||||||
let res = panic::catch_unwind(|| {
|
|
||||||
let network = parse_network(network_id as u32)?;
|
|
||||||
let db_data = wallet_db(&env, network, db_data)?;
|
|
||||||
let mut db_data = db_data.get_update_ops()?;
|
|
||||||
let addr = utils::java_string_to_rust(&env, taddress);
|
|
||||||
let taddress = TransparentAddress::decode(&network, &addr).unwrap();
|
|
||||||
let height = BlockHeight::from(above_height as u32);
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"clearing UTXOs that were found above height: {}",
|
|
||||||
above_height
|
|
||||||
);
|
|
||||||
match delete_utxos_above(&mut db_data, &taddress, height) {
|
|
||||||
Ok(rows) => Ok(rows as i32),
|
|
||||||
Err(e) => Err(format_err!("Error while clearing UTXOs: {}", e)),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
unwrap_exc_or(&env, res, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ADDED BY ANDROID
|
// ADDED BY ANDROID
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_scanBlockBatch(
|
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_scanBlockBatch(
|
||||||
|
@ -1234,7 +1204,6 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_createToAdd
|
||||||
env: JNIEnv<'_>,
|
env: JNIEnv<'_>,
|
||||||
_: JClass<'_>,
|
_: JClass<'_>,
|
||||||
db_data: JString<'_>,
|
db_data: JString<'_>,
|
||||||
account: jint,
|
|
||||||
usk: jbyteArray,
|
usk: jbyteArray,
|
||||||
to: JString<'_>,
|
to: JString<'_>,
|
||||||
value: jlong,
|
value: jlong,
|
||||||
|
@ -1247,11 +1216,6 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_createToAdd
|
||||||
let network = parse_network(network_id as u32)?;
|
let network = parse_network(network_id as u32)?;
|
||||||
let db_data = wallet_db(&env, network, db_data)?;
|
let db_data = wallet_db(&env, network, db_data)?;
|
||||||
let mut db_data = db_data.get_update_ops()?;
|
let mut db_data = db_data.get_update_ops()?;
|
||||||
let account = if account >= 0 {
|
|
||||||
account as u32
|
|
||||||
} else {
|
|
||||||
return Err(format_err!("account argument must be nonnegative"));
|
|
||||||
};
|
|
||||||
let usk = decode_usk(&env, usk)?;
|
let usk = decode_usk(&env, usk)?;
|
||||||
let to = utils::java_string_to_rust(&env, to);
|
let to = utils::java_string_to_rust(&env, to);
|
||||||
let value =
|
let value =
|
||||||
|
@ -1282,13 +1246,11 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_createToAdd
|
||||||
|
|
||||||
let prover = LocalTxProver::new(Path::new(&spend_params), Path::new(&output_params));
|
let prover = LocalTxProver::new(Path::new(&spend_params), Path::new(&output_params));
|
||||||
|
|
||||||
// let branch = if
|
|
||||||
create_spend_to_address(
|
create_spend_to_address(
|
||||||
&mut db_data,
|
&mut db_data,
|
||||||
&network,
|
&network,
|
||||||
prover,
|
prover,
|
||||||
AccountId::from(account),
|
&usk,
|
||||||
usk.sapling(),
|
|
||||||
&to,
|
&to,
|
||||||
value,
|
value,
|
||||||
memo,
|
memo,
|
||||||
|
@ -1305,7 +1267,6 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_shieldToAdd
|
||||||
env: JNIEnv<'_>,
|
env: JNIEnv<'_>,
|
||||||
_: JClass<'_>,
|
_: JClass<'_>,
|
||||||
db_data: JString<'_>,
|
db_data: JString<'_>,
|
||||||
account: jint,
|
|
||||||
usk: jbyteArray,
|
usk: jbyteArray,
|
||||||
memo: jbyteArray,
|
memo: jbyteArray,
|
||||||
spend_params: JString<'_>,
|
spend_params: JString<'_>,
|
||||||
|
@ -1316,11 +1277,6 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_shieldToAdd
|
||||||
let network = parse_network(network_id as u32)?;
|
let network = parse_network(network_id as u32)?;
|
||||||
let db_data = wallet_db(&env, network, db_data)?;
|
let db_data = wallet_db(&env, network, db_data)?;
|
||||||
let mut db_data = db_data.get_update_ops()?;
|
let mut db_data = db_data.get_update_ops()?;
|
||||||
let account = if account >= 0 {
|
|
||||||
account as u32
|
|
||||||
} else {
|
|
||||||
return Err(format_err!("account argument must be nonnegative"));
|
|
||||||
};
|
|
||||||
let usk = decode_usk(&env, usk)?;
|
let usk = decode_usk(&env, usk)?;
|
||||||
let memo_bytes = env.convert_byte_array(memo).unwrap();
|
let memo_bytes = env.convert_byte_array(memo).unwrap();
|
||||||
let spend_params = utils::java_string_to_rust(&env, spend_params);
|
let spend_params = utils::java_string_to_rust(&env, spend_params);
|
||||||
|
@ -1334,8 +1290,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_shieldToAdd
|
||||||
&mut db_data,
|
&mut db_data,
|
||||||
&network,
|
&network,
|
||||||
prover,
|
prover,
|
||||||
usk.transparent(),
|
&usk,
|
||||||
AccountId::from(account),
|
|
||||||
&MemoBytes::from(&memo),
|
&MemoBytes::from(&memo),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
|
|
@ -56,18 +56,12 @@ fn throw(env: &JNIEnv, description: &str) {
|
||||||
let exception = match env.find_class("java/lang/RuntimeException") {
|
let exception = match env.find_class("java/lang/RuntimeException") {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
error!("Unable to find 'RuntimeException' class: {}", e.to_string());
|
||||||
"Unable to find 'RuntimeException' class: {}",
|
|
||||||
e.description()
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Err(e) = env.throw_new(exception, description) {
|
if let Err(e) = env.throw_new(exception, description) {
|
||||||
error!(
|
error!("Unable to find 'RuntimeException' class: {}", e.to_string());
|
||||||
"Unable to find 'RuntimeException' class: {}",
|
|
||||||
e.description()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +82,7 @@ pub fn any_to_string(any: &Box<dyn Any + Send>) -> String {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
use std::panic;
|
use std::panic;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -118,7 +113,7 @@ mod tests {
|
||||||
assert_eq!("Unknown error occurred", any_to_string(&error));
|
assert_eq!("Unknown error occurred", any_to_string(&error));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn panic_error<T: Send + 'static>(val: T) -> Box<dyn Any + Send> {
|
fn panic_error<T: fmt::Display + Send + 'static>(val: T) -> Box<dyn Any + Send> {
|
||||||
panic::catch_unwind(panic::AssertUnwindSafe(|| panic!(val))).unwrap_err()
|
panic::catch_unwind(panic::AssertUnwindSafe(|| panic!("{}", val))).unwrap_err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue