diff --git a/Cargo.lock b/Cargo.lock index f7c2af35..933cff9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,8 +266,7 @@ dependencies = [ [[package]] name = "commit_encoding_derive" version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00033f14d67c4169d588f085ea2faeb7b610cf03a74d42ea09eeba31abef2047" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#5d1e05803158dc6a59599dcf43657d31ff6911e8" dependencies = [ "amplify", "amplify_syn", @@ -278,9 +277,8 @@ dependencies = [ [[package]] name = "commit_verify" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d9d6e86f6cec8d4af19a0e418bac9cb266a6dc70660bcdcdac1e0fa924e0d1" +version = "0.11.0-beta.1" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#5d1e05803158dc6a59599dcf43657d31ff6911e8" dependencies = [ "amplify", "commit_encoding_derive", @@ -655,9 +653,8 @@ dependencies = [ [[package]] name = "single_use_seals" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7655b4b597fca10d2cf7579d3dfee1987a45342bdeecf90cab5affec1c7197" +version = "0.11.0-beta.1" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#5d1e05803158dc6a59599dcf43657d31ff6911e8" dependencies = [ "amplify_derive", ] diff --git a/Cargo.toml b/Cargo.toml index c8945b5c..8f7109aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,8 @@ license = "Apache-2.0" amplify = "4.5.0" strict_encoding = "2.6.1" strict_types = "1.6.3" -commit_verify = "0.10.6" -single_use_seals = "0.10.1" +commit_verify = "0.11.0-beta.1" +single_use_seals = "0.11.0-beta.1" bp-consensus = { version = "0.10.11", path = "consensus" } bp-dbc = { version = "0.10.11", path = "./dbc" } bp-seals = { version = "0.10.11", path = "./seals" } @@ -81,3 +81,7 @@ stl = ["strict_types", "strict_types/base64", "bp-consensus/stl", "commit_verify [package.metadata.docs.rs] features = [ "all" ] + +[patch.crates-io] +commit_verify = { git = "https://github.com/LNP-BP/client_side_validation", branch = "v0.11" } +single_use_seals = { git = "https://github.com/LNP-BP/client_side_validation", branch = "v0.11" } diff --git a/dbc/src/anchor.rs b/dbc/src/anchor.rs index 27e088de..72b0cd62 100644 --- a/dbc/src/anchor.rs +++ b/dbc/src/anchor.rs @@ -29,10 +29,10 @@ use std::cmp::Ordering; use amplify::{Bytes32, Wrapper}; use bc::{ScriptPubkey, Tx, Txid}; use commit_verify::mpc::{self, Message, ProtocolId}; -use commit_verify::{CommitmentId, ConvolveCommitProof}; +use commit_verify::{CommitmentId, ConvolveCommitProof, ConvolveVerifyError}; use strict_encoding::{StrictDumb, StrictEncode}; -use crate::tapret::{TapretError, TapretProof}; +use crate::tapret::TapretProof; use crate::LIB_NAME_BPCORE; /// Default depth of LNPBP-4 commitment tree @@ -65,9 +65,9 @@ pub struct AnchorId( serde(crate = "serde_crate", rename_all = "camelCase") )] pub enum VerifyError { - /// Tapret commitment verification failure. + /// invalid deterministic bitcoin commitment. Details: {0} #[from] - Tapret(TapretError), + Dbc(AnchorError), /// LNPBP-4 invalid proof. Details: {0} #[from] @@ -174,7 +174,7 @@ impl Anchor { protocol_id: impl Into, message: Message, tx: &Tx, - ) -> Result { + ) -> Result<(), VerifyError> { self.dbc_proof .verify(&self.mpc_proof.convolve(protocol_id.into(), message)?, tx) .map_err(VerifyError::from) @@ -236,6 +236,35 @@ impl Anchor { } } +/// Errors covering failed anchor validation. +#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +#[display(doc_comments)] +pub enum AnchorError { + /// witness transaction {txid} contains invalid OP_RETURN commitment + /// {present:x} instead of {expected:x}. + OpretMismatch { + /// Transaction id + txid: Txid, + /// Commitment from the first OP_RETURN transaction output + present: ScriptPubkey, + /// Expected commitment absent in the first OP_RETURN transaction output + expected: ScriptPubkey, + }, + + /// witness transaction {0} does not contain any OP_RETURN commitment + /// required by the seal definition. + OpretAbsent(Txid), + + #[from] + /// witness transaction does not contain a valid tapret commitment. {0}. + Tapret(ConvolveVerifyError), +} + /// Type and type-specific proof information of a deterministic bitcoin /// commitment. #[derive(Clone, PartialEq, Eq, Debug)] @@ -260,17 +289,28 @@ pub enum Proof { impl Proof { /// Verifies validity of the proof. - pub fn verify(&self, msg: &mpc::Commitment, tx: &Tx) -> Result { + pub fn verify(&self, msg: &mpc::Commitment, tx: &Tx) -> Result<(), AnchorError> { match self { Proof::OpretFirst => { for txout in &tx.outputs { if txout.script_pubkey.is_op_return() { - return Ok(txout.script_pubkey == ScriptPubkey::op_return(msg.as_slice())); + let expected = ScriptPubkey::op_return(msg.as_slice()); + if txout.script_pubkey == expected { + return Ok(()); + } else { + return Err(AnchorError::OpretMismatch { + txid: tx.txid(), + present: txout.script_pubkey.clone(), + expected, + }); + } } } - Ok(false) + Err(AnchorError::OpretAbsent(tx.txid())) + } + Proof::TapretFirst(proof) => { + ConvolveCommitProof::<_, Tx, _>::verify(proof, msg, tx).map_err(AnchorError::from) } - Proof::TapretFirst(proof) => ConvolveCommitProof::<_, Tx, _>::verify(proof, msg, tx), } } } diff --git a/dbc/src/tapret/xonlypk.rs b/dbc/src/tapret/xonlypk.rs index 65cc05ae..cfcfd482 100644 --- a/dbc/src/tapret/xonlypk.rs +++ b/dbc/src/tapret/xonlypk.rs @@ -125,12 +125,8 @@ mod test { internal_pk }); - assert!( - ConvolveCommitProof::::verify( - &proof, &msg, &outer_key - ) - .unwrap() - ); + ConvolveCommitProof::::verify(&proof, &msg, &outer_key) + .unwrap(); } #[test] @@ -153,12 +149,8 @@ mod test { internal_pk }); - assert!( - ConvolveCommitProof::::verify( - &proof, &msg, &outer_key - ) - .unwrap() - ); + ConvolveCommitProof::::verify(&proof, &msg, &outer_key) + .unwrap(); } #[test] @@ -182,11 +174,7 @@ mod test { internal_pk }); - assert!( - ConvolveCommitProof::::verify( - &proof, &msg, &outer_key - ) - .unwrap() - ); + ConvolveCommitProof::::verify(&proof, &msg, &outer_key) + .unwrap(); } } diff --git a/seals/src/txout/error.rs b/seals/src/txout/error.rs index 283ca8a7..0d89efb9 100644 --- a/seals/src/txout/error.rs +++ b/seals/src/txout/error.rs @@ -20,6 +20,7 @@ // limitations under the License. use bc::Outpoint; +use dbc::anchor::AnchorError; /// Seal verification errors. #[derive(Clone, PartialEq, Eq, Debug, Display, From, Error)] @@ -39,11 +40,10 @@ pub enum VerifyError { /// seal lacks witness transaction id information. NoWitnessTxid, - /// tapret commitment is invalid. - /// - /// Details: {0} + /// invalid anchor. #[from] - InvalidTapretCommitment(dbc::tapret::TapretError), + #[display(inner)] + InvalidAnchor(AnchorError), } /// Error happening if the seal data holds only witness transaction output diff --git a/seals/src/txout/witness.rs b/seals/src/txout/witness.rs index 783485ba..cf115bc0 100644 --- a/seals/src/txout/witness.rs +++ b/seals/src/txout/witness.rs @@ -56,7 +56,7 @@ impl SealWitness for Witness { type Message = mpc::Commitment; type Error = VerifyError; - fn verify_seal(&self, seal: &Seal, msg: &Self::Message) -> Result { + fn verify_seal(&self, seal: &Seal, msg: &Self::Message) -> Result<(), Self::Error> { // 1. The seal must match tx inputs let outpoint = seal.outpoint().ok_or(VerifyError::NoWitnessTxid)?; if !self @@ -76,7 +76,7 @@ impl SealWitness for Witness { &self, seals: impl IntoIterator, msg: &Self::Message, - ) -> Result + ) -> Result<(), Self::Error> where Seal: 'seal, {