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:
Jacek Sieka 2019-09-01 17:02:49 +02:00 committed by zah
parent 1ec4c5aef7
commit 7b73b40bab
24 changed files with 454 additions and 122 deletions

View File

@ -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 $@

View File

@ -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",
]

View File

@ -1,2 +1 @@
beacon_node
validator_keygen

View File

@ -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)

View File

@ -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) =

View File

@ -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}"

View File

@ -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
View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,3 @@
data
lighthouse
validators

37
interop/README.md Normal file
View 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
View 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
View 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
View 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
View 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"

View File

@ -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

View File

@ -12,6 +12,7 @@ import # Unit test
./test_beaconstate,
./test_block_pool,
./test_helpers,
./test_interop,
./test_ssz,
./test_state_transition,
./test_sync_protocol

View File

@ -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" \

View File

@ -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
View 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