Skip to content

Commit 4e68edf

Browse files
committed
feat(stm): scaffold multi-proof aggregation
1 parent 029aa8b commit 4e68edf

File tree

2 files changed

+142
-19
lines changed

2 files changed

+142
-19
lines changed

Cargo.lock

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-stm/src/stm.rs

+136-13
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ use std::cmp::Ordering;
122122
use std::collections::{BTreeMap, HashMap, HashSet};
123123
use std::convert::{From, TryFrom, TryInto};
124124
use std::hash::{Hash, Hasher};
125+
use strum::{Display, EnumDiscriminants};
125126

126127
/// The quantity of stake held by a party, represented as a `u64`.
127128
pub type Stake = u64;
@@ -641,7 +642,7 @@ pub struct StmClerk<D: Clone + Digest> {
641642
pub(crate) params: StmParameters,
642643
}
643644

644-
impl<D: Digest + Clone + FixedOutput> StmClerk<D> {
645+
impl<D: Digest + Clone + FixedOutput + Send + Sync> StmClerk<D> {
645646
/// Create a new `Clerk` from a closed registration instance.
646647
pub fn from_registration(params: &StmParameters, closed_reg: &ClosedKeyReg<D>) -> Self {
647648
Self {
@@ -700,10 +701,12 @@ impl<D: Digest + Clone + FixedOutput> StmClerk<D> {
700701

701702
let batch_proof = self.closed_reg.merkle_tree.get_batched_path(mt_index_list);
702703

703-
Ok(StmAggrSig {
704-
signatures: unique_sigs,
705-
batch_proof,
706-
})
704+
Ok(StmAggrSig::StmAggrSigConcatenation(
705+
StmAggrSigConcatenationProof {
706+
signatures: unique_sigs,
707+
batch_proof,
708+
},
709+
))
707710
}
708711

709712
/// Compute the `StmAggrVerificationKey` related to the used registration.
@@ -720,21 +723,139 @@ impl<D: Digest + Clone + FixedOutput> StmClerk<D> {
720723
}
721724
}
722725

723-
/// `StmMultiSig` uses the "concatenation" proving system (as described in Section 4.3 of the original paper.)
726+
/// A STM aggregate signature.
727+
#[derive(Debug, Clone, Serialize, Deserialize, EnumDiscriminants)]
728+
#[strum(serialize_all = "PascalCase")]
729+
#[strum_discriminants(derive(Serialize, Hash, Display))]
730+
#[strum_discriminants(name(StmAggrSigType))]
731+
#[serde(bound(
732+
serialize = "BatchPath<D>: Serialize",
733+
deserialize = "BatchPath<D>: Deserialize<'de>"
734+
))]
735+
#[serde(untagged)]
736+
pub enum StmAggrSig<D: Clone + Digest + FixedOutput + Send + Sync> {
737+
/// STM aggregate signature with concatenation proving system.
738+
StmAggrSigConcatenation(StmAggrSigConcatenationProof<D>),
739+
}
740+
741+
impl<D: Clone + Digest + FixedOutput + Send + Sync> StmAggrSig<D> {
742+
/// Verify aggregate signature, by checking that
743+
/// * each signature contains only valid indices,
744+
/// * the lottery is indeed won by each one of them,
745+
/// * the merkle tree path is valid,
746+
/// * the aggregate signature validates with respect to the aggregate verification key
747+
/// (aggregation is computed using functions `MSP.BKey` and `MSP.BSig` as described in Section 2.4 of the paper).
748+
pub fn verify(
749+
&self,
750+
msg: &[u8],
751+
avk: &StmAggrVerificationKey<D>,
752+
parameters: &StmParameters,
753+
) -> Result<(), StmAggregateSignatureError<D>> {
754+
match self {
755+
StmAggrSig::StmAggrSigConcatenation(stm_aggr_sig) => {
756+
stm_aggr_sig.verify(msg, avk, parameters)
757+
}
758+
}
759+
}
760+
761+
/// Batch verify a set of signatures, with different messages and avks.
762+
#[cfg(feature = "batch-verify-aggregates")]
763+
pub fn batch_verify(
764+
stm_signatures: &[Self],
765+
msgs: &[Vec<u8>],
766+
avks: &[StmAggrVerificationKey<D>],
767+
parameters: &[StmParameters],
768+
) -> Result<(), StmAggregateSignatureError<D>> {
769+
let stm_signatures: HashMap<StmAggrSigType, Vec<Self>> =
770+
stm_signatures.iter().fold(HashMap::new(), |mut acc, sig| {
771+
acc.entry(sig.into()).or_default().push(sig.clone());
772+
acc
773+
});
774+
stm_signatures
775+
.into_iter()
776+
.try_for_each(
777+
|(stm_aggr_sig_type, stm_aggr_sigs)| match stm_aggr_sig_type {
778+
StmAggrSigType::StmAggrSigConcatenation => {
779+
StmAggrSigConcatenationProof::batch_verify(
780+
&stm_aggr_sigs
781+
.into_iter()
782+
.filter_map(|s| match s {
783+
Self::StmAggrSigConcatenation(stm_aggr_sig) => {
784+
Some(stm_aggr_sig)
785+
}
786+
_ => None,
787+
})
788+
.collect::<Vec<_>>(),
789+
msgs,
790+
avks,
791+
parameters,
792+
)
793+
}
794+
},
795+
)
796+
.map_err(|_| StmAggregateSignatureError::BatchInvalid)
797+
}
798+
799+
/// Convert multi signature to bytes
800+
/// # Layout
801+
/// * Number of the pairs of Signatures and Registered Parties (SigRegParty) (as u64)
802+
/// * Size of a pair of Signature and Registered Party
803+
/// * Pairs of Signatures and Registered Parties
804+
/// * Batch proof
805+
pub fn to_bytes(&self) -> Vec<u8> {
806+
match self {
807+
StmAggrSig::StmAggrSigConcatenation(stm_aggr_sig) => stm_aggr_sig.to_bytes(),
808+
}
809+
}
810+
811+
///Extract a `StmAggrSig` from a byte slice.
812+
pub fn from_bytes(bytes: &[u8]) -> Result<Self, StmAggregateSignatureError<D>> {
813+
// TODO: How to properly support multiple variants?
814+
Ok(Self::StmAggrSigConcatenation(
815+
StmAggrSigConcatenationProof::from_bytes(bytes)?,
816+
))
817+
}
818+
819+
/// Extract the list of signatures.
820+
pub fn signatures(&self) -> Vec<StmSigRegParty> {
821+
match self {
822+
StmAggrSig::StmAggrSigConcatenation(stm_aggr_sig) => stm_aggr_sig.signatures.clone(),
823+
}
824+
}
825+
826+
/// Extract the list of unique merkle tree nodes that covers path for all signatures.
827+
pub fn batch_proof(&self) -> BatchPath<D> {
828+
match self {
829+
StmAggrSig::StmAggrSigConcatenation(stm_aggr_sig) => stm_aggr_sig.batch_proof.clone(),
830+
}
831+
}
832+
833+
/// Extract the list of unique merkle tree nodes that covers path for all signatures. (test only)
834+
#[cfg(test)]
835+
pub(crate) fn set_batch_proof(&mut self, batch_proof: BatchPath<D>) {
836+
match self {
837+
StmAggrSig::StmAggrSigConcatenation(ref mut stm_aggr_sig) => {
838+
stm_aggr_sig.batch_proof = batch_proof
839+
}
840+
}
841+
}
842+
}
843+
844+
/// `StmAggrSigConcatenationProof` uses the "concatenation" proving system (as described in Section 4.3 of the original paper.)
724845
/// This means that the aggregated signature contains a vector with all individual signatures.
725846
/// BatchPath is also a part of the aggregate signature which covers path for all signatures.
726847
#[derive(Debug, Clone, Serialize, Deserialize)]
727848
#[serde(bound(
728849
serialize = "BatchPath<D>: Serialize",
729850
deserialize = "BatchPath<D>: Deserialize<'de>"
730851
))]
731-
pub struct StmAggrSig<D: Clone + Digest + FixedOutput> {
852+
pub struct StmAggrSigConcatenationProof<D: Clone + Digest + FixedOutput> {
732853
pub(crate) signatures: Vec<StmSigRegParty>,
733854
/// The list of unique merkle tree nodes that covers path for all signatures.
734855
pub batch_proof: BatchPath<D>,
735856
}
736857

737-
impl<D: Clone + Digest + FixedOutput + Send + Sync> StmAggrSig<D> {
858+
impl<D: Clone + Digest + FixedOutput + Send + Sync> StmAggrSigConcatenationProof<D> {
738859
/// Verify all checks from signatures, except for the signature verification itself.
739860
///
740861
/// Indices and quorum are checked by `CoreVerifier::preliminary_verify` with `msgp`.
@@ -859,7 +980,9 @@ impl<D: Clone + Digest + FixedOutput + Send + Sync> StmAggrSig<D> {
859980
}
860981

861982
///Extract a `StmAggrSig` from a byte slice.
862-
pub fn from_bytes(bytes: &[u8]) -> Result<StmAggrSig<D>, StmAggregateSignatureError<D>> {
983+
pub fn from_bytes(
984+
bytes: &[u8],
985+
) -> Result<StmAggrSigConcatenationProof<D>, StmAggregateSignatureError<D>> {
863986
let mut u64_bytes = [0u8; 8];
864987

865988
u64_bytes.copy_from_slice(&bytes[..8]);
@@ -881,7 +1004,7 @@ impl<D: Clone + Digest + FixedOutput + Send + Sync> StmAggrSig<D> {
8811004
let offset = 16 + sig_reg_size * size;
8821005
let batch_proof = BatchPath::from_bytes(&bytes[offset..])?;
8831006

884-
Ok(StmAggrSig {
1007+
Ok(StmAggrSigConcatenationProof {
8851008
signatures: sig_reg_list,
8861009
batch_proof,
8871010
})
@@ -1526,7 +1649,7 @@ mod tests {
15261649
#[test]
15271650
fn test_invalid_proof_index_unique(tc in arb_proof_setup(10)) {
15281651
with_proof_mod(tc, |aggr, clerk, _msg| {
1529-
for sig_reg in aggr.signatures.iter_mut() {
1652+
for sig_reg in aggr.signatures().iter_mut() {
15301653
for index in sig_reg.sig.indexes.iter_mut() {
15311654
*index %= clerk.params.k - 1
15321655
}
@@ -1536,7 +1659,7 @@ mod tests {
15361659
#[test]
15371660
fn test_invalid_proof_path(tc in arb_proof_setup(10)) {
15381661
with_proof_mod(tc, |aggr, _, _msg| {
1539-
let p = aggr.batch_proof.clone();
1662+
let p = aggr.batch_proof().clone();
15401663
let mut index_list = p.indices.clone();
15411664
let values = p.values;
15421665
let batch_proof = {
@@ -1547,7 +1670,7 @@ mod tests {
15471670
hasher: Default::default()
15481671
}
15491672
};
1550-
aggr.batch_proof = batch_proof;
1673+
aggr.set_batch_proof(batch_proof);
15511674
})
15521675
}
15531676
}

0 commit comments

Comments
 (0)