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] sidecar send new block updates #1409

Open
wants to merge 14 commits into
base: feat/emily-new-block-handler
Choose a base branch
from

Conversation

Jiloc
Copy link
Collaborator

@Jiloc Jiloc commented Feb 21, 2025

Description

Part of #1067

Changes

  • Update from Flask to FastAPI to reduce boilerplate for parsing jsons and error handling (discussed in feat: add emily sidecar #1050)
  • Sends new-block events instead of chainstate

Testing Information

  • updated unit tests to work with new block events
  • added integration tests - currently not integrated into Makefile make integration-test

Checklist:

  • I have performed a self-review of my code
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@Jiloc Jiloc added this to the sBTC: Withdrawal Code Complete milestone Feb 21, 2025
@Jiloc Jiloc self-assigned this Feb 21, 2025
@Jiloc Jiloc changed the base branch from feat/emily-client-python to feat/emily-new-block-handler February 23, 2025 14:45
@@ -30,7 +30,7 @@
"Tuple": {
"data_map": {
"request-id": {
"UInt": 2
"UInt": 1
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was needed to make the test_withdrawal_reject integration tests. Where a withdrawal can be rejected only if a connected withdrawal already exists

Copy link
Collaborator

Choose a reason for hiding this comment

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

Changing the test is fine, but what do we do if Emily does not have a create-withdrawal event in the database and it receives either an accept or a reject? Shouldn't it just store the event?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

At the moment, Emily returns an error when the update_withdrawal is called, and the sidecar retries indefinitely. Emily cannot store the event because withdrawals are stored as objects within a list in the corresponding Withdrawal entry in DynamoDB. If the Withdrawal entry doesn’t exist, we have no place to store its related update.

By design, this situation shouldn't happen—an update should never arrive before the withdrawal itself unless the entry was forcefully removed from the DB. Ideally, we could create the withdrawal entry when receiving an update, but the event doesn't have enough information.

I was considering a follow-up PR to introduce a new query for withdrawal lookups by [req_id, txid] (which isn't currently there). I would modify both handle_withdrawal_accept and handle_withdrawal_reject to check if the corresponding withdrawal exists before proceeding. If it doesn’t, they would return an error to the new_block handler, which would log the issue and skip processing the request, preventing unnecessary retries and potential stalls. (Same behavior as in handle_completed_deposits )

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmmm, I think that we can prevent it from happening. The three situations that came to mind are:

  1. Some bug causes the code to bail before writing to the database.
  2. Some error in AWS prevents writing to the database.
  3. The side-car being down and missing the event initiate-withdrawal-request event.

They all seem unlikely for one reason or another, but I was wondering if there is some way that we can handle this more gracefully. One potential alternative to the current design is to write all events in some sort of history table. We would then potentially recreate the current state by looking at the relevant events at request time. I'm not sure how to handle the fact that the update event doesn't have enough data, although it seems better than missing even more data by dropping the event.

This kind of design would work for both deposits and withdrawals, and gets us out of having to delete or miss data, which is would save us from some headaches. The migration from our current design to the new one might be annoying but we could have both systems in place while we iron out some potential issues. What do you think?

@Jiloc Jiloc changed the title [chore] update sidecar to FastAPI [chore] sidecar send new block updates Feb 24, 2025
Copy link
Collaborator

@djordon djordon left a comment

Choose a reason for hiding this comment

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

Nice, this largely looks good.

Comment on lines +1 to +2
fastapi==0.115.8
uvicorn==0.34.0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice, this is the way

@@ -30,7 +30,7 @@
"Tuple": {
"data_map": {
"request-id": {
"UInt": 2
"UInt": 1
Copy link
Collaborator

Choose a reason for hiding this comment

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

Changing the test is fine, but what do we do if Emily does not have a create-withdrawal event in the database and it receives either an accept or a reject? Shouldn't it just store the event?

EXPOSE 20540
ENV EMILY_API_KEY=testApiKey
ENV EMILY_CHAINSTATE_URL=http://emily-server:3031/chainstate
ENV DEPLOYER_ADDRESS=SN3R84XZYA63QS28932XQF3G1J8R9PC3W76P9CSQS
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ahh we don't need the deployer in the side-car since filtering is getting done in Emily itself.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

exactly, now the sidecar does the bare minimum

Comment on lines +25 to +27
class NewBlockEventModel(BaseModel, extra="allow"):
block_height: int
index_block_hash: str
Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh nice, that extra field is critical because we expect more fields to be sent in event from the stacks node but pydantic ignores them by default. We should probably add a comment above here describing why it's necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

addressed in 658aa45


@app.get("/")
def read_root() -> dict:
return {"message": "Hello, World!"}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should probably just return 200 Ok here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

changed in 658aa45

github-merge-queue bot pushed a commit that referenced this pull request Feb 26, 2025
## Description

Part of #1067

Following #1409, the deposits and withdrawal updates will be sent to
Emily only from the sidecar. In this PR we remove all calls to Emily
from the new-block handler, that will now be only used to store the
events in the DB.

Currently, the only update that the signers send to Emily is the Deposit
Accepted. We should probably consider whether we want to remove it as
well.

 
## Changes
- Remove Emily calls from signer's new_block 
- Adapt sbtc event handlers to only store the data and log the event.

## Testing Information
- Simplify new block unit tests
- Remove the stacks_events_observer integration tests

## Checklist:

- [x] I have performed a self-review of my code
- [x] My changes generate no new warnings
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream
modules
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In Review
Development

Successfully merging this pull request may close these issues.

2 participants