mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-16 16:38:07 +00:00
interop updates
* add interop launcher scripts * stick validator_keygen into beacon_node * fix lmd ghost slot number on missing block * use mocked eth1data when producing blocks * use bls public key method for withdrawal credentials * fix deposit domain * prefer lowercase for a bunch of toHex * build simulation binary in data folder to avoid data types confusion
This commit is contained in:
parent
1ec4c5aef7
commit
7b73b40bab
6
Makefile
6
Makefile
@ -11,7 +11,7 @@ BUILD_SYSTEM_DIR := vendor/nimbus-build-system
|
||||
# we don't want an error here, so we can handle things later, in the build-system-checks target
|
||||
-include $(BUILD_SYSTEM_DIR)/makefiles/variables.mk
|
||||
|
||||
TOOLS := beacon_node validator_keygen bench_bls_sig_agggregation state_sim
|
||||
TOOLS := beacon_node bench_bls_sig_agggregation state_sim
|
||||
TOOLS_DIRS := beacon_chain benchmarks research
|
||||
TOOLS_CSV := $(subst $(SPACE),$(COMMA),$(TOOLS))
|
||||
|
||||
@ -75,8 +75,8 @@ $(TOOLS): | build deps nat-libs p2pd
|
||||
clean_eth2_network_simulation_files:
|
||||
rm -rf tests/simulation/{data,validators}
|
||||
|
||||
eth2_network_simulation: | beacon_node validator_keygen clean_eth2_network_simulation_files
|
||||
SKIP_BUILDS=1 GIT_ROOT="$$PWD" BUILD_OUTPUTS_DIR="./build" $(ENV_SCRIPT) tests/simulation/start.sh
|
||||
eth2_network_simulation: | build deps nat-libs p2pd clean_eth2_network_simulation_files
|
||||
GIT_ROOT="$$PWD" BUILD_OUTPUTS_DIR="./build" $(ENV_SCRIPT) tests/simulation/start.sh
|
||||
|
||||
testnet0 testnet1: | build deps nat-libs p2pd
|
||||
NIM_PARAMS="$(NIM_PARAMS)" $(ENV_SCRIPT) scripts/build_testnet_node.sh $@
|
||||
|
@ -11,7 +11,6 @@ license = "MIT or Apache License 2.0"
|
||||
installDirs = @["beacon_chain", "research"]
|
||||
bin = @[
|
||||
"beacon_chain/beacon_node",
|
||||
"beacon_chain/validator_keygen",
|
||||
"research/serialized_sizes",
|
||||
"research/state_sim",
|
||||
]
|
||||
|
1
beacon_chain/.gitignore
vendored
1
beacon_chain/.gitignore
vendored
@ -1,2 +1 @@
|
||||
beacon_node
|
||||
validator_keygen
|
||||
|
@ -9,7 +9,7 @@ import
|
||||
conf, time, state_transition, fork_choice, ssz, beacon_chain_db,
|
||||
validator_pool, extras, attestation_pool, block_pool, eth2_network,
|
||||
beacon_node_types, mainchain_monitor, trusted_state_snapshots, version,
|
||||
sync_protocol, request_manager, genesis
|
||||
sync_protocol, request_manager, genesis, validator_keygen, interop
|
||||
|
||||
const
|
||||
topicBeaconBlocks = "/eth2/beacon_block/ssz"
|
||||
@ -281,19 +281,14 @@ proc updateHead(node: BeaconNode, slot: Slot): BlockRef =
|
||||
justifiedHead = node.blockPool.latestJustifiedBlock()
|
||||
|
||||
debug "Preparing for fork choice",
|
||||
justifiedHeadRoot = shortLog(justifiedHead.root),
|
||||
justifiedHeadRoot = shortLog(justifiedHead.blck.root),
|
||||
justifiedHeadSlot = shortLog(justifiedHead.slot),
|
||||
justifiedHeadEpoch = shortLog(justifiedHead.slot.compute_epoch_of_slot),
|
||||
connectedPeers = node.network.peersCount
|
||||
|
||||
# TODO slot number is wrong here, it should be the start of the epoch that
|
||||
# got finalized:
|
||||
# https://github.com/ethereum/eth2.0-specs/issues/768
|
||||
let newHead = node.blockPool.withState(
|
||||
node.justifiedStateCache,
|
||||
BlockSlot(blck: justifiedHead, slot: justifiedHead.slot)):
|
||||
|
||||
lmdGhost(node.attestationPool, state, justifiedHead)
|
||||
node.justifiedStateCache, justifiedHead):
|
||||
lmdGhost(node.attestationPool, state, justifiedHead.blck)
|
||||
|
||||
info "Fork chosen",
|
||||
newHeadSlot = shortLog(newHead.slot),
|
||||
@ -356,10 +351,13 @@ proc proposeBlock(node: BeaconNode,
|
||||
node.stateCache, BlockSlot(blck: head, slot: slot - 1)):
|
||||
# To create a block, we'll first apply a partial block to the state, skipping
|
||||
# some validations.
|
||||
# TODO monitor main chain here: node.mainchainMonitor.getBeaconBlockRef()
|
||||
|
||||
let
|
||||
blockBody = BeaconBlockBody(
|
||||
randao_reveal: validator.genRandaoReveal(state, slot),
|
||||
eth1_data: node.mainchainMonitor.getBeaconBlockRef(),
|
||||
eth1_data: get_eth1data_stub(
|
||||
state.eth1_deposit_index, slot.compute_epoch_of_slot()),
|
||||
attestations:
|
||||
node.attestationPool.getAttestationsForBlock(state, slot))
|
||||
|
||||
@ -793,13 +791,13 @@ when isMainModule:
|
||||
except SerializationError as err:
|
||||
stderr.write "Error while loading a deposit file:\n"
|
||||
stderr.write err.formatMsg(depositFile), "\n"
|
||||
stderr.write "Please regenerate the deposit files by running validator_keygen again\n"
|
||||
stderr.write "Please regenerate the deposit files by running makeDeposits again\n"
|
||||
quit 1
|
||||
|
||||
let initialState = initialize_beacon_state_from_eth1(
|
||||
deposits,
|
||||
uint64(times.toUnix(times.getTime()) + config.genesisOffset),
|
||||
Eth1Data(), {skipValidation})
|
||||
get_eth1data_stub(deposits.len().uint64, 0.Epoch), {skipValidation})
|
||||
|
||||
doAssert initialState.validators.len > 0
|
||||
|
||||
@ -861,3 +859,11 @@ when isMainModule:
|
||||
dynamicLogScope(node = node.nickname): node.start(node.stateCache.data.data)
|
||||
else:
|
||||
node.start(node.stateCache.data.data)
|
||||
|
||||
of makeDeposits:
|
||||
let deposits = generateDeposits(
|
||||
config.totalDeposits, config.depositDir, config.randomKeys)
|
||||
|
||||
if config.depositWeb3Url.len() > 0 and config.depositContractAddress.len() > 0:
|
||||
waitFor sendDeposits(
|
||||
deposits, config.depositWeb3Url, config.depositContractAddress)
|
||||
|
@ -592,7 +592,7 @@ proc updateHead*(pool: BlockPool, state: var StateData, blck: BlockRef) =
|
||||
not pool.heads[n].blck.isAncestorOf(pool.finalizedHead.blck):
|
||||
pool.heads.del(n)
|
||||
|
||||
proc latestJustifiedBlock*(pool: BlockPool): BlockRef =
|
||||
proc latestJustifiedBlock*(pool: BlockPool): BlockSlot =
|
||||
## Return the most recent block that is justified and at least as recent
|
||||
## as the latest finalized block
|
||||
|
||||
@ -602,10 +602,10 @@ proc latestJustifiedBlock*(pool: BlockPool): BlockRef =
|
||||
"Genesis block will be head, if nothing else"
|
||||
|
||||
# Prefer stability: use justified block from current head to break ties!
|
||||
result = pool.head.justified.blck
|
||||
result = pool.head.justified
|
||||
for head in pool.heads[1 ..< ^0]:
|
||||
if head.justified.blck.slot > result.slot:
|
||||
result = head.justified.blck
|
||||
if head.justified.slot > result.slot:
|
||||
result = head.justified
|
||||
|
||||
proc preInit*(
|
||||
T: type BlockPool, db: BeaconChainDB, state: BeaconState, blck: BeaconBlock) =
|
||||
|
@ -17,6 +17,7 @@ type
|
||||
importValidator
|
||||
createTestnet
|
||||
updateTestnet
|
||||
makeDeposits
|
||||
|
||||
BeaconNodeConf* = object
|
||||
logLevel* {.
|
||||
@ -35,6 +36,14 @@ type
|
||||
shortform: "d"
|
||||
defaultValue: config.defaultDataDir().}: OutDir
|
||||
|
||||
depositWeb3Url* {.
|
||||
desc: "URL of the Web3 server to observe Eth1",
|
||||
defaultValue: ""}: string
|
||||
|
||||
depositContractAddress* {.
|
||||
desc: "Address of the deposit contract",
|
||||
defaultValue: ""}: string
|
||||
|
||||
case cmd* {.
|
||||
command
|
||||
defaultValue: noCommand.}: StartUpCommand
|
||||
@ -64,7 +73,7 @@ type
|
||||
|
||||
validators* {.
|
||||
required
|
||||
desc: "Path to a validator private key, as generated by validator_keygen"
|
||||
desc: "Path to a validator private key, as generated by makeDeposits"
|
||||
longform: "validator"
|
||||
shortform: "v".}: seq[ValidatorKeyPath]
|
||||
|
||||
@ -77,14 +86,6 @@ type
|
||||
"If you set this to 'auto', a persistent automatically generated ID will be seleceted for each --dataDir folder"
|
||||
defaultValue: ""}: string
|
||||
|
||||
depositWeb3Url* {.
|
||||
desc: "URL of the Web3 server to observe Eth1",
|
||||
defaultValue: ""}: string
|
||||
|
||||
depositContractAddress* {.
|
||||
desc: "Address of the deposit contract",
|
||||
defaultValue: ""}: string
|
||||
|
||||
of createTestnet:
|
||||
networkId* {.
|
||||
desc: "An unique numeric identifier for the network".}: uint8
|
||||
@ -131,6 +132,16 @@ type
|
||||
of updateTestnet:
|
||||
discard
|
||||
|
||||
of makeDeposits:
|
||||
totalDeposits* {.
|
||||
desc: "Total number of deposits and keys to generate".}: int
|
||||
|
||||
depositDir* {.
|
||||
desc: "Folder to write deposits to", defaultValue: "validators".}: string
|
||||
|
||||
randomKeys* {.
|
||||
desc: "Use random keys (instead of interop keys)", defaultValue: false.}: bool
|
||||
|
||||
proc defaultPort*(config: BeaconNodeConf): int =
|
||||
if config.network == "testnet1": 9100
|
||||
else: 9000
|
||||
@ -155,4 +166,3 @@ proc defaultDataDir*(conf: BeaconNodeConf): string =
|
||||
proc validatorFileBaseName*(validatorIdx: int): string =
|
||||
# there can apparently be tops 4M validators so we use 7 digits..
|
||||
fmt"v{validatorIdx:07}"
|
||||
|
||||
|
@ -12,7 +12,10 @@ proc get_ancestor(blck: BlockRef, slot: Slot): BlockRef =
|
||||
else:
|
||||
get_ancestor(blck.parent, slot)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.4.0/specs/core/0_beacon-chain.md#beacon-chain-fork-choice-rule
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.3/specs/core/0_fork-choice.md
|
||||
# The structure of this code differs from the spec since we use a different
|
||||
# strategy for storing states and justification points - it should nonetheless
|
||||
# be close in terms of functionality.
|
||||
proc lmdGhost*(
|
||||
pool: AttestationPool, start_state: BeaconState,
|
||||
start_block: BlockRef): BlockRef =
|
||||
@ -22,28 +25,22 @@ proc lmdGhost*(
|
||||
# Nim implementation for cumulative frequencies at
|
||||
# https://github.com/numforge/laser/blob/990e59fffe50779cdef33aa0b8f22da19e1eb328/benchmarks/random_sampling/fenwicktree.nim
|
||||
|
||||
const FORK_CHOICE_BALANCE_INCREMENT = 2'u64^0 * 10'u64^9
|
||||
|
||||
let
|
||||
active_validator_indices =
|
||||
get_active_validator_indices(
|
||||
start_state, compute_epoch_of_slot(start_state.slot))
|
||||
|
||||
var attestation_targets: seq[tuple[validator: ValidatorIndex, blck: BlockRef]]
|
||||
var latest_messages: seq[tuple[validator: ValidatorIndex, blck: BlockRef]]
|
||||
for i in active_validator_indices:
|
||||
let pubKey = start_state.validators[i].pubkey
|
||||
if (let vote = pool.latestAttestation(pubKey); not vote.isNil):
|
||||
attestation_targets.add((i, vote))
|
||||
latest_messages.add((i, vote))
|
||||
|
||||
template get_vote_count(blck: BlockRef): uint64 =
|
||||
template get_latest_attesting_balance(blck: BlockRef): uint64 =
|
||||
var res: uint64
|
||||
for validator_index, target in attestation_targets.items():
|
||||
for validator_index, target in latest_messages.items():
|
||||
if get_ancestor(target, blck.slot) == blck:
|
||||
# The div on the balance is to chop off the insignification bits that
|
||||
# fluctuate a lot epoch to epoch to have a more stable fork choice
|
||||
res +=
|
||||
start_state.validators[validator_index].effective_balance div
|
||||
FORK_CHOICE_BALANCE_INCREMENT
|
||||
res += start_state.validators[validator_index].effective_balance
|
||||
res
|
||||
|
||||
var head = start_block
|
||||
@ -53,9 +50,9 @@ proc lmdGhost*(
|
||||
|
||||
head = head.children[0]
|
||||
var
|
||||
headCount = get_vote_count(head)
|
||||
headCount = get_latest_attesting_balance(head)
|
||||
|
||||
for i in 1..<head.children.len:
|
||||
if (let hc = get_vote_count(head.children[i]); hc > headCount):
|
||||
if (let hc = get_latest_attesting_balance(head.children[i]); hc > headCount):
|
||||
head = head.children[i]
|
||||
headCount = hc
|
||||
|
60
beacon_chain/interop.nim
Normal file
60
beacon_chain/interop.nim
Normal file
@ -0,0 +1,60 @@
|
||||
import
|
||||
os, ospaths,
|
||||
stew/endians2, stint,
|
||||
./conf, ./extras, ./ssz,
|
||||
spec/[crypto, datatypes, digest, helpers]
|
||||
|
||||
func get_eth1data_stub*(deposit_count: uint64, current_epoch: Epoch): Eth1Data =
|
||||
# https://github.com/ethereum/eth2.0-pm/blob/e596c70a19e22c7def4fd3519e20ae4022349390/interop/mocked_eth1data/README.md
|
||||
let
|
||||
epochs_per_period = SLOTS_PER_ETH1_VOTING_PERIOD div SLOTS_PER_EPOCH
|
||||
voting_period = current_epoch.uint64 div epochs_per_period.uint64
|
||||
|
||||
Eth1Data(
|
||||
deposit_root: hash_tree_root(voting_period),
|
||||
deposit_count: deposit_count,
|
||||
block_hash: hash_tree_root(hash_tree_root(voting_period).data),
|
||||
)
|
||||
|
||||
when ValidatorPrivKey is BlsValue:
|
||||
func makeInteropPrivKey*(i: int): ValidatorPrivKey =
|
||||
discard
|
||||
{.fatal: "todo/unused?".}
|
||||
else:
|
||||
func makeInteropPrivKey*(i: int): ValidatorPrivKey =
|
||||
var bytes: array[32, byte]
|
||||
bytes[0..7] = uint64(i).toBytesLE()
|
||||
|
||||
let
|
||||
# BLS381-12 curve order - same as milagro but formatted different
|
||||
curveOrder =
|
||||
"52435875175126190479447740508185965837690552500527637822603658699938581184513".parse(UInt256)
|
||||
|
||||
privkeyBytes = eth2hash(bytes)
|
||||
key = (UInt256.fromBytesLE(privkeyBytes.data) mod curveOrder).toBytesBE()
|
||||
|
||||
ValidatorPrivKey.init(key)
|
||||
|
||||
func makeWithdrawalCredentials*(k: ValidatorPubKey): Eth2Digest =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.3/specs/core/0_deposit-contract.md#withdrawal-credentials
|
||||
var bytes = eth2hash(k.getBytes())
|
||||
bytes.data[0] = BLS_WITHDRAWAL_PREFIX.uint8
|
||||
bytes
|
||||
|
||||
func makeDeposit*(
|
||||
pubkey: ValidatorPubKey, privkey: ValidatorPrivKey, epoch = 0.Epoch,
|
||||
amount: Gwei = MAX_EFFECTIVE_BALANCE.Gwei,
|
||||
flags: UpdateFlags = {}): Deposit =
|
||||
var
|
||||
ret = Deposit(
|
||||
data: DepositData(
|
||||
amount: amount,
|
||||
pubkey: pubkey,
|
||||
withdrawal_credentials: makeWithdrawalCredentials(pubkey)))
|
||||
|
||||
if skipValidation notin flags:
|
||||
ret.data.signature =
|
||||
bls_sign(
|
||||
privkey, signing_root(ret.data).data, compute_domain(DOMAIN_DEPOSIT))
|
||||
|
||||
ret
|
@ -79,10 +79,9 @@ func process_deposit*(
|
||||
|
||||
if index == -1:
|
||||
# Verify the deposit signature (proof of possession)
|
||||
# TODO should be get_domain(state, DOMAIN_DEPOSIT)
|
||||
if skipValidation notin flags and not bls_verify(
|
||||
pubkey, signing_root(deposit.data).data, deposit.data.signature,
|
||||
3'u64):
|
||||
compute_domain(DOMAIN_DEPOSIT)):
|
||||
return false
|
||||
|
||||
# Add validator and balance entries
|
||||
|
@ -98,7 +98,7 @@ func `$`*(x: BlsValue): string =
|
||||
else:
|
||||
# r: is short for random. The prefix must be short
|
||||
# due to the mechanics of the `shortLog` function.
|
||||
"r:" & toHex(x.blob)
|
||||
"r:" & toHex(x.blob, true)
|
||||
|
||||
func `==`*(a, b: BlsValue): bool =
|
||||
if a.kind != b.kind: return false
|
||||
|
@ -20,7 +20,8 @@
|
||||
# we call this function `eth2hash`, and it outputs a `Eth2Digest`. Easy to sed :)
|
||||
|
||||
import
|
||||
nimcrypto/[sha2, hash], eth/common/eth_types_json_serialization,
|
||||
chronicles,
|
||||
nimcrypto/[sha2, hash, utils], eth/common/eth_types_json_serialization,
|
||||
hashes
|
||||
|
||||
export
|
||||
@ -30,9 +31,12 @@ type
|
||||
Eth2Digest* = MDigest[32 * 8] ## `hash32` from spec
|
||||
Eth2Hash* = sha256 ## Context for hash function
|
||||
|
||||
chronicles.formatIt Eth2Digest:
|
||||
mixin toHex
|
||||
it.data.toHex(true)
|
||||
|
||||
func shortLog*(x: Eth2Digest): string =
|
||||
# result = is needed to fix https://github.com/status-im/nim-beacon-chain/issues/209
|
||||
result = ($x)[0..7]
|
||||
x.data.toHex(true)[0..7]
|
||||
|
||||
# TODO: expose an in-place digest function
|
||||
# when hashing in loop or into a buffer
|
||||
|
@ -145,8 +145,9 @@ func int_to_bytes4*(x: uint64): array[4, byte] =
|
||||
result[3] = ((x shr 24) and 0xff).byte
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.3/specs/core/0_beacon-chain.md#compute_domain
|
||||
func compute_domain*(domain_type: DomainType, fork_version: array[4, byte]):
|
||||
uint64 =
|
||||
func compute_domain*(
|
||||
domain_type: DomainType,
|
||||
fork_version: array[4, byte] = [0'u8, 0, 0, 0]): uint64 =
|
||||
var buf: array[8, byte]
|
||||
buf[0..3] = int_to_bytes4(domain_type.uint64)
|
||||
buf[4..7] = fork_version
|
||||
|
@ -1,8 +1,7 @@
|
||||
import
|
||||
os, ospaths, strutils, strformat, json,
|
||||
chronos, blscurve, nimcrypto, json_serialization, confutils, web3, stint,
|
||||
spec/[datatypes, digest, crypto], conf, time, ssz,
|
||||
../tests/testutil
|
||||
os, ospaths, strutils, strformat,
|
||||
chronicles, chronos, blscurve, nimcrypto, json_serialization, web3, stint,
|
||||
spec/[datatypes, digest, crypto], conf, time, ssz, interop
|
||||
|
||||
contract(DepositContract):
|
||||
proc deposit(pubkey: Bytes48, withdrawalCredentials: Bytes32, signature: Bytes96)
|
||||
@ -15,20 +14,12 @@ proc writeFile(filename: string, value: auto) =
|
||||
Json.saveFile(filename, value, pretty = true)
|
||||
echo "Wrote ", filename
|
||||
|
||||
|
||||
proc ethToWei(eth: UInt256): UInt256 =
|
||||
eth * 1000000000000000000.u256
|
||||
|
||||
proc main(totalValidators: int, outputDir: string, generateFakeKeys: bool, depositWeb3Url, depositContractAddress: string) {.async.} =
|
||||
var web3: Web3
|
||||
var contractAddress: Address
|
||||
var eth1Addresses: seq[Address]
|
||||
|
||||
if depositWeb3Url.len > 0:
|
||||
web3 = await newWeb3(depositWeb3Url)
|
||||
contractAddress = Address.fromHex(depositContractAddress)
|
||||
eth1Addresses = await web3.provider.eth_accounts()
|
||||
|
||||
proc generateDeposits*(
|
||||
totalValidators: int, outputDir: string, randomKeys: bool): seq[Deposit] =
|
||||
info "Generating deposits", totalValidators, outputDir, randomKeys
|
||||
for i in 0 ..< totalValidators:
|
||||
let
|
||||
v = validatorFileBaseName(i)
|
||||
@ -36,44 +27,49 @@ proc main(totalValidators: int, outputDir: string, generateFakeKeys: bool, depos
|
||||
privKeyFn = outputDir / v & ".privkey"
|
||||
|
||||
if existsFile(depositFn) and existsFile(privKeyFn):
|
||||
continue
|
||||
try:
|
||||
result.add Json.loadFile(depositFn, Deposit)
|
||||
continue
|
||||
except: # CatchableError?
|
||||
discard
|
||||
|
||||
let
|
||||
privKey = if generateFakeKeys: makeFakeValidatorPrivKey(i)
|
||||
else: ValidatorPrivKey.random
|
||||
privKey = if randomKeys: ValidatorPrivKey.random
|
||||
else: makeInteropPrivKey(i)
|
||||
pubKey = privKey.pubKey()
|
||||
|
||||
let
|
||||
withdrawalCredentials = makeFakeHash(i)
|
||||
domain = 3'u64
|
||||
|
||||
var
|
||||
dp = Deposit(
|
||||
data: DepositData(
|
||||
amount: MAX_EFFECTIVE_BALANCE,
|
||||
pubkey: pubKey,
|
||||
withdrawal_credentials: withdrawalCredentials))
|
||||
|
||||
dp.data.signature =
|
||||
bls_sign(privkey, signing_root(dp.data).data,
|
||||
domain)
|
||||
let dp = makeDeposit(pubKey, privKey)
|
||||
|
||||
writeTextFile(privKeyFn, $privKey)
|
||||
writeFile(depositFn, dp)
|
||||
|
||||
if not web3.isNil:
|
||||
web3.defaultAccount = eth1Addresses[i]
|
||||
let depositContract = web3.contractSender(DepositContract, contractAddress)
|
||||
let tx = await depositContract.deposit(Bytes48(pubKey.getBytes()), Bytes32(withdrawalCredentials.data), Bytes96(dp.data.signature.getBytes())).send(value = 32.u256.ethToWei)
|
||||
result.add(dp)
|
||||
|
||||
if generateFakeKeys:
|
||||
echo "Keys generated by this tool are only for testing!"
|
||||
proc sendDeposits*(
|
||||
deposits: seq[Deposit],
|
||||
depositWeb3Url, depositContractAddress: string) {.async.} =
|
||||
let
|
||||
web3 = await newWeb3(depositWeb3Url)
|
||||
contractAddress = Address.fromHex(depositContractAddress)
|
||||
eth1Addresses = await web3.provider.eth_accounts()
|
||||
|
||||
for i, dp in deposits:
|
||||
web3.defaultAccount = eth1Addresses[i]
|
||||
let depositContract = web3.contractSender(DepositContract, contractAddress)
|
||||
let tx = await depositContract.deposit(
|
||||
Bytes48(dp.data.pubKey.getBytes()),
|
||||
Bytes32(dp.data.withdrawal_credentials.data),
|
||||
Bytes96(dp.data.signature.getBytes())).send(value = 32.u256.ethToWei)
|
||||
|
||||
cli do (totalValidators: int = 125000,
|
||||
outputDir: string = "validators",
|
||||
generateFakeKeys = false,
|
||||
depositWeb3Url: string = "",
|
||||
depositContractAddress: string = ""):
|
||||
when isMainModule:
|
||||
import confutils
|
||||
|
||||
waitFor main(totalValidators, outputDir, generateFakeKeys, depositWeb3Url, depositContractAddress)
|
||||
cli do (totalValidators: int = 125000,
|
||||
outputDir: string = "validators",
|
||||
randomKeys: bool = false,
|
||||
depositWeb3Url: string = "",
|
||||
depositContractAddress: string = ""):
|
||||
let deposits = generateDeposits(totalValidators, outputDir, randomKeys)
|
||||
|
||||
if depositWeb3Url.len() > 0 and depositContractAddress.len() > 0:
|
||||
waitFor sendDeposits(deposits, depositWeb3Url, depositContractAddress)
|
||||
|
3
interop/.gitignore
vendored
Normal file
3
interop/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
data
|
||||
lighthouse
|
||||
validators
|
37
interop/README.md
Normal file
37
interop/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
This folder contains scripts for launching the nimbus beacon chain node in a configuration appropriate for [interop](https://github.com/ethereum/eth2.0-pm/tree/master/interop).
|
||||
|
||||
## Building
|
||||
|
||||
In general, follow the build instructions of `nim-beacon-chain` as documented in the main repo - make sure to set up your build environment with all necessary system libraries as documented there:
|
||||
|
||||
```bash
|
||||
# Clone repo
|
||||
git clone https://github.com/status-im/nim-beacon-chain.git
|
||||
|
||||
cd nim-beacon-chain
|
||||
|
||||
make # prepare build system (cloning the correct submodules)
|
||||
make update deps # build dependencies
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
Look in the scripts for options - the default config is a small setup using the `minimal` state spec.
|
||||
|
||||
```
|
||||
# Clear data from previous run, then start a new simulation
|
||||
rm -rf data ; ./start.sh
|
||||
|
||||
# In a separate terminal, can run another beacon node, such as lighthouse:
|
||||
./run_lighthouse.sh
|
||||
```
|
||||
|
||||
## Diagnostics
|
||||
|
||||
```bash
|
||||
# Nimbus genesis state
|
||||
less data/state_snapshot.json
|
||||
|
||||
# Lighthouse genesis state
|
||||
curl localhost:5052/beacon/state?slot=0 | jq . | sed 's/"0x/"/' | /tmp/lighthouse_state.json
|
||||
```
|
32
interop/run_lighthouse.sh
Executable file
32
interop/run_lighthouse.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Helper script for running a lighthouse node and connecting to the beacon node
|
||||
# that's set up by start.sh
|
||||
|
||||
# https://github.com/sigp/lighthouse/blob/master/docs/interop.md
|
||||
|
||||
cargo_path=$(which cargo)
|
||||
[[ -x "$cargo_path" ]] || { echo "install rust first (https://rust-lang.org)"; exit 1; }
|
||||
|
||||
[[ -d "lighthouse" ]] || {
|
||||
git clone https://github.com/sigp/lighthouse.git
|
||||
cd lighthouse
|
||||
git checkout interop # temporary interop branch - will get merged soon I expect!
|
||||
cargo update
|
||||
cd ..
|
||||
}
|
||||
|
||||
# Fetch genesis time, as set up by start.sh
|
||||
genesis_time=$(grep -oP '(?<=genesis_time": )\w+(?=,)' data/state_snapshot.json)
|
||||
|
||||
cd lighthouse
|
||||
cargo build
|
||||
|
||||
cd target/debug
|
||||
|
||||
#$export RUST_LOG=libp2p=trace,multistream=trace,gossipsub=trace
|
||||
|
||||
# fresh start!
|
||||
rm -rf ~/.lighthouse
|
||||
|
||||
./beacon_node --libp2p-addresses="/ip4/127.0.0.1/tcp/50000" --api --rpc testnet --spec minimal quick 16 $genesis_time
|
35
interop/run_node.sh
Executable file
35
interop/run_node.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
. $(dirname $0)/vars.sh
|
||||
cd "$GIT_ROOT"
|
||||
|
||||
DATA_DIR="${SIMULATION_DIR}/node-${1}"
|
||||
|
||||
V_PREFIX="${VALIDATORS_DIR}/v$(printf '%06d' ${1})"
|
||||
PORT=$(printf '5%04d' ${1})
|
||||
|
||||
NAT_FLAG="--nat:none"
|
||||
if [ "${NAT:-}" == "1" ]; then
|
||||
NAT_FLAG="--nat:any"
|
||||
fi
|
||||
|
||||
FIRST_VALIDATOR_IDX=$(( (NUM_VALIDATORS / ($NUM_NODES + $NUM_MISSING_NODES)) * $1 ))
|
||||
LAST_VALIDATOR_IDX=$(( (NUM_VALIDATORS / ($NUM_NODES + $NUM_MISSING_NODES)) * ($1 + 1) - 1 ))
|
||||
|
||||
mkdir -p $DATA_DIR/validators
|
||||
rm -f $DATA_DIR/validators/*
|
||||
|
||||
pushd $VALIDATORS_DIR
|
||||
cp $(seq -s " " -f v%07g.privkey $FIRST_VALIDATOR_IDX $LAST_VALIDATOR_IDX) $DATA_DIR/validators
|
||||
popd
|
||||
|
||||
$BEACON_NODE_BIN \
|
||||
--network:$NETWORK_METADATA_FILE \
|
||||
--dataDir:$DATA_DIR \
|
||||
--nodename:${1} \
|
||||
--tcpPort:$PORT \
|
||||
--udpPort:$PORT \
|
||||
$NAT_FLAG \
|
||||
--stateSnapshot:$SNAPSHOT_FILE
|
100
interop/start.sh
Executable file
100
interop/start.sh
Executable file
@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Read in variables
|
||||
. "$(dirname "$0")/vars.sh"
|
||||
|
||||
cd "$SIM_ROOT"
|
||||
mkdir -p "$SIMULATION_DIR"
|
||||
mkdir -p "$VALIDATORS_DIR"
|
||||
|
||||
cd "$GIT_ROOT"
|
||||
|
||||
make update deps
|
||||
|
||||
NIMFLAGS="-d:chronicles_log_level=DEBUG --hints:off --opt:speed --debuginfo"
|
||||
|
||||
# For interop, we run the minimal config
|
||||
DEFS="-d:const_preset=minimal"
|
||||
|
||||
LAST_VALIDATOR_NUM=$(( NUM_VALIDATORS - 1 ))
|
||||
LAST_VALIDATOR="$VALIDATORS_DIR/v$(printf '%07d' $LAST_VALIDATOR_NUM).deposit.json"
|
||||
|
||||
[[ -x "$BEACON_NODE_BIN" ]] || {
|
||||
echo "Building $BEACON_NODE_BIN ($DEFS)"
|
||||
"$SIM_ROOT/../env.sh" nim c -o:"$BEACON_NODE_BIN" $NIMFLAGS $DEFS beacon_chain/beacon_node
|
||||
}
|
||||
|
||||
if [ ! -f "${LAST_VALIDATOR}" ]; then
|
||||
$BEACON_NODE_BIN makeDeposits \
|
||||
--totalDeposits="${NUM_VALIDATORS}" \
|
||||
--depositDir="$VALIDATORS_DIR" \
|
||||
--randomKeys=false
|
||||
fi
|
||||
|
||||
if [ ! -f "${SNAPSHOT_FILE}" ]; then
|
||||
$BEACON_NODE_BIN \
|
||||
--dataDir="${SIMULATION_DIR}/node-0" \
|
||||
createTestnet \
|
||||
--networkId=1000 \
|
||||
--validatorsDir="${VALIDATORS_DIR}" \
|
||||
--totalValidators="${NUM_VALIDATORS}" \
|
||||
--outputGenesis="${SNAPSHOT_FILE}" \
|
||||
--outputNetwork="${NETWORK_METADATA_FILE}" \
|
||||
--bootstrapAddress=127.0.0.1 \
|
||||
--bootstrapPort=50000 \
|
||||
--genesisOffset=5 # Delay in seconds
|
||||
fi
|
||||
|
||||
# Delete any leftover address files from a previous session
|
||||
if [ -f "${MASTER_NODE_ADDRESS_FILE}" ]; then
|
||||
rm "${MASTER_NODE_ADDRESS_FILE}"
|
||||
fi
|
||||
|
||||
# multitail support
|
||||
MULTITAIL="${MULTITAIL:-multitail}" # to allow overriding the program name
|
||||
USE_MULTITAIL="${USE_MULTITAIL:-no}" # make it an opt-in
|
||||
type "$MULTITAIL" &>/dev/null || USE_MULTITAIL="no"
|
||||
|
||||
# Kill child processes on Ctrl-C by sending SIGTERM to the whole process group,
|
||||
# passing the negative PID of this shell instance to the "kill" command.
|
||||
# Trap and ignore SIGTERM, so we don't kill this process along with its children.
|
||||
if [ "$USE_MULTITAIL" = "no" ]; then
|
||||
trap '' SIGTERM
|
||||
trap 'kill -- -$$' SIGINT EXIT
|
||||
fi
|
||||
|
||||
COMMANDS=()
|
||||
LAST_NODE=$(( NUM_NODES - 1 ))
|
||||
|
||||
for i in $(seq 0 $LAST_NODE); do
|
||||
if [[ "$i" == "0" ]]; then
|
||||
sleep 0
|
||||
elif [ "$USE_MULTITAIL" = "no" ]; then
|
||||
# Wait for the master node to write out its address file
|
||||
while [ ! -f "${MASTER_NODE_ADDRESS_FILE}" ]; do
|
||||
sleep 0.1
|
||||
done
|
||||
fi
|
||||
|
||||
CMD="${SIM_ROOT}/run_node.sh $i"
|
||||
|
||||
if [ "$USE_MULTITAIL" != "no" ]; then
|
||||
if [ "$i" = "0" ]; then
|
||||
SLEEP="0"
|
||||
else
|
||||
SLEEP="2"
|
||||
fi
|
||||
# "multitail" closes the corresponding panel when a command exits, so let's make sure it doesn't exit
|
||||
COMMANDS+=( " -cT ansi -t 'node #$i' -l 'sleep $SLEEP; $CMD; echo [node execution completed]; while true; do sleep 100; done'" )
|
||||
else
|
||||
eval "${CMD}" &
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$USE_MULTITAIL" != "no" ]; then
|
||||
eval $MULTITAIL -s 3 -M 0 -x \"Nimbus beacon chain\" "${COMMANDS[@]}"
|
||||
else
|
||||
wait # Stop when all nodes have gone down
|
||||
fi
|
25
interop/vars.sh
Normal file
25
interop/vars.sh
Normal file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
PWD_CMD="pwd"
|
||||
# get native Windows paths on Mingw
|
||||
uname | grep -qi mingw && PWD_CMD="pwd -W"
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
SIM_ROOT="$($PWD_CMD)"
|
||||
|
||||
# Set a default value for the env vars usually supplied by a Makefile
|
||||
cd $(git rev-parse --show-toplevel)
|
||||
: ${GIT_ROOT:="$($PWD_CMD)"}
|
||||
cd - &>/dev/null
|
||||
|
||||
NUM_VALIDATORS=${VALIDATORS:-16}
|
||||
NUM_NODES=${NODES:-3}
|
||||
NUM_MISSING_NODES=${MISSING_NODES:-1}
|
||||
|
||||
SIMULATION_DIR="${SIM_ROOT}/data"
|
||||
VALIDATORS_DIR="${SIM_ROOT}/validators"
|
||||
SNAPSHOT_FILE="${SIMULATION_DIR}/state_snapshot.json"
|
||||
NETWORK_METADATA_FILE="${SIMULATION_DIR}/network.json"
|
||||
BEACON_NODE_BIN="${SIMULATION_DIR}/beacon_node"
|
||||
MASTER_NODE_ADDRESS_FILE="${SIMULATION_DIR}/node-0/beacon_node.address"
|
@ -19,14 +19,17 @@ NETWORK_DIR=$WWW_DIR/$NETWORK_NAME
|
||||
|
||||
NIM_FLAGS="-d:release -d:SECONDS_PER_SLOT=$SECONDS_PER_SLOT -d:SHARD_COUNT=$SHARD_COUNT -d:SLOTS_PER_EPOCH=$SLOTS_PER_EPOCH ${2:-}"
|
||||
|
||||
nim c -d:"network_type=$NETWORK_TYPE" $NIM_FLAGS beacon_chain/beacon_node
|
||||
|
||||
if [ ! -f $NETWORK_DIR/genesis.json ]; then
|
||||
rm -f $NETWORK_DIR/*
|
||||
nim c -r $NIM_FLAGS beacon_chain/validator_keygen \
|
||||
--totalValidators=$VALIDATOR_COUNT \
|
||||
--outputDir="$NETWORK_DIR"
|
||||
beacon_chain/beacon_node makeDeposits \
|
||||
--totalDeposits=$VALIDATOR_COUNT \
|
||||
--depositDir="$NETWORK_DIR" \
|
||||
--randomKeys=true
|
||||
fi
|
||||
|
||||
nim c -d:"network_type=$NETWORK_TYPE" -r $NIM_FLAGS beacon_chain/beacon_node \
|
||||
beacon_chain/beacon_node \
|
||||
--network=$NETWORK_NAME \
|
||||
--dataDir=$DATA_DIR/node-0 \
|
||||
createTestnet \
|
||||
@ -39,4 +42,3 @@ nim c -d:"network_type=$NETWORK_TYPE" -r $NIM_FLAGS beacon_chain/beacon_node \
|
||||
--bootstrapAddress=$PUBLIC_IP \
|
||||
--bootstrapPort=$BOOTSTRAP_PORT \
|
||||
--genesisOffset=600 # Delay in seconds
|
||||
|
||||
|
@ -12,6 +12,7 @@ import # Unit test
|
||||
./test_beaconstate,
|
||||
./test_block_pool,
|
||||
./test_helpers,
|
||||
./test_interop,
|
||||
./test_ssz,
|
||||
./test_state_transition,
|
||||
./test_sync_protocol
|
||||
|
@ -15,10 +15,12 @@ mkdir -p "$SIMULATION_DIR"
|
||||
mkdir -p "$VALIDATORS_DIR"
|
||||
|
||||
cd "$GIT_ROOT"
|
||||
mkdir -p "${BUILD_OUTPUTS_DIR}"
|
||||
|
||||
NIMFLAGS="-d:chronicles_log_level=DEBUG --hints:off --opt:speed --debuginfo"
|
||||
|
||||
# Run with "SHARD_COUNT=4 ./start.sh" to change these
|
||||
DEFS="-d:chronicles_log_level=DEBUG "
|
||||
DEFS=""
|
||||
|
||||
DEFS+="-d:SHARD_COUNT=${SHARD_COUNT:-16} " # Spec default: 1024
|
||||
DEFS+="-d:SLOTS_PER_EPOCH=${SLOTS_PER_EPOCH:-16} " # Spec default: 64
|
||||
DEFS+="-d:SECONDS_PER_SLOT=${SECONDS_PER_SLOT:-6} " # Spec default: 6
|
||||
@ -26,30 +28,26 @@ DEFS+="-d:SECONDS_PER_SLOT=${SECONDS_PER_SLOT:-6} " # Spec default: 6
|
||||
LAST_VALIDATOR_NUM=$(( NUM_VALIDATORS - 1 ))
|
||||
LAST_VALIDATOR="$VALIDATORS_DIR/v$(printf '%07d' $LAST_VALIDATOR_NUM).deposit.json"
|
||||
|
||||
if [ ! -f "${LAST_VALIDATOR}" ]; then
|
||||
if [[ -z "$SKIP_BUILDS" ]]; then
|
||||
nim c -o:"$VALIDATOR_KEYGEN_BIN" $DEFS -d:release beacon_chain/validator_keygen
|
||||
nim c -o:"$DEPLOY_DEPOSIT_CONTRACT_BIN" $DEFS -d:release beacon_chain/deploy_deposit_contract
|
||||
echo "Building $BEACON_NODE_BIN ($DEFS)"
|
||||
"$SIM_ROOT/../../env.sh" nim c -o:"$BEACON_NODE_BIN" $NIMFLAGS $DEFS beacon_chain/beacon_node
|
||||
|
||||
fi
|
||||
if [ ! -f "${LAST_VALIDATOR}" ]; then
|
||||
echo Building $DEPLOY_DEPOSIT_CONTRACT_BIN
|
||||
"$SIM_ROOT/../../env.sh" nim c -o:"$DEPLOY_DEPOSIT_CONTRACT_BIN" $NIMFLAGS $DEFS --hints:off -d:release beacon_chain/deploy_deposit_contract
|
||||
|
||||
if [ "$DEPOSIT_WEB3_URL_ARG" != "" ]; then
|
||||
DEPOSIT_CONTRACT_ADDRESS=$($DEPLOY_DEPOSIT_CONTRACT_BIN $DEPOSIT_WEB3_URL_ARG)
|
||||
export DEPOSIT_CONTRACT_ADDRESS
|
||||
fi
|
||||
|
||||
$VALIDATOR_KEYGEN_BIN \
|
||||
--totalValidators="${NUM_VALIDATORS}" \
|
||||
--outputDir="$VALIDATORS_DIR" \
|
||||
--generateFakeKeys=yes \
|
||||
$BEACON_NODE_BIN makeDeposits \
|
||||
--totalDeposits="${NUM_VALIDATORS}" \
|
||||
--depositDir="$VALIDATORS_DIR" \
|
||||
--randomKeys=false \
|
||||
$DEPOSIT_WEB3_URL_ARG \
|
||||
--depositContractAddress="${DEPOSIT_CONTRACT_ADDRESS}"
|
||||
fi
|
||||
|
||||
if [[ -z "$SKIP_BUILDS" ]]; then
|
||||
nim c -o:"$BEACON_NODE_BIN" $DEFS --opt:speed --debuginfo beacon_chain/beacon_node
|
||||
fi
|
||||
|
||||
if [ ! -f "${SNAPSHOT_FILE}" ]; then
|
||||
$BEACON_NODE_BIN \
|
||||
--dataDir="${SIMULATION_DIR}/node-0" \
|
||||
|
@ -5,14 +5,13 @@ PWD_CMD="pwd"
|
||||
uname | grep -qi mingw && PWD_CMD="pwd -W"
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
SIM_ROOT="$($PWD_CMD)"
|
||||
|
||||
# Set a default value for the env vars usually supplied by a Makefile
|
||||
cd $(git rev-parse --show-toplevel)
|
||||
: ${GIT_ROOT:="$($PWD_CMD)"}
|
||||
cd - &>/dev/null
|
||||
: ${SKIP_BUILDS:=""}
|
||||
: ${BUILD_OUTPUTS_DIR:="$GIT_ROOT/build"}
|
||||
|
||||
NUM_VALIDATORS=${VALIDATORS:-1000}
|
||||
NUM_NODES=${NODES:-4}
|
||||
@ -22,7 +21,6 @@ SIMULATION_DIR="${SIM_ROOT}/data"
|
||||
VALIDATORS_DIR="${SIM_ROOT}/validators"
|
||||
SNAPSHOT_FILE="${SIMULATION_DIR}/state_snapshot.json"
|
||||
NETWORK_METADATA_FILE="${SIMULATION_DIR}/network.json"
|
||||
BEACON_NODE_BIN="${BUILD_OUTPUTS_DIR}/beacon_node"
|
||||
VALIDATOR_KEYGEN_BIN="${BUILD_OUTPUTS_DIR}/validator_keygen"
|
||||
DEPLOY_DEPOSIT_CONTRACT_BIN="${BUILD_OUTPUTS_DIR}/deploy_deposit_contract"
|
||||
BEACON_NODE_BIN="${SIMULATION_DIR}/beacon_node"
|
||||
DEPLOY_DEPOSIT_CONTRACT_BIN="${SIMULATION_DIR}/deploy_deposit_contract"
|
||||
MASTER_NODE_ADDRESS_FILE="${SIMULATION_DIR}/node-0/beacon_node.address"
|
||||
|
30
tests/test_interop.nim
Normal file
30
tests/test_interop.nim
Normal file
@ -0,0 +1,30 @@
|
||||
import
|
||||
unittest, stint, blscurve,
|
||||
../beacon_chain/interop
|
||||
|
||||
# Interop test yaml, found here:
|
||||
# https://github.com/ethereum/eth2.0-pm/blob/a0b9d22fad424574b1307828f867b30237758468/interop/mocked_start/keygen_10_validators.yaml
|
||||
|
||||
const privateKeys = [
|
||||
"0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
|
||||
"0x51d0b65185db6989ab0b560d6deed19c7ead0e24b9b6372cbecb1f26bdfad000",
|
||||
"0x315ed405fafe339603932eebe8dbfd650ce5dafa561f6928664c75db85f97857",
|
||||
"0x25b1166a43c109cb330af8945d364722757c65ed2bfed5444b5a2f057f82d391",
|
||||
"0x3f5615898238c4c4f906b507ee917e9ea1bb69b93f1dbd11a34d229c3b06784b",
|
||||
"0x055794614bc85ed5436c1f5cab586aab6ca84835788621091f4f3b813761e7a8",
|
||||
"0x1023c68852075965e0f7352dee3f76a84a83e7582c181c10179936c6d6348893",
|
||||
"0x3a941600dc41e5d20e818473b817a28507c23cdfdb4b659c15461ee5c71e41f5",
|
||||
"0x066e3bdc0415530e5c7fed6382d5c822c192b620203cf669903e1810a8c67d06",
|
||||
"0x2b3b88a041168a1c4cd04bdd8de7964fd35238f95442dc678514f9dadb81ec34",
|
||||
]
|
||||
|
||||
suite "Interop":
|
||||
test "Mocked start private key":
|
||||
for i, k in privateKeys:
|
||||
let
|
||||
key = makeInteropPrivKey(i)
|
||||
v = k.parse(UInt256, 16)
|
||||
|
||||
check:
|
||||
# getBytes is bigendian and returns full 48 bytes of key..
|
||||
Uint256.fromBytesBE(key.getBytes()[48-32..<48]) == v
|
Loading…
x
Reference in New Issue
Block a user