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

chore(ckbtc): add check_transaction_query method to Bitcoin checker #3454

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

lpahlavi
Copy link
Contributor

@lpahlavi lpahlavi commented Jan 15, 2025

(XC-194) Query interface to retrieve check_transaction status.

The check_transaction call requires cycles to execute due to to the HTTP outcalls it has to perform. However, in the event a transaction and all of its inputs have already been loaded to the cache in heap memory, no HTTP calls are required. This new query method therefore checks whether a transaction status may be obtained using only cached information, and if so, returns it without charging any cycles.

@github-actions github-actions bot added the chore label Jan 15, 2025
@lpahlavi lpahlavi force-pushed the lpahlavi/XC-194-query-retrieve-check-transaction-status branch 2 times, most recently from f4bfb78 to 5354b6f Compare January 15, 2025 10:44
@lpahlavi lpahlavi force-pushed the lpahlavi/XC-194-query-retrieve-check-transaction-status branch from 5354b6f to 0331d1d Compare January 23, 2025 14:08
@lpahlavi lpahlavi force-pushed the lpahlavi/XC-194-query-retrieve-check-transaction-status branch from 0331d1d to 3184c66 Compare January 23, 2025 14:14
@lpahlavi lpahlavi added the CI_ALL_BAZEL_TARGETS Runs all bazel targets and uploads them to S3 label Jan 23, 2025
@lpahlavi lpahlavi marked this pull request as ready for review January 23, 2025 14:19
@lpahlavi lpahlavi requested a review from a team as a code owner January 23, 2025 14:19
Copy link
Member

@gregorydemay gregorydemay left a comment

Choose a reason for hiding this comment

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

Thanks @lpahlavi for this PR! Some preliminary comments.

rs/bitcoin/checker/src/fetch.rs Outdated Show resolved Hide resolved
rs/bitcoin/checker/src/main.rs Outdated Show resolved Hide resolved
// Same as check_transaction, but query method, hence it does not perform any HTTP
// outcalls to fetch the required information.
// IMPORTANT: this endpoint is meant as a debugging tool and is not guaranteed to be backwards-compatible.
check_transaction_query: (CheckTransactionArgs) -> (CheckTransactionQueryResponse) query;
Copy link
Member

Choose a reason for hiding this comment

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

I think there I would provide a new input type, e.g. CheckTransactionQueryArgs which would be an enum, to be able to handle the transaction Id given in binary or in String format. This is to avoid having a check_transaction_query_str, which would be the pendant to check_transaction_str

Copy link
Member

Choose a reason for hiding this comment

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

nit: the names of the variants are not too descriptive (Blob/Text) so I would rename them to something like TxIdBin/TxIdStr (or similar) since it should be clear that those are transaction IDs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! Done.

Comment on lines 65 to 71
type CheckTransactionQueryStatus = variant {
// The result is not available, but may be obtained via a call to the non-query version
// of `check_transaction`.
HttpOutcallRequired;
// The result is unknown due to an irrecoverable error.
Error: CheckTransactionIrrecoverableError;
};
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure we need this:

  1. CheckTransactionIrrecoverableError::InvalidTransaction would not be seen from the query call which only access the cache, since this error is not recorded in the state
  2. CheckTransactionIrrecoverableError::InvalidTransactionId: in that case the query call should just panic.
  3. I don't know if need to differentiate CheckTransactionIrrecoverableError::ResponseTooLarge from CheckTransactionQueryStatus::HttpOutcallRequired in the query call.

With all this I would tend to just have an Unknown; variant (like Passed). WDYT @ninegua ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that's a sensible suggestion. I've implemented it as you suggested, @ninegua lmk if you disagree.

Copy link
Member

Choose a reason for hiding this comment

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

LGTM with the simplified result type for queries.

rs/bitcoin/checker/src/main.rs Outdated Show resolved Hide resolved
@lpahlavi
Copy link
Contributor Author

@gregorydemay Thanks a lot for the feedback! I've implemented your suggested changes, and will wait for @ninegua's +1 before adding all of the required tests.

.input_addresses
.iter()
.flatten()
.find(|address| is_blocked(address))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ninegua : Do you have any idea why we only return one blocked address here? I tried to keep the same behavior as before, but imo we could also return all blocked addresses, no?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it should be totally fine to return all blocked addresses. I don't even remember why it was coded this way to return early

rs/bitcoin/checker/btc_checker_canister.did Show resolved Hide resolved
Comment on lines 534 to 541
CheckMode::Normal => {
if let Some(FetchTxStatus::Fetched(fetched)) = state::get_fetch_status(txid) {
if let Some(response) = check_no_input_address_is_blocked(&fetched) {
return response.into();
}
}
CheckTransactionQueryStatus::HttpOutcallRequired.into()
}
Copy link
Member

Choose a reason for hiding this comment

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

nit: you could have everything under a single match statement

Suggested change
CheckMode::Normal => {
if let Some(FetchTxStatus::Fetched(fetched)) = state::get_fetch_status(txid) {
if let Some(response) = check_no_input_address_is_blocked(&fetched) {
return response.into();
}
}
CheckTransactionQueryStatus::HttpOutcallRequired.into()
}
CheckMode::Normal => match state::get_fetch_status(txid) {
Some(FetchTxStatus::Fetched(fetched)) => {
check_for_blocked_input_addresses(&fetched).into()
}
Some(
FetchTxStatus::PendingOutcall
| FetchTxStatus::PendingRetry { .. }
| FetchTxStatus::Error(_),
)
| None => CheckTransactionQueryResponse::Unknown,
},

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice! I didn't realize I could use the | operator inside Some(...). I thought I had to do something like Some(A) | Some(B) | ... | None which didn't look so great... Always happy to learn some new Rust tricks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
chore CI_ALL_BAZEL_TARGETS Runs all bazel targets and uploads them to S3 @cross-chain-team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants