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

Fix duplicate signature aggregation #133

Merged
merged 3 commits into from
Sep 25, 2024
Merged
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
145 changes: 144 additions & 1 deletion crates/services/bls_aggregation/src/bls_agg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,19 +316,42 @@ impl<A: AvsRegistryService + Send + Sync + Clone + 'static> BlsAggregatorService
})
.map_err(|_| BlsAggregationServiceError::TaskExpired)?
{
// check if the operator has already signed for this digest
if aggregated_operators
.get(&signed_task_digest.task_response_digest)
.map(|operators| {
operators
.signers_operator_ids_set
.contains_key(&signed_task_digest.operator_id)
})
.unwrap_or(false)
{
signed_task_digest
.signature_verification_channel
.send(Err(SignatureVerificationError::DuplicateSignature))
.await
.map_err(|_| BlsAggregationServiceError::ChannelError)?;
continue;
}

let verification_result = BlsAggregatorService::<A>::verify_signature(
task_index,
&signed_task_digest,
&operator_state_avs,
)
.await;
let verification_failed = verification_result.is_err();

signed_task_digest
.signature_verification_channel
.send(verification_result)
.await
.map_err(|_| BlsAggregationServiceError::ChannelError)?;

if verification_failed {
continue;
}

let operator_state = operator_state_avs
.get(&signed_task_digest.operator_id)
.unwrap();
Expand Down Expand Up @@ -543,7 +566,7 @@ mod tests {
use alloy_primitives::{B256, U256};
use eigen_crypto_bls::{BlsG1Point, BlsG2Point, BlsKeyPair, Signature};
use eigen_services_avsregistry::fake_avs_registry_service::FakeAvsRegistryService;
use eigen_types::avs::SignatureVerificationError::IncorrectSignature;
use eigen_types::avs::SignatureVerificationError::{DuplicateSignature, IncorrectSignature};
use eigen_types::operator::{QuorumNum, QuorumThresholdPercentages};
use eigen_types::{avs::TaskIndex, test::TestOperator};
use sha2::{Digest, Sha256};
Expand Down Expand Up @@ -663,6 +686,113 @@ mod tests {
assert_eq!(task_index, response.unwrap().unwrap().task_index);
}

#[tokio::test]
async fn test_1_quorum_2_operator_2_duplicated_signatures() {
let test_operator_1 = TestOperator {
operator_id: U256::from(1).into(),
stake_per_quorum: HashMap::from([(0u8, U256::from(100))]),
bls_keypair: BlsKeyPair::new(PRIVATE_KEY_1.into()).unwrap(),
};
let test_operator_2 = TestOperator {
operator_id: U256::from(2).into(),
stake_per_quorum: HashMap::from([(0u8, U256::from(100))]),
bls_keypair: BlsKeyPair::new(PRIVATE_KEY_2.into()).unwrap(),
};
let test_operators = vec![test_operator_1.clone(), test_operator_2.clone()];
let block_number = 1;
let task_index: TaskIndex = 0;
let quorum_numbers = vec![0];
let quorum_threshold_percentages: QuorumThresholdPercentages = vec![100];
let time_to_expiry = Duration::from_secs(1);
let task_response = 123; // Initialize with appropriate data
let task_response_digest = hash(task_response);

let fake_avs_registry_service =
FakeAvsRegistryService::new(block_number, test_operators.clone());
let bls_agg_service = BlsAggregatorService::new(fake_avs_registry_service);
bls_agg_service
.initialize_new_task(
task_index,
block_number as u32,
quorum_numbers,
quorum_threshold_percentages,
time_to_expiry,
)
.await
.unwrap();

let bls_signature_1 = test_operator_1
.bls_keypair
.sign_message(task_response_digest.as_ref());
bls_agg_service
.process_new_signature(
task_index,
task_response_digest,
bls_signature_1.clone(),
test_operator_1.operator_id,
)
.await
.unwrap();

let second_signature_processing_result = bls_agg_service
.process_new_signature(
task_index,
task_response_digest,
bls_signature_1.clone(),
test_operator_1.operator_id,
)
.await;

assert_eq!(
second_signature_processing_result,
Err(BlsAggregationServiceError::SignatureVerificationError(
DuplicateSignature
))
);

let bls_signature_2 = test_operator_2
.bls_keypair
.sign_message(task_response_digest.as_ref());
bls_agg_service
.process_new_signature(
task_index,
task_response_digest,
bls_signature_2.clone(),
test_operator_2.operator_id,
)
.await
.unwrap();

let quorum_apks_g1 = aggregate_g1_public_keys(&test_operators);
let signers_apk_g2 = aggregate_g2_public_keys(&test_operators);
let signers_agg_sig_g1 = aggregate_g1_signatures(&[bls_signature_1, bls_signature_2]);
let expected_agg_service_response = BlsAggregationServiceResponse {
task_index,
task_response_digest,
non_signers_pub_keys_g1: vec![],
quorum_apks_g1: vec![quorum_apks_g1],
signers_apk_g2,
signers_agg_sig_g1,
non_signer_quorum_bitmap_indices: vec![],
quorum_apk_indices: vec![],
total_stake_indices: vec![],
non_signer_stake_indices: vec![],
};

let response = bls_agg_service
.aggregated_response_receiver
.lock()
.await
.recv()
.await;

assert_eq!(
expected_agg_service_response,
response.clone().unwrap().unwrap()
);
assert_eq!(task_index, response.unwrap().unwrap().task_index);
}

#[tokio::test]
async fn test_1_quorum_3_operator_3_correct_signatures() {
let test_operator_1 = TestOperator {
Expand Down Expand Up @@ -1789,5 +1919,18 @@ mod tests {
)),
result
);

// Also test that the aggregator service is not affected by the invalid signature, so the task should expire
let response = bls_agg_service
.aggregated_response_receiver
.lock()
.await
.recv()
.await;

assert_eq!(
Err(BlsAggregationServiceError::TaskExpired),
response.unwrap()
);
}
}
2 changes: 2 additions & 0 deletions crates/types/src/avs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub enum SignatureVerificationError {
OperatorPublicKeyNotFound,
#[error("operator not found")]
OperatorNotFound,
#[error("duplicate signature error")]
DuplicateSignature,
}

/// Represents a signed task response digest
Expand Down
Loading