Skip to content

Commit 9fbe41f

Browse files
feat(outcalls): add price_version parameter to the http canister request args (#7447)
This PR adds the price version, as described in dfinity/portal#6101 to the management canister `http_request` endpoint. This PR also persists the version in the replicated state. In the future that will be used to determine which pricing strategy should be applied. The flag is a candid opt nat32, which, if missing, defaults to "1", which corresponds to the "current" pricing. Similarly, if set to "2", the flag will default back to 1, as version 2 is not implemented yet. In the future, we will support pricing_Version = 2, which corresponds to the "pay-as-you-go" pricing. --------- Co-authored-by: kpop-dfinity <125868903+kpop-dfinity@users.noreply.github.com>
1 parent 8f636fc commit 9fbe41f

File tree

23 files changed

+217
-12
lines changed

23 files changed

+217
-12
lines changed

rs/execution_environment/src/execution_environment/tests.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,6 +1988,7 @@ fn http_request_bound_holds() {
19881988
context: transform_context.clone(),
19891989
}),
19901990
is_replicated: None,
1991+
pricing_version: None,
19911992
};
19921993

19931994
// Create request to HTTP_REQUEST method.
@@ -2769,6 +2770,7 @@ fn execute_canister_http_request() {
27692770
context: transform_context.clone(),
27702771
}),
27712772
is_replicated: None,
2773+
pricing_version: None,
27722774
};
27732775

27742776
// Create request to HTTP_REQUEST method.
@@ -2849,6 +2851,7 @@ fn execute_canister_http_request_disabled() {
28492851
context: vec![0, 1, 2],
28502852
}),
28512853
is_replicated: None,
2854+
pricing_version: None,
28522855
};
28532856

28542857
// Create request to HTTP_REQUEST method.

rs/execution_environment/src/scheduler/tests.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3042,6 +3042,7 @@ fn canister_is_stopped_if_timeout_occurs_and_ready_to_stop() {
30423042
transform: None,
30433043
max_response_bytes: None,
30443044
is_replicated: None,
3045+
pricing_version: None,
30453046
})
30463047
.unwrap();
30473048

@@ -4115,6 +4116,7 @@ fn consumed_cycles_http_outcalls_are_added_to_consumed_cycles_total() {
41154116
context: transform_context,
41164117
}),
41174118
is_replicated: None,
4119+
pricing_version: None,
41184120
};
41194121

41204122
// Create request to `HttpRequest` method.
@@ -4204,6 +4206,7 @@ fn http_outcalls_free() {
42044206
context: transform_context,
42054207
}),
42064208
is_replicated: None,
4209+
pricing_version: None,
42074210
};
42084211

42094212
// Create request to `HttpRequest` method.

rs/execution_environment/tests/subnet_size_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ fn simulate_http_request_cost(subnet_type: SubnetType, subnet_size: usize) -> Cy
534534
context: vec![],
535535
}),
536536
is_replicated: None,
537+
pricing_version: None,
537538
})
538539
.unwrap(),
539540
),

rs/https_outcalls/client/src/client.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use ic_https_outcalls_service::{
88
};
99
use ic_interfaces::execution_environment::{QueryExecutionInput, QueryExecutionService};
1010
use ic_interfaces_adapter_client::{NonBlockingChannel, SendError, TryReceiveError};
11-
use ic_logger::{ReplicaLogger, info};
11+
use ic_logger::{ReplicaLogger, info, warn};
1212
use ic_management_canister_types_private::{CanisterHttpResponsePayload, TransformArgs};
1313
use ic_metrics::MetricsRegistry;
1414
use ic_nns_delegation_manager::{CanisterRangesFilter, NNSDelegationReader};
@@ -146,11 +146,33 @@ impl NonBlockingChannel<CanisterHttpRequest> for CanisterHttpAdapterClientImpl {
146146
http_method: request_http_method,
147147
max_response_bytes: request_max_response_bytes,
148148
transform: request_transform,
149+
pricing_version: request_pricing_version,
149150
..
150151
},
151152
socks_proxy_addrs,
152153
} = canister_http_request;
153154

