Contracts: only create a context on a genuine miss

get_private_context() flattened "missing context" and real store/keychain
errors into one error, so the contract code created a fresh context on ANY
error (e.g. a decryption failure from a wrong keychain mask), masking the
real problem.

- add Error::NotFoundErr; the grin_store conversion maps NotFoundErr through
  faithfully (other store errors stay Backend).
- contract get_or_create / get_net_change / sign create-or-skip only on
  NotFoundErr and propagate every other error.

Addresses PR #754 review (context.rs, utils.rs, sign.rs).
This commit is contained in:
jwinterm
2026-06-04 19:34:21 -04:00
parent 382cf869c9
commit 571b1f968a
5 changed files with 23 additions and 5 deletions
+2
View File
@@ -377,6 +377,8 @@ where
let (blind_xor_key, nonce_xor_key) =
private_ctx_xor_keys(&self.keychain(keychain_mask)?, slate_id)?;
// A missing context surfaces as Error::NotFoundErr (via the grin_store
// conversion), letting callers distinguish it from other store errors.
let mut ctx: Context = option_to_not_found(self.db.get_ser(&ctx_key, None), || {
format!("Slate id: {:x?}", slate_id.to_vec())
})?;
+4 -2
View File
@@ -35,10 +35,12 @@ where
C: NodeClient + 'a,
K: Keychain + 'a,
{
// Compute if we will add outputs at this step
// Compute if we will add outputs at this step. Only a missing context means we
// still need to add them; propagate any other error.
let will_add_outputs = match w.get_private_context(keychain_mask, slate.id.as_bytes()) {
Ok(ctx) => ctx.get_inputs().len() + ctx.get_outputs().len() == 0,
Err(_) => true,
Err(Error::NotFoundErr(_)) => true,
Err(e) => return Err(e),
};
// Compute state for 'sign'
let (sl, mut context) = compute(w, keychain_mask, slate, setup_args)?;
+3 -1
View File
@@ -41,7 +41,8 @@ where
let maybe_context = w.get_private_context(keychain_mask, slate.id.as_bytes());
let context = match maybe_context {
Err(_) => {
// Only create a new context when there genuinely isn't one; propagate other errors.
Err(Error::NotFoundErr(_)) => {
// Get data required for creating a context
let height = w.w2n_client().get_chain_tip()?.0;
let parent_key_id =
@@ -57,6 +58,7 @@ where
false,
)?
}
Err(e) => return Err(e),
Ok(ctx) => ctx,
};
Ok(context)
+4 -1
View File
@@ -158,7 +158,10 @@ where
}
expected_net_change = Some(ctx.get_net_change());
}
Err(_) => debug!("contract::utils::get_net_change => context not found"),
Err(Error::NotFoundErr(_)) => {
debug!("contract::utils::get_net_change => context not found")
}
Err(e) => return Err(e),
};
// Fail if net_change was not passed to setup_args and was also not present in the context.
+10 -1
View File
@@ -81,6 +81,10 @@ pub enum Error {
#[error("Wallet store error: {0}")]
Backend(String),
/// Requested item was not found in the wallet store
#[error("Not found: {0}")]
NotFoundErr(String),
/// Callback implementation error conversion
#[error("Restore Error")]
Restore,
@@ -348,7 +352,12 @@ pub enum Error {
impl From<grin_store::Error> for Error {
fn from(error: grin_store::Error) -> Error {
Error::Backend(format!("{}", error))
// Preserve "not found" so callers can distinguish a missing item from other
// store errors; everything else collapses to a generic backend error.
match error {
grin_store::Error::NotFoundErr(s) => Error::NotFoundErr(s),
other => Error::Backend(format!("{}", other)),
}
}
}