Skip to content

Commit 93684ff

Browse files
Merge branch 'alin/MR-441-retain-subnet-messages' into 'master'
fix: [MR-441] Also retain ingress messages addressed to subnet in `after_split()` In addition to ingress messages in terminal states; and ingress messages addressed to local canisters; also retain ingress messages addressed to either `IC_00` or to the actual subnet ID on subnet A'. Closes MR-441 Closes MR-441 See merge request dfinity-lab/public/ic!12668
2 parents 9e1a6fc + 1ddeee4 commit 93684ff

File tree

4 files changed

+49
-25
lines changed

4 files changed

+49
-25
lines changed

rs/replicated_state/src/metadata_state.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use ic_registry_subnet_type::SubnetType;
3131
use ic_types::{
3232
crypto::CryptoHash,
3333
ingress::{IngressState, IngressStatus},
34-
messages::{MessageId, RequestOrResponse},
34+
messages::{is_subnet_id, MessageId, RequestOrResponse},
3535
node_id_into_protobuf, node_id_try_from_option,
3636
nominal_cycles::NominalCycles,
3737
state_sync::{StateSyncVersion, CURRENT_STATE_SYNC_VERSION},
@@ -810,7 +810,7 @@ impl SystemMetadata {
810810
/// with canisters split among the two subnets according to the routing table.
811811
/// Because subnet A' retains the subnet ID of subnet A, it is identified by
812812
/// having `self.split_from == Some(self.own_subnet_id)`. Conversely, subnet B
813-
/// has `self.split_from == Some(self.own_subnet_id)`.
813+
/// has `self.split_from != Some(self.own_subnet_id)`.
814814
///
815815
/// In the first phase (see [`Self::split()`]), the ingress history was left
816816
/// untouched on both subnets, in order to make it trivial to verify that no
@@ -866,13 +866,18 @@ impl SystemMetadata {
866866
bitcoin_get_successors_follow_up_responses,
867867
} = self;
868868

869-
split_from.expect("Not a state resulting from a subnet split");
869+
let split_from = split_from.expect("Not a state resulting from a subnet split");
870870

871871
assert_eq!(0, heap_delta_estimate.get());
872872
assert!(expected_compiled_wasms.is_empty());
873873

874874
// Prune the ingress history.
875-
ingress_history = ingress_history.prune_after_split(is_local_canister);
875+
ingress_history = ingress_history.prune_after_split(|canister_id: &CanisterId| {
876+
// An actual local canister.
877+
is_local_canister(canister_id)
878+
// Or this is subnet A' and message is addressed to the management canister.
879+
|| split_from == own_subnet_id && is_subnet_id(*canister_id, own_subnet_id)
880+
});
876881

877882
SystemMetadata {
878883
ingress_history,
@@ -1643,9 +1648,9 @@ impl IngressHistoryState {
16431648
/// Prunes the ingress history (as part of subnet splitting phase 2), retaining:
16441649
///
16451650
/// * all terminal states (since they are immutable and will get pruned); and
1646-
/// * all non-terminal states for ingress messages addressed to local canisters
1647-
/// (as determined by the provided predicate).
1648-
fn prune_after_split<F>(self, is_local_canister: F) -> Self
1651+
/// * all non-terminal states for ingress messages addressed to local receivers
1652+
/// (canisters or subnet; as determined by the provided predicate).
1653+
fn prune_after_split<F>(self, is_local_receiver: F) -> Self
16491654
where
16501655
F: Fn(&CanisterId) -> bool,
16511656
{
@@ -1662,7 +1667,7 @@ impl IngressHistoryState {
16621667
let should_retain = |status: &IngressStatus| match status {
16631668
IngressStatus::Known {
16641669
receiver, state, ..
1665-
} => state.is_terminal() || is_local_canister(&CanisterId::new(*receiver).unwrap()),
1670+
} => state.is_terminal() || is_local_receiver(&CanisterId::new(*receiver).unwrap()),
16661671
IngressStatus::Unknown => false,
16671672
};
16681673

rs/replicated_state/src/metadata_state/tests.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::*;
22
use crate::metadata_state::subnet_call_context_manager::SubnetCallContextManager;
33
use ic_constants::MAX_INGRESS_TTL;
44
use ic_error_types::{ErrorCode, UserError};
5-
use ic_ic00_types::EcdsaCurve;
5+
use ic_ic00_types::{EcdsaCurve, IC_00};
66
use ic_registry_routing_table::CanisterIdRange;
77
use ic_test_utilities::{
88
mock_time,
@@ -456,15 +456,27 @@ fn system_metadata_split() {
456456
const SUBNET_B: SubnetId = SUBNET_1;
457457
const SUBNET_C: SubnetId = SUBNET_2;
458458

459-
// Ingress history with 2 Received messages, addressed to canisters 1 and 2.
459+
// 2 canisters: we will retain `CANISTER_1` on `SUBNET_A` and split off
460+
// `CANISTER_2` to `SUBNET_B`.
461+
const CANISTER_1: CanisterId = CanisterId::from_u64(1);
462+
const CANISTER_2: CanisterId = CanisterId::from_u64(2);
463+
464+
// Ingress history with 4 Received messages, addressed to canisters 1 and 2;
465+
// `IC_00`; and respectively `SUBNET_A`.
460466
let mut ingress_history = IngressHistoryState::new();
461467
let time = mock_time();
462-
for i in (1..=2u64).rev() {
468+
let receivers = [
469+
CANISTER_1.get(),
470+
CANISTER_2.get(),
471+
IC_00.get(),
472+
SUBNET_A.get(),
473+
];
474+
for (i, receiver) in receivers.into_iter().enumerate().rev() {
463475
ingress_history.insert(
464-
message_test_id(i),
476+
message_test_id(i as u64),
465477
IngressStatus::Known {
466-
receiver: canister_test_id(i).get(),
467-
user_id: user_test_id(i),
478+
receiver,
479+
user_id: user_test_id(i as u64),
468480
time,
469481
state: IngressState::Received,
470482
},
@@ -473,9 +485,17 @@ fn system_metadata_split() {
473485
);
474486
}
475487

488+
// `CANISTER_1` remains on `SUBNET_A`.
489+
let is_canister_on_subnet_a = |canister_id: &CanisterId| *canister_id == CANISTER_1;
490+
// All ingress messages except the one addressed to `CANISTER_2` (including the
491+
// ones for `IC_00` and `SUBNET_A`) should remain on `SUBNET_A` after the split.
492+
let is_receiver_on_subnet_a = |canister_id: &CanisterId| *canister_id != CANISTER_2;
493+
// Only ingress messages for `CANISTER_2` should be retained on `SUBNET_B`.
494+
let is_canister_on_subnet_b = |canister_id: &CanisterId| *canister_id == CANISTER_2;
495+
476496
let streams = Streams {
477497
streams: btreemap! { SUBNET_C => Stream::new(StreamIndexedQueue::with_begin(13.into()), 14.into()) },
478-
responses_size_bytes: btreemap! { canister_test_id(1) => 169 },
498+
responses_size_bytes: btreemap! { CANISTER_1 => 169 },
479499
};
480500

481501
// Use uncommon `SubnetType::VerifiedApplication` to make it more likely to
@@ -503,14 +523,12 @@ fn system_metadata_split() {
503523
// Technically some parts of the `SystemMetadata` (such as `prev_state_hash` and
504524
// `own_subnet_type`) would be replaced during loading. However, we only care
505525
// that `after_split()` does not touch them.
506-
let is_canister_on_subnet_a = |canister_id: &CanisterId| *canister_id == canister_test_id(0);
507526
let metadata_a_phase_2 = metadata_a_phase_1.after_split(is_canister_on_subnet_a);
508527

509-
// Expect same metadata, but with pruned ingress history, no previous hash and
510-
// no split marker.
528+
// Expect same metadata, but with pruned ingress history and no split marker.
511529
expected.ingress_history = expected
512530
.ingress_history
513-
.prune_after_split(is_canister_on_subnet_a);
531+
.prune_after_split(is_receiver_on_subnet_a);
514532
expected.split_from = None;
515533
assert_eq!(expected, metadata_a_phase_2);
516534

@@ -528,10 +546,9 @@ fn system_metadata_split() {
528546
// Technically some parts of the `SystemMetadata` (such as `prev_state_hash` and
529547
// `own_subnet_type`) would be replaced during loading. However, we only care
530548
// that `after_split()` does not touch them.
531-
let is_canister_on_subnet_b = |canister_id: &CanisterId| !is_canister_on_subnet_a(canister_id);
532549
let metadata_b_phase_2 = metadata_b_phase_1.after_split(is_canister_on_subnet_b);
533550

534-
// Expect pruned ingress history and no previous hash or split marker.
551+
// Expect pruned ingress history and no split marker.
535552
expected.split_from = None;
536553
expected.ingress_history = expected
537554
.ingress_history

rs/types/types/src/messages.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use ic_protobuf::proxy::{try_from_option_field, ProxyDecodeError};
2222
use ic_protobuf::state::canister_state_bits::v1 as pb;
2323
use ic_protobuf::types::v1 as pb_types;
2424
pub use ingress_messages::{
25-
extract_effective_canister_id, Ingress, ParseIngressError, SignedIngress, SignedIngressContent,
25+
extract_effective_canister_id, is_subnet_id, Ingress, ParseIngressError, SignedIngress,
26+
SignedIngressContent,
2627
};
2728
pub use inter_canister::{
2829
CallContextId, CallbackId, Payload, RejectContext, Request, RequestOrResponse, Response,

rs/types/types/src/messages/ingress_messages.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
use ic_error_types::{ErrorCode, UserError};
1313
use ic_ic00_types::{
1414
CanisterIdRecord, CanisterInfoRequest, InstallCodeArgs, Method, Payload, SetControllerArgs,
15-
UpdateSettingsArgs,
15+
UpdateSettingsArgs, IC_00,
1616
};
1717
use ic_protobuf::{
1818
log::ingress_message_log_entry::v1::IngressMessageLogEntry,
@@ -554,6 +554,7 @@ mod test {
554554
}
555555
}
556556

557-
fn is_subnet_id(canister_id: CanisterId, own_subnet_id: SubnetId) -> bool {
558-
canister_id == CanisterId::ic_00() || canister_id.get_ref() == own_subnet_id.get_ref()
557+
/// Checks whether the given canister ID refers to the subnet (directly or as `IC_00`).
558+
pub fn is_subnet_id(canister_id: CanisterId, own_subnet_id: SubnetId) -> bool {
559+
canister_id == IC_00 || canister_id.get_ref() == own_subnet_id.get_ref()
559560
}

0 commit comments

Comments
 (0)