Skip to content

Commit

Permalink
feat!: implement command inclusion proof for eviction
Browse files Browse the repository at this point in the history
  • Loading branch information
sdbondi committed Dec 12, 2024
1 parent 817bacb commit 92d3606
Show file tree
Hide file tree
Showing 21 changed files with 3,026 additions and 579 deletions.
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ members = [
"common_sqlite",
"infrastructure/libtor",
"infrastructure/metrics",
"infrastructure/jellyfish",
"infrastructure/shutdown",
"infrastructure/storage",
"infrastructure/tari_script",
Expand All @@ -44,6 +45,7 @@ members = [
]

[workspace.dependencies]
tari_jellyfish = { path = "infrastructure/jellyfish" }
tari_comms = { path = "comms/core" }
tari_comms_dht = { path = "comms/dht", default-features = false }
tari_common = { path = "common" }
Expand Down
1 change: 1 addition & 0 deletions applications/minotari_app_grpc/proto/sidechain_types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ message CommitProof {
message CommitProofV1 {
bytes command = 1;
SidechainBlockCommitProof commit_proof = 2;
bytes encoded_inclusion_proof = 3;
}

message SidechainBlockCommitProof {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ impl TryFrom<grpc::CommitProofV1> for CommandCommitProofV1<EvictNodeAtom> {
Ok(CommandCommitProofV1 {
command: command.try_into()?,
commit_proof: value.commit_proof.ok_or("commit_proof not provided")?.try_into()?,

inclusion_proof: borsh::from_slice(&value.encoded_inclusion_proof)
.map_err(|e| format!("Failed to decode SparseMerkleProofExt: {e}"))?,
})
}
}
Expand All @@ -370,6 +373,10 @@ impl From<&CommandCommitProofV1<EvictNodeAtom>> for grpc::CommitProofV1 {
Self {
command: grpc::EvictAtom::from(value.command()).encode_to_vec(),
commit_proof: Some(value.commit_proof().into()),
// Encode since the type is complex
// TODO: making this fallible is a pain - we may need to implement the proto for this
encoded_inclusion_proof: borsh::to_vec(value.inclusion_proof())
.expect("Failed to encode SparseMerkleProofExt"),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions base_layer/core/src/proto/sidechain_feature.proto
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ message CommitProof {
message CommitProofV1 {
bytes command = 1;
SidechainBlockCommitProof commit_proof = 2;
bytes encoded_inclusion_proof = 3;
}

message SidechainBlockCommitProof {
Expand Down
7 changes: 7 additions & 0 deletions base_layer/core/src/proto/sidechain_feature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,15 +355,22 @@ impl TryFrom<proto::types::CommitProofV1> for CommandCommitProofV1<EvictNodeAtom
Ok(CommandCommitProofV1 {
command: command.try_into()?,
commit_proof: value.commit_proof.ok_or("commit_proof not provided")?.try_into()?,
inclusion_proof: borsh::from_slice(&value.encoded_inclusion_proof)
.map_err(|e| format!("Failed to decode SparseMerkleProofExt: {e}"))?,
})
}
}

impl From<&CommandCommitProofV1<EvictNodeAtom>> for proto::types::CommitProofV1 {
fn from(value: &CommandCommitProofV1<EvictNodeAtom>) -> Self {
Self {
// Encode since command is generic
command: proto::types::EvictAtom::from(value.command()).encode_to_vec(),
commit_proof: Some(value.commit_proof().into()),
// Encode since the type is complex
// TODO: making this fallible is a pain - we may need to implement the proto for this
encoded_inclusion_proof: borsh::to_vec(value.inclusion_proof())
.expect("Failed to encode SparseMerkleProofExt"),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions base_layer/sidechain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ tari_hashing = { workspace = true }
tari_crypto = { version = "0.21.0", features = ["borsh"] }
tari_utilities = "0.8"
tari_common_types = { workspace = true }
tari_jellyfish = { workspace = true }

log = "0.4.22"
thiserror = "2.0"
Expand Down
26 changes: 22 additions & 4 deletions base_layer/sidechain/src/commit_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use tari_hashing::{
layer2::{block_hasher, vote_signature_hasher},
ValidatorNodeHashDomain,
};
use tari_jellyfish::{LeafKey, SparseMerkleProofExt, TreeHash};

use super::error::SidechainProofValidationError;
use crate::{
Expand All @@ -29,8 +30,12 @@ pub enum CommandCommitProof<C> {
}

impl<C: ToCommand> CommandCommitProof<C> {
pub fn new(command: C, commit_proof: SidechainBlockCommitProof) -> Self {
Self::V1(CommandCommitProofV1 { command, commit_proof })
pub fn new(command: C, commit_proof: SidechainBlockCommitProof, inclusion_proof: SparseMerkleProofExt) -> Self {
Self::V1(CommandCommitProofV1 {
command,
commit_proof,
inclusion_proof,
})
}

pub fn command(&self) -> &C {
Expand Down Expand Up @@ -71,10 +76,9 @@ impl<C: ToCommand> CommandCommitProof<C> {

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
pub struct CommandCommitProofV1<C> {
// TODO: Implement MerkleProof
// command_merkle_proof: MerkleProof,
pub command: C,
pub commit_proof: SidechainBlockCommitProof,
pub inclusion_proof: SparseMerkleProofExt,
}

impl<C: ToCommand> CommandCommitProofV1<C> {
Expand All @@ -86,12 +90,26 @@ impl<C: ToCommand> CommandCommitProofV1<C> {
&self.commit_proof
}

pub fn inclusion_proof(&self) -> &SparseMerkleProofExt {
&self.inclusion_proof
}

fn validate_inclusion_proof(&self, command: &Command) -> Result<(), SidechainProofValidationError> {
let command_hash = TreeHash::new(command.hash().into_array());
// Command JMT uses an identity mapping between hashes and keys.
let key = LeafKey::new(command_hash);
let root_hash = TreeHash::new(self.commit_proof.header.command_merkle_root.into_array());
self.inclusion_proof.verify_inclusion(&root_hash, &key, &command_hash)?;
Ok(())
}

pub fn validate_committed(
&self,
quorum_threshold: usize,
check_vn: &CheckVnFunc<'_>,
) -> Result<(), SidechainProofValidationError> {
let command = self.command.to_command();
self.validate_inclusion_proof(&command)?;
self.commit_proof
.validate_committed(&command, quorum_threshold, check_vn)
}
Expand Down
2 changes: 2 additions & 0 deletions base_layer/sidechain/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub enum SidechainProofValidationError {
InvalidProof { details: String },
#[error("Internal error: {details}")]
InternalError { details: String },
#[error("Jellyfish proof verification error: {0}")]
JmtProofVerifyError(#[from] tari_jellyfish::JmtProofVerifyError),
}

impl SidechainProofValidationError {
Expand Down
4 changes: 2 additions & 2 deletions base_layer/sidechain/tests/eviction_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ mod validate {
#[test]
fn it_validates_a_valid_proof() {
let proof = support::load_fixture::<EvictionProof>("eviction_proof1.json");
proof.validate(5, &|_| Ok(true)).unwrap();
proof.validate(4, &|_| Ok(true)).unwrap();
}

#[test]
fn it_rejects_if_qc_signs_for_unknown_validator() {
let proof = support::load_fixture::<EvictionProof>("eviction_proof1.json");
proof.validate(5, &|_| Ok(false)).unwrap_err();
proof.validate(4, &|_| Ok(false)).unwrap_err();
}
}
Loading

0 comments on commit 92d3606

Please sign in to comment.