Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tx_id_to export #5126

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions applications/tari_console_wallet/src/automation/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ pub async fn command_runner(
},
ExportUtxos(args) => match output_service.get_unspent_outputs().await {
Ok(utxos) => {
let utxos: Vec<UnblindedOutput> = utxos.into_iter().map(|v| v.0).collect();
let count = utxos.len();
let sum: MicroTari = utxos.iter().map(|utxo| utxo.value).sum();
if let Some(file) = args.output_file {
Expand Down Expand Up @@ -801,6 +802,7 @@ pub async fn command_runner(
},
CountUtxos => match output_service.get_unspent_outputs().await {
Ok(utxos) => {
let utxos: Vec<UnblindedOutput> = utxos.into_iter().map(|v| v.0).collect();
let count = utxos.len();
let values: Vec<MicroTari> = utxos.iter().map(|utxo| utxo.value).collect();
let sum: MicroTari = values.iter().sum();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ impl wallet_server::Wallet for WalletGrpcServer {
Ok(Response::new(GetUnspentAmountsResponse {
amount: unspent_amounts
.into_iter()
.map(|o| o.value.as_u64())
.map(|o| o.0.value.as_u64())
.filter(|&a| a > 0)
.collect(),
}))
Expand Down
4 changes: 2 additions & 2 deletions base_layer/wallet/src/output_manager_service/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ pub enum OutputManagerResponse {
TransactionToSend(SenderTransactionProtocol),
TransactionCancelled,
SpentOutputs(Vec<UnblindedOutput>),
UnspentOutputs(Vec<UnblindedOutput>),
UnspentOutputs(Vec<(UnblindedOutput, u64)>),
SWvheerden marked this conversation as resolved.
Show resolved Hide resolved
Outputs(Vec<UnblindedOutput>),
InvalidOutputs(Vec<UnblindedOutput>),
BaseNodePublicKeySet,
Expand Down Expand Up @@ -625,7 +625,7 @@ impl OutputManagerHandle {
}

/// Sorted from lowest value to highest
pub async fn get_unspent_outputs(&mut self) -> Result<Vec<UnblindedOutput>, OutputManagerError> {
pub async fn get_unspent_outputs(&mut self) -> Result<Vec<(UnblindedOutput, u64)>, OutputManagerError> {
match self.handle.call(OutputManagerRequest::GetUnspentOutputs).await?? {
OutputManagerResponse::UnspentOutputs(s) => Ok(s),
_ => Err(OutputManagerError::UnexpectedApiResponse),
Expand Down
8 changes: 6 additions & 2 deletions base_layer/wallet/src/output_manager_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,11 @@ where
Ok(OutputManagerResponse::SpentOutputs(outputs))
},
OutputManagerRequest::GetUnspentOutputs => {
let outputs = self.fetch_unspent_outputs()?.into_iter().map(|v| v.into()).collect();
let outputs = self
.fetch_unspent_outputs()?
.into_iter()
.map(|(output, tx_id)| (output.into(), tx_id))
.collect();
Ok(OutputManagerResponse::UnspentOutputs(outputs))
},
OutputManagerRequest::GetOutputsBy(q) => {
Expand Down Expand Up @@ -1533,7 +1537,7 @@ where
Ok(self.resources.db.fetch_spent_outputs()?)
}

pub fn fetch_unspent_outputs(&self) -> Result<Vec<DbUnblindedOutput>, OutputManagerError> {
pub fn fetch_unspent_outputs(&self) -> Result<Vec<(DbUnblindedOutput, u64)>, OutputManagerError> {
Ok(self.resources.db.fetch_all_unspent_outputs()?)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub enum DbKey {
pub enum DbValue {
SpentOutput(Box<DbUnblindedOutput>),
UnspentOutput(Box<DbUnblindedOutput>),
UnspentOutputs(Vec<DbUnblindedOutput>),
UnspentOutputs(Vec<(DbUnblindedOutput, u64)>),
SpentOutputs(Vec<DbUnblindedOutput>),
InvalidOutputs(Vec<DbUnblindedOutput>),
KnownOneSidedPaymentScripts(Vec<KnownOneSidedPaymentScript>),
Expand Down Expand Up @@ -218,7 +218,7 @@ where T: OutputManagerBackend + 'static
self.db.cancel_pending_transaction(tx_id)
}

pub fn fetch_all_unspent_outputs(&self) -> Result<Vec<DbUnblindedOutput>, OutputManagerStorageError> {
pub fn fetch_all_unspent_outputs(&self) -> Result<Vec<(DbUnblindedOutput, u64)>, OutputManagerStorageError> {
let result = match self.db.fetch(&DbKey::UnspentOutputs)? {
Some(DbValue::UnspentOutputs(outputs)) => outputs,
Some(other) => return unexpected_result(DbKey::UnspentOutputs, other),
Expand Down Expand Up @@ -300,7 +300,8 @@ where T: OutputManagerBackend + 'static
Ok(Some(other)) => unexpected_result(DbKey::UnspentOutputs, other),
Err(e) => log_error(DbKey::UnspentOutputs, e),
}?;
Ok(uo)
let outputs = uo.into_iter().map(|(output, _)| (output)).collect();
Ok(outputs)
}

pub fn get_invalid_outputs(&self) -> Result<Vec<DbUnblindedOutput>, OutputManagerStorageError> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,11 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase {
Some(DbValue::UnspentOutputs(
outputs
.iter()
.map(|o| o.clone().to_db_unblinded_output(&cipher))
.map(|o| {
o.clone()
.to_db_unblinded_output(&cipher)
.map(|v| (v, o.received_in_tx_id.unwrap_or_default() as u64))
SWvheerden marked this conversation as resolved.
Show resolved Hide resolved
})
.collect::<Result<Vec<_>, _>>()?,
))
},
Expand All @@ -222,7 +226,11 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase {
Some(DbValue::UnspentOutputs(
outputs
.iter()
.map(|o| o.clone().to_db_unblinded_output(&cipher))
.map(|o| {
o.clone()
.to_db_unblinded_output(&cipher)
.map(|v| (v, o.received_in_tx_id.unwrap_or_default() as u64))
})
.collect::<Result<Vec<_>, _>>()?,
))
},
Expand Down
2 changes: 1 addition & 1 deletion base_layer/wallet/src/storage/sqlite_db/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ mod test {
let value2 = "value2".to_string();

let passphrase = "a very very secret key example.".to_string().into();
let db = WalletSqliteDatabase::new(connection.clone(), passphrase).unwrap();
let db = WalletSqliteDatabase::new(connection, passphrase).unwrap();
let cipher = db.cipher();

ClientKeyValueSql::new(key1.clone(), value1.clone(), &cipher)
Expand Down
14 changes: 7 additions & 7 deletions base_layer/wallet/tests/output_manager_service_tests/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,8 @@ async fn test_utxo_selection_no_chain_metadata() {
assert_eq!(utxos.len(), 8);
for (index, utxo) in utxos.iter().enumerate() {
let i = index as u64 + 3;
assert_eq!(utxo.features.maturity, i);
assert_eq!(utxo.value, i * amount);
assert_eq!(utxo.0.features.maturity, i);
assert_eq!(utxo.0.value, i * amount);
}

// test that we can get a fee estimate with no chain metadata
Expand Down Expand Up @@ -530,7 +530,7 @@ async fn test_utxo_selection_no_chain_metadata() {
// test that largest utxo was encumbered
let utxos = oms.get_unspent_outputs().await.unwrap();
assert_eq!(utxos.len(), 7);
for (index, utxo) in utxos.iter().enumerate() {
for (index, (utxo, _)) in utxos.iter().enumerate() {
let i = index as u64 + 3;
assert_eq!(utxo.features.maturity, i);
assert_eq!(utxo.value, i * amount);
Expand Down Expand Up @@ -621,7 +621,7 @@ async fn test_utxo_selection_with_chain_metadata() {
// test that largest spendable utxo was encumbered
let utxos = oms.get_unspent_outputs().await.unwrap();
assert_eq!(utxos.len(), 9);
let found = utxos.iter().any(|u| u.value == 6 * amount);
let found = utxos.iter().any(|u| u.0.value == 6 * amount);
assert!(!found, "An unspendable utxo was selected");

// test transactions
Expand All @@ -645,7 +645,7 @@ async fn test_utxo_selection_with_chain_metadata() {
// test that utxos with the lowest 2 maturities were encumbered
let utxos = oms.get_unspent_outputs().await.unwrap();
assert_eq!(utxos.len(), 7);
for utxo in &utxos {
for (utxo, _) in &utxos {
assert_ne!(utxo.features.maturity, 1);
assert_ne!(utxo.value, amount);
assert_ne!(utxo.features.maturity, 2);
Expand Down Expand Up @@ -673,7 +673,7 @@ async fn test_utxo_selection_with_chain_metadata() {
// test that utxos with the highest spendable 2 maturities were encumbered
let utxos = oms.get_unspent_outputs().await.unwrap();
assert_eq!(utxos.len(), 5);
for utxo in &utxos {
for (utxo, _) in &utxos {
assert_ne!(utxo.features.maturity, 4);
assert_ne!(utxo.value, 4 * amount);
assert_ne!(utxo.features.maturity, 5);
Expand Down Expand Up @@ -752,7 +752,7 @@ async fn test_utxo_selection_with_tx_priority() {
let utxos = oms.get_unspent_outputs().await.unwrap();
assert_eq!(utxos.len(), 1);

assert_ne!(utxos[0].features.output_type, OutputType::Coinbase);
assert_ne!(utxos[0].0.features.output_type, OutputType::Coinbase);
}

#[tokio::test]
Expand Down
137 changes: 133 additions & 4 deletions base_layer/wallet_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ pub type TariEncryptedValue = tari_core::transactions::transaction_components::E
pub type TariComAndPubSignature = tari_common_types::types::ComAndPubSignature;
pub type TariUnblindedOutput = tari_core::transactions::transaction_components::UnblindedOutput;

pub struct TariUnblindedOutputs(Vec<TariUnblindedOutput>);
pub struct TariUnblindedOutputs(Vec<(TariUnblindedOutput, u64)>);

pub struct TariContacts(Vec<TariContact>);

Expand Down Expand Up @@ -1613,10 +1613,10 @@ pub unsafe extern "C" fn tari_unblinded_output_to_json(
ptr::swap(error_out, &mut error as *mut c_int);
} else {
match serde_json::to_string(&*output) {
Ok(json_string) => match CString::new(json_string.to_string()) {
Ok(json_string) => match CString::new(json_string) {
Ok(v) => hex_bytes = v,
_ => {
error = LibWalletError::from(InterfaceError::PointerError("contact".to_string())).code;
error = LibWalletError::from(InterfaceError::PointerError("output".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
},
},
Expand Down Expand Up @@ -1747,7 +1747,43 @@ pub unsafe extern "C" fn unblinded_outputs_get_at(
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
}
Box::into_raw(Box::new((*outputs).0[position as usize].clone()))
Box::into_raw(Box::new((*outputs).0[position as usize].0.clone()))
}

/// Gets a TariUnblindedOutput from TariUnblindedOutputs at position
///
/// ## Arguments
/// `outputs` - The pointer to a TariUnblindedOutputs
/// `position` - The integer position
/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions
/// as an out parameter.
///
/// ## Returns
/// `*mut TariUnblindedOutput` - Returns a TariUnblindedOutput, note that it returns ptr::null_mut() if
/// TariUnblindedOutputs is null or position is invalid
///
/// # Safety
/// The ```contact_destroy``` method must be called when finished with a TariContact to prevent a memory leak
#[no_mangle]
pub unsafe extern "C" fn unblinded_outputs_tx_id_get_at(
outputs: *mut TariUnblindedOutputs,
position: c_uint,
error_out: *mut c_int,
) -> *mut c_ulonglong {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);
if outputs.is_null() {
error = LibWalletError::from(InterfaceError::NullError("outputs".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
}
let len = unblinded_outputs_get_length(outputs, error_out) as c_int - 1;
if len < 0 || position > len as c_uint {
error = LibWalletError::from(InterfaceError::PositionInvalidError).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
}
Box::into_raw(Box::new((*outputs).0[position as usize].1))
}

/// Frees memory for a TariUnblindedOutputs
Expand Down Expand Up @@ -3679,6 +3715,99 @@ pub unsafe extern "C" fn completed_transaction_get_cancellation_reason(
}
}

/// returns the TariCompletedTransaction as a json string
///
/// ## Arguments
/// `tx` - The pointer to a TariCompletedTransaction
///
/// ## Returns
/// `*mut c_char` - Returns a pointer to a char array. Note that it returns an empty char array if
/// TariCompletedTransaction is null or the position is invalid
///
/// # Safety
/// The ```completed_transaction_destroy``` function must be called when finished with a TariCompletedTransaction to
/// prevent a memory leak
#[no_mangle]
pub unsafe extern "C" fn tari_completed_transaction_to_json(
tx: *mut TariCompletedTransaction,
error_out: *mut c_int,
) -> *mut c_char {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);
let mut hex_bytes = CString::new("").expect("Blank CString will not fail.");
if tx.is_null() {
error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
} else {
match serde_json::to_string(&*tx) {
Ok(json_string) => match CString::new(json_string) {
Ok(v) => hex_bytes = v,
_ => {
error = LibWalletError::from(InterfaceError::PointerError("transaction".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
},
},
Err(_) => {
error = LibWalletError::from(HexError::HexConversionError).code;
ptr::swap(error_out, &mut error as *mut c_int);
},
}
}
CString::into_raw(hex_bytes)
}

/// Creates a TariUnblindedOutput from a char array
///
/// ## Arguments
/// `tx_json` - The pointer to a char array which is json of the TariCompletedTransaction
/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions
/// as an out parameter.
///
/// ## Returns
/// `*mut TariCompletedTransaction` - Returns a pointer to a TariCompletedTransaction. Note that it returns
/// ptr::null_mut() if key is null or if there was an error creating the TariCompletedTransaction from key
///
/// # Safety
/// The ```completed_transaction_destroy``` function must be called when finished with a TariCompletedTransaction to
// /// prevent a memory leak
#[no_mangle]
pub unsafe extern "C" fn create_tari_completed_transaction_from_json(
tx_json: *const c_char,
error_out: *mut c_int,
) -> *mut TariCompletedTransaction {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);
let tx_json_str;
if tx_json.is_null() {
error = LibWalletError::from(InterfaceError::NullError("tx_json".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
} else {
match CStr::from_ptr(tx_json).to_str() {
Ok(v) => {
tx_json_str = v.to_owned();
},
_ => {
error = LibWalletError::from(InterfaceError::PointerError("tx_json".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
},
};
}
let tx: Result<TariCompletedTransaction, _> = serde_json::from_str(&tx_json_str);

match tx {
Ok(tx) => Box::into_raw(Box::new(tx)),
Err(e) => {
error!(target: LOG_TARGET, "Error creating a transaction from json: {:?}", e);

error = LibWalletError::from(HexError::HexConversionError).code;
ptr::swap(error_out, &mut error as *mut c_int);
ptr::null_mut()
},
}
}

/// Frees memory for a TariCompletedTransaction
///
/// ## Arguments
Expand Down
Loading