-
Notifications
You must be signed in to change notification settings - Fork 759
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: allow optimistic block insertion in blockchain #3584
base: master
Are you sure you want to change the base?
Changes from all commits
3b24f64
a69d63f
e640849
bbb140b
23fd13f
c7f2ae2
d9e3297
7bd20c2
e4253d6
3bbed08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { createBlock } from '@ethereumjs/block' | ||
import { createBlockchain } from '@ethereumjs/blockchain' | ||
import { Common, Hardfork, Mainnet } from '@ethereumjs/common' | ||
import { bytesToHex, hexToBytes } from '@ethereumjs/util' | ||
|
||
const main = async () => { | ||
const common = new Common({ chain: Mainnet, hardfork: Hardfork.Shanghai }) | ||
// Use the safe static constructor which awaits the init method | ||
const blockchain = await createBlockchain({ | ||
common, | ||
}) | ||
|
||
// We are using this to create minimal post merge blocks between shanghai and cancun in line with the | ||
// block hardfork configuration of mainnet | ||
const chainTTD = BigInt('58750000000000000000000') | ||
const shanghaiTimestamp = 1681338455 | ||
|
||
// We use minimal data to provide a sequence of blocks (increasing number, difficulty, and then setting parent hash to previous block) | ||
const block1 = createBlock( | ||
{ | ||
header: { | ||
// 15537393n is terminal block in mainnet config | ||
number: 15537393n + 500n, | ||
// Could be any parenthash other than 0x00..00 as we will set this block as a TRUSTED 4444 anchor | ||
// instead of genesis to build blockchain on top of. One could use any criteria to set a block | ||
// as trusted 4444 anchor | ||
parentHash: hexToBytes(`0x${'20'.repeat(32)}`), | ||
timestamp: shanghaiTimestamp + 12 * 500, | ||
}, | ||
}, | ||
{ common, setHardfork: true }, | ||
) | ||
const block2 = createBlock( | ||
{ | ||
header: { | ||
number: block1.header.number + 1n, | ||
parentHash: block1.header.hash(), | ||
timestamp: shanghaiTimestamp + 12 * 501, | ||
}, | ||
}, | ||
{ common, setHardfork: true }, | ||
) | ||
const block3 = createBlock( | ||
{ | ||
header: { | ||
number: block2.header.number + 1n, | ||
parentHash: block2.header.hash(), | ||
timestamp: shanghaiTimestamp + 12 * 502, | ||
}, | ||
}, | ||
{ common, setHardfork: true }, | ||
) | ||
|
||
let headBlock, blockByHash, blockByNumber | ||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
console.log( | ||
`Blockchain ${blockchain.consensus.algorithm} Head: ${headBlock.header.number} ${bytesToHex(headBlock.hash())}`, | ||
) | ||
// Blockchain casper Head: 0 0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3 | ||
|
||
// 1. We can put any post merge block as 4444 anchor by using TTD as parentTD | ||
// 2. For pre-merge blocks its prudent to supply correct parentTD so as to respect the | ||
// hardfork configuration as well as to determine the canonicality of the chain on future putBlocks | ||
await blockchain.putBlock(block1, { parentTd: chainTTD }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still do not fully understand this anchor concept. What is anchored and how? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. block1 is the 4444 anchor i.e. the parent chain to block1 doesn't need to be added to the blockchain and blockchain can be build forward from here it acts as a re-genesis anchor, one specifies an anchor by providing parentTD, for a PoS block parentTD can just be chainTTD, for a PoW block an actual parentTD should be added so as to eventually and correctly matchup with the terminal block TTD when forward chain blocks will be added on top of it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are one of the things which are too implicit, at least on the API, maybe the DB level. I am still not fully grasping if the No matter how it is: this is unnecessarily implicit and we should think about the "anchor" (or call it checkpoint?) status and give this its own API and database representation. I think "Checkpoint" might actually be the more common and established terminolgy. So I think we definitely want to have some external relation in the DB where we store all the checkpoints (and not only have this in some very implicit form), and can e.g. extend our API with things like: blockchain.getCheckpoints()
blockchain.isCheckpoint(5n) (or first there would be the question if there can be only one of these, but guess there can be several respectively would make sense?) (wonder if we can even lign in the genesis block in this concept, guess this is exactly the same as a checkpoint?) And then for setting these checkpoints, this should be very explicitly named. So if |
||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
console.log( | ||
`Blockchain ${blockchain.consensus.algorithm} Head: ${headBlock.header.number} ${bytesToHex(headBlock.hash())}`, | ||
) | ||
// Blockchain casper Head: 15537893 0x26cb3bfda75027016c17d737fdabe56f412925311b42178a675da88a41bbb7e7 | ||
|
||
await blockchain.putBlock(block2) | ||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
console.log( | ||
`Blockchain ${blockchain.consensus.algorithm} Head: ${headBlock.header.number} ${bytesToHex(headBlock.hash())}`, | ||
) | ||
// Blockchain casper Head: 15537894 0x6c33728cd8aa21db683d94418fec1f7ee1cfdaa9b77781762ec832da40ec3a7c | ||
|
||
await blockchain.putBlock(block3) | ||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
console.log( | ||
`Blockchain ${blockchain.consensus.algorithm} Head: ${headBlock.header.number} ${bytesToHex(headBlock.hash())}`, | ||
) | ||
// Blockchain casper Head: 15537895 0x4263ec367ce44e4092b79ea240f132250d0d341639afbaf8c0833fbdd6160d0f | ||
} | ||
void main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import { createBlock } from '@ethereumjs/block' | ||
import { createBlockchain } from '@ethereumjs/blockchain' | ||
import { Common, Hardfork, Mainnet } from '@ethereumjs/common' | ||
import { bytesToHex, hexToBytes } from '@ethereumjs/util' | ||
|
||
const main = async () => { | ||
const common = new Common({ chain: Mainnet, hardfork: Hardfork.Shanghai }) | ||
// Use the safe static constructor which awaits the init method | ||
const blockchain = await createBlockchain({ | ||
common, | ||
}) | ||
|
||
// We are using this to create minimal post merge blocks between shanghai and cancun in line with the | ||
// block hardfork configuration of mainnet | ||
const chainTTD = BigInt('58750000000000000000000') | ||
const shanghaiTimestamp = 1681338455 | ||
|
||
// We use minimal data to construct random block sequence post merge/paris to worry not much about | ||
// td's pre-merge while constructing chain | ||
const block1 = createBlock( | ||
{ | ||
header: { | ||
// 15537393n is terminal block in mainnet config | ||
number: 15537393n + 500n, | ||
// Could be any parenthash other than 0x00..00 as we will set this block as a TRUSTED 4444 anchor | ||
// instead of genesis to build blockchain on top of. One could use any criteria to set a block | ||
// as trusted 4444 anchor | ||
parentHash: hexToBytes(`0x${'20'.repeat(32)}`), | ||
timestamp: shanghaiTimestamp + 12 * 500, | ||
}, | ||
}, | ||
{ common, setHardfork: true }, | ||
) | ||
const block2 = createBlock( | ||
{ | ||
header: { | ||
number: block1.header.number + 1n, | ||
parentHash: block1.header.hash(), | ||
timestamp: shanghaiTimestamp + 12 * 501, | ||
}, | ||
}, | ||
{ common, setHardfork: true }, | ||
) | ||
const block3 = createBlock( | ||
{ | ||
header: { | ||
number: block2.header.number + 1n, | ||
parentHash: block2.header.hash(), | ||
timestamp: shanghaiTimestamp + 12 * 502, | ||
}, | ||
}, | ||
{ common, setHardfork: true }, | ||
) | ||
|
||
let headBlock, blockByHash, blockByNumber | ||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
console.log( | ||
`Blockchain ${blockchain.consensus.algorithm} Head: ${headBlock.header.number} ${bytesToHex(headBlock.hash())}`, | ||
) | ||
// Blockchain casper Head: 0 0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3 | ||
|
||
// allows any block > head + 1 to be put as non canonical block | ||
await blockchain.putBlock(block3, { canonical: true }) | ||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
blockByHash = await blockchain.getBlock(block3.hash()).catch((e) => null) | ||
blockByNumber = await blockchain.getBlock(block3.header.number).catch((e) => null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We expect that this does not throw - right? So the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, its just a pattern used to log the status, sometimes blockByNumber will not be found depending upon the scenario |
||
console.log( | ||
`putBlock ${block3.header.number} ${bytesToHex(block3.hash())} byHash: ${blockByHash ? true : false} byNumber: ${blockByNumber ? true : false} headBlock=${headBlock.header.number}`, | ||
) | ||
// putBlock 15537895 0x4263ec367ce44e4092b79ea240f132250d0d341639afbaf8c0833fbdd6160d0f byHash: true byNumber: true headBlock=0 | ||
|
||
let hasBlock1, hasBlock2, hasBlock3 | ||
hasBlock1 = (await blockchain.getBlock(block1.header.number).catch((e) => null)) ? true : false | ||
hasBlock2 = (await blockchain.getBlock(block2.header.number).catch((e) => null)) ? true : false | ||
hasBlock3 = (await blockchain.getBlock(block3.header.number).catch((e) => null)) ? true : false | ||
console.log( | ||
`canonicality: head=${headBlock.header.number}, 0 ... ${block1.header.number}=${hasBlock1} ${block2.header.number}=${hasBlock2} ${block3.header.number}=${hasBlock3} `, | ||
) | ||
// canonicality: head=0, 0 ... 15537893=false 15537894=false 15537895=true | ||
|
||
await blockchain.putBlock(block2, { canonical: true }) | ||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
blockByHash = await blockchain.getBlock(block2.hash()).catch((e) => null) | ||
blockByNumber = await blockchain.getBlock(block2.header.number).catch((e) => null) | ||
console.log( | ||
`putBlock ${block2.header.number} ${bytesToHex(block2.hash())} byHash: ${blockByHash ? true : false} byNumber: ${blockByNumber ? true : false} headBlock=${headBlock.header.number}`, | ||
) | ||
// putBlock 15537894 0x6c33728cd8aa21db683d94418fec1f7ee1cfdaa9b77781762ec832da40ec3a7c byHash: true byNumber: true headBlock=0 | ||
|
||
hasBlock1 = (await blockchain.getBlock(block1.header.number).catch((e) => null)) ? true : false | ||
hasBlock2 = (await blockchain.getBlock(block2.header.number).catch((e) => null)) ? true : false | ||
hasBlock3 = (await blockchain.getBlock(block3.header.number).catch((e) => null)) ? true : false | ||
console.log( | ||
`canonicality: head=${headBlock.header.number}, 0 ... ${block1.header.number}=${hasBlock1} ${block2.header.number}=${hasBlock2} ${block3.header.number}=${hasBlock3} `, | ||
) | ||
// canonicality: head=0, 0 ... 15537893=false 15537894=true 15537895=true | ||
|
||
// 1. We can put any post merge block as 4444 anchor by using TTD as parentTD | ||
// 2. For pre-merge blocks its prudent to supply correct parentTD so as to respect the | ||
// hardfork configuration as well as to determine the canonicality of the chain on future putBlocks | ||
await blockchain.putBlock(block1, { parentTd: chainTTD }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I were to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blockvalidation depends on parent availability in the blockchain and not on any other factor, for optimistic blocks it doesn't happen. so lets say this was an optimistic block i.e. its parent was not in blockchain, validate blocks will be skipped. if you add canonical=true here, it will add the number => hash index and will run the head update rules (depending on if this is pow block or pos block) since parentTd has been specified and it will assume that it parent chain is confirmed and doesn't need to be added to blockchain There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is too implicit behavior. If we have set If one wants to deactivate block validation, we should add an option Guess this should then be accompanied by a flag for blockchain (if we go this route) to indicate that we might deal with a not-fully validated blockchain. At least all these things (the "state" of the blockchain) should be transparent and requestable, and not just be "implicitly there". |
||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
hasBlock1 = (await blockchain.getBlock(block1.header.number).catch((e) => null)) ? true : false | ||
hasBlock2 = (await blockchain.getBlock(block2.header.number).catch((e) => null)) ? true : false | ||
hasBlock3 = (await blockchain.getBlock(block3.header.number).catch((e) => null)) ? true : false | ||
console.log( | ||
`canonicality: head=${headBlock.header.number}, 0 ... ${block1.header.number}=${hasBlock1} ${block2.header.number}=${hasBlock2} ${block3.header.number}=${hasBlock3} `, | ||
) | ||
// canonicality: head=15537893, 0 ... 15537893=true 15537894=true 15537895=true | ||
|
||
await blockchain.putBlock(block2) | ||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
console.log( | ||
`Blockchain ${blockchain.consensus.algorithm} Head: ${headBlock.header.number} ${bytesToHex(headBlock.hash())}`, | ||
) | ||
// Blockchain casper Head: 15537894 0x6c33728cd8aa21db683d94418fec1f7ee1cfdaa9b77781762ec832da40ec3a7c | ||
|
||
hasBlock1 = (await blockchain.getBlock(block1.header.number).catch((e) => null)) ? true : false | ||
hasBlock2 = (await blockchain.getBlock(block2.header.number).catch((e) => null)) ? true : false | ||
hasBlock3 = (await blockchain.getBlock(block3.header.number).catch((e) => null)) ? true : false | ||
console.log( | ||
`canonicality: head=${headBlock.header.number}, 0 ... ${block1.header.number}=${hasBlock1} ${block2.header.number}=${hasBlock2} ${block3.header.number}=${hasBlock3} `, | ||
) | ||
// canonicality: head=15537894, 0 ... 15537893=true 15537894=true 15537895=true | ||
|
||
await blockchain.putBlock(block3) | ||
headBlock = await blockchain.getCanonicalHeadBlock() | ||
console.log( | ||
`Blockchain ${blockchain.consensus.algorithm} Head: ${headBlock.header.number} ${bytesToHex(headBlock.hash())}`, | ||
) | ||
// Blockchain casper Head: 15537895 0x4263ec367ce44e4092b79ea240f132250d0d341639afbaf8c0833fbdd6160d0f | ||
|
||
hasBlock1 = (await blockchain.getBlock(block1.header.number).catch((e) => null)) ? true : false | ||
hasBlock2 = (await blockchain.getBlock(block2.header.number).catch((e) => null)) ? true : false | ||
hasBlock3 = (await blockchain.getBlock(block3.header.number).catch((e) => null)) ? true : false | ||
console.log( | ||
`canonicality: head=${headBlock.header.number}, 0 ... ${block1.header.number}=${hasBlock1} ${block2.header.number}=${hasBlock2} ${block3.header.number}=${hasBlock3} `, | ||
) | ||
// canonicality: head=15537895, 0 ... 15537893=true 15537894=true 15537895=true | ||
} | ||
void main() |
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.
Nit: could these be packed in a for loop, and the "magic numbers" (500) be put as a constant?