From fbdd7f30d67551af598381885d9005630015921f Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 14 Dec 2023 20:19:58 +0100 Subject: [PATCH] dbc: refactor validation workflow --- consensus/src/script.rs | 2 +- dbc/src/anchor.rs | 18 +++++++----- dbc/src/opret/mod.rs | 56 ++++++++++++++--------------------- dbc/src/opret/spk.rs | 58 +++++++++++++++++++++++++++++++++++++ dbc/src/opret/tx.rs | 56 +++++++++++++++++++++++++++++++++++ dbc/src/opret/txout.rs | 46 +++++++++++++++++++++++++++++ dbc/src/tapret/mod.rs | 17 +++++------ dbc/src/tapret/spk.rs | 6 ++-- dbc/src/tapret/tapscript.rs | 4 +-- dbc/src/tapret/tx.rs | 6 ++-- dbc/src/tapret/txout.rs | 6 ++-- dbc/src/tapret/xonlypk.rs | 12 ++++---- seals/src/txout/blind.rs | 4 +-- 13 files changed, 220 insertions(+), 71 deletions(-) create mode 100644 dbc/src/opret/spk.rs create mode 100644 dbc/src/opret/tx.rs create mode 100644 dbc/src/opret/txout.rs diff --git a/consensus/src/script.rs b/consensus/src/script.rs index f5ffe6b4..7de1b318 100644 --- a/consensus/src/script.rs +++ b/consensus/src/script.rs @@ -148,7 +148,7 @@ impl ScriptPubkey { } #[inline] - pub fn is_op_return(&self) -> bool { self[0] == OpCode::Return as u8 } + pub fn is_op_return(&self) -> bool { !self.is_empty() && self[0] == OpCode::Return as u8 } /// Adds a single opcode to the script. #[inline] diff --git a/dbc/src/anchor.rs b/dbc/src/anchor.rs index 07262dd2..8855c2c8 100644 --- a/dbc/src/anchor.rs +++ b/dbc/src/anchor.rs @@ -119,16 +119,16 @@ impl PartialOrd for Anchor { #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error, From)] #[display(doc_comments)] pub enum MergeError { - /// Error merging two LNPBP-4 proofs, which are unrelated. + /// Error merging two MPC proofs, which are unrelated. #[display(inner)] #[from] - Lnpbp4Mismatch(mpc::MergeError), + MpcMismatch(mpc::MergeError), /// anchors can't be merged since they have different witness transactions TxidMismatch, - /// anchors can't be merged since they have different proofs - ProofMismatch, + /// anchors can't be merged since they have different DBC proofs + DbcMismatch, } impl Anchor { @@ -178,10 +178,12 @@ impl Anchor { protocol_id: impl Into, message: Message, tx: &Tx, - ) -> Result<(), VerifyError> { + ) -> Result> { + let mpc_commitment = self.convolve(protocol_id, message)?; self.dbc_proof - .verify(&self.mpc_proof.convolve(protocol_id.into(), message)?, tx) - .map_err(VerifyError::Dbc) + .verify(&mpc_commitment, tx) + .map_err(VerifyError::Dbc)?; + Ok(mpc_commitment) } /// Verifies that the anchor commits to the given message under the given @@ -233,7 +235,7 @@ impl Anchor { return Err(MergeError::TxidMismatch); } if self.dbc_proof != other.dbc_proof { - return Err(MergeError::ProofMismatch); + return Err(MergeError::DbcMismatch); } self.mpc_proof.merge_reveal(other.mpc_proof)?; Ok(self) diff --git a/dbc/src/opret/mod.rs b/dbc/src/opret/mod.rs index 8939c8f9..2b533d0b 100644 --- a/dbc/src/opret/mod.rs +++ b/dbc/src/opret/mod.rs @@ -21,12 +21,23 @@ //! ScriptPubkey-based OP_RETURN commitments. -use bc::{ScriptPubkey, Tx, Txid}; +mod tx; +mod txout; +mod spk; + +use bc::Tx; use commit_verify::mpc::Commitment; +use commit_verify::{CommitmentProtocol, EmbedCommitVerify, EmbedVerifyError}; use crate::{Proof, LIB_NAME_BPCORE}; -/// Errors covering failed anchor validation. +/// Marker non-instantiable enum defining LNPBP-12 taproot OP_RETURN (`tapret`) +/// protocol. +pub enum Opret {} + +impl CommitmentProtocol for Opret {} + +/// Errors during tapret commitment. #[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] #[cfg_attr( feature = "serde", @@ -34,21 +45,13 @@ use crate::{Proof, LIB_NAME_BPCORE}; serde(crate = "serde_crate", rename_all = "camelCase") )] #[display(doc_comments)] -pub enum OpretVerifyError { - /// 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, - }, +pub enum OpretError { + /// transaction doesn't contain OP_RETURN output. + NoOpretOutput, - /// witness transaction {0} does not contain any OP_RETURN commitment - /// required by the seal definition. - OpretAbsent(Txid), + /// first OP_RETURN output inside the transaction already contains some + /// data. + InvalidOpretScript, } /// Empty type for use inside [`crate::Anchor`] for opret commitment scheme. @@ -65,24 +68,9 @@ pub enum OpretVerifyError { pub struct OpretProof(()); impl Proof for OpretProof { - type Error = OpretVerifyError; + type Error = EmbedVerifyError; - fn verify(&self, msg: &Commitment, tx: &Tx) -> Result<(), OpretVerifyError> { - // TODO: Use embed-commit-verify - for txout in &tx.outputs { - if txout.script_pubkey.is_op_return() { - let expected = ScriptPubkey::op_return(msg.as_slice()); - if txout.script_pubkey == expected { - return Ok(()); - } else { - return Err(OpretVerifyError::OpretMismatch { - txid: tx.txid(), - present: txout.script_pubkey.clone(), - expected, - }); - } - } - } - Err(OpretVerifyError::OpretAbsent(tx.txid())) + fn verify(&self, msg: &Commitment, tx: &Tx) -> Result<(), EmbedVerifyError> { + tx.verify(msg, self) } } diff --git a/dbc/src/opret/spk.rs b/dbc/src/opret/spk.rs new file mode 100644 index 00000000..f66fadc4 --- /dev/null +++ b/dbc/src/opret/spk.rs @@ -0,0 +1,58 @@ +// Deterministic bitcoin commitments library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bc::opcodes::OP_RETURN; +use bc::ScriptPubkey; +use commit_verify::mpc::Commitment; +use commit_verify::{EmbedCommitProof, EmbedCommitVerify, EmbedVerifyError}; + +use crate::opret::{Opret, OpretError, OpretProof}; + +impl EmbedCommitProof for OpretProof { + fn restore_original_container( + &self, + commit_container: &ScriptPubkey, + ) -> Result> { + if !commit_container.is_op_return() { + return Err(OpretError::NoOpretOutput.into()); + } + if commit_container.len() != 34 { + return Err(OpretError::InvalidOpretScript.into()); + } + Ok(ScriptPubkey::from_unsafe(vec![OP_RETURN])) + } +} + +impl EmbedCommitVerify for ScriptPubkey { + type Proof = OpretProof; + type CommitError = OpretError; + + fn embed_commit(&mut self, msg: &Commitment) -> Result { + if !self.is_op_return() { + return Err(OpretError::NoOpretOutput); + } + if self.len() != 1 { + return Err(OpretError::InvalidOpretScript); + } + *self = ScriptPubkey::op_return(msg.as_slice()); + Ok(OpretProof::default()) + } +} diff --git a/dbc/src/opret/tx.rs b/dbc/src/opret/tx.rs new file mode 100644 index 00000000..95d6c5a2 --- /dev/null +++ b/dbc/src/opret/tx.rs @@ -0,0 +1,56 @@ +// Deterministic bitcoin commitments library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bc::Tx; +use commit_verify::mpc::Commitment; +use commit_verify::{EmbedCommitProof, EmbedCommitVerify, EmbedVerifyError}; + +use super::{Opret, OpretError, OpretProof}; + +impl EmbedCommitProof for OpretProof { + fn restore_original_container( + &self, + commit_container: &Tx, + ) -> Result> { + let mut tx = commit_container.clone(); + for txout in &mut tx.outputs { + if txout.script_pubkey.is_op_return() { + *txout = self.restore_original_container(txout)?; + return Ok(tx); + } + } + Err(OpretError::NoOpretOutput.into()) + } +} + +impl EmbedCommitVerify for Tx { + type Proof = OpretProof; + type CommitError = OpretError; + + fn embed_commit(&mut self, msg: &Commitment) -> Result { + for txout in &mut self.outputs { + if txout.script_pubkey.is_op_return() { + return txout.script_pubkey.embed_commit(msg); + } + } + Err(OpretError::NoOpretOutput) + } +} diff --git a/dbc/src/opret/txout.rs b/dbc/src/opret/txout.rs new file mode 100644 index 00000000..40d61dd4 --- /dev/null +++ b/dbc/src/opret/txout.rs @@ -0,0 +1,46 @@ +// Deterministic bitcoin commitments library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bc::TxOut; +use commit_verify::mpc::Commitment; +use commit_verify::{EmbedCommitProof, EmbedCommitVerify, EmbedVerifyError}; + +use crate::opret::{Opret, OpretError, OpretProof}; + +impl EmbedCommitProof for OpretProof { + fn restore_original_container( + &self, + commit_container: &TxOut, + ) -> Result> { + let mut txout = commit_container.clone(); + txout.script_pubkey = self.restore_original_container(&txout.script_pubkey)?; + Ok(txout) + } +} + +impl EmbedCommitVerify for TxOut { + type Proof = OpretProof; + type CommitError = OpretError; + + fn embed_commit(&mut self, msg: &Commitment) -> Result { + self.script_pubkey.embed_commit(msg) + } +} diff --git a/dbc/src/tapret/mod.rs b/dbc/src/tapret/mod.rs index 0453ff99..a2e57b35 100644 --- a/dbc/src/tapret/mod.rs +++ b/dbc/src/tapret/mod.rs @@ -67,22 +67,21 @@ mod txout; mod spk; mod xonlypk; -pub use tapscript::{TapretCommitment, TAPRET_SCRIPT_COMMITMENT_PREFIX}; -pub use tx::TapretError; -pub use xonlypk::TapretKeyError; - -/// Marker non-instantiable enum defining LNPBP-12 taproot OP_RETURN (`tapret`) -/// protocol. -pub enum Lnpbp12 {} - use bc::{InternalPk, IntoTapHash, LeafScript, ScriptPubkey, TapBranchHash, TapNodeHash, Tx}; use commit_verify::mpc::Commitment; use commit_verify::{CommitmentProtocol, ConvolveCommitProof, ConvolveVerifyError}; use strict_encoding::{StrictDeserialize, StrictSerialize}; +pub use tapscript::{TapretCommitment, TAPRET_SCRIPT_COMMITMENT_PREFIX}; +pub use tx::TapretError; +pub use xonlypk::TapretKeyError; use crate::{Proof, LIB_NAME_BPCORE}; -impl CommitmentProtocol for Lnpbp12 {} +/// Marker non-instantiable enum defining LNPBP-12 taproot OP_RETURN (`tapret`) +/// protocol. +pub enum Tapret {} + +impl CommitmentProtocol for Tapret {} /// Errors in constructing tapret path proof [`TapretPathProof`]. #[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error)] diff --git a/dbc/src/tapret/spk.rs b/dbc/src/tapret/spk.rs index 668a5ec9..c18aa10e 100644 --- a/dbc/src/tapret/spk.rs +++ b/dbc/src/tapret/spk.rs @@ -22,9 +22,9 @@ use bc::ScriptPubkey; use commit_verify::{mpc, ConvolveCommit, ConvolveCommitProof}; -use super::{Lnpbp12, TapretKeyError, TapretProof}; +use super::{Tapret, TapretKeyError, TapretProof}; -impl ConvolveCommitProof for TapretProof { +impl ConvolveCommitProof for TapretProof { type Suppl = Self; fn restore_original(&self, _: &ScriptPubkey) -> ScriptPubkey { self.original_pubkey_script() } @@ -32,7 +32,7 @@ impl ConvolveCommitProof for TapretProof fn extract_supplement(&self) -> &Self::Suppl { self } } -impl ConvolveCommit for ScriptPubkey { +impl ConvolveCommit for ScriptPubkey { type Commitment = ScriptPubkey; type CommitError = TapretKeyError; diff --git a/dbc/src/tapret/tapscript.rs b/dbc/src/tapret/tapscript.rs index 00d68836..aa6ba3ba 100644 --- a/dbc/src/tapret/tapscript.rs +++ b/dbc/src/tapret/tapscript.rs @@ -28,7 +28,7 @@ use bc::{TapCode, TapScript}; use commit_verify::{mpc, CommitEncode, CommitVerify}; use strict_encoding::{DecodeError, DeserializeError, StrictDeserialize, StrictSerialize}; -use super::Lnpbp12; +use super::Tapret; use crate::LIB_NAME_BPCORE; /// Hardcoded tapret script prefix consisting of 29 `OP_RESERVED` pushes, @@ -99,7 +99,7 @@ impl TapretCommitment { pub fn with(mpc: mpc::Commitment, nonce: u8) -> Self { Self { mpc, nonce } } } -impl CommitVerify for TapScript { +impl CommitVerify for TapScript { /// Tapret script consists of 29 `OP_RESERVED` pushes, followed by /// `OP_RETURN`, `OP_PUSHBYTES_33` and serialized commitment data (MPC /// commitment + nonce as a single slice). diff --git a/dbc/src/tapret/tx.rs b/dbc/src/tapret/tx.rs index 07295e23..ac12d772 100644 --- a/dbc/src/tapret/tx.rs +++ b/dbc/src/tapret/tx.rs @@ -22,7 +22,7 @@ use bc::Tx; use commit_verify::{mpc, ConvolveCommit, ConvolveCommitProof}; -use super::{Lnpbp12, TapretKeyError, TapretProof}; +use super::{Tapret, TapretKeyError, TapretProof}; /// Errors during tapret commitment. #[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] @@ -42,7 +42,7 @@ pub enum TapretError { NoTaprootOutput, } -impl ConvolveCommitProof for TapretProof { +impl ConvolveCommitProof for TapretProof { type Suppl = Self; fn restore_original(&self, commitment: &Tx) -> Tx { @@ -59,7 +59,7 @@ impl ConvolveCommitProof for TapretProof { fn extract_supplement(&self) -> &Self::Suppl { self } } -impl ConvolveCommit for Tx { +impl ConvolveCommit for Tx { type Commitment = Tx; type CommitError = TapretError; diff --git a/dbc/src/tapret/txout.rs b/dbc/src/tapret/txout.rs index 24fe7f7e..cdbc77e0 100644 --- a/dbc/src/tapret/txout.rs +++ b/dbc/src/tapret/txout.rs @@ -22,9 +22,9 @@ use bc::{ScriptPubkey, TxOut}; use commit_verify::{mpc, ConvolveCommit, ConvolveCommitProof}; -use super::{Lnpbp12, TapretKeyError, TapretProof}; +use super::{Tapret, TapretKeyError, TapretProof}; -impl ConvolveCommitProof for TapretProof { +impl ConvolveCommitProof for TapretProof { type Suppl = Self; fn restore_original(&self, commitment: &TxOut) -> TxOut { @@ -37,7 +37,7 @@ impl ConvolveCommitProof for TapretProof { fn extract_supplement(&self) -> &Self::Suppl { self } } -impl ConvolveCommit for TxOut { +impl ConvolveCommit for TxOut { type Commitment = TxOut; type CommitError = TapretKeyError; diff --git a/dbc/src/tapret/xonlypk.rs b/dbc/src/tapret/xonlypk.rs index cfcfd482..5e6792cf 100644 --- a/dbc/src/tapret/xonlypk.rs +++ b/dbc/src/tapret/xonlypk.rs @@ -22,7 +22,7 @@ use bc::{InternalPk, OutputPk, TapBranchHash, TapLeafHash, TapNodeHash, TapScript}; use commit_verify::{mpc, CommitVerify, ConvolveCommit, ConvolveCommitProof}; -use super::{Lnpbp12, TapretNodePartner, TapretPathProof, TapretProof}; +use super::{Tapret, TapretNodePartner, TapretPathProof, TapretProof}; use crate::tapret::tapscript::TapretCommitment; /// Errors during tapret commitment embedding into x-only public key. @@ -42,7 +42,7 @@ pub enum TapretKeyError { IncorrectOrdering(TapretNodePartner, TapLeafHash), } -impl ConvolveCommitProof for TapretProof { +impl ConvolveCommitProof for TapretProof { type Suppl = TapretPathProof; fn restore_original(&self, _: &OutputPk) -> InternalPk { self.internal_pk } @@ -50,7 +50,7 @@ impl ConvolveCommitProof for TapretProof { fn extract_supplement(&self) -> &Self::Suppl { &self.path_proof } } -impl ConvolveCommit for InternalPk { +impl ConvolveCommit for InternalPk { type Commitment = OutputPk; type CommitError = TapretKeyError; @@ -125,7 +125,7 @@ mod test { internal_pk }); - ConvolveCommitProof::::verify(&proof, &msg, &outer_key) + ConvolveCommitProof::::verify(&proof, &msg, &outer_key) .unwrap(); } @@ -149,7 +149,7 @@ mod test { internal_pk }); - ConvolveCommitProof::::verify(&proof, &msg, &outer_key) + ConvolveCommitProof::::verify(&proof, &msg, &outer_key) .unwrap(); } @@ -174,7 +174,7 @@ mod test { internal_pk }); - ConvolveCommitProof::::verify(&proof, &msg, &outer_key) + ConvolveCommitProof::::verify(&proof, &msg, &outer_key) .unwrap(); } } diff --git a/seals/src/txout/blind.rs b/seals/src/txout/blind.rs index 4dbdcb7c..0aa4db3a 100644 --- a/seals/src/txout/blind.rs +++ b/seals/src/txout/blind.rs @@ -29,7 +29,7 @@ use amplify::{hex, Bytes32, Wrapper}; use baid58::{Baid58ParseError, Chunking, FromBaid58, ToBaid58, CHUNKING_32CHECKSUM}; use bc::{Outpoint, Txid, Vout}; use commit_verify::{CommitVerify, Conceal}; -use dbc::tapret::Lnpbp12; +use dbc::tapret::Tapret; use rand::{thread_rng, RngCore}; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; @@ -389,7 +389,7 @@ impl From for SecretSeal { fn from(outpoint: Outpoint) -> Self { BlindSeal::::from(outpoint).conceal() } } -impl CommitVerify, Lnpbp12> for SecretSeal { +impl CommitVerify, Tapret> for SecretSeal { fn commit(reveal: &BlindSeal) -> Self { Bytes32::commit(reveal).into() } }