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

[VM] fix blockchain tests and add Homestead support #815

Merged
merged 5 commits into from
Aug 12, 2020

Conversation

jochem-brouwer
Copy link
Member

@jochem-brouwer jochem-brouwer commented Jul 15, 2020

Closes #813. Closes #824.

Note: I have put more forks in the PR CI file.

@codecov
Copy link

codecov bot commented Jul 15, 2020

Codecov Report

Merging #815 into master will decrease coverage by 0.31%.
The diff coverage is 58.62%.

Impacted file tree graph

Flag Coverage Δ
#account 92.85% <ø> (ø)
#block 79.11% <ø> (ø)
#blockchain 82.88% <ø> (ø)
#common 94.14% <ø> (ø)
#ethash 81.93% <0.00%> (+0.55%) ⬆️
#tx 94.02% <ø> (ø)
#vm 92.35% <60.71%> (-0.56%) ⬇️

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

@jochem-brouwer
Copy link
Member Author

jochem-brouwer commented Jul 15, 2020

Right this is bad.

TangerineWhistle tests are not passing at all. Byzantium+ seems to locally pass (haven't ran all tests though but it seems like those forks work).

TangerineWhistle:

1..3745
# tests 3745
# pass  2530
# fail  1215

SpuriousDragon:

1..3769
# tests 3769
# pass  2546
# fail  1223

My gut says it is because of Embedding Transaction Status in Receipt Trie (introduced in Byzantium). VM also has a note about this.

@jochem-brouwer
Copy link
Member Author

jochem-brouwer commented Jul 15, 2020

If I generate the state root and put that state root in the receipt then the tests pass for Spurious Dragon and for Tangerine Whistle 1 test fails. Failing test: OOGStateCopyContainingDeletedContract

We thus need a way to keep the checkpoint/revert/commit functionality of StateManager, but we also need to be able to generate the state root if we have not yet committed this. I can think of a rather dirty way to do this; we add a method to getStateRootTemp to StateManager which flushes the cache to the trie, but keeps the original values in memory. It thus temporarily commits the state to the trie, gets the state root and then reverts all changes. This is essentially a checkpointing mechanism on top of the checkpointing mechanism, it is dirty, but it will do the job. Thoughts?

t.comment("skipping test: no data available for " + options.forkConfigTestSuite)
return
}

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 too late in the process, false forks should be already filtered out when the test files are read (currently the code logic in ethereumjs-util) and there should be no output on this, this is otherwise causing too much unnecessary noise.

Will do a follow-up comment here: #808

Copy link
Member Author

Choose a reason for hiding this comment

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

I see where you are getting from, but this is not the case for state tests either: files are read and are then filtered out (this is thus not done in the ethereumjs-testing module, but actually in the state test runner which we define in the VM package).

So this is inconsistent. Do you think we should move this check to ethereumjs-testing? (Such that the runner never skips any tests?). I see in #808 you have moved all the testing logic in the VM package and I think this is a good idea; we can then move these fork checks to a more 'natural' place (i.e. the tests filter) and then let the runner only do anything if it actually has the right data to test.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, will see this as resolved for now.

@jochem-brouwer
Copy link
Member Author

jochem-brouwer commented Jul 30, 2020

Will push some commits soon which has #814 merged on top of it.

This changes getStateRoot() of StateManager to getStateManager(force: boolean), where force is a parameter which forces the cache to be flushed even if there are uncommited checkpoints. This is necessary to get the intermediate state roots pre-Byzantium. This has the side-effect that if there is an internal VM error which rolls back the block, the state trie (MPT) now has values in them which should not be there (as they do not seem to be rolled back if the cache is reverted). This might be an undesirable side-effect. Idea proposed by @s1na (thanks! 😄 ) after a discussion on discord.

@jochem-brouwer
Copy link
Member Author

jochem-brouwer commented Jul 30, 2020

npm run build && npm run test:blockchain -- --fork=TangerineWhistle
1..2568
# tests 2568
# pass  2568
npm run build && npm run test:blockchain -- --fork=SpuriousDragon
1..2583
# tests 2583
# pass  2583

@jochem-brouwer jochem-brouwer changed the title [VM] fix blockchain tests [VM] fix blockchain tests and add Homestead support Jul 31, 2020
@jochem-brouwer
Copy link
Member Author

jochem-brouwer commented Jul 31, 2020

Homestead:

1..4370
# tests 4370
# pass  4370

This does need the ethereumjs/ethereumjs-util#269 PR merged though, otherwise randomStatetest94 fails. Will push asap.

@jochem-brouwer jochem-brouwer mentioned this pull request Jul 31, 2020
5 tasks
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount')))
}

