Skip to content

Fix for issue with fee calculation and cbor overhead for complex utxo… #2603

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

neil-iohk
Copy link
Contributor

I have tried address two fundamental issues here, the first is the decoding of the cbor transactions witness and encoding again, while the size should remain constant there is a possibility that additional data could be introduced, the data should only be encoded and never decoded, keep a copy if needed. this is addressed in the submit transaction which I merged to a single function for simplicity, can't see a reason for it being 2 ..

the second is the fee calculation:
the code is currently building a “draft” transaction with zero fee, stubbed in the correct number of fake signatures, serialised to get body+stubs size, then computed:

baseFee = a * draftSize + b

then adds change output by doing:

changeFee = a * sizeOf(changeOutput)

combines the fees*

totalFee = baseFee + changeFee

But it never ran the full a * txSize + b over the larger transaction.

For complex transactions which may have numerous outputs / inputs adding additional data also introduces extra bytes for the CBOR map/array the difference might be tiny but compounds over a couple of areas to be sufficient enough to make the transaction fail.

What we were seeing:

transactions with no change were no problem they succeeded, when change was introduced the calulation was slightly too smal and did not rerun the calculation to include the cbor overhead of the extra outputs.

Where there was change but just a single utxo output these also seemed to work but anywhere there was multiple UTXOs and change it would fail.

There are a couple of options the one I drafted introduces a slight buffer depending on the number of inputs, the greater the inputs the more buffer required.

We need to decide which is best, a more precise fee calculation with the potential of some failed transactions or introducing a slight buffer when there are more inputs we may be unable to be precise with the calculation

Example response from wallet...

Full JS error JSON: {"code":2,"info":"BAD_REQUEST ({\"error\":\"Bad Request\",\"message\":\"{\\\"contents\\\":{\\\"contents\\\":{\\\"contents\\\":{\\\"era\\\":\\\"ShelleyBasedEraConway\\\",\\\"error\\\":[\\\"ConwayUtxowFailure (UtxoFailure (FeeTooSmallUTxO (Mismatch {mismatchSupplied = Coin 194805, mismatchExpected = Coin 194937})))\\\"],\\\"kind\\\":\\\"ShelleyTxValidationError\\\"},\\\"tag\\\":\\\"TxValidationErrorInCardanoMode\\\"},\\\"tag\\\":\\\"TxCmdTxSubmitValidationError\\\"},\\\"tag\\\":\\\"TxSubmitFail\\\"}\",\"status_code\":400}) due to\n BlockfrostError: Blockfrost error with status '400': {\"error\":\"Bad Request\",\"message\":\"{\\\"contents\\\":{\\\"contents\\\":{\\\"contents\\\":{\\\"era\\\":\\\"ShelleyBasedEraConway\\\",\\\"error\\\":[\\\"ConwayUtxowFailure (UtxoFailure (FeeTooSmallUTxO (Mismatch {mismatchSupplied = Coin 194805, mismatchExpected = Coin 194937})))\\\"],\\\"kind\\\":\\\"ShelleyTxValidationError\\\"},\\\"tag\\\":\\\"TxValidationErrorInCardanoMode\\\"},\\\"tag\\\":\\\"TxCmdTxSubmitValidationError\\\"},\\\"tag\\\":\\\"TxSubmitFail\\\"}\",\"status_code\":400}","name":"TxSendError","message":"","stack":"TxSendError\n    at Object.submitTx (chrome-extension://gafhhkghbfjjkeiendhlofajokpaflmk/sw/171.chunk.js:7:70423)\n    at async chrome-extension://gafhhkghbfjjkeiendhlofajokpaflmk/sw/171.chunk.js:7:124552"}

… inputs and optimised selection, also include removal of cbor decode of transaction
@nathanbogale
Copy link
Contributor

nathanbogale commented May 26, 2025

You should put into account the CBOR headers that gets added when modifying the output, or the transaction might be underpaid by that much (the additional CBOR bytes)

ex:
draft transaction (No Change): Size = 200 bytes → Fee = 200a + b
lets say we made a changeon output (add): Raw output = 30 bytes
expected final size: 200 + 30 = 230 → Fee = 230a + b
but actual final size: 235 bytes (due to 5 bytes of CBOR overhead)
fee paid: 230a + b
fee required: 235a + b → UNDERPAID

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: New
Development

Successfully merging this pull request may close these issues.

2 participants