Skip to content

Commit

Permalink
dbc: refactor validation workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-orlovsky committed Dec 14, 2023
1 parent 49a08d3 commit e776a8d
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 152 deletions.
83 changes: 1 addition & 82 deletions consensus/src/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,87 +25,6 @@ use amplify::confinement::Confined;
use crate::opcodes::*;
use crate::{VarInt, VarIntArray, LIB_NAME_BITCOIN};

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
// TODO: Replace `try_from` with `from` since opcodes cover whole range of u8
#[strict_type(lib = LIB_NAME_BITCOIN, tags = repr, into_u8, try_from_u8)]
#[non_exhaustive]
#[repr(u8)]
pub enum OpCode {
/// Push an empty array onto the stack.
#[display("OP_PUSH_BYTES0")]
PushBytes0 = OP_PUSHBYTES_0,

/// Push the next 32 bytes as an array onto the stack.
#[display("OP_PUSH_BYTES32")]
PushBytes32 = OP_PUSHBYTES_32,

/// Synonym for OP_RETURN.
Reserved = OP_RESERVED,

/// Fail the script immediately.
#[display("OP_RETURN")]
#[strict_type(dumb)]
Return = OP_RETURN,

/// Read the next byte as N; push the next N bytes as an array onto the
/// stack.
#[display("OP_PUSH_DATA1")]
PushData1 = OP_PUSHDATA1,
/// Read the next 2 bytes as N; push the next N bytes as an array onto the
/// stack.
#[display("OP_PUSH_DATA2")]
PushData2 = OP_PUSHDATA2,
/// Read the next 4 bytes as N; push the next N bytes as an array onto the
/// stack.
#[display("OP_PUSH_DATA3")]
PushData4 = OP_PUSHDATA4,

/// Push the array `0x01` onto the stack.
#[display("OP_PUSHNUM_1")]
PushNum1 = OP_PUSHNUM_1,

/// Duplicates the top stack item.
#[display("OP_DUP")]
Dup = OP_DUP,

/// Pushes 1 if the inputs are exactly equal, 0 otherwise.
#[display("OP_EQUAL")]
Equal = OP_EQUAL,

/// Returns success if the inputs are exactly equal, failure otherwise.
#[display("OP_EQUALVERIFY")]
EqualVerify = OP_EQUALVERIFY,

/// Pop the top stack item and push its RIPEMD160 hash.
#[display("OP_RIPEMD160")]
Ripemd160 = OP_RIPEMD160,

/// Pop the top stack item and push its SHA1 hash.
#[display("OP_SHA1")]
Sha1 = OP_SHA1,

/// Pop the top stack item and push its SHA256 hash.
#[display("OP_SHA256")]
Sha256 = OP_SHA256,

/// Pop the top stack item and push its RIPEMD(SHA256) hash.
#[display("OP_HASH160")]
Hash160 = OP_HASH160,

/// Pop the top stack item and push its SHA256(SHA256) hash.
#[display("OP_HASH256")]
Hash256 = OP_HASH256,

/// <https://en.bitcoin.it/wiki/OP_CHECKSIG> pushing 1/0 for success/failure.
#[display("OP_CHECKSIG")]
CheckSig = OP_CHECKSIG,

/// <https://en.bitcoin.it/wiki/OP_CHECKSIG> returning success/failure.
#[display("OP_CHECKSIGVERIFY")]
CheckSigVerify = OP_CHECKSIGVERIFY,
}

#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
#[wrapper(Deref, AsSlice, Hex)]
#[wrapper_mut(DerefMut, AsSliceMut)]
Expand Down Expand Up @@ -229,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]
Expand Down
18 changes: 10 additions & 8 deletions dbc/src/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,16 @@ impl<D: dbc::Proof> PartialOrd for Anchor<mpc::MerkleBlock, D> {
#[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<D: dbc::Proof> Anchor<mpc::MerkleBlock, D> {
Expand Down Expand Up @@ -178,10 +178,12 @@ impl<D: dbc::Proof> Anchor<mpc::MerkleProof, D> {
protocol_id: impl Into<ProtocolId>,
message: Message,
tx: &Tx,
) -> Result<(), VerifyError<D::Error>> {
) -> Result<mpc::Commitment, VerifyError<D::Error>> {
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
Expand Down Expand Up @@ -233,7 +235,7 @@ impl<D: dbc::Proof> Anchor<mpc::MerkleBlock, D> {
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)
Expand Down
56 changes: 22 additions & 34 deletions dbc/src/opret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,37 @@

//! 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",
derive(Serialize, Deserialize),
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.
Expand All @@ -65,24 +68,9 @@ pub enum OpretVerifyError {
pub struct OpretProof(());

impl Proof for OpretProof {
type Error = OpretVerifyError;
type Error = EmbedVerifyError<OpretError>;

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<OpretError>> {
tx.verify(msg, self)
}
}
58 changes: 58 additions & 0 deletions dbc/src/opret/spk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Deterministic bitcoin commitments library.
//
// SPDX-License-Identifier: Apache-2.0
//
// Written in 2019-2023 by
// Dr Maxim Orlovsky <[email protected]>
//
// 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<Commitment, ScriptPubkey, Opret> for OpretProof {
fn restore_original_container(
&self,
commit_container: &ScriptPubkey,
) -> Result<ScriptPubkey, EmbedVerifyError<OpretError>> {
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<Commitment, Opret> for ScriptPubkey {
type Proof = OpretProof;
type CommitError = OpretError;

fn embed_commit(&mut self, msg: &Commitment) -> Result<Self::Proof, Self::CommitError> {
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())
}
}
56 changes: 56 additions & 0 deletions dbc/src/opret/tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Deterministic bitcoin commitments library.
//
// SPDX-License-Identifier: Apache-2.0
//
// Written in 2019-2023 by
// Dr Maxim Orlovsky <[email protected]>
//
// 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<Commitment, Tx, Opret> for OpretProof {
fn restore_original_container(
&self,
commit_container: &Tx,
) -> Result<Tx, EmbedVerifyError<OpretError>> {
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<Commitment, Opret> for Tx {
type Proof = OpretProof;
type CommitError = OpretError;

fn embed_commit(&mut self, msg: &Commitment) -> Result<Self::Proof, Self::CommitError> {
for txout in &mut self.outputs {
if txout.script_pubkey.is_op_return() {
return txout.script_pubkey.embed_commit(msg);
}
}
Err(OpretError::NoOpretOutput)
}
}
46 changes: 46 additions & 0 deletions dbc/src/opret/txout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Deterministic bitcoin commitments library.
//
// SPDX-License-Identifier: Apache-2.0
//
// Written in 2019-2023 by
// Dr Maxim Orlovsky <[email protected]>
//
// 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<Commitment, TxOut, Opret> for OpretProof {
fn restore_original_container(
&self,
commit_container: &TxOut,
) -> Result<TxOut, EmbedVerifyError<OpretError>> {
let mut txout = commit_container.clone();
txout.script_pubkey = self.restore_original_container(&txout.script_pubkey)?;
Ok(txout)
}
}

impl EmbedCommitVerify<Commitment, Opret> for TxOut {
type Proof = OpretProof;
type CommitError = OpretError;

fn embed_commit(&mut self, msg: &Commitment) -> Result<Self::Proof, Self::CommitError> {
self.script_pubkey.embed_commit(msg)
}
}
Loading

0 comments on commit e776a8d

Please sign in to comment.