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

Blockchain concurrency and safety improvements #930

Merged
merged 16 commits into from
Nov 9, 2020

Conversation

jochem-brouwer
Copy link
Member

@jochem-brouwer jochem-brouwer commented Oct 28, 2020

WIP. Cherry-picked from #895 and builds upon #927

Closes #690

This PR aims to:

  • Fix concurrency issues when using the Blockchain (getters/setters now all wait)
  • Document the library
  • Make variable names more correct
  • Make the library more readable (more docs, some (unnecessary?) recursion removed)

Also security:

  • Genesis block should be put in the Blockchain constructor to prevent accidentally wiping the Genesis block from the DB / Tracking a completely other network (!)
  • This also means that putGenesis is now not possible anymore. You set Genesis in constructor and it cannot change after that.
  • Safe static constructor which throws if the init method throws

@jochem-brouwer jochem-brouwer changed the base branch from master to remove-db-coupling-blockchain October 28, 2020 21:50
@codecov
Copy link

codecov bot commented Oct 28, 2020

Codecov Report

Merging #930 (00ef9cf) into master (7371fdf) will increase coverage by 0.55%.
The diff coverage is 79.31%.

Impacted file tree graph

Flag Coverage Δ
block 74.71% <79.20%> (+1.26%) ⬆️
blockchain 78.04% <79.20%> (+2.70%) ⬆️
common 92.03% <ø> (ø)
ethash 82.08% <ø> (ø)
tx 88.37% <ø> (-0.18%) ⬇️
vm 87.21% <100.00%> (+0.03%) ⬆️

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

@jochem-brouwer
Copy link
Member Author

There is a problem here when creating a DB and then trying to instantiate the Blockchain which should initialize, reading from this DB. Either the order is wrong in _init() or we are actually testing something which we do not want to test. Will investigate ASAP.

}
// Q: is it safe to make this not wait for a lock?
// this is called from `runBlockchain` in case `runBlock` throws (i.e. the block is invalid). But is this the way to go?
// If we know this is called from the iterator/runBlockchain we are safe, but if this is called from anywhere else then this might lead to a concurrency problem?
Copy link
Member Author

Choose a reason for hiding this comment

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

delBlock has no lock because VM calls it for deletion... do we want this? The alternative here is to delete the block and halt the iterator if the onBlock function in the iterator throws. We can thus internally call _delBlock and can put a lock on this one, which I think is safer. Then the only function without a lock is getBlock, which should be fine as the iterator blocks any put operation until it is done (so BLOCKHASH will read from current canonical chain)

@jochem-brouwer
Copy link
Member Author

Rebased this upon the latest changes of #927

@holgerd77 holgerd77 force-pushed the remove-db-coupling-blockchain branch from a673fdc to 57bcd88 Compare November 6, 2020 09:13
Base automatically changed from remove-db-coupling-blockchain to master November 6, 2020 09:36
@jochem-brouwer jochem-brouwer force-pushed the blockchain-concurrency-and-safety-improvements branch 3 times, most recently from 4757523 to 8296f13 Compare November 6, 2020 18:59
@jochem-brouwer
Copy link
Member Author

OK - at this point the logic of this PR is done. I will update a bit more with some tests, which besides verifying the code is correct also serves a more explanatory role.

At this point the logic of this PR can already be reviewed. Since I've moved a lot of code around (without changing it) the diff looks rather big here: it is easier to review this per commit. If the commit is rather large there's a big chance there's code moved around (but might still change the code).

packages/vm/lib/index.ts Outdated Show resolved Hide resolved
* Fetches the meta info about the blockchain from the db. Meta info contains
* hashes of the headerchain head, blockchain head, genesis block and iterator
* heads.
* This method is called in the constructor and either setups the DB or reads values from the DB and makes these available to the consumers of Blockchain.
Copy link
Member

Choose a reason for hiding this comment

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

Please keep the length of comment lines roughly to the line length of the code blocks (I am actually wondering why this is not done by linting automatically?). This is otherwise not very well readable, on my MacBook Air it e.g. completely goes out of screen.

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 will manually lint those, thought it indeed would be done by linter as well

Copy link
Member

Choose a reason for hiding this comment

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

(this applies to most of the comments added)

Copy link
Member

Choose a reason for hiding this comment

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