155+
if request_pricing_version == ic_types::canister_http::PricingVersion::PayAsYouGo {
156+
warn!(
157+
log,
158+
"Canister HTTP request with PayAsYouGo pricing is not supported yet: request_id {}, sender {}, process_id: {}",
159+
request_id,
160+
request_sender,
161+
std::process::id(),
162+
);
163+
let _ = permit.send(CanisterHttpResponse {
164+
id: request_id,
165+
timeout: request_timeout,
166+
canister_id: request_sender,
167+
content: CanisterHttpResponseContent::Reject(CanisterHttpReject {
168+
reject_code: RejectCode::SysFatal,
169+
message: "Canister HTTP request with PayAsYouGo pricing is not supported"
170+
.to_string(),
171+
}),
172+
});
173+
return;
174+
}
175+
154176
let adapter_req_timer = Instant::now();
155177
let max_response_size_bytes = request_max_response_bytes
156178
.unwrap_or(NumBytes::new(MAX_CANISTER_HTTP_RESPONSE_BYTES))
@@ -396,7 +418,7 @@ mod tests {
396418
use ic_interfaces::execution_environment::{QueryExecutionError, QueryExecutionResponse};
397419
use ic_logger::replica_logger::no_op_logger;
398420
use ic_test_utilities_types::messages::RequestBuilder;
399-
use ic_types::canister_http::{Replication, Transform};
421+
use ic_types::canister_http::{PricingVersion, Replication, Transform};
400422
use ic_types::{
401423
Time, canister_http::CanisterHttpMethod, messages::CallbackId, time::UNIX_EPOCH,
402424
time::current_time,
@@ -489,6 +511,7 @@ mod tests {
489511
}),
490512
time: UNIX_EPOCH,
491513
replication: Replication::FullyReplicated,
514+
pricing_version: PricingVersion::Legacy,
492515
},
493516
socks_proxy_addrs: vec![],
494517
}

rs/https_outcalls/consensus/src/payload_builder/tests.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ fn timeout_priority() {
343343
// this is the important one
344344
time: UNIX_EPOCH,
345345
replication: ic_types::canister_http::Replication::FullyReplicated,
346+
pricing_version: ic_types::canister_http::PricingVersion::Legacy,
346347
};
347348
init_state
348349
.metadata
@@ -849,6 +850,7 @@ fn non_replicated_request_response_coming_in_gossip_payload_created() {
849850
transform: None,
850851
time: UNIX_EPOCH,
851852
replication: ic_types::canister_http::Replication::NonReplicated(delegated_node_id),
853+
pricing_version: ic_types::canister_http::PricingVersion::Legacy,
852854
};
853855

854856
// Insert the context in the replicated state
@@ -952,6 +954,7 @@ fn non_replicated_request_with_extra_share_includes_only_delegated_share() {
952954
transform: None,
953955
time: UNIX_EPOCH,
954956
replication: ic_types::canister_http::Replication::NonReplicated(delegated_node_id),
957+
pricing_version: ic_types::canister_http::PricingVersion::Legacy,
955958
};
956959

957960
// Insert the context in the replicated state
@@ -1056,6 +1059,7 @@ fn non_replicated_share_is_ignored_if_content_is_missing() {
10561059
transform: None,
10571060
time: UNIX_EPOCH,
10581061
replication: ic_types::canister_http::Replication::NonReplicated(delegated_node_id),
1062+
pricing_version: ic_types::canister_http::PricingVersion::Legacy,
10591063
};
10601064

10611065
let mut init_state = ic_test_utilities_state::get_initial_state(0, 0);
@@ -1134,6 +1138,7 @@ fn validate_payload_succeeds_for_valid_non_replicated_response() {
11341138
transform: None,
11351139
time: UNIX_EPOCH,
11361140
replication: ic_types::canister_http::Replication::NonReplicated(delegated_node_id),
1141+
pricing_version: ic_types::canister_http::PricingVersion::Legacy,
11371142
};
11381143

11391144
// Inject this context into the state reader used by the validator.
@@ -1200,6 +1205,7 @@ fn validate_payload_fails_for_non_replicated_response_with_wrong_signer() {
12001205
transform: None,
12011206
time: UNIX_EPOCH,
12021207
replication: ic_types::canister_http::Replication::NonReplicated(delegated_node_id),
1208+
pricing_version: ic_types::canister_http::PricingVersion::Legacy,
12031209
};
12041210

12051211
// Inject this context into the state reader.
@@ -1282,6 +1288,7 @@ fn validate_payload_fails_for_response_with_no_signatures() {
12821288
transform: None,
12831289
time: UNIX_EPOCH,
12841290
replication: ic_types::canister_http::Replication::NonReplicated(delegated_node_id),
1291+
pricing_version: ic_types::canister_http::PricingVersion::Legacy,
12851292
};
12861293

12871294
// Inject this context into the state reader used by the validator.
@@ -1369,6 +1376,7 @@ fn validate_payload_fails_when_non_replicated_proof_is_for_fully_replicated_requ
13691376
time: UNIX_EPOCH,
13701377
// The state says the request is replicated.
13711378
replication: ic_types::canister_http::Replication::FullyReplicated,
1379+
pricing_version: ic_types::canister_http::PricingVersion::Legacy,
13721380
};
13731381

