Skip to content

Commit

Permalink
Merge branch 'master' into ephemery-genesis
Browse files Browse the repository at this point in the history
  • Loading branch information
pk910 committed Jan 14, 2025
2 parents 6233f38 + 89642ae commit 6b49962
Show file tree
Hide file tree
Showing 13 changed files with 390 additions and 70 deletions.
10 changes: 8 additions & 2 deletions .github/actions/prepare/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,17 @@ runs:
flavor: latest=false
tags: |
type=match,pattern=ephemery-v(.*),group=1,prefix=ephemery-
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=ref,event=branch
type=sha
type=match,event=tag,pattern=^v\d+\.\d+\.\d+$,group=0
type=match,event=tag,pattern=^verkle-gen-v\d+\.\d+\.\d+$,group=0
type=match,event=tag,pattern=^ephemery-v\d+\.\d+\.\d+$,group=0
- name: Set tag
id: set_tag
shell: bash
run: |
echo "tag=${{ fromJSON(steps.meta.outputs.json).tags[0] }}" >> $GITHUB_OUTPUT
1 change: 0 additions & 1 deletion .github/workflows/build-push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ jobs:
needs:
- prepare
runs-on: ${{ matrix.runner }}
continue-on-error: true
strategy:
matrix:
include: ${{fromJson(needs.prepare.outputs.platforms)}}
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.21 as builder
FROM golang:1.22 as builder
RUN git clone https://github.com/protolambda/eth2-testnet-genesis.git \
&& cd eth2-testnet-genesis \
&& go install . \
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Create a ethereum consensus/execution layer testnet genesis and optionally expos

### Examples

You can provide your own configuration directory. Have a look at the example in [`config-example`](config-example).
Create a new file with your custom configuration in `./config/values.env`. You can use the [defaults.env](defaults/defaults.env) file as a template.

```sh
# Create the output directory
Expand All @@ -13,17 +13,17 @@ mkdir output
# Overwriting the config files and generating the EL and CL genesis
docker run --rm -it -u $UID -v $PWD/output:/data \
-v $PWD/config/values.env:/config/values.env \
ethpandaops/ethereum-genesis-generator:latest all
ethpandaops/ethereum-genesis-generator:master all

# Just creating the EL genesis
docker run --rm -it -u $UID -v $PWD/output:/data \
-v $PWD/config/values.env:/config/values.env \
ethpandaops/ethereum-genesis-generator:latest el
ethpandaops/ethereum-genesis-generator:master el

# Just creating the CL genesis
docker run --rm -it -u $UID -v $PWD/output:/data \
-v $PWD/config/values.env:/config/values.env \
ethpandaops/ethereum-genesis-generator:latest cl
ethpandaops/ethereum-genesis-generator:master cl
```
### Environment variables

Expand Down
41 changes: 37 additions & 4 deletions apps/el-gen/genesis_besu.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
sepolia_config_path = "/apps/el-gen/sepolia/besu_genesis.json"
goerli_config_path = "/apps/el-gen/goerli/besu_genesis.json"
holesky_config_path = "/apps/el-gen/holesky/besu_genesis.json"
isNamedTestnet = False
combined_allocs = {}

if len(sys.argv) > 1:
Expand All @@ -18,6 +19,9 @@
with open(testnet_config_path) as stream:
data = yaml.safe_load(stream)

if int(data['chain_id']) == 1 or int(data['chain_id']) == 11155111 or int(data['chain_id']) == 17000:
isNamedTestnet = True

if int(data['chain_id']) == 1:
with open(mainnet_config_path) as m:
mainnet_json = json.loads(m.read())
Expand Down Expand Up @@ -45,9 +49,6 @@
"berlinBlock":0,
"londonBlock":0,
"preMergeForkBlock":0,
"terminalTotalDifficulty":0,
"shanghaiTime": 0,
"cancunTime": 0,
"depositContractAddress": data["deposit_contract_address"],
},
"alloc": {
Expand Down Expand Up @@ -107,7 +108,7 @@
},
"coinbase": "0x0000000000000000000000000000000000000000",
"baseFeePerGas": "0x3B9ACA00",
"difficulty": "0x01",
"difficulty": "0x0",
"extraData": "",
"gasLimit": hex(int(data['genesis_gaslimit'] if 'genesis_gaslimit' in data and data['genesis_gaslimit'] is not None else 25000000)),
"nonce": "0x1234",
Expand Down Expand Up @@ -158,6 +159,38 @@ def add_alloc_entry(addr, account):
for addr, account in data['additional_preloaded_contracts'].items():
add_alloc_entry(addr, account)

