From 793101621fc20f007029af4a11ec04d6e974e6a5 Mon Sep 17 00:00:00 2001 From: Janito Vaqueiro Ferreira Filho Date: Mon, 10 Mar 2025 21:03:03 +0000 Subject: [PATCH 1/4] Always decrement `active_instances` on `Drop` Don't condition it to having depleted its expected calls. --- linera-execution/src/test_utils/mock_application.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/linera-execution/src/test_utils/mock_application.rs b/linera-execution/src/test_utils/mock_application.rs index 38ff7934e082..e35adfecb8dc 100644 --- a/linera-execution/src/test_utils/mock_application.rs +++ b/linera-execution/src/test_utils/mock_application.rs @@ -115,9 +115,7 @@ impl Eq for MockApplication {} impl Drop for MockApplicationInstance { fn drop(&mut self) { - if self.expected_calls.is_empty() { - self.active_instances.fetch_sub(1, Ordering::AcqRel); - } + self.active_instances.fetch_sub(1, Ordering::AcqRel); } } From 2a00d80078fe0152ade38612a14961dc30c93417 Mon Sep 17 00:00:00 2001 From: Janito Vaqueiro Ferreira Filho Date: Mon, 10 Mar 2025 21:04:35 +0000 Subject: [PATCH 2/4] Share expected calls among mock app. instances Allow writing tests where the application calls its service as an oracle. --- linera-execution/src/test_utils/mock_application.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/linera-execution/src/test_utils/mock_application.rs b/linera-execution/src/test_utils/mock_application.rs index e35adfecb8dc..1a491a0bd115 100644 --- a/linera-execution/src/test_utils/mock_application.rs +++ b/linera-execution/src/test_utils/mock_application.rs @@ -39,7 +39,7 @@ pub struct MockApplication { /// /// Will expect certain calls previously configured through [`MockApplication`]. pub struct MockApplicationInstance { - expected_calls: VecDeque, + expected_calls: Arc>>, runtime: Runtime, active_instances: Arc, } @@ -61,7 +61,7 @@ impl MockApplication { self.active_instances.fetch_add(1, Ordering::AcqRel); MockApplicationInstance { - expected_calls: mem::take(&mut self.expected_calls.lock().expect("Mutex is poisoned")), + expected_calls: self.expected_calls.clone(), runtime, active_instances: self.active_instances.clone(), } @@ -292,7 +292,10 @@ impl UserServiceModule for MockApplication { impl MockApplicationInstance { /// Retrieves the next [`ExpectedCall`] in the queue. fn next_expected_call(&mut self) -> Option { - self.expected_calls.pop_front() + self.expected_calls + .lock() + .expect("Queue of expected calls was poisoned") + .pop_front() } } From 0167820b5bbc3ed6499337ef39af27b64c2904dd Mon Sep 17 00:00:00 2001 From: Janito Vaqueiro Ferreira Filho Date: Tue, 11 Mar 2025 02:31:56 +0000 Subject: [PATCH 3/4] Add `http_request_timeout_ms` policy Allow configuring the timeout for HTTP requests. --- linera-chain/src/unit_tests/chain_tests.rs | 2 +- linera-execution/src/policy.rs | 6 ++++++ linera-execution/tests/fee_consumption.rs | 1 + linera-rpc/tests/snapshots/format__format.yaml.snap | 1 + linera-service-graphql-client/gql/service_schema.graphql | 4 ++++ 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/linera-chain/src/unit_tests/chain_tests.rs b/linera-chain/src/unit_tests/chain_tests.rs index 61c175944a8a..7bff34cc5fca 100644 --- a/linera-chain/src/unit_tests/chain_tests.rs +++ b/linera-chain/src/unit_tests/chain_tests.rs @@ -117,7 +117,7 @@ async fn test_block_size_limit() { let mut chain = ChainStateView::new(chain_id).await; // The size of the executed valid block below. - let maximum_executed_block_size = 725; + let maximum_executed_block_size = 733; // Initialize the chain. let mut config = make_open_chain_config(); diff --git a/linera-execution/src/policy.rs b/linera-execution/src/policy.rs index de8a8136d46e..8de337cf2731 100644 --- a/linera-execution/src/policy.rs +++ b/linera-execution/src/policy.rs @@ -61,6 +61,8 @@ pub struct ResourceControlPolicy { pub maximum_bytes_read_per_block: u64, /// The maximum data to write per block pub maximum_bytes_written_per_block: u64, + /// The maximum amount of time allowed to wait for an HTTP response. + pub http_request_timeout_ms: u64, /// The list of hosts that contracts and services can send HTTP requests to. pub http_request_allow_list: BTreeSet, } @@ -88,6 +90,7 @@ impl fmt::Display for ResourceControlPolicy { maximum_bytes_read_per_block, maximum_bytes_written_per_block, http_request_allow_list, + http_request_timeout_ms, } = self; write!( f, @@ -111,6 +114,7 @@ impl fmt::Display for ResourceControlPolicy { {maximum_block_proposal_size} maximum size of a block proposal\n\ {maximum_bytes_read_per_block} maximum number bytes read per block\n\ {maximum_bytes_written_per_block} maximum number bytes written per block\n\ + {http_request_timeout_ms} ms timeout for HTTP requests\n\ HTTP hosts allowed for contracts and services: {http_request_allow_list:#?}\n", )?; Ok(()) @@ -148,6 +152,7 @@ impl ResourceControlPolicy { maximum_block_proposal_size: u64::MAX, maximum_bytes_read_per_block: u64::MAX, maximum_bytes_written_per_block: u64::MAX, + http_request_timeout_ms: u64::MAX, http_request_allow_list: BTreeSet::new(), } } @@ -213,6 +218,7 @@ impl ResourceControlPolicy { maximum_block_proposal_size: 13_000_000, maximum_bytes_read_per_block: 100_000_000, maximum_bytes_written_per_block: 10_000_000, + http_request_timeout_ms: 20_000, http_request_allow_list: BTreeSet::new(), } } diff --git a/linera-execution/tests/fee_consumption.rs b/linera-execution/tests/fee_consumption.rs index 3ec7af3dcc9b..646abdf38eb6 100644 --- a/linera-execution/tests/fee_consumption.rs +++ b/linera-execution/tests/fee_consumption.rs @@ -153,6 +153,7 @@ async fn test_fee_consumption( maximum_block_proposal_size: 53, maximum_bytes_read_per_block: 59, maximum_bytes_written_per_block: 61, + http_request_timeout_ms: 67, http_request_allow_list: BTreeSet::new(), }; diff --git a/linera-rpc/tests/snapshots/format__format.yaml.snap b/linera-rpc/tests/snapshots/format__format.yaml.snap index f8b1cecfb80c..d0434b2cd71f 100644 --- a/linera-rpc/tests/snapshots/format__format.yaml.snap +++ b/linera-rpc/tests/snapshots/format__format.yaml.snap @@ -865,6 +865,7 @@ ResourceControlPolicy: - maximum_block_proposal_size: U64 - maximum_bytes_read_per_block: U64 - maximum_bytes_written_per_block: U64 + - http_request_timeout_ms: U64 - http_request_allow_list: SEQ: STR Response: diff --git a/linera-service-graphql-client/gql/service_schema.graphql b/linera-service-graphql-client/gql/service_schema.graphql index f5b13e2e7798..36c29c67b082 100644 --- a/linera-service-graphql-client/gql/service_schema.graphql +++ b/linera-service-graphql-client/gql/service_schema.graphql @@ -1112,6 +1112,10 @@ input ResourceControlPolicy { """ maximumBytesWrittenPerBlock: Int! """ + The maximum amount of time allowed to wait for an HTTP response. + """ + httpRequestTimeoutMs: Int! + """ The list of hosts that contracts and services can send HTTP requests to. """ httpRequestAllowList: [String!]! From f74cb2115c387aaf88ac4ec2f4ea65f4eb39d587 Mon Sep 17 00:00:00 2001 From: Janito Vaqueiro Ferreira Filho Date: Tue, 11 Mar 2025 02:37:44 +0000 Subject: [PATCH 4/4] Configure timeout when performing HTTP requests Ensure that it takes a bounded amount of time to complete. --- linera-execution/src/execution_state_actor.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/linera-execution/src/execution_state_actor.rs b/linera-execution/src/execution_state_actor.rs index c63ba72e9eb1..4d77aa0e3191 100644 --- a/linera-execution/src/execution_state_actor.rs +++ b/linera-execution/src/execution_state_actor.rs @@ -5,6 +5,8 @@ #[cfg(with_metrics)] use std::sync::LazyLock; +#[cfg(not(web))] +use std::time::Duration; use custom_debug_derive::Debug; use futures::channel::mpsc; @@ -395,12 +397,20 @@ where ExecutionError::UnauthorizedHttpRequest(url) ); - let response = Client::new() + #[cfg_attr(web, allow(unused_mut))] + let mut request = Client::new() .request(request.method.into(), url) .body(request.body) - .headers(headers) - .send() - .await?; + .headers(headers); + #[cfg(not(web))] + { + request = request.timeout(Duration::from_millis( + committee.policy().http_request_timeout_ms, + )); + } + + let response = request.send().await?; + callback.respond(http::Response::from_reqwest(response).await?); }