make child key allocation atomic

This commit is contained in:
wiesche
2026-09-01 20:51:13 +02:00
parent 9838ac3d8d
commit 893e598512
2 changed files with 48 additions and 16 deletions
+48 -14
View File
@@ -466,13 +466,7 @@ where
/// Return the current child index. /// Return the current child index.
pub fn current_child_index(&mut self, parent_key_id: &Identifier) -> Result<u32, Error> { pub fn current_child_index(&mut self, parent_key_id: &Identifier) -> Result<u32, Error> {
let index = { self.batch_no_mask()?.current_child_index(parent_key_id)
let batch = self.db.batch()?;
batch
.get_ser(Some(DERIV_PREFIX), &parent_key_id.to_bytes(), None)?
.unwrap_or_else(|| 0)
};
Ok(index)
} }
/// Next child ID for the given parent key, when we want to create a new output. /// Next child ID for the given parent key, when we want to create a new output.
@@ -483,17 +477,12 @@ where
parent_key_id: &Identifier, parent_key_id: &Identifier,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
) -> Result<Identifier, Error> { ) -> Result<Identifier, Error> {
let mut deriv_idx = { let mut batch = self.batch(keychain_mask)?;
let batch = self.db.batch()?; let mut deriv_idx = batch.current_child_index(parent_key_id)?;
batch
.get_ser(Some(DERIV_PREFIX), &parent_key_id.to_bytes(), None)?
.unwrap_or_else(|| 0)
};
let mut return_path = parent_key_id.to_path(); let mut return_path = parent_key_id.to_path();
return_path.depth += 1; return_path.depth += 1;
return_path.path[return_path.depth as usize - 1] = ChildNumber::from(deriv_idx); return_path.path[return_path.depth as usize - 1] = ChildNumber::from(deriv_idx);
deriv_idx += 1; deriv_idx += 1;
let mut batch = self.batch(keychain_mask)?;
batch.save_child_index(parent_key_id, deriv_idx)?; batch.save_child_index(parent_key_id, deriv_idx)?;
batch.commit()?; batch.commit()?;
Ok(Identifier::from_path(&return_path)) Ok(Identifier::from_path(&return_path))
@@ -624,6 +613,14 @@ where
Ok(()) Ok(())
} }
/// Return the current child index.
pub fn current_child_index(&self, parent_id: &Identifier) -> Result<u32, Error> {
Ok(self
.db
.get_ser(Some(DERIV_PREFIX), &parent_id.to_bytes(), None)?
.unwrap_or_else(|| 0))
}
/// Save last stored child index of a given parent. /// Save last stored child index of a given parent.
pub fn save_child_index(&mut self, parent_id: &Identifier, child_n: u32) -> Result<(), Error> { pub fn save_child_index(&mut self, parent_id: &Identifier, child_n: u32) -> Result<(), Error> {
self.db self.db
@@ -785,3 +782,40 @@ fn to_key_u64<K: AsRef<[u8]>>(k: K, val: u64) -> Vec<u8> {
res.write_u64::<BigEndian>(val).unwrap(); res.write_u64::<BigEndian>(val).unwrap();
res res
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn child_index_in_batch() -> Result<(), Error> {
grin_core::global::set_local_chain_type(grin_core::global::ChainTypes::AutomatedTesting);
let data_dir = std::env::temp_dir().join(format!("grin_wallet_{}", Uuid::new_v4()));
let db_dir = data_dir.join(DB_DIR);
fs::create_dir_all(&db_dir)?;
let store = Store::new(
db_dir.to_str().unwrap(),
None,
Some(DB_DIR),
DB_PREFIXES.to_vec(),
None,
None,
)?;
let parent = ExtKeychain::derive_key_id(2, 0, 0, 0, 0);
{
let mut batch = WalletBatch::<ExtKeychain> {
db: store.batch()?,
keychain: None,
};
assert_eq!(batch.current_child_index(&parent)?, 0);
batch.save_child_index(&parent, 1)?;
assert_eq!(batch.current_child_index(&parent)?, 1);
batch.commit()?;
}
drop(store);
fs::remove_dir_all(data_dir)?;
Ok(())
}
}
-2
View File
@@ -36,8 +36,6 @@ Separating side effects until the 'save_step' part would make these functions mu
#### TODOs #### TODOs
- `next_child_for` reads the derivation index and writes it back in two batches, so
concurrent steps can be handed the same child key. `next_child` has the same shape.
- Honour `min_input_confirmation`; contract input selection currently uses one confirmation. - Honour `min_input_confirmation`; contract input selection currently uses one confirmation.
- Decide whether `--use-inputs` or `--make-outputs` should imply early locking; currently - Decide whether `--use-inputs` or `--make-outputs` should imply early locking; currently
only `--add-outputs` locks during `new`. only `--add-outputs` locks during `new`.