# Add fork timestamps for each fork
# Altair and Bellatrix have no EL implication in terms of timestamp and hence are not included here
if 'terminal_total_difficulty' in data and not isNamedTestnet:
if data['terminal_total_difficulty'] != 0:
out['config']['terminalTotalDifficulty'] = data['terminal_total_difficulty']
else:
out['config']['terminalTotalDifficulty'] = 0


if 'capella_fork_epoch' in data and not isNamedTestnet:
if data['capella_fork_epoch'] != 0:
out['config']['shanghaiTime'] = \
int(data['genesis_timestamp']) + \
int(data['genesis_delay']) + \
int(data['capella_fork_epoch']) * ( 32 if data['preset_base']=='mainnet' else 8 ) * int(data['slot_duration_in_seconds'])
else:
out['config']['shanghaiTime'] = 0

if 'deneb_fork_epoch' in data and not isNamedTestnet:
if data['deneb_fork_epoch'] != 0:
out['config']['cancunTime'] = \
int(data['genesis_timestamp']) + \
int(data['genesis_delay']) + \
int(data['deneb_fork_epoch']) * ( 32 if data['preset_base']=='mainnet' else 8 ) * int(data['slot_duration_in_seconds'])
else:
out['config']['cancunTime'] = 0

out['config']['blobSchedule'] = {}
out['config']['blobSchedule']['cancun'] = {
"target": data['target_blobs_per_block_cancun'],
"max": data['max_blobs_per_block_cancun']
}

out['config']['ethash'] = {}
print(json.dumps(out, indent=' '))
75 changes: 62 additions & 13 deletions apps/el-gen/genesis_chainspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
sepolia_config_path = "/apps/el-gen/sepolia/chainspec.json"
goerli_config_path = "/apps/el-gen/goerli/chainspec.json"
holesky_config_path = "/apps/el-gen/holesky/chainspec.json"
isNamedTestnet = False
combined_allocs = {}
if len(sys.argv) > 1:
testnet_config_path = sys.argv[1]

with open(testnet_config_path) as stream:
data = yaml.safe_load(stream)

if int(data['chain_id']) == 1 or int(data['chain_id']) == 11155111 or int(data['chain_id']) == 17000:
isNamedTestnet = True

