-
Notifications
You must be signed in to change notification settings - Fork 773
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
Block, Tx: Caching for hash method #1445
Conversation
@@ -232,6 +235,9 @@ export default class Transaction extends BaseTransaction<Transaction> { | |||
* Use {@link Transaction.getMessageToSign} to get a tx hash for the purpose of signing. | |||
*/ | |||
hash(): Buffer { |
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.
@holgerd77 One thing I noticed while working on this... The comment for the hash
method here says it should throw if not signed (https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/tx/src/legacyTransaction.ts#L231)... but it's not actually checking?
Contrast with the other transaction types, which both have an explicit check:
https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/tx/src/eip2930Transaction.ts#L299
https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/tx/src/eip1559Transaction.ts#L329
It's definitely possible I'm not understanding fully, but wanted to surface the question in case it is missing the isSigned
check here.
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.
good catch, seems like it should be added to legacy.
Codecov Report
Flags with carried forward coverage won't be shown. Click here to find out more. |
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.
Hi there, one bigger structural change request.
Additionally: can you please also add a comment to the freeze
option comment for tx and blocks on the caching? Something like "...parameters. It also enables tx hash caching when the hash()
function is called several times." (feel free to improve on this). 😄
@@ -228,6 +230,9 @@ export default class FeeMarketEIP1559Transaction extends BaseTransaction<FeeMark | |||
|
|||
const freeze = opts?.freeze ?? true | |||
if (freeze) { | |||
if (this.isSigned()) { | |||
this._cachedHash = this.hash() | |||
} |
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.
Hi there, thanks for the PR! 😄
The cache creation for the hash shouldn't be done in the constructor but in the hash()
function itself on the first execution (same for Block
). Otherwise we are actually adding overhead for use cases where thehash()
function is not needed.
If your problem is that the object actually will get frozen after the constructor is called:
I did some experiments on this and I guess it should work to wrap the _cachedHash
property into some object, which will then still be modifiable. See the following results:
> const inner = { hash: '123' }
'use strict'
> const outer = { inner, test: 5 }
undefined
> outer.test = 4
4
> outer
{ inner: { hash: '123' }, test: 4 }
> Object.freeze(outer)
{ inner: { hash: '123' }, test: 4 }
> outer.test = 3
3
> outer
{ inner: { hash: '123' }, test: 4 }
> outer.inner.hash = '345'
'345'
> outer
{ inner: { hash: '345' }, test: 4 }
So as one can see the inner
object hash
property can still be modified on a frozen object (maybe someone else should nevertheless have an additional look here to see if this has no side effects whatsoever. Can't think of any thow atm).
Maybe choose some structure like:
private cache = { hash: undefined }
Then we can eventually expand this with other stuff. 😄
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.
Sounds good - will give it a try
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.
@holgerd77 I have an implementation in packages/block/src/header.ts
based on your suggestion - tests are passing and I think it makes sense. Wanted to get your feedback on the approach, and if it looks good, I'll add it in tx
and add the documentation comments. Thanks!
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, that looks good, ready to proceed! 😄
packages/block/src/header.ts
Outdated
@@ -295,6 +297,7 @@ export class BlockHeader { | |||
|
|||
const freeze = options?.freeze ?? true | |||
if (freeze) { | |||
this._cachedHash = this.hash() |
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.
-> to hash()
function (see tx comment).
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.
Hi there, one small change needed.
Can you also make sure that the test coverage is above the threshold? I guess this mainly means that - if we decide to keep this new throw-clause for the legacy tx, you would need to add one test within the legacy.spec.ts
file (there is an existing test on this in the typedTxsAndEIP2930.spec.ts
test file for reference).
packages/tx/src/legacyTransaction.ts
Outdated
@@ -232,6 +232,17 @@ export default class Transaction extends BaseTransaction<Transaction> { | |||
* Use {@link Transaction.getMessageToSign} to get a tx hash for the purpose of signing. | |||
*/ | |||
hash(): Buffer { | |||
if (!this.isSigned()) { | |||
throw new Error('Cannot call hash method if transaction is not signed') | |||
} |
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.
Maybe I am getting paranoid, but I am getting a bit shaky here and wondering if we did have some reason to not throw here yet and rather just add to the new typed txs for some weird backwards compatibility reason.
CC-ing @jochem-brouwer in here, was just seeing that you wrote a lot around the whole topic in your typed tx implementation #1048. Can you eventually remember something on this? (if not we should really do here and give this a final go)
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 do remember that I wanted to implement that logic first, but that broke things for some reason which is why we did not edit it. (It is a breaking change also, I guess?). I'll take a look this afternoon to figure out what it was.
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.
The thing that prompted me to add it to this PR was this comment - https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/tx/src/legacyTransaction.ts#L231. I noticed the typed txs had an explicit check but the legacy tx didn't, so I thought it might just have been missed. But maybe the comment was copied over and it wasn't meant to be implemented. Happy to take out that change if that's the case.
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.
@jochem-brouwer Ok, thanks for the clarification. I guess this is to risky then to add on the side line here, I've reverted the change respectively replaced with a longer comment so that no future devs will stumble upon this again.
Will also add a note to the breaking changes TODO so that we will not forget on next breaking tx release.
Nevermind, found the issue. Because we were always using the default tx options, the tx was always frozen, so we were always returning the cached value. Added some cases where we test unfrozen txs. |
…to eventual compatibility issues, added longer explanatory comment
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.
LGTM, great work Emmett! 😄
Addresses a couple items from #775, specifically "
Transaction#hash
should be cached" and "BlockHeader#hash
should be cached".