gasLimit = maxCallGas(
Copy link
Member Author

Choose a reason for hiding this comment

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

Note that maxCallGas is called after we have deducted all the call gas (this was not done before, callNewAccount could be deducted after maxCallGas). This was a difference from geth, so this might actually fix some consensus issues if we would do a mainnet sync (although it is very strange that no state test has found this!). The issue is thus that the forwarded gas between geth/EthereumJS might be different.

@jochem-brouwer
Copy link
Member Author

Blockchain tests, local:

Homestead
1..4370
# tests 4370
# pass  4370

TangerineWhistle
1..2562
# tests 2562
# pass  2562

SpuriousDragon
1..2577
# tests 2577
# pass  2577

Byzantium
1..9958
# tests 9958
# pass  9958

Constantinople
1..21554
# tests 21554
# pass  21554

Petersburg
1..21544
# tests 21544
# pass  21544

Istanbul
1..23665
# tests 23665
# pass  23665

MuirGlacier
1..23665
# tests 23665
# pass  23665

@holgerd77
Copy link
Member

What is missing on this on this PR? Can this be updated easily? Would have some time for a review later during the evening.

@jochem-brouwer
Copy link
Member Author

@holgerd77 will update!

@jochem-brouwer jochem-brouwer force-pushed the fix-blockchain-tests branch 5 times, most recently from 17e0b70 to b303a21 Compare August 6, 2020 19:08
@jochem-brouwer
Copy link
Member Author

jochem-brouwer commented Aug 6, 2020

@holgerd77 I spent more time on this than I want to admit 😅 But it works! Note that in the tests all forks supported forks are added as state test; Homestead is also checked in the blockchain test.

The tests are still a bit ugly (but they do work now for older forks). I could make them a bit more prettier but I think that should be done in a new PR.

@evertonfraga could you specifically review the CI? 😄

EDIT: also note the state tests pass for all supported forks! 😄

@jochem-brouwer jochem-brouwer marked this pull request as ready for review August 6, 2020 19:22
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.

Didn't got so deep into opFns.ts yet, some comments to address.

PR also needs a conflict resolve.

}
return Buffer.from(loaded)

return returnBuffer
Copy link
Member

Choose a reason for hiding this comment

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

Just for my understanding, since I've read through both code versions a couple of times now and would assume that both would work: what has been changed/fixed here?

Copy link
Member Author

Choose a reason for hiding this comment

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

There is a KECCAK test in the Homestead suite which requires one to hash ~1.5GB of data (randomStateTest94). The old approach uses too much memory (I assume because of how JS-arrays are implemented; one keeps pushing values into this array, thus expanding it). It runs out of memory on my pc (not sure if it also runs OOM on the CI) which has 16GB of RAM (also tested it with bumping the amount of RAM node can access).

The issue is thus not that this changes any functionality; this however uses less memory as the new approach does not go OOM on my pc.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, nice explanation, thanks!

(and also cool "digging-deep")