if int(data['chain_id']) == 1:
with open(mainnet_config_path) as m:
mainnet_json = json.loads(m.read())
Expand Down Expand Up @@ -71,16 +75,6 @@
"eip3198Transition": "0x0",
"eip3529Transition": "0x0",
"eip3541Transition": "0x0",
"eip4895TransitionTimestamp": "0x0",
"eip3855TransitionTimestamp": "0x0",
"eip3651TransitionTimestamp": "0x0",
"eip3860TransitionTimestamp": "0x0",
"terminalTotalDifficulty":"0x0",
"eip4844TransitionTimestamp": "0x0",
"eip4788TransitionTimestamp": "0x0",
"eip1153TransitionTimestamp": "0x0",
"eip5656TransitionTimestamp": "0x0",
"eip6780TransitionTimestamp": "0x0",
"depositContractAddress": data["deposit_contract_address"],
},
"genesis": {
Expand All @@ -90,7 +84,7 @@
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x01",
"difficulty": "0x0",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": hex(data['genesis_timestamp']),
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
Expand Down Expand Up @@ -198,6 +192,61 @@ def add_alloc_entry(addr, account):

for addr, account in data['additional_preloaded_contracts'].items():
add_alloc_entry(addr, account)



# Terminal total difficulty
if 'terminal_total_difficulty' in data and not isNamedTestnet:
if data['terminal_total_difficulty'] != 0:
out['params']['terminalTotalDifficulty'] = hex(data['terminal_total_difficulty'])
else:
out['params']['terminalTotalDifficulty'] = "0x0"
# Capella fork
if 'capella_fork_epoch' in data and not isNamedTestnet:
if data['capella_fork_epoch'] != 0:
capella_timestamp = hex(
int(data['genesis_timestamp']) +
int(data['genesis_delay']) +
int(data['capella_fork_epoch']) * ( 32 if data['preset_base']=='mainnet' else 8 ) * int(data['slot_duration_in_seconds'])
)
out['params']['eip4895TransitionTimestamp'] = capella_timestamp
out['params']['eip3651TransitionTimestamp'] = capella_timestamp
out['params']['eip3855TransitionTimestamp'] = capella_timestamp
out['params']['eip3860TransitionTimestamp'] = capella_timestamp
else:
out['params']['eip4895TransitionTimestamp'] = 0
out['params']['eip3651TransitionTimestamp'] = 0
out['params']['eip3855TransitionTimestamp'] = 0
out['params']['eip3860TransitionTimestamp'] = 0

# Dencun fork
if 'deneb_fork_epoch' in data and not isNamedTestnet:
if data['deneb_fork_epoch'] != 0:
deneb_timestamp = hex(
int(data['genesis_timestamp']) +
int(data['genesis_delay']) +
int(data['deneb_fork_epoch']) * ( 32 if data['preset_base']=='mainnet' else 8 ) * int(data['slot_duration_in_seconds'])
)
out['params']['eip4844TransitionTimestamp'] = deneb_timestamp
out['params']['eip4788TransitionTimestamp'] = deneb_timestamp
out['params']['eip1153TransitionTimestamp'] = deneb_timestamp
out['params']['eip5656TransitionTimestamp'] = deneb_timestamp
out['params']['eip6780TransitionTimestamp'] = deneb_timestamp
else:
out['params']['eip4844TransitionTimestamp'] = 0
out['params']['eip4788TransitionTimestamp'] = 0
out['params']['eip1153TransitionTimestamp'] = 0
out['params']['eip5656TransitionTimestamp'] = 0
out['params']['eip6780TransitionTimestamp'] = 0

# add the cancun blobSchedule
out['params']['blobSchedule'] = {}
out['params']['blobSchedule']['cancun'] = {
"target": data['target_blobs_per_block_cancun'],
"max": data['max_blobs_per_block_cancun']
}

# add the osaka blobSchedule
out['params']['blobSchedule']['osaka'] = {
"target": data['target_blobs_per_block_osaka'],
"max": data['max_blobs_per_block_osaka']
}
print(json.dumps(out, indent=' '))
44 changes: 39 additions & 5 deletions apps/el-gen/genesis_geth.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
sepolia_config_path = "/apps/el-gen/sepolia/genesis.json"
goerli_config_path = "/apps/el-gen/goerli/genesis.json"
holesky_config_path = "/apps/el-gen/holesky/genesis.json"
isNamedTestnet = False
combined_allocs = {}
if len(sys.argv) > 1:
testnet_config_path = sys.argv[1]

with open(testnet_config_path) as stream:
data = yaml.safe_load(stream)

if int(data['chain_id']) == 1 or int(data['chain_id']) == 11155111 or int(data['chain_id']) == 17000:
isNamedTestnet = True

if int(data['chain_id']) == 1:
with open(mainnet_config_path) as m:
mainnet_json = json.loads(m.read())
Expand Down Expand Up @@ -44,10 +48,6 @@
"berlinBlock":0,
"londonBlock":0,
"mergeNetsplitBlock":0,
"terminalTotalDifficulty":0,
"terminalTotalDifficultyPassed": True,
"shanghaiTime": 0,
"cancunTime": 0,
"depositContractAddress": data["deposit_contract_address"],
},
"alloc": {
Expand Down Expand Up @@ -106,7 +106,7 @@
}
},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x01",
"difficulty": "0x0",
"extraData": "",
"gasLimit": hex(int(data['genesis_gaslimit'] if 'genesis_gaslimit' in data and data['genesis_gaslimit'] is not None else 25000000)),
"nonce": "0x1234",
Expand Down Expand Up @@ -157,4 +157,38 @@ def add_alloc_entry(addr, account):
for addr, account in data['additional_preloaded_contracts'].items():
add_alloc_entry(addr, account)