So printWidth setting in the linting config is set to 100, for orientation

Copy link
Member

Choose a reason for hiding this comment

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

I will manually lint those, thought it indeed would be done by linter as well

??? Can't you then just run the linter or is it not working for you locally?

Copy link
Member Author

Choose a reason for hiding this comment

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

Just checked, if I run npm run lint:fix doesn't change anything here, I'd assume it would break up those comments.

Copy link
Member Author

Choose a reason for hiding this comment

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

Seems like linter does not fix it. I have installed this Rewrap extension in VSCode which helps wrapping the comments. Wrapped comments now in 4db67e9

}

// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.initPromise = this._init(opts.genesisBlock)
Copy link
Member

Choose a reason for hiding this comment

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

I think I find the pattern we have in the VM now with this init() method to be called from outside the constructor more intuitive than this solution, do we eventually want to apply this here as well also for consistency reasons? 🤔

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 apply this here, I just think it makes more sense to just let this Promise run in advance and not call this Promise at the point we actually need it to be initialized. But for consistency we can instead use the pattern from VM - let me know what you think 😄

Copy link
Member

Choose a reason for hiding this comment

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

I think it's ok as it is now on reconsideration. I think I was just irritated by the external usage of initPromise within the VM, but this was before I realized that this is somewhat of a hack due to the Blockchain initialization in the VM constructor.

@@ -225,6 +232,8 @@ export default class VM extends AsyncEventEmitter {
return
}

await this.blockchain.initPromise
Copy link
Member

Choose a reason for hiding this comment

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

Can't you just use your static constructor here?

Copy link
Member

Choose a reason for hiding this comment

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

Update: ah, got it, Blockchain is created in the constructor.

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 will remove this since all public blockchain methods await this one internally; just bloats the logic 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.

Never mind; if you don't await this Promise then after init VM might be initialized, but Blockchain might not be initialized.

blockchain: fix concurrency issues

blockchain: fix test

blockchain: fix infinite while loop

blockchain: delete `putGenesis`
blockchain: more comments
blockchain: rename/remove some unused/confusing variables

blockchain: fix reorg test
blockchain: fix rebase, fix docs
vm: fix example

blockchain: fix tests
blockchain: import bn from ethereumjs-util

vm: await initPromise of blockchain to ensure blockchain is initialized
@jochem-brouwer jochem-brouwer force-pushed the blockchain-concurrency-and-safety-improvements branch from 4db67e9 to 12f0dcb Compare November 8, 2020 13:11
@jochem-brouwer
Copy link
Member Author

So just to recap here, the logic of this PR changes: constructing the DB and the concurrency things. I have written the tests for the constructor.

Note that if you pass a custom genesis block, it is mandatory to also pass this again when re-creating the DB (but now from a given database). This ensures that the user is aware that they are using a custom genesis block. I think this is much safer than "just reading the genesis from the DB": users might take a long time to figure out they are on the wrong chain.

The concurrency is a bit hard to test and I'd need some suggestions here, because I find myself writing rather complex tests with a lot of Promises in them. I do think though that at this point this is correct, but it should be tested anyways.

In general I think at this point the PR is ready for review.

@holgerd77
Copy link
Member

holgerd77 commented Nov 9, 2020

Will do a per-commit review on this now and check the commits along review:

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.

I am now through with this, hanks Jochem, this really looks fantastic and really is a milestone regarding the readability of this library and will give us a super-solid base for further client work! 👍 😄 Would nevertheless also assume that this library will further evolve.

I've done a per-commit review and pushed a small interface fix, a new CHANGELOG entry and some doc improvements and I think the review was thorough enough to justify a merge, also considering our timeline for the releases. I was a bit loose on the last commits, particularly f6498a0, 87b069b and 24f2d54. If someone wants to do a post-merge-review here this would be encouraged.

Ok: last "biggie". 😄

@holgerd77 holgerd77 merged commit 84aaf81 into master Nov 9, 2020
@holgerd77 holgerd77 deleted the blockchain-concurrency-and-safety-improvements branch November 9, 2020 13:21
@jochem-brouwer
Copy link
Member Author

Great, thanks a lot @holgerd77, thanks for the interface fix and also for writing the changelog 😄 👍 Very happy to see this merged!

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.

Declare fields and methods starting with _ as private
3 participants