async getStateRoot(): Promise<Buffer> {
if (this._checkpointCount !== 0) {
async getStateRoot(force: boolean = false): Promise<Buffer> {
if (!force && this._checkpointCount !== 0) {
throw new Error('Cannot get state root with uncommitted checkpoints')
}
await this._cache.flush()
Copy link
Member

Choose a reason for hiding this comment

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

Ok, looks good.

@@ -22,7 +22,7 @@ export interface StateManager {
checkpoint(): Promise<void>
commit(): Promise<void>
revert(): Promise<void>
getStateRoot(): Promise<Buffer>
getStateRoot(force: boolean): Promise<Buffer>
Copy link
Member

Choose a reason for hiding this comment

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

Correct me if I am wrong, but I think force should be marked as optional in the interface, since there is a default value in the implementation.

Copy link
Member Author

Choose a reason for hiding this comment

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

I can try this, I tried using a default value in the interface (false) but TypeScript does not support this. Marking it optional in the interface sounds like a good idea, I think that might work, but I don't know if TypeScript supports these optional values in the interface. Will try.

"test:state:slow": "npm run test:state -- --runSkipped=slow",
"test:buildIntegrity": "npm run test:state -- --test='stackOverflow'",
"test:blockchain": "node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain",
"test:API": "tape -r ts-node/register --stack-size=1500 './tests/api/**/*.js'",
"test:API:browser": "karma start karma.conf.js",
"test:blockchain:allForks": "node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain --fork=Homestead && node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain --fork=TangerineWhistle && node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain --fork=SpuriousDragon && node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain --fork=Byzantium && node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain --fork=Constantinople && node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain --fork=Petersburg && node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain --fork=Istanbul && node -r ts-node/register --stack-size=1500 ./tests/tester --blockchain --fork=MuirGlacier",
Copy link
Member

Choose a reason for hiding this comment

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

Wonder if this can be simplified with something build upon something like echo {'Istanbul','Byzantium'} | xargs -n1 | xargs -I v1 echo v1, see this StackExchange question for some inspiration.

@@ -14,13 +14,14 @@
"coverage:test": "tape './tests/api/**/*.js' ./tests/tester.js --state --dist",
"docs:build": "typedoc --options typedoc.js",
"test:state": "ts-node ./tests/tester --state",
"test:state:allForks": "npm run test:state -- --fork=TangerineWhistle && npm run test:state -- --fork=SpuriousDragon && npm run test:state -- --fork=Byzantium && npm run test:state -- --fork=Constantinople && npm run test:state -- --fork=Petersburg && npm run test:state -- --fork=Istanbul && npm run test:state -- --fork=MuirGlacier",
"test:state:selectedForks": "npm run test:state -- --fork=Petersburg && npm run test:state -- --fork=TangerineWhistle",
"test:state:allForks": "npm run test:state -- --fork=Homestead && npm run test:state -- --fork=TangerineWhistle && npm run test:state -- --fork=SpuriousDragon && npm run test:state -- --fork=Byzantium && npm run test:state -- --fork=Constantinople && npm run test:state -- --fork=Petersburg && npm run test:state -- --fork=Istanbul && npm run test:state -- --fork=MuirGlacier",
Copy link
Member

Choose a reason for hiding this comment

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

Similar suggestion as for blockchain:allForks

t.comment("skipping test: no data available for " + options.forkConfigTestSuite)
return
}

Copy link
Member

Choose a reason for hiding this comment

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

Ok, will see this as resolved for now.

packages/vm/lib/evm/opFns.ts Show resolved Hide resolved
[VM] TangerineWhistle: fix tests

[VM] remove obsolete test

[VM] remove allforks test from default workflow (it is still in nightly tests)
[VM] Pre-TangerineWhistle: make too much gas forwarded in calls go OOG

[VM] lint

[VM] update package scripts

[VM] fix workflow

[VM] include Homestead in PR test runner

[VM] fix package-lock
@jochem-brouwer
Copy link
Member Author

Looks like the rebase introduced an error.

@jochem-brouwer
Copy link
Member Author

Okay, this should be fixed now (Ethash had an unhandled rejection in a Promise).

Ready for re-review @holgerd77

@holgerd77
Copy link
Member

@jochem-brouwer ah, great, will start in 10 min. 😛

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.

This looks good now, also checked the test run results (so: check that both blockchain and state tests are actually executed), thanks Jochem! 😄

Will merge.

packages/vm/lib/evm/opFns.ts Show resolved Hide resolved
"test:state:allForks": "npm run test:state -- --fork=TangerineWhistle && npm run test:state -- --fork=SpuriousDragon && npm run test:state -- --fork=Byzantium && npm run test:state -- --fork=Constantinople && npm run test:state -- --fork=Petersburg && npm run test:state -- --fork=Istanbul && npm run test:state -- --fork=MuirGlacier",
"test:state:selectedForks": "npm run test:state -- --fork=Petersburg && npm run test:state -- --fork=TangerineWhistle",
"test:state:allForks": "echo {'Homestead','TangerineWhistle','SpuriousDragon','Byzantium','Constantinople','Petersburg','Istanbul','MuirGlacier'} | xargs -n1 | xargs -I v1 npm run test:state -- --fork=v1",
"test:state:selectedForks": "echo {'Homestead','TangerineWhistle','SpuriousDragon','Petersburg',} | xargs -n1 | xargs -I v1 npm run test:state -- --fork=v1",
Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -713,7 +718,11 @@ export const handlers: { [k: string]: OpHandler } = {
if (!value.isZero()) {
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callValueTransfer')))
}
gasLimit = maxCallGas(gasLimit, runState.eei.getGasLeft())
gasLimit = maxCallGas(gasLimit, runState.eei.getGasLeft(), runState)
// note that TW or later this cannot happen (it could have ran out of gas prior to getting here though)
Copy link
Member

Choose a reason for hiding this comment

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

Won't make this a blocker either, but such abbreviations like TW are suboptimal since hard to read (I didn't make it at all to "Tangerine Whistle" on first round), better to fully write stuff like this down.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, will keep it in mind 👍

@holgerd77 holgerd77 merged commit e0c56b2 into master Aug 12, 2020
@holgerd77 holgerd77 deleted the fix-blockchain-tests branch August 12, 2020 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants