Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Await for a settlement tx in autopilot #2630

Merged
merged 13 commits into from
Apr 24, 2024

Conversation

squadgazzz
Copy link
Contributor

@squadgazzz squadgazzz commented Apr 18, 2024

Description

This PR introduces improvements to Autopilot's process of waiting for tx hashes from the driver after a settlement. Currently, the Autopilot passively awaits the tx_hash from the driver, which can introduce unnecessary delays, particularly in a colocated environment where the driver might not immediately report the hash.

Changes

  • The Autopilot will now independently monitor the blockchain to detect the transaction hash.
  • In cases where the driver communicates earlier—either confirming the transaction or indicating a failure—the blockchain monitoring task will be cancelled immediately. This ensures that Autopilot does not expend unnecessary resources waiting for a transaction that the driver has already acknowledged as failed.

How to test

Current e2e tests. A unit test can be created, but that would require refactoring to introduce mocks. Worth a separate PR.

Related issues

Fixes #2608, #2609

Comment on lines 480 to 486
let start_offset = MAX_REORG_BLOCK_COUNT;
let max_blocks_wait = 20;
let current = eth.current_block().borrow().number;
let start = current.saturating_sub(start_offset);
let deadline = current.saturating_add(max_blocks_wait);
tracing::debug!(%current, %start, %deadline, ?tag, "waiting for tag");
let mut seen_transactions: HashSet<H256> = Default::default();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is inspired/copied from this PR: #1135
Which was already explained in the comment: https://github.com/cowprotocol/services/pull/1135/files#r1088803161

@squadgazzz squadgazzz marked this pull request as ready for review April 18, 2024 11:26
@squadgazzz squadgazzz requested a review from a team as a code owner April 18, 2024 11:26
@squadgazzz squadgazzz force-pushed the 2609/autopilot-tracks-for-settlements branch from 97fc21a to b1bc38d Compare April 18, 2024 11:30
@squadgazzz
Copy link
Contributor Author

The following config now controls both response awaiting and blockchain monitoring tasks.

/// The amount of time that the autopilot waits looking for a settlement
/// transaction onchain after the driver acknowledges the receipt of a
/// settlement.
#[clap(
long,
env,
default_value = "1m",
value_parser = humantime::parse_duration,
)]
pub max_settlement_transaction_wait: Duration,

crates/autopilot/src/run.rs Outdated Show resolved Hide resolved
crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
///
/// Returns None if no transaction was found within the deadline or the task
/// is cancelled.
async fn wait_for_settlement_transaction(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to test this in e2e or unit test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I couldn't find a quick way to achieve that. That would require either an artificial delay in the driver's response or creating unit tests with mocks. Someone may have a better idea of how to test it.

let max_blocks_wait = self.max_blocks_wait;
let (cancellation_tx, cancellation_rx) = watch::channel(false);
let settlement_tx_wait_task = tokio::spawn(async move {
let tag = auction_id.to_be_bytes();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the

input.extend(auction_id.to_be_bytes());

crates/autopilot/src/arguments.rs Outdated Show resolved Hide resolved
crates/autopilot/src/run.rs Outdated Show resolved Hide resolved
crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
@squadgazzz squadgazzz force-pushed the 2609/autopilot-tracks-for-settlements branch from f953bcb to 11cda11 Compare April 19, 2024 13:54
@squadgazzz squadgazzz requested a review from MartinquaXD April 22, 2024 18:31
Copy link
Contributor

@MartinquaXD MartinquaXD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic is good the code just seems like it could be nicer to read.

crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
}
let Ok(mut hashes) = self
.persistence
.recent_settlement_tx_hashes(start..deadline + 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that you are using the existing event indexing instead of trying to inspect new blocks consecutively (which makes the code simpler)

However, the settlements table also contains the auction_id as a column. It might require exposing a new method on persistence, but I think it would be even simpler and more coherent to just see if we can fetch a settlement by auction and return its tx_hash.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come I missed that column 🙈 Thanks, updated the PR.

Copy link
Contributor

@sunce86 sunce86 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG.

We can improve by not trusting the driver in cases when driver returns success, but can be done in a separate PR.

Also we might reconsider dropping the tx_hash as a response from settle since it's not used for anything important in the competition (afais, maybe we can redesign the inflight orders).

crates/autopilot/src/run_loop.rs Outdated Show resolved Hide resolved
tokio::time::sleep(Duration::from_secs(5)).await;
}
Err(SettleError::Failure(anyhow::anyhow!(
"settlement transaction await reached deadline"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we could define a separate variant Deadline instead of anyhow error.

@squadgazzz squadgazzz enabled auto-merge (squash) April 24, 2024 13:52
@squadgazzz squadgazzz merged commit bdcfd27 into main Apr 24, 2024
9 checks passed
@squadgazzz squadgazzz deleted the 2609/autopilot-tracks-for-settlements branch April 24, 2024 14:02
@github-actions github-actions bot locked and limited conversation to collaborators Apr 24, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat: [Solver/driver collocation] Allow driver to report cancellation/intention not to settle to the autopilot
5 participants