Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SPV proof option for Anchors. Differentiate anchors, extra-tx witnesses and tx witnesses. #83

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions consensus/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@
Bytes32StrRev,
);

#[derive(Wrapper, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),

Check warning on line 51 in consensus/src/block.rs

View check run for this annotation

Codecov / codecov/patch

consensus/src/block.rs#L51

Added line #L51 was not covered by tests
serde(crate = "serde_crate", transparent)
)]
#[wrapper(BorrowSlice, Index, RangeOps, Debug, Hex, Display, FromStr)]
pub struct TxMerkleNode(
#[from]
#[from([u8; 32])]
Bytes32StrRev,
);

#[derive(Wrapper, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
Expand Down
2 changes: 1 addition & 1 deletion consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ mod weights;
pub mod stl;
mod coding;

pub use block::{BlockHash, BlockHeader, BlockMerkleRoot};
pub use block::{BlockHash, BlockHeader, BlockMerkleRoot, TxMerkleNode};
pub use coding::{
ByteStr, ConsensusDataError, ConsensusDecode, ConsensusDecodeError, ConsensusEncode, LenVarInt,
VarInt, VarIntArray, VarIntBytes,
Expand Down
218 changes: 176 additions & 42 deletions dbc/src/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(missing_docs)]

//! Anchors are data structures used in deterministic bitcoin commitments for
//! keeping information about the proof of the commitment in connection to the
//! transaction which contains the commitment, and multi-protocol merkle tree as
//! transaction which contains the commitment, and multiprotocol merkle tree as
//! defined by LNPBP-4.

use std::error::Error;
use std::marker::PhantomData;

use bc::{Tx, Txid};
use amplify::confinement::TinyVec;
use bc::{Tx, TxMerkleNode, Txid};
use commit_verify::mpc::{self, Message, ProtocolId};
use commit_verify::ReservedBytes;
use strict_encoding::{StrictDumb, StrictEncode};

use crate::opret::OpretProof;
use crate::tapret::TapretProof;
use crate::{DbcMethod, Method, LIB_NAME_BPCORE};

mod dbc {
Expand All @@ -56,28 +60,62 @@
Mpc(mpc::InvalidProof),
}

/// Anchor is a data structure used in deterministic bitcoin commitments for
/// keeping information about the proof of the commitment in connection to the
/// transaction which contains the commitment, and multi-protocol merkle tree as
/// defined by LNPBP-4.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE)]
#[strict_type(lib = LIB_NAME_BPCORE, tags = custom, dumb = Self::Txid(strict_dumb!()))]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct Anchor<L: mpc::Proof + StrictDumb, D: dbc::Proof<M>, M: DbcMethod = Method> {
/// Bytes reserved for enum tag for the future versions of anchors.
pub reserved1: ReservedBytes<1>,
pub enum TxWitness {
#[strict_type(tag = 1)]
Txid(Txid),
#[strict_type(tag = 2)]
Spv(Tx, TinyVec<TxMerkleNode>), // TODO: Introduce merkle path type
}

/// Transaction containing deterministic bitcoin commitment.
pub txid: Txid,
impl TxWitness {
pub fn txid(&self) -> Txid {
match self {
TxWitness::Txid(txid) => *txid,
TxWitness::Spv(tx, _) => tx.txid(),

Check warning on line 82 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L79-L82

Added lines #L79 - L82 were not covered by tests
}
}

Check warning on line 84 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L84

Added line #L84 was not covered by tests

/// Bytes reserved for the optional SPV information.
pub reserved2: ReservedBytes<1>,
pub fn merge_reveal(self, other: Self) -> Result<Self, MergeError> {
let txid = self.txid();
if txid != other.txid() {
return Err(MergeError::TxidMismatch(txid, other.txid()));
}
Ok(match (self, other) {
(Self::Txid(txid), Self::Txid(_)) => Self::Txid(txid),
(Self::Spv(tx, spv), Self::Txid(_)) | (Self::Txid(_), Self::Spv(tx, spv)) => {
Self::Spv(tx, spv)

Check warning on line 94 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L86-L94

Added lines #L86 - L94 were not covered by tests
}
(Self::Spv(tx, spv1), Self::Spv(_, spv2)) if spv1 == spv2 => Self::Spv(tx, spv1),

Check warning on line 96 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L96

Added line #L96 was not covered by tests
// We do not error here since if the merkle path is invalid we will error in validation.
// Here we just take the longest one and try to recover from the corrupted data.
(Self::Spv(tx, spv1), Self::Spv(_, spv2)) if spv1.len() > spv2.len() => {
Self::Spv(tx, spv1)

Check warning on line 100 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L99-L100

Added lines #L99 - L100 were not covered by tests
}
(Self::Spv(tx, _), Self::Spv(_, spv2)) => Self::Spv(tx, spv2),

Check warning on line 102 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L102

Added line #L102 was not covered by tests
})
}

Check warning on line 104 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L104

Added line #L104 was not covered by tests
}

/// Anchor is a data structure used in deterministic bitcoin commitments for
/// keeping information about the proof of the commitment in a transactions,
/// and multiprotocol merkle tree as defined by LNPBP-4.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),

Check warning on line 115 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L115

Added line #L115 was not covered by tests
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct EtxWitness<L: mpc::Proof + StrictDumb, D: dbc::Proof<M>, M: DbcMethod = Method> {
/// Structured multi-protocol LNPBP-4 data the transaction commits to.
pub mpc_proof: L,

Expand All @@ -89,14 +127,11 @@
pub _method: PhantomData<M>,
}

impl<L: mpc::Proof + StrictDumb, D: dbc::Proof<M>, M: DbcMethod> Anchor<L, D, M> {
/// Constructs anchor for a given witness transaction id, MPC and DBC
impl<L: mpc::Proof + StrictDumb, D: dbc::Proof<M>, M: DbcMethod> EtxWitness<L, D, M> {
/// Constructs extra-transaction witness for a given MPC and DBC
/// proofs.
pub fn new(witness_txid: Txid, mpc_proof: L, dbc_proof: D) -> Self {
pub fn new(mpc_proof: L, dbc_proof: D) -> Self {

Check warning on line 133 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L133

Added line #L133 was not covered by tests
Self {
reserved1: default!(),
txid: witness_txid,
reserved2: default!(),
mpc_proof,
dbc_proof,
_method: PhantomData,
Expand All @@ -114,25 +149,23 @@
MpcMismatch(mpc::MergeError),

/// anchors can't be merged since they have different witness transactions
TxidMismatch,
/// {0} and {1}.
TxidMismatch(Txid, Txid),

/// anchors can't be merged since they have different DBC proofs
/// anchors can't be merged since they have different DBC proofs.
DbcMismatch,
}

impl<D: dbc::Proof<M>, M: DbcMethod> Anchor<mpc::MerkleProof, D, M> {
impl<D: dbc::Proof<M>, M: DbcMethod> EtxWitness<mpc::MerkleProof, D, M> {
/// Reconstructs anchor containing merkle block
pub fn into_merkle_block(
self,
protocol_id: impl Into<ProtocolId>,
message: impl Into<Message>,
) -> Result<Anchor<mpc::MerkleBlock, D, M>, mpc::InvalidProof> {
) -> Result<EtxWitness<mpc::MerkleBlock, D, M>, mpc::InvalidProof> {

Check warning on line 165 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L165

Added line #L165 was not covered by tests
let lnpbp4_proof =
mpc::MerkleBlock::with(&self.mpc_proof, protocol_id.into(), message.into())?;
Ok(Anchor {
reserved1: self.reserved1,
txid: self.txid,
reserved2: self.reserved2,
Ok(EtxWitness {

Check warning on line 168 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L168

Added line #L168 was not covered by tests
mpc_proof: lnpbp4_proof,
dbc_proof: self.dbc_proof,
_method: default!(),
Expand All @@ -144,7 +177,7 @@
&self,
protocol_id: impl Into<ProtocolId>,
message: impl Into<Message>,
) -> Result<Anchor<mpc::MerkleBlock, D, M>, mpc::InvalidProof> {
) -> Result<EtxWitness<mpc::MerkleBlock, D, M>, mpc::InvalidProof> {

Check warning on line 180 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L180

Added line #L180 was not covered by tests
self.clone().into_merkle_block(protocol_id, message)
}

Expand Down Expand Up @@ -174,13 +207,13 @@
}
}

impl<D: dbc::Proof<M>, M: DbcMethod> Anchor<mpc::MerkleBlock, D, M> {
impl<D: dbc::Proof<M>, M: DbcMethod> EtxWitness<mpc::MerkleBlock, D, M> {
/// Conceals all LNPBP-4 data except specific protocol and produces merkle
/// proof anchor.
pub fn to_merkle_proof(
&self,
protocol: impl Into<ProtocolId>,
) -> Result<Anchor<mpc::MerkleProof, D, M>, mpc::LeafNotKnown> {
) -> Result<EtxWitness<mpc::MerkleProof, D, M>, mpc::LeafNotKnown> {

Check warning on line 216 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L216

Added line #L216 was not covered by tests
self.clone().into_merkle_proof(protocol)
}

Expand All @@ -189,12 +222,9 @@
pub fn into_merkle_proof(
self,
protocol: impl Into<ProtocolId>,
) -> Result<Anchor<mpc::MerkleProof, D, M>, mpc::LeafNotKnown> {
) -> Result<EtxWitness<mpc::MerkleProof, D, M>, mpc::LeafNotKnown> {

Check warning on line 225 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L225

Added line #L225 was not covered by tests
let lnpbp4_proof = self.mpc_proof.to_merkle_proof(protocol.into())?;
Ok(Anchor {
reserved1: self.reserved1,
txid: self.txid,
reserved2: self.reserved2,
Ok(EtxWitness {

Check warning on line 227 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L227

Added line #L227 was not covered by tests
mpc_proof: lnpbp4_proof,
dbc_proof: self.dbc_proof,
_method: default!(),
Expand All @@ -209,15 +239,119 @@
self.mpc_proof.conceal_except(protocols)
}

/// Merges two anchors keeping revealed data.
/// Merges two extra-transaction witnesses keeping revealed data.
pub fn merge_reveal(mut self, other: Self) -> Result<Self, MergeError> {
if self.txid != other.txid {
return Err(MergeError::TxidMismatch);
}
if self.dbc_proof != other.dbc_proof {
return Err(MergeError::DbcMismatch);
}
self.mpc_proof.merge_reveal(other.mpc_proof)?;
Ok(self)
}
}

#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE, tags = custom)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),

Check warning on line 257 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L257

Added line #L257 was not covered by tests
serde(crate = "serde_crate", rename_all = "camelCase", untagged)
)]
pub enum Anchor<P: mpc::Proof + StrictDumb = mpc::MerkleProof> {
#[strict_type(tag = 0x01)]
Tapret {
tapret: EtxWitness<P, TapretProof>,
txw: TxWitness,
},
#[strict_type(tag = 0x02)]
Opret {
opret: EtxWitness<P, OpretProof>,
txw: TxWitness,
},
#[strict_type(tag = 0x03)]
Dual {
tapret: EtxWitness<P, TapretProof>,
opret: EtxWitness<P, OpretProof>,
txw: TxWitness,
},
}

impl<P: mpc::Proof + StrictDumb> StrictDumb for Anchor<P> {
fn strict_dumb() -> Self {
Self::Tapret {
tapret: strict_dumb!(),
txw: strict_dumb!(),
}
}
}

impl<P: mpc::Proof + StrictDumb> Anchor<P> {
pub fn txid(&self) -> Txid {
match self {
Anchor::Tapret { txw, .. } | Anchor::Opret { txw, .. } | Anchor::Dual { txw, .. } => {
txw.txid()
}
}
}

Check warning on line 295 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L289-L295

Added lines #L289 - L295 were not covered by tests

pub fn tx_witness(&self) -> &TxWitness {
match self {
Anchor::Tapret { txw, .. } | Anchor::Opret { txw, .. } | Anchor::Dual { txw, .. } => {
txw
}
}
}

Check warning on line 303 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L297-L303

Added lines #L297 - L303 were not covered by tests

pub fn mpc_proofs(&self) -> impl Iterator<Item = &P> {
let (t, o) = match self {
Anchor::Tapret { tapret, txw: _ } => (Some(tapret), None),
Anchor::Opret { opret, txw: _ } => (None, Some(opret)),

Check warning on line 308 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L305-L308

Added lines #L305 - L308 were not covered by tests
Anchor::Dual {
tapret,
opret,
txw: _,
} => (Some(tapret), Some(opret)),

Check warning on line 313 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L310-L313

Added lines #L310 - L313 were not covered by tests
};
t.map(|a| &a.mpc_proof)
.into_iter()
.chain(o.map(|a| &a.mpc_proof))
}

Check warning on line 318 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L315-L318

Added lines #L315 - L318 were not covered by tests

fn split(
self,
) -> (TxWitness, Option<EtxWitness<P, TapretProof>>, Option<EtxWitness<P, OpretProof>>) {
match self {
Anchor::Tapret { tapret, txw } => (txw, Some(tapret), None),
Anchor::Opret { opret, txw } => (txw, None, Some(opret)),
Anchor::Dual { tapret, opret, txw } => (txw, Some(tapret), Some(opret)),

Check warning on line 326 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L320-L326

Added lines #L320 - L326 were not covered by tests
}
}

Check warning on line 328 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L328

Added line #L328 was not covered by tests
}

impl Anchor<mpc::MerkleBlock> {
/// Merges two anchors keeping revealed data.
pub fn merge_reveal(self, other: Self) -> Result<Self, MergeError> {
if self == other {
return Ok(self);
}
let (txw1, tapret1, opret1) = self.split();
let (txw2, tapret2, opret2) = other.split();
let txw = txw1.merge_reveal(txw2)?;
let tapret = match (tapret1, tapret2) {
(None, None) => None,
(Some(tapret), None) | (None, Some(tapret)) => Some(tapret),
(Some(tapret1), Some(tapret2)) => Some(tapret1.merge_reveal(tapret2)?),

Check warning on line 343 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L333-L343

Added lines #L333 - L343 were not covered by tests
};
let opret = match (opret1, opret2) {
(None, None) => None,
(Some(opret), None) | (None, Some(opret)) => Some(opret),
(Some(opret1), Some(opret2)) => Some(opret1.merge_reveal(opret2)?),

Check warning on line 348 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L345-L348

Added lines #L345 - L348 were not covered by tests
};
Ok(match (tapret, opret) {
(None, None) => unreachable!(),
(Some(tapret), None) => Self::Tapret { tapret, txw },
(None, Some(opret)) => Self::Opret { opret, txw },
(Some(tapret), Some(opret)) => Self::Dual { tapret, opret, txw },

Check warning on line 354 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L350-L354

Added lines #L350 - L354 were not covered by tests
})
}

Check warning on line 356 in dbc/src/anchor.rs

View check run for this annotation

Codecov / codecov/patch

dbc/src/anchor.rs#L356

Added line #L356 was not covered by tests
}
19 changes: 11 additions & 8 deletions seals/src/txout/witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

use bc::{Tx, Txid};
use commit_verify::mpc;
use dbc::{Anchor, DbcMethod, Method};
use dbc::anchor::EtxWitness;
use dbc::{DbcMethod, Method};
use single_use_seals::SealWitness;
use strict_encoding::StrictDumb;

Expand All @@ -33,14 +34,16 @@
/// Witness of a bitcoin-based seal being closed. Includes both transaction and
/// extra-transaction data.
pub struct Witness<D: dbc::Proof<M>, M: DbcMethod = Method> {
/// Transaction id of the witness transaction above.
pub txid: Txid,

/// Witness transaction: transaction which contains commitment to the
/// message over which the seal is closed.
pub tx: Tx,

/// Transaction id of the witness transaction above.
pub txid: Txid,

/// Deterministic bitcoin commitment proof from the anchor.
// TODO: Add optional SPV proof
/// Deterministic bitcoin commitment proof from the extra-transaction
/// witness.
pub proof: D,

#[doc(hidden)]
Expand All @@ -50,11 +53,11 @@
impl<D: dbc::Proof<M>, M: DbcMethod> Witness<D, M> {
/// Constructs witness from a witness transaction and extra-transaction
/// proof, taken from an anchor.
pub fn with<L: mpc::Proof + StrictDumb>(tx: Tx, anchor: Anchor<L, D, M>) -> Witness<D, M> {
pub fn with<L: mpc::Proof + StrictDumb>(tx: Tx, etx: EtxWitness<L, D, M>) -> Witness<D, M> {

Check warning on line 56 in seals/src/txout/witness.rs

View check run for this annotation

Codecov / codecov/patch

seals/src/txout/witness.rs#L56

Added line #L56 was not covered by tests
Witness {
txid: tx.txid(),

Check warning on line 58 in seals/src/txout/witness.rs

View check run for this annotation

Codecov / codecov/patch

seals/src/txout/witness.rs#L58

Added line #L58 was not covered by tests
tx,
txid: anchor.txid,
proof: anchor.dbc_proof,
proof: etx.dbc_proof,

Check warning on line 60 in seals/src/txout/witness.rs

View check run for this annotation

Codecov / codecov/patch

seals/src/txout/witness.rs#L60

Added line #L60 was not covered by tests
_phantom: default!(),
}
}
Expand Down
Loading
Loading