-
Notifications
You must be signed in to change notification settings - Fork 791
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
tx: consolidate generic tx capabilities #2993
Conversation
414afdc
to
dd5c76f
Compare
Codecov Report
Additional details and impacted files
Flags with carried forward coverage won't be shown. Click here to find out more. |
General question on this PR. What is the advantage of declaring these helpers as If there's some SO thread of something that explains the motivation for this, I'd be happy to read if that's easier to do. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great!
import { SECP256K1_ORDER_DIV_2, bigIntToUnpaddedBytes, ecrecover } from '@ethereumjs/util' | ||
import { keccak256 } from 'ethereum-cryptography/keccak.js' | ||
|
||
import { Capability, type TypedTransaction } from '../types.js' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I didn't know you could import a type inside a general import statement like this. Neat!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found that out recently too!
Super great work, thanks a lot! 🤩 I would really like to put an emphasis here that we get the associations right, so to expand: that we a) name the capability files correctly and b) associate/integrate the correct methods, so that this whole things directly structure things in a right way along, I have some remarks on this, will do along the code. The most basic specific thing here: EIP-2930 is really only this specific (very much unused) transaction type with access lists. So this should - if including anything - only include access list specific functionality (which we have already extracted in a similar way in former work in All the generic tx type specific functionality has been introduced in EIP-2718 and I would therefore strongly lean towards also labeling the capability file this way. Your local transaction type at the beginning of the file is actually the very definition of EIP-2718 typed transactions 🙂: type TypedTransactionEIP2930 = Exclude<TypedTransaction, Transaction[TransactionType.Legacy]> Will do some more comments on the files. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, gone through the list of capability methods with various comments.
|
||
type TypedTransactionEIP1559 = | ||
| Transaction[TransactionType.BlobEIP4844] | ||
| Transaction[TransactionType.FeeMarketEIP1559] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it generally possible to type in a way that these methods can also be called by custom transaction type implementations? 🤔 So without the need to predefine all the existing tx types which come into question atm here?
Also coming to surface here again: we really really need to be super careful to make the distinction clear between "EIP-1559 -> the specific transaction" and "Capabilities derived from EIP-1559".
If we do not make this very clear things will get blurry over time and mixed up all the time I fear. In this context the naming above "TypedTransactionEIP1559" is too close to the wrong association thing and we should rename these throughout the files for clarity.
Just need to think right now myself: hmm. I would find EIP1559CompatibleTx
already better. 🤔 Not sure if it is the last word of wisdom here though.
(maybe this whole discussion interplays with an eventually possible re-typing though)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll think about this a bit more about how to architect this. You're right that these "capabilities" are currently tightly paired with "transaction types", whereas they shouldn't. I'm currently wondering if we make use of the existing tx Capability enum. Will reflect a bit more and follow up...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's what I feel is a more sound approach: #3010, curious to hear your thoughts!
const inclusionFeePerGas = prio < maxBase ? prio : maxBase | ||
const gasPrice = inclusionFeePerGas + baseFee | ||
return tx.gasLimit * gasPrice + tx.value | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I will just go through the methods one-by-one. Think this initial structure is really crucial so that things can be build cleanly on top of this work.
So: getUpfrontCost()
should be ok here.
} | ||
|
||
return cost | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would cautiously say this one is actually well placed in a eip2930.ts
capability file (?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I would - also cautiously - say that the "other" version of getDataFee()
would actually be a candidate for a legacy.ts
capability file?
Also to re-iterate here: so legacy.ts
also would include capabilities which were introduced by the legacy transaction and would therefore be wider than the specific legacy transaction itself.
Since also typed transactions have just inherited specific functionality from the legacy transaction, it would very much be ok if typed transactions call into the legacy.ts
capability file (same logic as an EIP4844 tx calls into the EIP1559 capability file).
Distinction between generic.ts
and legacy.ts
would just be that legacy.ts
only includes functionality (like getUpfrontCost()
) which then changed along the introduction of a new transaction type.
When thinking about it: when staying in this logic it might be even more consistent to skip generic.ts
alltogether and just do legacy.ts
and move everything being now in generic.ts over there. These is even more consistent with this capabilities-thinking.
const base = tx.raw() | ||
const txTypeBytes = hexToBytes('0x' + tx.type.toString(16).padStart(2, '0')) | ||
return concatBytes(txTypeBytes, RLP.encode(base)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, so this is definitely an EIP-2718 thing.
|
||
export function getHashedMessageToSign(tx: TypedTransactionEIP2930): Uint8Array { | ||
return keccak256(tx.getMessageToSign()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> EIP-2718.
(just to mention: there is also a legacy version of this)
} else { | ||
return true | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, guess ok, respectively: -> legacy.ts
} | ||
|
||
return keccak256(tx.serialize()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I see correctly this is actually also an EIP-2718 thing, so this specific implementation (sorry: I suggested this placing wrongly initially)
) | ||
throw new Error(msg) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> legacy.ts
(I think I am getting more and more a fan of the idea to remove generic.ts while going through here)
const msg = errorMsg(tx, 'The y-parity of the transaction should either be 0 or 1') | ||
throw new Error(msg) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually an EIP-2718 thing and definitely not generic.
const msg = errorMsg(tx, 'Invalid Signature') | ||
throw new Error(msg) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> legacy.ts
This PR resolves #2987