13741382
// Inject this context into the state reader.
@@ -1461,6 +1469,7 @@ fn validate_payload_fails_for_duplicate_non_replicated_response() {
14611469
transform: None,
14621470
time: UNIX_EPOCH,
14631471
replication: ic_types::canister_http::Replication::NonReplicated(delegated_node_id),
1472+
pricing_version: ic_types::canister_http::PricingVersion::Legacy,
14641473
};
14651474

14661475
// 2. Inject this context into the state reader

rs/https_outcalls/consensus/src/pool_manager.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,7 @@ pub mod test {
748748
transform: None,
749749
time: ic_types::Time::from_nanos_since_unix_epoch(10),
750750
replication: Replication::FullyReplicated,
751+
pricing_version: PricingVersion::Legacy,
751752
};
752753

753754
state_manager
@@ -854,6 +855,7 @@ pub mod test {
854855
transform: None,
855856
time: ic_types::Time::from_nanos_since_unix_epoch(10),
856857
replication: Replication::FullyReplicated,
858+
pricing_version: PricingVersion::Legacy,
857859
};
858860

859861
// NOTE: We need at least some context in the state, otherwise next_callback_id will be 0 and no
@@ -967,6 +969,7 @@ pub mod test {
967969
transform: None,
968970
time: ic_types::Time::from_nanos_since_unix_epoch(10),
969971
replication: Replication::FullyReplicated,
972+
pricing_version: PricingVersion::Legacy,
970973
};
971974

972975
state_manager
@@ -1099,6 +1102,7 @@ pub mod test {
10991102
transform: None,
11001103
time: ic_types::Time::from_nanos_since_unix_epoch(10),
11011104
replication: Replication::NonReplicated(delegated_node_id),
1105+
pricing_version: PricingVersion::Legacy,
11021106
};
11031107

11041108
state_manager
@@ -1237,6 +1241,7 @@ pub mod test {
12371241
transform: None,
12381242
time: ic_types::Time::from_nanos_since_unix_epoch(10),
12391243
replication: Replication::NonReplicated(delegated_node_id),
1244+
pricing_version: PricingVersion::Legacy,
12401245
};
12411246
state_manager
12421247
.get_mut()
@@ -1340,6 +1345,7 @@ pub mod test {
13401345
transform: None,
13411346
time: ic_types::Time::from_nanos_since_unix_epoch(10),
13421347
replication: Replication::FullyReplicated,
1348+
pricing_version: PricingVersion::Legacy,
13431349
};
13441350

13451351
state_manager
@@ -1452,6 +1458,7 @@ pub mod test {
14521458
transform: None,
14531459
time: ic_types::Time::from_nanos_since_unix_epoch(10),
14541460
replication: Replication::NonReplicated(delegated_node_id),
1461+
pricing_version: PricingVersion::Legacy,
14551462
};
14561463

14571464
state_manager
@@ -1633,6 +1640,7 @@ pub mod test {
16331640
transform: None,
16341641
time: ic_types::Time::from_nanos_since_unix_epoch(10),
16351642
replication: Replication::NonReplicated(delegated_node_id),
1643+
pricing_version: PricingVersion::Legacy,
16361644
};
16371645

16381646
state_manager
@@ -1744,6 +1752,7 @@ pub mod test {
17441752
transform: None,
17451753
time: ic_types::Time::from_nanos_since_unix_epoch(10),
17461754
replication: Replication::NonReplicated(delegated_node_id),
1755+
pricing_version: PricingVersion::Legacy,
17471756
};
17481757
state_manager
17491758
.get_mut()
@@ -1867,6 +1876,7 @@ pub mod test {
18671876
transform: None,
18681877
time: ic_types::Time::from_nanos_since_unix_epoch(10),
18691878
replication: Replication::NonReplicated(delegated_node_id),
1879+
pricing_version: PricingVersion::Legacy,
18701880
};
18711881
state_manager
18721882
.get_mut()
@@ -1998,6 +2008,7 @@ pub mod test {
19982008
transform: None,
19992009
time: ic_types::Time::from_nanos_since_unix_epoch(10),
20002010
replication: Replication::NonReplicated(delegated_node_id),
2011+
pricing_version: PricingVersion::Legacy,
20012012
};
20022013
state_manager
20032014
.get_mut()
@@ -2120,6 +2131,7 @@ pub mod test {
21202131
transform: None,
21212132
time: ic_types::Time::from_nanos_since_unix_epoch(10),
21222133
replication: Replication::NonReplicated(delegated_node_id),
2134+
pricing_version: PricingVersion::Legacy,
21232135
};
21242136

21252137
state_manager
@@ -2236,6 +2248,7 @@ pub mod test {
22362248
transform: None,
22372249
time: ic_types::Time::from_nanos_since_unix_epoch(10),
22382250
replication: Replication::FullyReplicated,
2251+
pricing_version: PricingVersion::Legacy,
22392252
};
22402253

22412254
state_manager
@@ -2384,6 +2397,7 @@ pub mod test {
23842397
transform: None,
23852398
time: ic_types::Time::from_nanos_since_unix_epoch(10),
23862399
replication: Replication::NonReplicated(delegated_node_id),
2400+
pricing_version: PricingVersion::Legacy,
23872401
};
23882402
state_manager
23892403
.get_mut()
@@ -2482,6 +2496,7 @@ pub mod test {
24822496
transform: None,
24832497
time: ic_types::Time::from_nanos_since_unix_epoch(10),
24842498
replication: Replication::FullyReplicated,
2499+
pricing_version: PricingVersion::Legacy,
24852500
};
24862501

24872502
// Expect times to be called exactly once to check that already

rs/protobuf/def/state/metadata/v1/metadata.proto

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,17 @@ message CanisterHttpRequestContext {
150150
optional uint64 max_response_bytes = 9;
151151
google.protobuf.BytesValue transform_context = 10;
152152
optional Replication replication = 11;
153+
optional PricingVersion pricing_version = 12;
153154
reserved 5;
154155
}
155156

157+
message PricingVersion {
158+
oneof version {
159+
google.protobuf.Empty legacy = 1;
160+
google.protobuf.Empty pay_as_you_go = 2;
161+
}
162+
}
163+
156164
message Replication {
157165
oneof replication_type {
158166
google.protobuf.Empty fully_replicated = 1;

rs/protobuf/src/gen/state/state.metadata.v1.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,23 @@ pub struct CanisterHttpRequestContext {
214214
pub transform_context: ::core::option::Option<::prost::alloc::vec::Vec<u8>>,
215215
#[prost(message, optional, tag = "11")]
216216
pub replication: ::core::option::Option<Replication>,
217+
#[prost(message, optional, tag = "12")]
218+
pub pricing_version: ::core::option::Option<PricingVersion>,
219+
}
220+
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
221+
pub struct PricingVersion {
222+
#[prost(oneof = "pricing_version::Version", tags = "1, 2")]
223+
pub version: ::core::option::Option<pricing_version::Version>,
224+
}
225+
/// Nested message and enum types in `PricingVersion`.
226+
pub mod pricing_version {
227+
#[derive(Clone, Copy, PartialEq, ::prost::Oneof)]
228+
pub enum Version {
229+
#[prost(message, tag = "1")]
230+
Legacy(()),
231+
#[prost(message, tag = "2")]
232+
PayAsYouGo(()),
233+
}
217234
}
218235
#[derive(Clone, PartialEq, ::prost::Message)]
219236
pub struct Replication {

rs/replicated_state/src/metadata_state/tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use ic_test_utilities_types::{
3030
use ic_types::{
3131
Cycles, ExecutionRound, Height,
3232
batch::BlockmakerMetrics,
33-
canister_http::{CanisterHttpMethod, CanisterHttpRequestContext, Replication},
33+
canister_http::{CanisterHttpMethod, CanisterHttpRequestContext, PricingVersion, Replication},
3434
consensus::idkg::{IDkgMasterPublicKeyId, PreSigId, common::PreSignature},
3535
crypto::{
3636
AlgorithmId,
@@ -603,6 +603,7 @@ fn subnet_call_contexts_deserialization() {
603603
transform: Some(transform.clone()),
604604
time: UNIX_EPOCH,
605605
replication: Replication::FullyReplicated,
606+
pricing_version: PricingVersion::Legacy,
606607
};
607608
subnet_call_context_manager.push_context(SubnetCallContext::CanisterHttpRequest(
608609
canister_http_request,

rs/rust_canisters/proxy_canister/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub struct UnvalidatedCanisterHttpRequestArgs {
3939
pub method: HttpMethod,
4040
pub transform: Option<TransformContext>,
4141
pub is_replicated: Option<bool>,
42+
pub pricing_version: Option<u32>,
4243
}
4344
impl Payload<'_> for UnvalidatedCanisterHttpRequestArgs {}
4445

@@ -53,7 +54,8 @@ impl From<UnvalidatedCanisterHttpRequestArgs>
5354
body: args.body,
5455
method: args.method,
5556
transform: args.transform,
56-
is_replicated: None,
57+
is_replicated: args.is_replicated,
58+
pricing_version: args.pricing_version,
5759
}
5860
}
5961
}

0 commit comments

Comments
 (0)