move sender nonce handling

This commit is contained in:
wiesche
2026-09-11 21:13:03 +02:00
parent 2b6b58a093
commit f7a69fe90a
3 changed files with 56 additions and 20 deletions
+2 -1
View File
@@ -44,7 +44,6 @@ without a user confirmation step.
- Add a Grin node API for kernel lookup by MMR index, then use the witness index instead
of searching by commitment.
- Expose the kernel feature constants from `grin_core` and reuse them here.
- Make sender-nonce commitment handling reusable outside contracts
- Add compact Slatepack support for contracts
- Keep Slate V5 experimental until the related RFCs are accepted
- Add contract history, lookup and transport.
@@ -52,6 +51,8 @@ without a user confirmation step.
- Store transactions in LMDB so they can be committed with the wallet state.
- Revisit automatic receive signing once there is a user confirmation step.
- Revisit `remove_other_sigdata` before allowing more than two participants.
- Check all preserved slate fields in the round-trip tests
- Test a full MWixnet round with a received contract output
### Side-effects
+7 -18
View File
@@ -17,9 +17,9 @@
use crate::backend::WalletBackend;
use crate::contract::types::ProofArgs;
use crate::grin_keychain::Keychain;
use crate::grin_util::secp::key::{PublicKey, SecretKey};
use crate::grin_util::secp::key::SecretKey;
use crate::grin_util::secp::Secp256k1;
use crate::payment_proof::{sender_nonce_tweak, EarlyPaymentProof};
use crate::payment_proof::EarlyPaymentProof;
use crate::slate::{PaymentProofType, Slate};
use crate::types::{Context, NodeClient};
use crate::{address, Error};
@@ -46,22 +46,11 @@ pub(super) fn commit_sender_nonce(
.sender_address
.ok_or(Error::NoSenderAddressProvided)?;
let proof = EarlyPaymentProof::from_slate(slate, 0, Some(sender_address))?;
if let Some(base_public) = context.sender_public_nonce.as_ref() {
let mut expected = base_public.clone();
expected.add_exp_assign(secp, &sender_nonce_tweak(secp, base_public, &proof)?)?;
if PublicKey::from_secret_key(secp, &context.sec_nonce)? != expected {
return Err(Error::PaymentProofValidation(
"Sender nonce proof details changed".into(),
));
}
} else {
let base_public = PublicKey::from_secret_key(secp, &context.sec_nonce)?;
context
.sec_nonce
.add_assign(secp, &sender_nonce_tweak(secp, &base_public, &proof)?)?;
context.sender_public_nonce = Some(base_public);
}
Ok(())
proof.commit_sender_nonce(
secp,
&mut context.sec_nonce,
&mut context.sender_public_nonce,
)
}
/// Add the receiver's proof data to a contract slate.
+47 -1
View File
@@ -279,6 +279,37 @@ impl EarlyPaymentProof {
Ok(proof)
}
/// Commit the sender nonce before sharing its public value
/// Save the base nonce on the first call and verify it on retries
pub fn commit_sender_nonce(
&self,
secp: &Secp256k1,
secret_nonce: &mut SecretKey,
base_public_nonce: &mut Option<PublicKey>,
) -> Result<(), Error> {
if self.proof_type != PaymentProofType::SenderNonce {
return Err(Error::PaymentProofValidation(
"Sender nonce commitment requires a sender-nonce proof".into(),
));
}
if let Some(base_public) = base_public_nonce.as_ref() {
let mut expected = base_public.clone();
expected.add_exp_assign(secp, &sender_nonce_tweak(secp, base_public, self)?)?;
if PublicKey::from_secret_key(secp, secret_nonce)? != expected {
return Err(Error::PaymentProofValidation(
"Sender nonce proof details changed".into(),
));
}
} else {
let base_public = PublicKey::from_secret_key(secp, secret_nonce)?;
let mut committed_nonce = secret_nonce.clone();
committed_nonce.add_assign(secp, &sender_nonce_tweak(secp, &base_public, self)?)?;
*secret_nonce = committed_nonce;
*base_public_nonce = Some(base_public);
}
Ok(())
}
/// Sign the payment promise
pub fn sign(&self, sec_key: &SecretKey) -> Result<(DalekSignature, DalekPublicKey), Error> {
let d_skey = DalekSecretKey::from_bytes(&sec_key.0);
@@ -403,7 +434,7 @@ impl EarlyPaymentProof {
}
}
pub(crate) fn sender_nonce_tweak(
fn sender_nonce_tweak(
secp: &Secp256k1,
sender_nonce: &PublicKey,
proof: &EarlyPaymentProof,
@@ -644,6 +675,21 @@ mod tests {
sender_nonce_tweak(&secp, &base, &sender_nonce)?.0.to_hex(),
"6bd7c4e86bd61999c26fdd4cc1af90cdcb0a79e39644f4d8045e2a6599bd4db0"
);
let mut secret_nonce = SecretKey::from_slice(&secp, &[9; 32])?;
let mut public_nonce = None;
assert!(proof
.commit_sender_nonce(&secp, &mut secret_nonce, &mut public_nonce)
.is_err());
sender_nonce.commit_sender_nonce(&secp, &mut secret_nonce, &mut public_nonce)?;
assert_eq!(public_nonce, Some(base));
let committed_nonce = secret_nonce.clone();
sender_nonce.commit_sender_nonce(&secp, &mut secret_nonce, &mut public_nonce)?;
assert_eq!(secret_nonce, committed_nonce);
let mut changed = sender_nonce.clone();
changed.memo = Some(PaymentMemo::new("changed details".into())?);
assert!(changed
.commit_sender_nonce(&secp, &mut secret_nonce, &mut public_nonce)
.is_err());
let (signature, recipient) = sender_nonce.sign(&proof_key)?;
sender_nonce.promise_signature = Some(signature);
sender_nonce.amount += 1;