verify invoice promises before signing

This commit is contained in:
wiesche
2026-09-01 16:13:22 +02:00
parent a9d82a5039
commit 88af108b88
4 changed files with 44 additions and 16 deletions
@@ -101,6 +101,10 @@ fn contract_early_proofs_srs_test_impl(test_dir: &'static str) -> Result<(), lib
let args = &ContractSetupArgsAPI {
..Default::default()
};
let mut tampered = slate.clone();
let proof = tampered.payment_proof.as_mut().unwrap();
proof.timestamp += Duration::from_secs(1);
assert!(api.contract_sign(m, &tampered, args).is_err());
slate = api.contract_sign(m, &slate, args)?;
Ok(())
-1
View File
@@ -59,7 +59,6 @@ Separating side effects until the 'save_step' part would make these functions mu
- Confirm that invoice-proof retrieval excludes the transaction fee from the amount.
- Move payment-proof creation and verification onto `Slate` so it is versioned with the
slate format.
- Verify an invoice promise when paying and derive the receiver from the participants.
- Bind and validate the contract proof type in the serialized proof.
- Keep V5 when decoding Slatepacks through Owner RPC.
- Preserve kernel features and their arguments across V4 and V5; see #793.
+1 -4
View File
@@ -79,10 +79,7 @@ where
let (mut sl, mut context) = setup::compute(w, keychain_mask, &mut sl, &setup_args)?;
// Add outputs to the slate, verify the payment proof and sign the slate
contract::slate::add_outputs(w, keychain_mask, &mut sl, &context)?;
if let Some(ref p) = sl.payment_proof {
contract::slate::verify_payment_proof(&sl, expected_net_change, &p.receiver_address)?;
// noop for the receiver
}
contract::slate::verify_invoice_promise(&sl, &w.keychain(keychain_mask)?, &context)?;
contract::slate::sign(w, keychain_mask, &mut sl, &mut context)?;
contract::slate::transition_state(&mut sl)?;
+39 -11
View File
@@ -21,11 +21,11 @@ use crate::grin_keychain::Keychain;
use crate::grin_util::secp::key::{PublicKey, SecretKey};
use crate::slate::{Slate, SlateState};
use crate::types::{Context, NodeClient};
use crate::util::OnionV3Address;
use crate::Error;
use super::types::ProofArgs;
use crate::contract::proofs::InvoiceProof;
use ed25519_dalek::VerifyingKey as DalekPublicKey;
/// Add payment proof data to slate, noop for sender
pub fn add_payment_proof<C, K>(
@@ -55,19 +55,47 @@ where
Ok(())
}
/// Verify payment proof signature
pub fn verify_payment_proof(
/// Verify the receiver's invoice promise before paying
pub fn verify_invoice_promise<K>(
slate: &Slate,
net_change: i64,
recipient_address: &DalekPublicKey,
) -> Result<(), Error> {
keychain: &K,
context: &Context,
) -> Result<(), Error>
where
K: Keychain,
{
// FUTURE: move proof verification onto Slate itself so it can be versioned (slate.verify_payment_proof_sig()).
debug!("contract::slate::verify_payment_proof => called");
if net_change > 0 && slate.payment_proof.is_some() {
let invoice_proof = InvoiceProof::from_slate(&slate, 1, None)?;
invoice_proof.verify_promise_signature(&recipient_address)?;
debug!("contract::slate::verify_invoice_promise => called");
if context.get_net_change()? >= 0 {
return Ok(());
}
Ok(())
let payment_proof = match slate.payment_proof.as_ref() {
Some(proof) => proof,
None => return Ok(()),
};
if slate.participant_data.len() != 2 {
return Err(Error::GenericError(format!(
"Expected 2 participants for an invoice promise, found {}",
slate.participant_data.len()
)));
}
let payer_index = slate.find_index_matching_context(keychain, context)?;
let receiver_index = slate
.participant_data
.iter()
.enumerate()
.find_map(|(index, _)| (index != payer_index).then_some(index))
.ok_or_else(|| Error::GenericError("Invoice promise has no receiver".to_string()))?;
let derivation_index = context.payment_proof_derivation_index.unwrap_or(0);
let sender_key = crate::address::address_from_derivation_path(
keychain,
&context.parent_key_id,
derivation_index,
)?;
let sender_address = OnionV3Address::from_private(&sender_key.0)?.to_ed25519()?;
let invoice_proof = InvoiceProof::from_slate(slate, receiver_index, Some(sender_address))?;
invoice_proof.verify_promise_signature(&payment_proof.receiver_address)
}
/// Adds inputs and outputs to slate