# Add fork timestamps for each fork
# Altair and Bellatrix have no EL implication in terms of timestamp and hence are not included here
if 'terminal_total_difficulty' in data and not isNamedTestnet:
if data['terminal_total_difficulty'] != 0:
out['config']['terminalTotalDifficulty'] = data['terminal_total_difficulty']
else:
out['config']['terminalTotalDifficulty'] = 0
out['config']['terminalTotalDifficultyPassed'] = True

if 'capella_fork_epoch' in data and not isNamedTestnet:
if data['capella_fork_epoch'] != 0:
out['config']['shanghaiTime'] = \
int(data['genesis_timestamp']) + \
int(data['genesis_delay']) + \
int(data['capella_fork_epoch']) * ( 32 if data['preset_base']=='mainnet' else 8 ) * int(data['slot_duration_in_seconds'])
else:
out['config']['shanghaiTime'] = 0


if 'deneb_fork_epoch' in data and not isNamedTestnet:
if data['deneb_fork_epoch'] != 0:
out['config']['cancunTime'] = \
int(data['genesis_timestamp']) + \
int(data['genesis_delay']) + \
int(data['deneb_fork_epoch']) * ( 32 if data['preset_base']=='mainnet' else 8 ) * int(data['slot_duration_in_seconds'])
else:
out['config']['cancunTime'] = 0

out['config']['blobSchedule'] = {}
out['config']['blobSchedule']['cancun'] = {
"target": data['target_blobs_per_block_cancun'],
"max": data['max_blobs_per_block_cancun']
}

print(json.dumps(out, indent=' '))
3 changes: 2 additions & 1 deletion apps/el-gen/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ruamel.yaml==0.17.16
web3>=6.15.0
setuptools>=69.1.0
setuptools>=69.1.0
python-dotenv==1.0.1
56 changes: 56 additions & 0 deletions apps/envsubst.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# This script substitutes environment variables in input lines using values from two .env files.
import os
import re
import sys
import json

from dotenv import dotenv_values

# Load default and full environment variables from specified files
# handle the case where the env file is not present
defaults = dotenv_values(os.environ['DEFAULT_ENV_FILE'])

config_file = os.environ.get('FULL_ENV_FILE')
config = dotenv_values(config_file) if config_file and os.path.isfile(config_file) else {}

env_vars = os.environ

# These regular expressions are used to identify and match environment variable patterns in the input text.
# The updated regex matches variables enclosed in either curly braces or square brackets.
# It captures nested structures as well, allowing for more complex variable patterns.
bracket_sub_re = re.compile(r'\$\{([A-Z0-9_]+)\}')
basic_sub_re = re.compile(r'\$([A-Z0-9_]+)')

def sub(m):
# Get variable name from match
var_name = m.group(1)

# Try env vars first, then config, then defaults
cfg = env_vars.get(var_name)

cfg = config.get(var_name)
if cfg is None:
cfg = defaults.get(var_name)

if cfg is None:
raise Exception(f"Missing environment variable: {var_name}")

# Handle JSON-like strings by removing trailing single quotes if present
cfg = cfg.strip().strip("'")

# Try to parse as JSON if it looks like JSON
if cfg.startswith('{') and cfg.endswith('}'):
try:
# Validate it's proper JSON
json.loads(cfg)
return cfg # Return the original string if it's valid JSON
except json.JSONDecodeError:
pass

return cfg

# Read from standard input line by line
for line in sys.stdin:
out = bracket_sub_re.sub(sub, line) # Substitute bracketed variables
out = basic_sub_re.sub(sub, out) # Substitute basic variables
sys.stdout.write(out) # Output the final substituted line
Loading

0 comments on commit 6b49962

Please sign in to comment.