-
Notifications
You must be signed in to change notification settings - Fork 756
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
Blockchain concurrency and safety improvements #930
Conversation
Codecov Report
Flags with carried forward coverage won't be shown. Click here to find out more. |
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 |
packages/blockchain/src/index.ts
Outdated
} | ||
// 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? |
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.
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)
07c5f70
to
9f8ff31
Compare
9f8ff31
to
d747941
Compare
652d9a1
to
e2f60e4
Compare
Rebased this upon the latest changes of #927 |
d747941
to
a673fdc
Compare
a673fdc
to
57bcd88
Compare
4757523
to
8296f13
Compare
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/blockchain/src/index.ts
Outdated
* 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. |
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.
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.
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 will manually lint those, thought it indeed would be done by linter as well
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 applies to most of the comments added)
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.
So printWidth setting in the linting config is set to 100
, for orientation
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 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?
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.
Just checked, if I run npm run lint:fix
doesn't change anything here, I'd assume it would break up those comments.
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.
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) |
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 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? 🤔
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 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 😄
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 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 |
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.
Can't you just use your static constructor 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.
Update: ah, got it, Blockchain
is created in the constructor.
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 will remove this since all public blockchain
methods await this one internally; just bloats the logic 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.
Never mind; if you don't await this Promise then after init
VM might be initialized, but Blockchain
might not be initialized.
vm, blockchain: lint
…tead of recursive calls
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
4db67e9
to
12f0dcb
Compare
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. |
… inlined _getCanonicalGenesisBlock() function
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 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". 😄
Great, thanks a lot @holgerd77, thanks for the interface fix and also for writing the changelog 😄 👍 Very happy to see this merged! |
WIP. Cherry-picked from #895 and builds upon #927
Closes #690
This PR aims to:
Also security:
static
constructor which throws if theinit
method throws