diff --git a/api/src/owner_rpc.rs b/api/src/owner_rpc.rs index feda342..78ddda1 100644 --- a/api/src/owner_rpc.rs +++ b/api/src/owner_rpc.rs @@ -597,7 +597,7 @@ pub trait OwnerRpc { }, "proof_args": { "suppress_proof": true, - "proof_type": "Invoice", + "proof_type": 1, "memo": null, "sender_address": null } @@ -685,7 +685,7 @@ pub trait OwnerRpc { }, "proof_args": { "suppress_proof": true, - "proof_type": "Invoice", + "proof_type": 1, "memo": null, "sender_address": null } diff --git a/libwallet/src/contract/types.rs b/libwallet/src/contract/types.rs index 9401ec5..fda442e 100644 --- a/libwallet/src/contract/types.rs +++ b/libwallet/src/contract/types.rs @@ -95,6 +95,7 @@ pub struct ProofArgs { /// If net change is positive during this step, whether to suppress the creation of payment proof pub suppress_proof: bool, /// Requested early payment proof type + #[serde(with = "crate::slate::payment_proof_type_serde")] pub proof_type: ProofType, /// Memo used when this step creates the payment proof pub memo: Option, @@ -249,6 +250,29 @@ pub struct ContractRevokeArgsAPI { mod tests { use super::*; + #[test] + fn proof_args_json() { + for (proof_type, value) in [ + (ProofType::Legacy, 0), + (ProofType::Invoice, 1), + (ProofType::SenderNonce, 2), + ] { + let args = ProofArgs { + proof_type, + ..Default::default() + }; + let json = serde_json::to_value(args).unwrap(); + assert_eq!(json["proof_type"], value); + let recovered: ProofArgs = serde_json::from_value(json).unwrap(); + assert_eq!(recovered.proof_type, proof_type); + } + let mut json = serde_json::to_value(ProofArgs::default()).unwrap(); + for invalid in [serde_json::json!(3), serde_json::json!("Invoice")] { + json["proof_type"] = invalid; + assert!(serde_json::from_value::(json.clone()).is_err()); + } + } + #[test] fn selection_args_json() { let explicit: OutputSelectionArgs = serde_json::from_value(serde_json::json!({ diff --git a/libwallet/src/types.rs b/libwallet/src/types.rs index a07e79b..f9dd24a 100644 --- a/libwallet/src/types.rs +++ b/libwallet/src/types.rs @@ -791,18 +791,41 @@ pub struct StoredProofInfo { pub sender_signature: Option, // Fields beyond here are specific to early payment proofs /// Assumed to be 0x00 (Legacy) if missing + #[serde(default, skip_serializing_if = "Option::is_none")] pub proof_type: Option, /// receiver's public nonce from signing + #[serde( + default, + with = "dalek_ser::option_pubkey_serde", + skip_serializing_if = "Option::is_none" + )] pub receiver_public_nonce: Option, /// receiver's public excess from signing + #[serde( + default, + with = "dalek_ser::option_pubkey_serde", + skip_serializing_if = "Option::is_none" + )] pub receiver_public_excess: Option, /// Timestamp provided by recipient when signing + #[serde(default, skip_serializing_if = "Option::is_none")] pub timestamp: Option>, /// Optional payment memo + #[serde(default, skip_serializing_if = "Option::is_none")] pub memo: Option, /// Early-proof sender partial signature + #[serde( + default, + with = "secp_ser::option_sig_serde", + skip_serializing_if = "Option::is_none" + )] pub sender_part_sig: Option, /// Untweaked sender public nonce for a sender-nonce proof + #[serde( + default, + with = "dalek_ser::option_pubkey_serde", + skip_serializing_if = "Option::is_none" + )] pub sender_public_nonce: Option, } @@ -1098,11 +1121,47 @@ mod tests { "sender_signature": null }); + let expected = proof.clone(); let proof: StoredProofInfo = serde_json::from_value(proof).unwrap(); assert!(proof.receiver_signature.is_none()); let proof = serde_json::to_value(proof).unwrap(); assert!(proof.get("receiver_signature").is_some()); assert!(proof.get("promise_signature").is_none()); + assert_eq!(proof, expected); + } + + #[test] + fn stored_proof_json() { + let secp = Secp256k1::new(); + let key = SecretKey::from_slice(&secp, &[1; 32]).unwrap(); + let public_key = PublicKey::from_secret_key(&secp, &key).unwrap(); + let signature = Signature::from_compact(&secp, &[11; 64]).unwrap(); + let address = DalekSecretKey::from_bytes(&[1; 32]).verifying_key(); + let proof = StoredProofInfo { + proof_type: Some(1), + receiver_public_nonce: Some(public_key), + receiver_public_excess: Some(public_key), + sender_public_nonce: Some(public_key), + sender_part_sig: Some(signature), + ..StoredProofInfo::new(address, None, address, 0, None) + }; + let value = serde_json::to_value(&proof).unwrap(); + for field in [ + "receiver_public_nonce", + "receiver_public_excess", + "sender_public_nonce", + ] { + assert_eq!(value[field], public_key.serialize_vec(&secp, true).to_hex()); + } + assert_eq!(value["sender_part_sig"], [11u8; 64].to_hex()); + let recovered: StoredProofInfo = serde_json::from_value(value).unwrap(); + assert_eq!(recovered.receiver_public_nonce, proof.receiver_public_nonce); + assert_eq!( + recovered.receiver_public_excess, + proof.receiver_public_excess + ); + assert_eq!(recovered.sender_public_nonce, proof.sender_public_nonce); + assert_eq!(recovered.sender_part_sig, proof.sender_part_sig); } #[test]