Skip to content

Commit 822bae4

Browse files
authored
[Cherrypick] Protocol update to allow recovery of stolen funds #22237 (#22259)
## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [x] Protocol: upgrade to allow recovery of stolen funds in accordance with community vote - [ ] Nodes (Validators and Full nodes): - [ ] gRPC: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK:
1 parent 376614f commit 822bae4

20 files changed

+1777
-27
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/sui-core/benches/batch_verification_bench.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ fn async_verifier_bench(c: &mut Criterion) {
8383
true,
8484
true,
8585
Some(30),
86+
vec![],
8687
));
8788

8889
b.iter(|| {
@@ -139,6 +140,7 @@ fn batch_verification_bench(c: &mut Criterion) {
139140
&committee,
140141
&certs.iter().collect_vec(),
141142
Arc::new(VerifiedDigestCache::new_empty()),
143+
None,
142144
);
143145
})
144146
},

crates/sui-core/src/authority.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -907,14 +907,39 @@ impl AuthorityState {
907907
// Note: the deny checks may do redundant package loads but:
908908
// - they only load packages when there is an active package deny map
909909
// - the loads are cached anyway
910-
sui_transaction_checks::deny::check_transaction_for_signing(
910+
let deny_status = sui_transaction_checks::deny::check_transaction_for_signing(
911911
tx_data,
912912
transaction.tx_signatures(),
913913
&input_object_kinds,
914914
&receiving_objects_refs,
915915
&self.config.transaction_deny_config,
916916
self.get_backing_package_store().as_ref(),
917-
)?;
917+
);
918+
919+
match deny_status {
920+
Ok(_) => {}
921+
// If the transaction was blocked, it may still be allowed if it is in
922+
// the aliased address list.
923+
// TODO: Delete this code after protocol version 86.
924+
Err(e) => {
925+
let allowed = transaction
926+
.inner()
927+
.data()
928+
.tx_signatures()
929+
.iter()
930+
.filter_map(|sig| sig.try_into().ok())
931+
.any(|signer: SuiAddress| {
932+
epoch_store.protocol_config().is_tx_allowed_via_aliasing(
933+
tx_data.sender().to_inner(),
934+
signer.to_inner(),
935+
tx_digest.inner(),
936+
)
937+
});
938+
if !allowed {
939+
return Err(e);
940+
}
941+
}
942+
}
918943

919944
let (input_objects, receiving_objects) = self.input_loader.read_objects_for_signing(
920945
Some(tx_digest),

crates/sui-core/src/authority/authority_per_epoch_store.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,7 @@ impl AuthorityPerEpochStore {
852852
protocol_config.accept_zklogin_in_multisig(),
853853
protocol_config.accept_passkey_in_multisig(),
854854
protocol_config.zklogin_max_epoch_upper_bound_delta(),
855+
protocol_config.get_aliased_addresses().clone(),
855856
);
856857

857858
let authenticator_state_exists = epoch_start_configuration

crates/sui-core/src/signature_verifier.rs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ use mysten_metrics::monitored_scope;
1212
use parking_lot::{Mutex, MutexGuard, RwLock};
1313
use prometheus::{register_int_counter_with_registry, IntCounter, Registry};
1414
use shared_crypto::intent::Intent;
15+
use std::collections::{BTreeMap, BTreeSet};
1516
use std::sync::Arc;
16-
use sui_types::digests::SenderSignedDataDigest;
17+
use sui_protocol_config::AliasedAddress;
18+
use sui_types::base_types::SuiAddress;
1719
use sui_types::digests::ZKLoginInputsDigest;
20+
use sui_types::digests::{SenderSignedDataDigest, TransactionDigest};
1821
use sui_types::signature_verification::{
1922
verify_sender_signed_data_message_signatures, VerifiedDigestCache,
2023
};
@@ -87,6 +90,7 @@ impl CertBuffer {
8790
}
8891
}
8992

93+
pub type AliasedAddressMap = BTreeMap<SuiAddress, (SuiAddress, BTreeSet<TransactionDigest>)>;
9094
/// Verifies signatures in ways that faster than verifying each signature individually.
9195
/// - BLS signatures - caching and batch verification.
9296
/// - User signed data - caching.
@@ -96,6 +100,10 @@ pub struct SignatureVerifier {
96100
signed_data_cache: VerifiedDigestCache<SenderSignedDataDigest>,
97101
zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
98102

103+
/// Map from original address to aliased address and the list of transaction digests for
104+
/// which the aliasing is allowed to be in effect.
105+
aliased_addresses: Option<Arc<AliasedAddressMap>>,
106+
99107
/// Map from JwkId (iss, kid) to the fetched JWK for that key.
100108
/// We use an immutable data structure because verification of ZKLogins may be slow, so we
101109
/// don't want to pass a reference to the map to the verify method, since that would lead to a
@@ -138,7 +146,36 @@ impl SignatureVerifier {
138146
accept_zklogin_in_multisig: bool,
139147
accept_passkey_in_multisig: bool,
140148
zklogin_max_epoch_upper_bound_delta: Option<u64>,
149+
aliased_addresses: Vec<AliasedAddress>,
141150
) -> Self {
151+
let aliased_addresses: Option<Arc<BTreeMap<_, _>>> = if aliased_addresses.is_empty() {
152+
None
153+
} else {
154+
Some(Arc::new(
155+
aliased_addresses
156+
.into_iter()
157+
.map(
158+
|AliasedAddress {
159+
original,
160+
aliased,
161+
allowed_tx_digests,
162+
}| {
163+
(
164+
SuiAddress::from_bytes(original).unwrap(),
165+
(
166+
SuiAddress::from_bytes(aliased).unwrap(),
167+
allowed_tx_digests
168+
.into_iter()
169+
.map(TransactionDigest::new)
170+
.collect(),
171+
),
172+
)
173+
},
174+
)
175+
.collect(),
176+
))
177+
};
178+
142179
Self {
143180
committee,
144181
certificate_cache: VerifiedDigestCache::new(
@@ -167,6 +204,7 @@ impl SignatureVerifier {
167204
accept_passkey_in_multisig,
168205
zklogin_max_epoch_upper_bound_delta,
169206
},
207+
aliased_addresses,
170208
}
171209
}
172210

@@ -179,6 +217,7 @@ impl SignatureVerifier {
179217
accept_zklogin_in_multisig: bool,
180218
accept_passkey_in_multisig: bool,
181219
zklogin_max_epoch_upper_bound_delta: Option<u64>,
220+
aliased_addresses: Vec<AliasedAddress>,
182221
) -> Self {
183222
Self::new_with_batch_size(
184223
committee,
@@ -190,6 +229,7 @@ impl SignatureVerifier {
190229
accept_zklogin_in_multisig,
191230
accept_passkey_in_multisig,
192231
zklogin_max_epoch_upper_bound_delta,
232+
aliased_addresses,
193233
)
194234
}
195235

@@ -319,9 +359,16 @@ impl SignatureVerifier {
319359
let committee = self.committee.clone();
320360
let metrics = self.metrics.clone();
321361
let zklogin_inputs_cache = self.zklogin_inputs_cache.clone();
362+
let aliased_addresses = self.aliased_addresses.clone();
322363
Handle::current()
323364
.spawn_blocking(move || {
324-
Self::process_queue_sync(committee, metrics, buffer, zklogin_inputs_cache)
365+
Self::process_queue_sync(
366+
committee,
367+
metrics,
368+
buffer,
369+
zklogin_inputs_cache,
370+
aliased_addresses.as_ref().map(|arc| arc.as_ref()),
371+
)
325372
})
326373
.await
327374
.expect("Spawn blocking should not fail");
@@ -332,13 +379,15 @@ impl SignatureVerifier {
332379
metrics: Arc<SignatureVerifierMetrics>,
333380
buffer: CertBuffer,
334381
zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
382+
aliased_addresses: Option<&BTreeMap<SuiAddress, (SuiAddress, BTreeSet<TransactionDigest>)>>,
335383
) {
336384
let _scope = monitored_scope("BatchCertificateVerifier::process_queue");
337385

338386
let results = batch_verify_certificates(
339387
&committee,
340388
&buffer.certs.iter().collect_vec(),
341389
zklogin_inputs_cache,
390+
aliased_addresses,
342391
);
343392
izip!(
344393
results.into_iter(),
@@ -403,6 +452,7 @@ impl SignatureVerifier {
403452
self.committee.epoch(),
404453
&verify_params,
405454
self.zklogin_inputs_cache.clone(),
455+
self.aliased_addresses.as_ref().map(|arc| arc.as_ref()),
406456
)
407457
},
408458
|| Ok(()),
@@ -544,6 +594,7 @@ pub fn batch_verify_certificates(
544594
committee: &Committee,
545595
certs: &[&CertifiedTransaction],
546596
zk_login_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
597+
aliased_addresses: Option<&BTreeMap<SuiAddress, (SuiAddress, BTreeSet<TransactionDigest>)>>,
547598
) -> Vec<SuiResult> {
548599
// certs.data() is assumed to be verified already by the caller.
549600
let verify_params = VerifyParams::default();
@@ -556,7 +607,12 @@ pub fn batch_verify_certificates(
556607
// TODO: verify_signature currently checks the tx sig as well, which might be cached
557608
// already.
558609
.map(|c| {
559-
c.verify_signatures_authenticated(committee, &verify_params, zk_login_cache.clone())
610+
c.verify_signatures_authenticated(
611+
committee,
612+
&verify_params,
613+
zk_login_cache.clone(),
614+
aliased_addresses,
615+
)
560616
})
561617
.collect(),
562618

crates/sui-core/src/test_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ pub fn make_cert_with_large_committee(
310310
committee,
311311
&Default::default(),
312312
Arc::new(VerifiedDigestCache::new_empty()),
313+
None,
313314
)
314315
.unwrap();
315316
cert

crates/sui-core/src/unit_tests/batch_verification_tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ async fn test_batch_verify() {
120120
&committee,
121121
&certs.iter().collect_vec(),
122122
Arc::new(VerifiedDigestCache::new_empty()),
123+
None,
123124
);
124125
results[i].as_ref().unwrap_err();
125126
for (_, r) in results.iter().enumerate().filter(|(j, _)| *j != i) {
@@ -147,6 +148,7 @@ async fn test_async_verifier() {
147148
true,
148149
true,
149150
Some(30),
151+
vec![],
150152
));
151153

152154
let tasks: Vec<_> = (0..32)

0 commit comments

Comments
 (0)