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

tx: consolidate generic tx capabilities #2993

Merged
merged 6 commits into from
Aug 30, 2023

Conversation

gabrocheleau
Copy link
Contributor

This PR resolves #2987

@gabrocheleau gabrocheleau force-pushed the tx/consolidate-functionalities branch from 414afdc to dd5c76f Compare August 30, 2023 14:41
@codecov
Copy link

codecov bot commented Aug 30, 2023

Codecov Report

Merging #2993 (339dc1f) into master (d6d9391) will decrease coverage by 0.08%.
The diff coverage is 95.53%.

Additional details and impacted files

Impacted file tree graph

Flag Coverage Δ
block 88.73% <ø> (ø)
blockchain 92.58% <ø> (ø)
client 87.35% <ø> (-0.01%) ⬇️
common 98.18% <ø> (ø)
ethash ∅ <ø> (∅)
evm 70.43% <ø> (ø)
rlp ∅ <ø> (∅)
statemanager 84.46% <ø> (ø)
trie 89.82% <ø> (-0.58%) ⬇️
tx 96.38% <95.53%> (+0.41%) ⬆️
util ?
vm 79.20% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

@gabrocheleau gabrocheleau marked this pull request as ready for review August 30, 2023 17:23
@acolytec3
Copy link
Contributor

General question on this PR. What is the advantage of declaring these helpers as
export function serialize(this: TypedTransactionEIP2930): Uint8Array and then using
return EIP2930.serialize.bind(this)() inside each Transaction subclass over just declaring the functions as generic pure functions like:
export function serialize(tx: TypedTransactionEIP2930): Uint8Array
and then calling them with
return EIP2930.serialize(this)

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.

Copy link
Contributor

@acolytec3 acolytec3 left a 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'
Copy link
Contributor

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!

Copy link
Contributor Author

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!

@acolytec3 acolytec3 merged commit f5c6769 into master Aug 30, 2023
@holgerd77 holgerd77 deleted the tx/consolidate-functionalities branch August 30, 2023 20:26
@holgerd77
Copy link
Member

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 util.ts and - while it would likely make sense to align this - we likely want/need to skip for now due to backwards compatibility reasons).

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.

Copy link
Member

@holgerd77 holgerd77 left a 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]
Copy link
Member

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)

Copy link
Contributor Author

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...

Copy link
Contributor Author

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
}
Copy link
Member

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
}
Copy link
Member

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 (?).

Copy link
Member

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))
}
Copy link
Member

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())
}
Copy link
Member

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
}
}
Copy link
Member

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())
}
Copy link
Member

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)
}
}
Copy link
Member

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)
}
}
Copy link
Member

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)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

-> legacy.ts

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

Successfully merging this pull request may close these issues.

Tx: Consolidate generic Functionality in Shared Static Helpers
3 participants