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

feat: Transfer event removal #701

Closed
wants to merge 124 commits into from
Closed

Conversation

Artemka374
Copy link
Contributor

@Artemka374 Artemka374 commented Dec 18, 2023

What ❔

Divide web3 server API into 2:

  • Old one
  • Test-drive API, which won't return ETH Transfer events

API that will be run per instance is defined via config:

  • API_WEB3_JSON_RPC_ETH_TRANSFER_EVENTS(disabled/enabled) modes
  • EN_ETH_TRANSFER_EVENTS(disabled/enabled) modes

Added new columns to events database to reassign indexes of logs correctly(added SQL migrations and Rust migrations, mostly inspired by #688):

  • event_index_in_block_without_eth_transfer
  • event_index_in_tx_without_eth_transfer

Why ❔

To be EVM compatible.

Checklist

  • PR title corresponds to the body of PR (we generate changelog entries from PRs).
  • Tests for the changes have been added / updated.
  • Documentation comments have been added / updated.
  • Code has been formatted via zk fmt and zk lint.
  • Spellcheck has been run via cargo spellcheck --cfg=./spellcheck/era.cfg --code 1.

@Artemka374 Artemka374 marked this pull request as ready for review December 18, 2023 17:53
@Artemka374
Copy link
Contributor Author

Artemka374 commented Dec 18, 2023

Do we need to fully ignore Transfer events? e.g. they appear and impact log indexes, is it okay to be this way or it is better to somehow change this behaviour?

@Artemka374 Artemka374 changed the title chore: Transfer event removal[WIP] chore: Transfer event removal Dec 19, 2023
# Conflicts:
#	core/lib/dal/sqlx-data.json
#	core/lib/dal/src/events_web3_dal.rs
#	core/lib/dal/src/transactions_web3_dal.rs
# Conflicts:
#	core/lib/dal/src/models/storage_event.rs
#	core/lib/zksync_core/src/api_server/web3/mod.rs
#	core/lib/zksync_core/src/api_server/web3/pubsub.rs
#	core/lib/zksync_core/src/api_server/web3/tests/mod.rs
#	core/lib/zksync_core/src/api_server/web3/tests/snapshots.rs
#	core/lib/zksync_core/src/api_server/web3/tests/ws.rs
#	core/lib/zksync_core/src/state_keeper/io/tests/mod.rs
#	core/lib/zksync_core/src/sync_layer/tests.rs
#	prover/Cargo.lock
#	yarn.lock
@Artemka374 Artemka374 requested a review from slowli January 23, 2024 08:01
@slowli
Copy link
Contributor

slowli commented Jan 23, 2024

A really late question, sorry for not raising it before. What if the event filtering is controlled via an additional field in Filter (used in eth_newFilter and eth_getLogs) / PubSubFilter (used in subscriptions) with a default value? In this case, a single API server could serve both new and legacy API (we may need to spin up 2 notifier tasks for logs instead of one; one with filtering and the other without it), and filtering would be controlled by the client. Would this contradict some other requirements / not work / require too much effort to integrate into Web3 clients? (I'd guess they should have interceptor functionality that would allow doing that, but I don't have much experience using Web3 clients.)

cc @RomanBrodetski @montekki @popzxc

core/lib/dal/src/events_web3_dal.rs Outdated Show resolved Hide resolved
core/lib/dal/src/models/storage_event.rs Outdated Show resolved Hide resolved
core/lib/zksync_core/src/api_server/web3/pubsub.rs Outdated Show resolved Hide resolved
}

/// Method checks, whether any event in the miniblock has `index_without_eth_transfer` equal to NULL, which means that indexes are not migrated.
pub async fn are_event_indexes_migrated(
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it strictly necessary to check all miniblocks in the range? Note that in #688, it's sufficient to check only the starting miniblock of the range provided that ranges are guaranteed to be identical across node restarts. Would a similar assumption not apply here? AFAIU, checking the entire range would put significantly more load on the DB if the range is reasonably large.

core/lib/dal/src/events_dal.rs Show resolved Hide resolved
@slowli slowli dismissed their stale review January 23, 2024 14:14

Looks much better than initially

@popzxc
Copy link
Member

popzxc commented Jan 24, 2024

What if the event filtering is controlled via an additional field in Filter (used in eth_newFilter and eth_getLogs) / PubSubFilter (used in subscriptions) with a default value?

@slowli I believe most people use SDKs (ethers and such) for that, and in most cases modifying the logic of interacting with filters is hidden. Probably it won't work.

@slowli
Copy link
Contributor

slowli commented Jan 24, 2024

@popzxc For ethers in particular, the interceptor abstraction I've talked about is encapsulated in the Provider interface. Specifically, BaseProvider (a base for other providers) defines _getFilter, which AFAIU maps all event filters processed by the provider (this is in ethers v5 used in integration tests; in ethers v6, it's structured similarly). So it seems possible to do something like this:

interface FilterWithEthTransfers extends Filter {
    filterEthTransfers?: boolean;
}

class CustomizableProvider extends zksync.Provider {
    private filterEthTransfers: boolean;

    override async _getFilter(filter: Filter | /* ... */): Promise<FilterWithEthTransfers | /* ... */> {
        const resolvedFilter = <FilterWithEthTransfers>await super._getFilter(filter);
        resolvedFilter.filterEthTransfers = this.filterEthTransfers; // passed to JSON-RPC call
        return resolvedFilter;
    }
}

IMO, controlling filtering on the client side makes the approach more maintainable and easier to reason about, both for us and clients. OTOH, I may be unaware of complexities associated with modifying and maintaining client code.

@popzxc
Copy link
Member

popzxc commented Jan 25, 2024

@slowli I think that if anything more complex than just "swap URL" would be required, people would just not bother. Though I may be wrong, cc @RomanBrodetski

Copy link
Contributor

@StanislavBreadless StanislavBreadless left a comment

Choose a reason for hiding this comment

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

Generally LGTM, I left a few nits

infrastructure/zk/src/env.ts Outdated Show resolved Hide resolved
export async function extractFee(receipt: zksync.types.TransactionReceipt, wallet: zksync.Wallet): Promise<Fee> {
const trace = await wallet!.provider.send('debug_traceTransaction', [receipt.transactionHash]);

if (!trace) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we need this check? Having the extractFee as async function is not bad, but most of the diff in tests comes from it, so just curious

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With this method we are checking whether all the balances were updated correctly(along with fees) and since in this new mode we can't just ask for receipt for native transfers - this is the only way to get fee

core/lib/dal/src/models/storage_event.rs Outdated Show resolved Hide resolved
# Conflicts:
#	.github/workflows/ci-core-reusable.yml
#	core/lib/zksync_core/src/api_server/web3/tests/mod.rs
ALTER TABLE events
ADD COLUMN IF NOT EXISTS event_index_in_block_without_eth_transfer INT DEFAULT NULL;
ALTER TABLE events
ADD COLUMN IF NOT EXISTS event_index_in_tx_without_eth_transfer INT DEFAULT NULL;
Copy link
Collaborator

Choose a reason for hiding this comment

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

According to quick research

In Postgres 10 or older (such as OP's 9.6), any DEFAULT clause even DEFAULT NULL will require a full rewrite of the table, which might take essentially forever for large tables[1].

so this is not safe. We can try it on stage2 (although please copy a subset of table first, don't amend eevents table there) , but I think it won't work.

We may find a better migration approach (just remove DEFAULT?) but even then we might negatively impact insert time to this table which will result in lower TPS. Is there an alternative solution?

Copy link
Member

Choose a reason for hiding this comment

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

We don't use postgres 10 or older, don't we? I thought we're at 14 or at least 12?

Copy link
Member

Choose a reason for hiding this comment

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

E.g. from the same response (I guess) below:

This was changed with Postgres 11, Vao Tsun's answer is only correct for these, per the Postgres 11 documentation:
When a column is added with ADD COLUMN and a non-volatile DEFAULT is specified, the default is evaluated at the time of the statement and the result stored in the table's metadata. That value will be used for the column for all existing rows. If no DEFAULT is specified, NULL is used. In neither case is a rewrite of the table required.
So as long as DEFAULT is a static value or a non-volatile expression the table will not need to be rewritten.

Copy link
Collaborator

Choose a reason for hiding this comment

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

oh sorry I misread it, you are right. Still wanted to ask if anyone sees an alternative solution so that we don't need to change schema, store more data per event and migrate that data as well

# Conflicts:
#	core/bin/external_node/src/main.rs
#	core/lib/dal/src/events_dal.rs
#	core/lib/zksync_core/src/api_server/web3/tests/mod.rs
#	core/lib/zksync_core/src/state_keeper/io/tests/mod.rs
#	core/lib/zksync_core/src/sync_layer/tests.rs
Copy link
Contributor

No performance difference detected (anymore)

@popzxc
Copy link
Member

popzxc commented Mar 21, 2024

@Artemka374 is this PR still relevant or can it be closed?

@Artemka374
Copy link
Contributor Author

I don't know to be honest. The discussion about it was brought up again not that long ago, but I don't remember any decision was made. Maybe @RomanBrodetski or @StanislavBreadless can say more?

@StanislavBreadless
Copy link
Contributor

I believe it will be needed in the long term anyway, so better to finish it up and get into production

@slowli slowli mentioned this pull request Aug 12, 2024
4 tasks
@perekopskiy perekopskiy closed this Oct 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants