-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Durable nonce transactions resulting in an instruction error allow fee theft #7443
Comments
How does advancing the Nonce outside the transaction work? Unless the Nonce is advanced in the same transaction as the Nonced operation, the Nonced operation can be replayed. I think moving the Nonce into the system program should be considered in any case. We might even be able to avoid requiring a Nonce instruction, and instead say that "any time a Nonce is used as the fee account, the Nonce must advance". |
we would also be able to do away with the nonce's authority field... |
Runtime will do the updating based on
I think you're right. Are you thinking all system accounts will carry nonce data, or that we'll introduce a new species of system account? |
new species of system account |
@rob-solana @t-nelson why not just let it be. The transaction should succeed, and the user can guard against failure by using a low balance system account. |
I'd rather not leave a footgun if at all possible. Have we received any feedback from partners regarding this issue and whether it's a concern? |
the main user is currently planning to have a non-trivial balance in the fee account |
Is the proposal basically: I guess I don’t hate it. Let’s do it. |
You got it! |
@t-nelson remember that transfers out of a system account (including paying the fee) with a Nonce need to have that "ensure cannot be re-created" check. The account has to stay rent exempt. I think this is the only messy thing about pushing Nonce facilities into system_program, but at least all those checks end up in the same couple of files. |
@rob-solana 10-4, they'll die with I'm less clear on the least intrusive way to discern between system account species. Could go with flags or use the owner ID and tweak the system program checks. wdyt? |
what's wrong with testing whether the data is present and an initialized Nonce? transfer() (and other lamport movement) would need to verify that the Nonce is empty (data.len() == 0 || deserialize(data) == Some(UninitializedNonce)). moving funds out of a Nonce has to go through withdraw(), which is presented with sysvar::RecentBlockhashes (right?) |
I think testing the data is fine. Since system controls this thing. One thing to add. System::Assign can only work on 0 data. Prior to assign we need to ensure the data is still 0, not a nonce account. |
I suppose there's nothing wrong with it. I just wasn't thinking of deserialization succeeding as much of a guaranty. This seems reasonable so long as we don't go and pull the same trick with the same data layout later. So we're looking at
|
@t-nelson only system will ever write to it. You can check if the first word in data is non-zero and deserialize. Just make sure assign checks that that it’s zero, otherwise we have an attack vector to leak state into programs :) |
@rob-solana on my way to an implementation of this, I've run into some rent-related tests with fee-paying accounts carrying non-zero-sized, but small and unused, data segments. Since we charge rent on zero-data accounts as of #6888, can these accounts safely be zero-data now? Alternative being to add a separate fee-payer account, which will require plumbing through a few of the transaction constructors. |
@t-nelson those test accounts can safely be made zero-data |
Problem
Solana typically charges fees on transactions which succeed or fail with an
InstructionError
. In the event of the latter, the state of every modified account is also rolled back to before execution. Since a durable nonce transaction advances the stored nonce with an instruction, if the transaction fails with anInstructionError
, the old stored nonce is replaced. Due to durable nonces having arbitrary lifetimes, it is infeasible to maintain a signature blacklist against each nonce value, ala.StatusCache
. As such, these failed transactions can be replayed and fees charged until the stored nonce value is successfully advanced.Proposed Solution
The stored nonce MUST advance whenever the account state or balance changes, including paying fees. This can be achieved by always storing the updated nonce accounts.
Alternatively, the stored nonce can be advanced outside the program. If this path is chosen, moving the durable nonce feature into system program should be considered.
cc/ @rob-solana
The text was updated successfully, but these errors were encountered: