Skip to content

Mempool block sort bug #3566

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

Closed
softsimon opened this issue Mar 24, 2023 · 5 comments · Fixed by #3673
Closed

Mempool block sort bug #3566

softsimon opened this issue Mar 24, 2023 · 5 comments · Fixed by #3673
Assignees
Labels
bug Something isn't working

Comments

@softsimon
Copy link
Member

softsimon commented Mar 24, 2023

I click on a TX in block 3, but it shows tracking in block 2.

Inspecting the Websocket response, it looks like the order of the feeRanges is not correct.

Screen.Recording.2023-03-25.at.00.14.17.mov

Screenshot 2023-03-25 at 00 12 52

@softsimon softsimon added the bug Something isn't working label Mar 24, 2023
@mononaut
Copy link
Collaborator

I think those fee ranges are actually accurate, and the problem is that it's not always possible to know which block a transaction belongs to from its fee rate alone, because transaction selection also depends on size (and to some extent randomness).

Consider an extreme example:

Imagine the mempool consists only of many thousands of huge 80,000vb transactions paying exactly 50 sats/vb, plus thousands of small 100vb transactions each paying 1 sat/vb.

Each projected block can fit 12 of the large, high-rate transactions, leaving 1,000,000 - (12 x 80,000) = 40,000 vbytes free.

That free space can only be filled by 400 of the small 100vb, 1 s/vb transactions.

So the first projected block's feeRange looks like [1, 1, 1, 50]. But so does the second, third, fourth block etc, and so their feeRanges all overlap.

That means if we're given only the fee rate of a random transaction, there's no way to tell which block it "belongs" to.

I'm not sure what the best solution is.

We do have the actual positions of transactions in each projected block available in the backend, so perhaps we should just send that info over the websocket when you subscribe to an unconfirmed transaction?

@softsimon
Copy link
Member Author

softsimon commented Apr 1, 2023

Ok. So that is also the cause of this thing when the fee range doesn't look sorted by fee and you get a sudden high fee data point like here?

I just don't seem to understand why it's not sorted by sats/vB / effective fee and we see 16 - 17 and 16- 17 in 2 blocks instead of 17-17 and 16-16. I guess a lower fee rate transaction that is bigger, can be more profitable than a few smaller high fee txs?

Screenshot 2023-03-15 at 11 25 49

@nymkappa
Copy link
Member

nymkappa commented Apr 1, 2023

We do have the actual positions of transactions in each projected block available in the backend, so perhaps we should just send that info over the websocket when you subscribe to an unconfirmed transaction?

Never really worked in that code but I would have thought it was already the case. Are you saying the backend and the frontend have different code to compute the position in the mempool?

@mononaut
Copy link
Collaborator

mononaut commented Apr 1, 2023

Ok. So that is also the cause of this thing when the fee range doesn't look sorted by fee and you get a sudden high fee data point like here?

I think the weird fee gradients are caused by something more complicated, but yes this probably contributes to it.

I just don't seem to understand why it's not sorted by sats/vB / effective fee and we see 16 - 17 and 16- 17 in 2 blocks instead of 17-17 and 16-16. I guess a lower fee rate transaction that is bigger, can be more profitable than a few smaller high fee txs?

More that a few smaller low fee transactions are more profitable than nothing.

Like in my example earlier, miners will try to fit as many large, high fee rate transactions as possible. But when they run out of space in a block for those large transactions, they'll fill up the rest with smaller lower rate transactions rather than leave any empty space.

When that happens, the lowest fee rate in the first projected block can be less than the highest fee rate in the second projected block etc.

@mononaut
Copy link
Collaborator

mononaut commented Apr 1, 2023

Never really worked in that code but I would have thought it was already the case. Are you saying the backend and the frontend have different code to compute the position in the mempool?

Well, the backend doesn't explicitly compute mempool positions, but the result of the advanced gbt algorithm is an ordered list of transactions for each of the first 8 mempool blocks.

We don't send those full lists of transactions to the frontend (it would be a lot of data), so instead we estimate mempool positions there by searching for the first mempool block feeRange interval that spans the transaction's effective fee rate:

let found = false;
for (let txInBlockIndex = 0; txInBlockIndex < this.mempoolBlocks.length && !found; txInBlockIndex++) {
const block = this.mempoolBlocks[txInBlockIndex];
for (let i = 0; i < block.feeRange.length - 1 && !found; i++) {
if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
const feeRangeIndex = i;
const feeRangeChunkSize = 1 / (block.feeRange.length - 1);
const txFee = this.txFeePerVSize - block.feeRange[i];
const max = block.feeRange[i + 1] - block.feeRange[i];
const blockLocation = txFee / max;
const chunkPositionOffset = blockLocation * feeRangeChunkSize;
const feePosition = feeRangeChunkSize * feeRangeIndex + chunkPositionOffset;
const blockedFilledPercentage = (block.blockVSize > this.stateService.blockVSize ? this.stateService.blockVSize : block.blockVSize) / this.stateService.blockVSize;
const arrowRightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding)
+ ((1 - feePosition) * blockedFilledPercentage * this.blockWidth);
this.rightPosition = arrowRightPosition;
found = true;
}
}
if (this.txFeePerVSize >= block.feeRange[block.feeRange.length - 1]) {
this.rightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding);
found = true;
}
}
}

When we used the old getSimonTemplate algorithm the two methods were ~equivalent because transactions in projected blocks were always strictly sorted by effective fee rate, but that's no longer true with advanced GBT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy