Skip to content

Commit

Permalink
Merge branch 'master' into update-vitest
Browse files Browse the repository at this point in the history
  • Loading branch information
ScottyPoi authored Jan 12, 2024
2 parents 34f40d8 + fc0433e commit 6012c57
Show file tree
Hide file tree
Showing 15 changed files with 447 additions and 40 deletions.
94 changes: 71 additions & 23 deletions packages/blockchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,58 @@ The library also supports reorg scenarios e.g. by allowing to add a new block wi
The following is an example to instantiate a simple Blockchain object, put blocks into the blockchain and then iterate through the blocks added:

```ts
// ./examples/simple.ts

import { Block } from '@ethereumjs/block'
import { Blockchain } from '@ethereumjs/blockchain'
import { Common, Hardfork } from '@ethereumjs/common'
import { bytesToHex } from '@ethereumjs/util'

// Use the safe static constructor which awaits the init method
const blockchain = Blockchain.create({ common, db })

// See @ethereumjs/block on how to create a block
await blockchain.putBlock(block1)
await blockchain.putBlock(block2)

blockchain.iterator('i', (block) => {
const blockNumber = block.header.number.toString()
const blockHash = bytesToHex(block.hash())
console.log(`Block ${blockNumber}: ${blockHash}`)
})
const main = async () => {
const common = new Common({ chain: 'mainnet', hardfork: Hardfork.London })
// Use the safe static constructor which awaits the init method
const blockchain = await Blockchain.create({
validateBlocks: false, // Skipping validation so we can make a simple chain without having to provide complete blocks
validateConsensus: false,
common,
})

// We use minimal data to provide a sequence of blocks (increasing number, difficulty, and then setting parent hash to previous block)
const block = Block.fromBlockData(
{
header: {
number: 1n,
parentHash: blockchain.genesisBlock.hash(),
difficulty: blockchain.genesisBlock.header.difficulty + 1n,
},
},
{ common, setHardfork: true }
)
const block2 = Block.fromBlockData(
{
header: {
number: 2n,
parentHash: block.header.hash(),
difficulty: block.header.difficulty + 1n,
},
},
{ common, setHardfork: true }
)
// See @ethereumjs/block for more details on how to create a block
await blockchain.putBlock(block)
await blockchain.putBlock(block2)

// We iterate over the blocks in the chain to the current head (block 2)
await blockchain.iterator('i', (block) => {
const blockNumber = block.header.number.toString()
const blockHash = bytesToHex(block.hash())
console.log(`Block ${blockNumber}: ${blockHash}`)
})

// Block 1: 0xa1a061528d74ba81f560e1ebc4f29d6b58171fc13b72b876cdffe6e43b01bdc5
// Block 2: 0x5583be91cf9fb14f5dbeb03ad56e8cef19d1728f267c35a25ba5a355a528f602
}
main()
```

### Database Abstraction / Removed LevelDB Dependency
Expand Down Expand Up @@ -102,18 +139,29 @@ A genesis state can be set along `Blockchain` creation by passing in a custom `g
For many custom chains we might come across a genesis configuration, which can be used to build both chain config as well the genesis state (and hence the genesis block as well to start off with)

```ts
import { Blockchain, parseGethGenesisState } from '@ethereumjs/blockchain'
import { Common, parseGethGenesis } from '@ethereumjs/common'
// ./examples/gethGenesis.ts

// Load geth genesis json file into lets say `gethGenesisJson`
const common = Common.fromGethGenesis(gethGenesisJson, { chain: 'customChain' })
const genesisState = parseGethGenesisState(gethGenesisJson)
const blockchain = await Blockchain.create({
genesisState,
common,
})
const genesisBlockHash = blockchain.genesisBlock.hash()
common.setForkHashes(genesisBlockHash)
import { Blockchain } from '@ethereumjs/blockchain'
import { Common, parseGethGenesis } from '@ethereumjs/common'
import { bytesToHex, parseGethGenesisState } from '@ethereumjs/util'
import gethGenesisJson from './genesisData/post-merge.json'

const main = async () => {
// Load geth genesis json file into lets say `gethGenesisJson`
const common = Common.fromGethGenesis(gethGenesisJson, { chain: 'customChain' })
const genesisState = parseGethGenesisState(gethGenesisJson)
const blockchain = await Blockchain.create({
genesisState,
common,
})
const genesisBlockHash = blockchain.genesisBlock.hash()
common.setForkHashes(genesisBlockHash)
console.log(
`Genesis hash from geth genesis parameters - ${bytesToHex(blockchain.genesisBlock.hash())}`
)
}

main()
```

The genesis block from the initialized `Blockchain` can be retrieved via the `Blockchain.genesisBlock` getter. For creating a genesis block from the params in `@ethereumjs/common`, the `createGenesisBlock(stateRoot: Buffer): Block` method can be used.
Expand Down
35 changes: 35 additions & 0 deletions packages/blockchain/examples/genesisData/post-merge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"config": {
"chainId": 1,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"clique": {
"period": 5,
"epoch": 30000
},
"terminalTotalDifficulty": 0
},
"nonce": "0x42",
"timestamp": "0x0",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1C9C380",
"difficulty": "0x400000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "balance": "0x6d6172697573766477000000" }
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"baseFeePerGas": "0x7"
}
21 changes: 21 additions & 0 deletions packages/blockchain/examples/gethGenesis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Blockchain } from '@ethereumjs/blockchain'
import { Common, parseGethGenesis } from '@ethereumjs/common'
import { bytesToHex, parseGethGenesisState } from '@ethereumjs/util'
import gethGenesisJson from './genesisData/post-merge.json'

const main = async () => {
// Load geth genesis json file into lets say `gethGenesisJson`
const common = Common.fromGethGenesis(gethGenesisJson, { chain: 'customChain' })
const genesisState = parseGethGenesisState(gethGenesisJson)
const blockchain = await Blockchain.create({
genesisState,
common,
})
const genesisBlockHash = blockchain.genesisBlock.hash()
common.setForkHashes(genesisBlockHash)
console.log(
`Genesis hash from geth genesis parameters - ${bytesToHex(blockchain.genesisBlock.hash())}`
)
}

main()
50 changes: 50 additions & 0 deletions packages/blockchain/examples/simple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Block } from '@ethereumjs/block'
import { Blockchain } from '@ethereumjs/blockchain'
import { Common, Hardfork } from '@ethereumjs/common'
import { bytesToHex } from '@ethereumjs/util'

const main = async () => {
const common = new Common({ chain: 'mainnet', hardfork: Hardfork.London })
// Use the safe static constructor which awaits the init method
const blockchain = await Blockchain.create({
validateBlocks: false, // Skipping validation so we can make a simple chain without having to provide complete blocks
validateConsensus: false,
common,
})

// We use minimal data to provide a sequence of blocks (increasing number, difficulty, and then setting parent hash to previous block)
const block = Block.fromBlockData(
{
header: {
number: 1n,
parentHash: blockchain.genesisBlock.hash(),
difficulty: blockchain.genesisBlock.header.difficulty + 1n,
},
},
{ common, setHardfork: true }
)
const block2 = Block.fromBlockData(
{
header: {
number: 2n,
parentHash: block.header.hash(),
difficulty: block.header.difficulty + 1n,
},
},
{ common, setHardfork: true }
)
// See @ethereumjs/block for more details on how to create a block
await blockchain.putBlock(block)
await blockchain.putBlock(block2)

// We iterate over the blocks in the chain to the current head (block 2)
await blockchain.iterator('i', (block) => {
const blockNumber = block.header.number.toString()
const blockHash = bytesToHex(block.hash())
console.log(`Block ${blockNumber}: ${blockHash}`)
})

// Block 1: 0xa1a061528d74ba81f560e1ebc4f29d6b58171fc13b72b876cdffe6e43b01bdc5
// Block 2: 0x5583be91cf9fb14f5dbeb03ad56e8cef19d1728f267c35a25ba5a355a528f602
}
main()
1 change: 1 addition & 0 deletions packages/blockchain/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"coverage": "DEBUG=ethjs npx vitest run --coverage.enabled --coverage.reporter=lcov",
"docs:build": "typedoc --options typedoc.cjs",
"examples": "tsx ../../scripts/examples-runner.ts -- blockchain",
"examples:build": "npx embedme README.md",
"lint": "../../config/cli/lint.sh",
"lint:diff": "../../config/cli/lint-diff.sh",
"lint:fix": "../../config/cli/lint-fix.sh",
Expand Down
67 changes: 51 additions & 16 deletions packages/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@ const { Common, Chain, Hardfork } = require('@ethereumjs/common')
All parameters can be accessed through the `Common` class, instantiated with an object containing either the `chain` (e.g. 'Chain.Mainnet') or the `chain` together with a specific `hardfork` provided:

```ts
// ./examples/common.ts#L1-L7

import { Chain, Common, Hardfork } from '@ethereumjs/common'

// With enums:
const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London })
const commonWithEnums = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London })

// (also possible with directly passing in strings:)
const common = new Common({ chain: 'mainnet', hardfork: 'london' })
const commonWithStrings = new Common({ chain: 'mainnet', hardfork: 'london' })
```

If no hardfork is provided, the common is initialized with the default hardfork.
Expand All @@ -54,19 +58,23 @@ Current `DEFAULT_HARDFORK`: `Hardfork.Shanghai`
Here are some simple usage examples:

```ts
// ./examples/common.ts#L9-L23

// Instantiate with the chain (and the default hardfork)
let c = new Common({ chain: Chain.Mainnet })
c.param('gasPrices', 'ecAddGas') // 500
console.log(`The gas price for ecAdd is ${c.param('gasPrices', 'ecAddGas')}`) // 500

// Chain and hardfork provided
c = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Byzantium })
c.param('pow', 'minerReward') // 3000000000000000000
console.log(`The miner reward under PoW on Byzantium us ${c.param('pow', 'minerReward')}`) // 3000000000000000000

// Get bootstrap nodes for chain/network
c.bootstrapNodes() // Array with current nodes
console.log('Below are the known bootstrap nodes')
console.log(c.bootstrapNodes()) // Array with current nodes

// Instantiate with an EIP activated
c = new Common({ chain: Chain.Mainnet, eips: [4844] })
console.log(`EIP 4844 is active -- ${c.isActivatedEIP(4844)}`)
```

## Browser
Expand Down Expand Up @@ -167,7 +175,11 @@ There are two distinct APIs available for setting up custom(ized) chains.
There is a dedicated `Common.custom()` static constructor which allows for an easy instantiation of a Common instance with somewhat adopted chain parameters, with the main use case to adopt on instantiating with a deviating chain ID (you can use this to adopt other chain parameters as well though). Instantiating a custom common instance with its own chain ID and inheriting all other parameters from `mainnet` can now be as easily done as:

```ts
const common = Common.custom({ chainId: 1234 })
// ./examples/common.ts#L25-L27

// Instantiate common with custom chainID
const commonWithCustomChainId = Common.custom({ chainId: 1234 })
console.log(`The current chain ID is ${commonWithCustomChainId.chainId}`)
```

The `custom()` method also takes a string as a first input (instead of a dictionary). This can be used in combination with the `CustomChain` enum dict which allows for the selection of predefined supported custom chains for an easier `Common` setup of these supported chains:
Expand All @@ -194,8 +206,14 @@ you can pass a dictionary - conforming to the parameter format described above -
values to the constructor using the `chain` parameter or the `setChain()` method, here is some example:

```ts
import myCustomChain from './[PATH]/myCustomChain.js'
const common = new Common({ chain: myCustomChain })
// ./examples/customChain.ts

import { Common } from '@ethereumjs/common'
import myCustomChain1 from './genesisData/testnet.json'

