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

fix(wallet): mark mined_height as null when pending outputs are cancelled #4686

Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub trait OutputManagerBackend: Send + Sync + Clone {
mined_timestamp: u64,
) -> Result<(), OutputManagerStorageError>;

fn set_output_to_unmined(&self, hash: FixedHash) -> Result<(), OutputManagerStorageError>;
fn set_output_to_unmined_and_invalid(&self, hash: FixedHash) -> Result<(), OutputManagerStorageError>;
fn set_outputs_to_be_revalidated(&self) -> Result<(), OutputManagerStorageError>;

fn mark_output_as_spent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,9 @@ where T: OutputManagerBackend + 'static
Ok(())
}

pub fn set_output_to_unmined(&self, hash: HashOutput) -> Result<(), OutputManagerStorageError> {
pub fn set_output_to_unmined_and_invalid(&self, hash: HashOutput) -> Result<(), OutputManagerStorageError> {
let db = self.db.clone();
db.set_output_to_unmined(hash)?;
db.set_output_to_unmined_and_invalid(hash)?;
Ok(())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase {
Ok(())
}

fn set_output_to_unmined(&self, hash: FixedHash) -> Result<(), OutputManagerStorageError> {
fn set_output_to_unmined_and_invalid(&self, hash: FixedHash) -> Result<(), OutputManagerStorageError> {
let start = Instant::now();
let conn = self.database_connection.get_pooled_connection()?;
let acquire_lock = start.elapsed();
Expand Down Expand Up @@ -899,6 +899,8 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase {
UpdateOutput {
status: Some(OutputStatus::Unspent),
spent_in_tx_id: Some(None),
// We clear these so that the output will be revalidated the next time a validation is done.
mined_height: Some(None),
mined_in_block: Some(None),
..Default::default()
},
Expand Down Expand Up @@ -1241,6 +1243,7 @@ pub struct UpdateOutput {
script_private_key: Option<Vec<u8>>,
metadata_signature_nonce: Option<Vec<u8>>,
metadata_signature_u_key: Option<Vec<u8>>,
mined_height: Option<Option<i64>>,
mined_in_block: Option<Option<Vec<u8>>>,
}

Expand All @@ -1254,18 +1257,10 @@ pub struct UpdateOutputSql {
script_private_key: Option<Vec<u8>>,
metadata_signature_nonce: Option<Vec<u8>>,
metadata_signature_u_key: Option<Vec<u8>>,
mined_height: Option<Option<i64>>,
mined_in_block: Option<Option<Vec<u8>>>,
}

#[derive(AsChangeset)]
#[table_name = "outputs"]
#[changeset_options(treat_none_as_null = "true")]
/// This struct is used to set the contained field to null
pub struct NullOutputSql {
received_in_tx_id: Option<i64>,
spent_in_tx_id: Option<i64>,
}

/// Map a Rust friendly UpdateOutput to the Sql data type form
impl From<UpdateOutput> for UpdateOutputSql {
fn from(u: UpdateOutput) -> Self {
Expand All @@ -1277,6 +1272,7 @@ impl From<UpdateOutput> for UpdateOutputSql {
metadata_signature_u_key: u.metadata_signature_u_key,
received_in_tx_id: u.received_in_tx_id.map(|o| o.map(TxId::as_i64_wrapped)),
spent_in_tx_id: u.spent_in_tx_id.map(|o| o.map(TxId::as_i64_wrapped)),
mined_height: u.mined_height,
mined_in_block: u.mined_in_block,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ where
self.operation_id
);
self.db
.set_output_to_unmined(last_mined_output.hash)
.set_output_to_unmined_and_invalid(last_mined_output.hash)
.for_protocol(self.operation_id)?;
} else {
debug!(
Expand Down
25 changes: 25 additions & 0 deletions base_layer/wallet/tests/output_manager_service_tests/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,28 @@ pub async fn test_no_duplicate_outputs() {
let outputs = db.fetch_mined_unspent_outputs().unwrap();
assert_eq!(outputs.len(), 1);
}

#[tokio::test]
pub async fn test_mark_as_unmined() {
let factories = CryptoFactories::default();
let (connection, _tempdir) = get_temp_sqlite_database_connection();
let backend = OutputManagerSqliteDatabase::new(connection, None);
let db = OutputManagerDatabase::new(backend);

// create an output
let (_ti, uo) = make_input(&mut OsRng, MicroTari::from(1000), &factories.commitment).await;
let uo = DbUnblindedOutput::from_unblinded_output(uo, &factories, None, OutputSource::Unknown).unwrap();

// add it to the database
db.add_unspent_output(uo.clone()).unwrap();
db.set_received_output_mined_height(uo.hash, 1, FixedHash::zero(), 1, true, 0)
.unwrap();
let o = db.get_last_mined_output().unwrap().unwrap();
assert_eq!(o.hash, uo.hash);
db.set_output_to_unmined_and_invalid(uo.hash).unwrap();
assert!(db.get_last_mined_output().unwrap().is_none());
let o = db.get_invalid_outputs().unwrap().pop().unwrap();
assert_eq!(o.hash, uo.hash);
assert!(o.mined_height.is_none());
assert!(o.mined_in_block.is_none());
}