fix: CanisterHttpPoolImpl::get() serves full response for flexible & non-replicated requests#10741
Open
eichhorl wants to merge 4 commits into
Open
fix: CanisterHttpPoolImpl::get() serves full response for flexible & non-replicated requests#10741eichhorl wants to merge 4 commits into
CanisterHttpPoolImpl::get() serves full response for flexible & non-replicated requests#10741eichhorl wants to merge 4 commits into
Conversation
|
✅ No security or compliance issues detected. Reviewed everything up to 478eb83. Security Overview
Detected Code Changes
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
For fully replicated HTTP outcalls requests, the gossiped shares include only the response hash, because each replica determines the HTTP response content by making the outcall themselves.
For flexible & non-replicated requests, not all replicas make the HTTP outcall. Yet, all replicas should still know the results of all outcalls, such that all replicas can potentially include them in a block. Therefore, the gossiped responses also include the full HTTP response content. Otherwise, a replica making a non-replicated request (where only one replica makes the outcall), would need to wait 1-13 blocks (13 node subnet) until it is the blockmaker to include the response in a block.
Additionally, HTTP outcalls shares are not relayed, or advertised. They are pushed to all peers immediately (
is_latency_sensitive: true).Problem
Because HTTP outcalls shares are pushed without adverts, the code before this PR assumed that
CanisterHttpPoolImpl::get()would never actually be called (get()is usually how a node returns an artifact that is requested / "pulled" by other nodes, i.e. when resolving an advert). Because of this assumption,get()never returned the full response content, and instead set it toNonefor simplicity:ic/rs/artifact_pool/src/canister_http_pool.rs
Lines 194 to 207 in 5ee93a4
However, this assumption is incorrect due to the following reason: When a node receives an HTTP outcalls share, it calls its "bouncer" to see if the artifact is
Wanted,UnwantedorMaybeWantsLater:ic/rs/https_outcalls/consensus/src/gossip.rs
Lines 58 to 67 in 478eb83
If the bouncer returns
MaybeWantsLater, then P2P will only stash the advert, and take out the (larger) artifact content in order to conserve memory, even if the advert was pushed together with its content:ic/rs/p2p/artifact_downloader/src/fetch_artifact/download.rs
Lines 180 to 184 in 478eb83
If at a later point, the bouncer value switches to
Wants, then the advert needs to be resolved from scratch, meaning the artifact content is downloaded from a peer which callsCanisterHttpPoolImpl::get().In case of non-replicated or flexible outcalls, due to the assumption above, the contacted peer will then serve the artifact without the HTTP response content. The HTTP share without response content is unexpected for non-replicated and flexible requests, meaning the receiving peer will invalidate it.
Proposed Changes
This PR fixes the problem by recording in the pool (at insertion time), if the response content should be served via
get()or not. This will allow nodes to correctly fetch shares for flexible or non-replicated requests (including the response content) from other nodes.