// Add custom chain config
const common1 = new Common({ chain: myCustomChain1 })
console.log(`Common is instantiated with custom chain parameters - ${common1.chainName()}`)
```

#### Initialize using customChains Array
Expand All @@ -208,17 +226,23 @@ use the `chain` option to activate one of the custom chains passed or activate a
(e.g. `mainnet`) and switch to other chains - including the custom ones - by using `Common.setChain()`.

```ts
import myCustomChain1 from './[PATH]/myCustomChain1.js'
import myCustomChain2 from './[PATH]/myCustomChain2.js'
// ./examples/customChains.ts

import { Common } from '@ethereumjs/common'
import myCustomChain1 from './genesisData/testnet.json'
import myCustomChain2 from './genesisData/testnet2.json'
// Add two custom chains, initial mainnet activation
const common1 = new Common({ chain: 'mainnet', customChains: [myCustomChain1, myCustomChain2] })
// Somewhat later down the road...
common1.setChain('customChain1')
console.log(`Common is instantiated with mainnet parameters - ${common1.chainName()}`)
common1.setChain('testnet1')
console.log(`Common is set to use testnet parameters - ${common1.chainName()}`)
// Add two custom chains, activate customChain1
const common1 = new Common({
chain: 'customChain1',
const common2 = new Common({
chain: 'testnet2',
customChains: [myCustomChain1, myCustomChain2],
})

console.log(`Common is instantiated with testnet2 parameters - ${common1.chainName()}`)
```

Starting with v3 custom genesis states should be passed to the [Blockchain](../blockchain/) library directly.
Expand All @@ -230,23 +254,34 @@ has both config specification for the chain as well as the genesis state specifi
common from such configuration in the following manner:

```ts
// ./examples/fromGeth.ts

import { Common } from '@ethereumjs/common'
import { hexToBytes } from '@ethereumjs/util'

import genesisJson from './genesisData/post-merge.json'

const genesisHash = hexToBytes('0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a')
// Load geth genesis json file into lets say `genesisJson` and optional `chain` and `genesisHash`
const common = Common.fromGethGenesis(genesisJson, { chain: 'customChain', genesisHash })
// If you don't have `genesisHash` while initiating common, you can later configure common (for e.g.
// post calculating it via `blockchain`)
// after calculating it via `blockchain`)
common.setForkHashes(genesisHash)

console.log(`The London forkhash for this custom chain is ${common.forkHash('london')}`)
```

### Hardforks

The `hardfork` can be set in constructor like this:

```ts
// ./examples/common.ts#L1-L4

import { Chain, Common, Hardfork } from '@ethereumjs/common'

const c = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Byzantium })
// With enums:
const commonWithEnums = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London })
```

### Active Hardforks
Expand Down
27 changes: 27 additions & 0 deletions packages/common/examples/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Chain, Common, Hardfork } from '@ethereumjs/common'

// With enums:
const commonWithEnums = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London })

// (also possible with directly passing in strings:)
const commonWithStrings = new Common({ chain: 'mainnet', hardfork: 'london' })

// Instantiate with the chain (and the default hardfork)
let c = new Common({ chain: Chain.Mainnet })
console.log(`The gas price for ecAdd is ${c.param('gasPrices', 'ecAddGas')}`) // 500

// Chain and hardfork provided
c = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Byzantium })
console.log(`The miner reward under PoW on Byzantium us ${c.param('pow', 'minerReward')}`) // 3000000000000000000

// Get bootstrap nodes for chain/network
console.log('Below are the known bootstrap nodes')
console.log(c.bootstrapNodes()) // Array with current nodes

// Instantiate with an EIP activated
c = new Common({ chain: Chain.Mainnet, eips: [4844] })
console.log(`EIP 4844 is active -- ${c.isActivatedEIP(4844)}`)

// Instantiate common with custom chainID
const commonWithCustomChainId = Common.custom({ chainId: 1234 })
console.log(`The current chain ID is ${commonWithCustomChainId.chainId}`)
Loading

0 comments on commit 6012c57

Please sign in to comment.