@@ -11,7 +11,12 @@ use actix::prelude::*;
1111use anyhow:: { anyhow, bail, ensure, Result } ;
1212use e3_data:: Persistable ;
1313use e3_events:: {
14- AggregationProofPending , AggregationProofSigned , BusHandle , CircuitName , CommitteeMemberExpelled , ComputeRequest , ComputeResponse , ComputeResponseKind , CorrelationId , DecryptedSharesAggregationProofRequest , DecryptionshareCreated , Die , E3id , EType , EnclaveEvent , EnclaveEventData , EventContext , PartyProofsToVerify , PlaintextAggregated , Proof , ProofType , Seed , Sequenced , ShareVerificationComplete , ShareVerificationDispatched , SignedProofFailed , SignedProofPayload , TypedEvent , VerificationKind , ZkResponse , prelude:: * , trap
14+ prelude:: * , trap, AggregationProofPending , AggregationProofSigned , BusHandle , CircuitName ,
15+ CommitteeMemberExpelled , ComputeRequest , ComputeResponse , ComputeResponseKind , CorrelationId ,
16+ DecryptedSharesAggregationProofRequest , DecryptionshareCreated , Die , E3id , EType , EnclaveEvent ,
17+ EnclaveEventData , EventContext , PartyProofsToVerify , PlaintextAggregated , Proof , ProofType ,
18+ Seed , Sequenced , ShareVerificationComplete , ShareVerificationDispatched , SignedProofFailed ,
19+ SignedProofPayload , TypedEvent , VerificationKind , ZkResponse ,
1520} ;
1621use e3_fhe_params:: BfvPreset ;
1722use e3_sortition:: { E3CommitteeContainsRequest , E3CommitteeContainsResponse , Sortition } ;
@@ -408,53 +413,6 @@ impl ThresholdPlaintextAggregator {
408413 share_mismatch_parties,
409414 ) ;
410415
411- // Emit SignedProofFailed for each mismatched party so the
412- // AccusationManager can initiate the slashing quorum protocol.
413- // NOTE: These proofs have already passed ECDSA validation in
414- // ShareVerificationActor, so recover_address() returns the
415- // authenticated signer. We still verify_address() as defense
416- // in depth before attributing a fault.
417- for party_id in & share_mismatch_parties {
418- if let Some ( proofs) = state. c6_proofs . get ( party_id) {
419- if let Some ( signed) = proofs. first ( ) {
420- let Ok ( faulting_node) = signed. recover_address ( ) else {
421- warn ! (
422- "Could not recover address for party {} C6 proof — skipping accusation" ,
423- party_id
424- ) ;
425- continue ;
426- } ;
427- // Defense in depth: only publish accusation if the
428- // recovered address matches the wrapper signature.
429- match signed. verify_address ( & faulting_node) {
430- Ok ( true ) => { }
431- _ => {
432- warn ! (
433- "Wrapper signature verification failed for party {} — \
434- excluding share locally but not publishing accusation",
435- party_id
436- ) ;
437- continue ;
438- }
439- }
440- if let Err ( err) = self . bus . publish (
441- SignedProofFailed {
442- e3_id : self . e3_id . clone ( ) ,
443- faulting_node,
444- proof_type : ProofType :: C6ThresholdShareDecryption ,
445- signed_payload : signed. clone ( ) ,
446- } ,
447- ec. clone ( ) ,
448- ) {
449- error ! (
450- "Failed to publish SignedProofFailed for party {}: {err}" ,
451- party_id
452- ) ;
453- }
454- }
455- }
456- }
457-
458416 dishonest_parties. extend ( & share_mismatch_parties) ;
459417 honest_shares. retain ( |( id, _) | !share_mismatch_parties. contains ( id) ) ;
460418 ensure ! (
@@ -527,6 +485,7 @@ impl ThresholdPlaintextAggregator {
527485 Ok ( ( ) )
528486 }
529487
488+ /// Verify that each honest party's decryption share bytes deserialize to
530489 /// Verify that each honest party's raw decryption share bytes match the
531490 /// `d_commitment` output in their verified C6 proof. Returns party IDs
532491 /// that failed the check.
@@ -537,7 +496,6 @@ impl ThresholdPlaintextAggregator {
537496 ) -> BTreeSet < u64 > {
538497 let mut mismatched = BTreeSet :: new ( ) ;
539498
540- // Build BFV params and compute d_bit
541499 let Ok ( ( threshold_params, _) ) = e3_fhe_params:: build_pair_for_preset ( self . params_preset )
542500 else {
543501 warn ! ( "Could not build BFV params for d_commitment check — skipping" ) ;
@@ -560,9 +518,9 @@ impl ThresholdPlaintextAggregator {
560518 let max_k =
561519 e3_zk_helpers:: circuits:: threshold:: decrypted_shares_aggregation:: MAX_MSG_NON_ZERO_COEFFS ;
562520 let c6_output_layout = CircuitName :: ThresholdShareDecryption . output_layout ( ) ;
521+ let moduli: Vec < u64 > = threshold_params. moduli ( ) . to_vec ( ) ;
563522
564523 for ( party_id, shares) in honest_shares {
565- // Extract d_commitment from C6 proof
566524 let Some ( proofs) = c6_proofs. get ( party_id) else {
567525 continue ;
568526 } ;
@@ -573,18 +531,16 @@ impl ThresholdPlaintextAggregator {
573531 . extract_field ( & first_proof. payload . proof . public_signals , "d_commitment" )
574532 else {
575533 warn ! (
576- "Could not extract d_commitment from C6 proof for party {} — skipping check " ,
534+ "Could not extract d_commitment from C6 proof for party {} — skipping" ,
577535 party_id
578536 ) ;
579537 continue ;
580538 } ;
581539
582- // Compute d_commitment from raw share bytes (first ciphertext index)
583540 let Some ( share_bytes) = shares. first ( ) else {
584541 continue ;
585542 } ;
586- let Ok ( mut poly) =
587- e3_trbfv:: helpers:: try_poly_from_bytes ( share_bytes, & threshold_params)
543+ let Ok ( poly) = e3_trbfv:: helpers:: try_poly_from_bytes ( share_bytes, & threshold_params)
588544 else {
589545 warn ! (
590546 "Could not deserialize share for party {} — marking as mismatched" ,
@@ -593,18 +549,14 @@ impl ThresholdPlaintextAggregator {
593549 mismatched. insert ( * party_id) ;
594550 continue ;
595551 } ;
596- poly. change_representation ( fhe_math:: rq:: Representation :: PowerBasis ) ;
597552 let mut crt = e3_polynomial:: CrtPolynomial :: from_fhe_polynomial ( & poly) ;
598553
599- // Apply the same transformations C6's Inputs::compute applies before
600- // hashing: reverse coefficient order + center each limb mod qi.
601- // Without this, the commitment is over a different polynomial
602- // representation and always mismatches the C6 proof output.
603- let moduli: Vec < u64 > = threshold_params. moduli ( ) . to_vec ( ) ;
554+ // Apply the same transformations C6's Inputs::compute applies:
555+ // reverse coefficient order + center each limb mod qi.
604556 crt. reverse ( ) ;
605557 if let Err ( e) = crt. center ( & moduli) {
606558 warn ! (
607- "Could not center d_share for party {} — skipping check : {e}" ,
559+ "Could not center d_share for party {} — skipping: {e}" ,
608560 party_id
609561 ) ;
610562 continue ;
@@ -615,11 +567,12 @@ impl ThresholdPlaintextAggregator {
615567 & crt, d_bit, max_k,
616568 ) ;
617569
618- // Convert BigInt to LE bytes for comparison
619- let computed_bytes = computed. to_bytes_le ( ) . 1 ;
570+ // Convert to big-endian 32-byte padded format matching
571+ // Barretenberg's public_signals encoding.
572+ let ( _, be_bytes) = computed. to_bytes_be ( ) ;
620573 let mut computed_padded = [ 0u8 ; 32 ] ;
621- let len = computed_bytes . len ( ) . min ( 32 ) ;
622- computed_padded[ ..len ] . copy_from_slice ( & computed_bytes [ ..len] ) ;
574+ let start = 32usize . saturating_sub ( be_bytes . len ( ) ) ;
575+ computed_padded[ start.. ] . copy_from_slice ( & be_bytes [ ..be_bytes . len ( ) . min ( 32 ) ] ) ;
623576
624577 if computed_padded != c6_d_bytes {
625578 warn ! (
0 commit comments