Skip to content

Commit

Permalink
feat: add utxo import and export for ffi (#4999)
Browse files Browse the repository at this point in the history
Description
---
Add the ability to export and import utxos to the faucet

Motivation and Context
---
We need to be able to backup the utxo's without exposing all private information from the wallet. 
This only exposes the limited selection of private keys that are exported

How Has This Been Tested?
---
Unit tests. 

Fixes: #4954

Open question:
Do we only export the unspent set, or do we export the spent and unspent?
  • Loading branch information
SWvheerden authored Dec 8, 2022
1 parent 49a11d9 commit 9cda0bb
Show file tree
Hide file tree
Showing 10 changed files with 1,151 additions and 58 deletions.
2 changes: 1 addition & 1 deletion base_layer/core/src/base_node/service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ where B: BlockchainBackend + 'static
if let Err(res) = result {
error!(
target: LOG_TARGET,
"BaseNodeService Caller dropped the oneshot receiver before reply could be sent. Reply: {:?}"
"BaseNodeService Caller dropped the oneshot receiver before reply could be sent. Reply: {:?}",
res.map(|r| r.to_string()).map_err(|e| e.to_string())
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -802,8 +802,6 @@ enum BanReason {
ValidationFailed(#[from] ValidationError),
#[error("Peer could not find the location of a chain split")]
ChainSplitNotFound,
#[error("Failed to synchronize headers from peer: {0}")]
GeneralHeaderSyncFailure(BlockHeaderSyncError),
#[error("Peer did not respond timeously during RPC negotiation")]
RpcNegotiationTimedOut,
#[error("Header at height {height} did not form a chain. Expected {actual} to equal the previous hash {expected}")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,10 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase {
))
},
DbKey::UnspentOutputs => {
let outputs = OutputSql::index_status(OutputStatus::Unspent, &conn)?;
let outputs = OutputSql::index_status(
vec![OutputStatus::Unspent, OutputStatus::UnspentMinedUnconfirmed],
&conn,
)?;

Some(DbValue::UnspentOutputs(
outputs
Expand All @@ -204,7 +207,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase {
))
},
DbKey::SpentOutputs => {
let outputs = OutputSql::index_status(OutputStatus::Spent, &conn)?;
let outputs = OutputSql::index_status(vec![OutputStatus::Spent], &conn)?;

Some(DbValue::SpentOutputs(
outputs
Expand All @@ -224,7 +227,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase {
))
},
DbKey::InvalidOutputs => {
let outputs = OutputSql::index_status(OutputStatus::Invalid, &conn)?;
let outputs = OutputSql::index_status(vec![OutputStatus::Invalid], &conn)?;

Some(DbValue::InvalidOutputs(
outputs
Expand Down Expand Up @@ -413,12 +416,14 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase {
let acquire_lock = start.elapsed();
let cipher = acquire_read_lock!(self.cipher);

let mut outputs = OutputSql::index_status(OutputStatus::EncumberedToBeReceived, &conn)?;
outputs.extend(OutputSql::index_status(
OutputStatus::ShortTermEncumberedToBeReceived,
let outputs = OutputSql::index_status(
vec![
OutputStatus::EncumberedToBeReceived,
OutputStatus::UnspentMinedUnconfirmed,
OutputStatus::ShortTermEncumberedToBeReceived,
],
&conn,
)?);
outputs.extend(OutputSql::index_status(OutputStatus::UnspentMinedUnconfirmed, &conn)?);
)?;

if start.elapsed().as_millis() > 0 {
trace!(
Expand Down Expand Up @@ -1458,7 +1463,7 @@ mod test {
outputs.iter().map(|o| o.spending_key.clone()).collect::<Vec<Vec<u8>>>()
);
assert_eq!(
OutputSql::index_status(OutputStatus::Unspent, &conn)
OutputSql::index_status(vec!(OutputStatus::Unspent), &conn)
.unwrap()
.iter()
.map(|o| o.spending_key.clone())
Expand All @@ -1469,7 +1474,7 @@ mod test {
.collect::<Vec<Vec<u8>>>()
);
assert_eq!(
OutputSql::index_status(OutputStatus::Spent, &conn)
OutputSql::index_status(vec!(OutputStatus::Spent), &conn)
.unwrap()
.iter()
.map(|o| o.spending_key.clone())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,12 @@ impl OutputSql {

/// Return all outputs with a given status
pub fn index_status(
status: OutputStatus,
statuses: Vec<OutputStatus>,
conn: &SqliteConnection,
) -> Result<Vec<OutputSql>, OutputManagerStorageError> {
Ok(outputs::table.filter(outputs::status.eq(status as i32)).load(conn)?)
Ok(outputs::table
.filter(outputs::status.eq_any::<Vec<i32>>(statuses.into_iter().map(|s| s as i32).collect()))
.load(conn)?)
}

/// Retrieves UTXOs by a set of given rules
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ where T: TransactionBackend + 'static
),
TransactionStatus::try_from(import_status)?,
message,
mined_timestamp.unwrap_or(Utc::now().naive_utc()),
mined_timestamp.unwrap_or_else(|| Utc::now().naive_utc()),
TransactionDirection::Inbound,
maturity,
current_height,
Expand Down
37 changes: 3 additions & 34 deletions base_layer/wallet/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,39 +433,8 @@ where
encrypted_value,
minimum_value_promise,
);

let tx_id = self
.transaction_service
.import_utxo_with_status(
amount,
source_address,
message,
Some(features.maturity),
ImportStatus::Imported,
None,
None,
None,
)
.await?;

let commitment_hex = unblinded_output
.as_transaction_input(&self.factories.commitment)?
.commitment()
.map_err(WalletError::TransactionError)?
.to_hex();

// As non-rewindable
self.output_manager_service
.add_unvalidated_output(tx_id, unblinded_output, None)
.await?;

info!(
target: LOG_TARGET,
"UTXO (Commitment: {}) imported into wallet as 'ImportStatus::Imported' and is non-rewindable",
commitment_hex
);

Ok(tx_id)
self.import_unblinded_output_as_non_rewindable(unblinded_output, source_address, message)
.await
}

/// Import an external spendable UTXO into the wallet as a non-rewindable/non-recoverable UTXO. The output will be
Expand Down Expand Up @@ -493,7 +462,7 @@ where

// As non-rewindable
self.output_manager_service
.add_output_with_tx_id(tx_id, unblinded_output.clone(), None)
.add_unvalidated_output(tx_id, unblinded_output.clone(), None)
.await?;

info!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1667,7 +1667,7 @@ async fn test_txo_validation() {
);
assert_eq!(MicroTari::from(0), balance.time_locked_balance.unwrap());

assert_eq!(oms.output_manager_handle.get_unspent_outputs().await.unwrap().len(), 2);
assert_eq!(oms.output_manager_handle.get_unspent_outputs().await.unwrap().len(), 5);

assert!(oms.output_manager_handle.get_spent_outputs().await.unwrap().is_empty());

Expand Down
2 changes: 1 addition & 1 deletion base_layer/wallet/tests/utxo_scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use tari_comms::{
};
use tari_core::{
base_node::rpc::BaseNodeWalletRpcServer,
blocks::{genesis_block, BlockHeader},
blocks::BlockHeader,
proto::base_node::{ChainMetadata, TipInfoResponse},
transactions::{tari_amount::MicroTari, transaction_components::UnblindedOutput, CryptoFactories},
};
Expand Down
Loading

0 comments on commit 9cda0bb

Please sign in to comment.