Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync master with devel #1673

Merged
merged 70 commits into from
Sep 16, 2020
Merged
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
ec61086
update Grafana dashboard
stefantalpalaru Aug 23, 2020
a7a279d
add option to disable discv5 (#1509)
arnetheduck Aug 24, 2020
0edacfe
important book updates (#1561)
unixpi Aug 24, 2020
ddd8489
bump vendor/nim-blscurve (#1560)
stefantalpalaru Aug 24, 2020
ab22bad
bump vendor/nim-metrics (#1565)
stefantalpalaru Aug 25, 2020
f26d6a4
reuse validator key cache better (#1562)
arnetheduck Aug 26, 2020
81788be
Fork choice - almost free pruning - fix #1534 (#1535)
mratsim Aug 26, 2020
b525dc9
Request manager ignores non-critical errors while processing blocks. …
cheatfate Aug 26, 2020
83667dd
harden beacon_pending_deposits metrics calculation against overflow (…
tersec Aug 26, 2020
142f791
Jenkins: run benchmarks (#1524)
stefantalpalaru Aug 26, 2020
950da7a
Medalla guide updates + troubleshooting page (#1571)
unixpi Aug 26, 2020
2073977
Update the Nimbus bootstrap nodes
zah Aug 26, 2020
2081c4f
avoid unnecessary seq allocations (#1573)
arnetheduck Aug 27, 2020
fa1621d
implement clock disparity for attestation validation (#1568)
arnetheduck Aug 27, 2020
c810b64
log getblocks error
arnetheduck Aug 27, 2020
b8da265
add setting for benchmarking and profiling of sqlite block storage ti…
tersec Aug 27, 2020
ecc70c6
remove BUILD_LOG_LEVEL
stefantalpalaru Aug 27, 2020
504e96c
fix medalla-dev
stefantalpalaru Aug 27, 2020
b457da3
update troubleshooting medalla (#1578)
unixpi Aug 27, 2020
f250012
Makefile: add clean-cross task for cross compiling (#1554)
zedeus Aug 28, 2020
d9f926b
bump nim-eth (#1579)
tersec Aug 31, 2020
ab25566
bound block quarantine size (#1564)
tersec Aug 31, 2020
02ddc41
ignore sqlite WAL journals in git; increase logging priority of attes…
tersec Aug 31, 2020
124ec4b
perform slashing check before mutation (#1594)
arnetheduck Sep 1, 2020
43c6a36
remove template expansion file from Git control (#1589)
stefantalpalaru Sep 1, 2020
67d73c4
added the --network=<x> option to the tools for which it matters
onqtam Sep 1, 2020
6000a49
auto-bump nim-libp2p (#1484)
nbc-bump-bot[bot] Sep 1, 2020
65d7787
50/50 bn/vc split for the validator keys ON by default for the testne…
onqtam Sep 1, 2020
d9f9949
use a separate process for the private keys (Off by default) - there …
onqtam Sep 1, 2020
e4a43f7
address issue #1580 (#1600)
tersec Sep 2, 2020
aa3298f
support spaces in $(MAKE) (#1603)
stefantalpalaru Sep 4, 2020
aed57df
avoid hash tree root calculation when loading blocks from database (#…
arnetheduck Sep 4, 2020
456bdc8
address issue #1552 (#1601)
tersec Sep 4, 2020
f61fdf9
bump chronos (#1598)
arnetheduck Sep 4, 2020
c5f8ef8
auto-bump nim-libp2p (#1604)
nbc-bump-bot[bot] Sep 5, 2020
d584591
simplify libp2p logging (#1605)
arnetheduck Sep 6, 2020
4c3dd85
bump libp2p (#1609)
arnetheduck Sep 7, 2020
3d5f24f
stop discarding future epochs; remove a StateCache() construction (#1…
tersec Sep 7, 2020
dde26f3
better state cache reuse (#1612)
arnetheduck Sep 8, 2020
b3b5785
mark comments documenting functions as a whole as such (#1613)
tersec Sep 8, 2020
d0de1a4
Fix some warnings and hints and partly revert #1610 (#1615)
tersec Sep 8, 2020
990bc2e
remove obsolete comment
tersec Sep 9, 2020
5d1ea5e
match log filenames more precisely for logtrace analysis (#1617)
tersec Sep 9, 2020
67f44d4
use V=1 in Jenkinsfile (#1618)
tersec Sep 9, 2020
7c21fa9
bump libp2p, introduce AsyncQueue BufferStream (#1599)
arnetheduck Sep 10, 2020
697bd23
fix low peer count command typo (#1619)
unixpi Sep 10, 2020
a087909
fix req/resp protocol (#1621)
arnetheduck Sep 10, 2020
9db05c4
Bump chronos to help investigate Index problem. (#1625)
cheatfate Sep 11, 2020
8a5a261
Quick fix to prune some states, pending smarter state storage (#1624)
arnetheduck Sep 11, 2020
775683c
await validator duties (#1626)
arnetheduck Sep 11, 2020
c7c9b9d
Syncing V2 (#1602)
cheatfate Sep 11, 2020
48893f1
add ncli_db subcommand to prune database of unnecessary blocks and st…
tersec Sep 11, 2020
eaea3db
remove chronos future tracking from default build (#1628)
arnetheduck Sep 11, 2020
5fff800
Bump nim-eth to use lrucache for discovery sessions (#1622)
kdeme Sep 11, 2020
aca1a31
cleanly close kvstore databases and bump nim-eth (#1630)
tersec Sep 12, 2020
e6c82d1
Update medalla-troubleshooting.md (#1633)
unixpi Sep 13, 2020
7d00ca2
Update medalla-troubleshooting.md (#1635)
unixpi Sep 13, 2020
ca2e7e2
update medalla guide: advanced options (WIP) (#1623)
unixpi Sep 13, 2020
60b8905
reorder startup steps (#1642)
stefantalpalaru Sep 14, 2020
9f21bbd
[WIP] skeleton of attester slashing pool & validators (#1639)
tersec Sep 14, 2020
c76305f
fix some todo (#1645)
arnetheduck Sep 14, 2020
617c295
Bump chronos to fix memory leaks. (#1648)
cheatfate Sep 15, 2020
7e10b98
enable topic unsubscribing and attestation subnet cycling (#1646)
tersec Sep 15, 2020
88eb0c0
auto-bump nim-libp2p (#1606)
nbc-bump-bot[bot] Sep 15, 2020
9abdbda
Fix sync_manager.nim not rewarding peers for good responses. (#1660)
cheatfate Sep 16, 2020
6d8130d
close block_sim database; remove code duplication in exit_pool (#1656)
tersec Sep 16, 2020
6e46325
PeerPool fixes. (#1654)
cheatfate Sep 16, 2020
52548f0
Opt-in Slashing protection + interchange (#1643)
mratsim Sep 16, 2020
df685f0
auto-bump nim-libp2p (#1659)
nbc-bump-bot[bot] Sep 16, 2020
ce85f58
better handling of Ctrl+C during `make update` (#1588)
stefantalpalaru Sep 16, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Opt-in Slashing protection + interchange (#1643)
* Slashing protection + interchange initial commit

* Restrict the when UseSlashingProtection dance in other modules

* Integrate slashing tests in other all_tests

* Add attestation slashing protection support

* Add a message that mention if built with/without slashing protection

* no op the initialization proc

* test slashing protection in Jenkins (temp)

* where to configure NIMFLAGS in Jenkins ...

* Jenkins -> ensure Built with slashing protection

* Add slashing protection complete import

* use Opt.get(otherwise)

* Don't use negation in proc name

* Turn slashing protection on by default
mratsim authored Sep 16, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 52548f079b9ed3d6ad19e9b47a829e9df28a793d
14 changes: 4 additions & 10 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -35,7 +35,9 @@ def runStages() {
sh """#!/bin/bash
set -e
make -j${env.NPROC} V=1
make -j${env.NPROC} V=1 LOG_LEVEL=TRACE NIMFLAGS='-d:testnet_servers_image' beacon_node
make -j${env.NPROC} V=1 LOG_LEVEL=TRACE NIMFLAGS='-d:UseSlashingProtection=true -d:testnet_servers_image' beacon_node
# Miracl fallback
# make -j${env.NPROC} V=1 LOG_LEVEL=TRACE NIMFLAGS='-d:BLS_FORCE_BACKEND=miracl -d:UseSlashingProtection=true -d:testnet_servers_image' beacon_node
"""
}
},
@@ -47,18 +49,11 @@ def runStages() {
// EXECUTOR_NUMBER will be 0 or 1, since we have 2 executors per Jenkins node
sh """#!/bin/bash
set -e
export NIMFLAGS='-d:UseSlashingProtection=true'
./scripts/launch_local_testnet.sh --testnet 0 --nodes 4 --stop-at-epoch 5 --log-level DEBUG --disable-htop --enable-logtrace --data-dir local_testnet0_data --base-port \$(( 9000 + EXECUTOR_NUMBER * 100 )) --base-rpc-port \$(( 7000 + EXECUTOR_NUMBER * 100 )) --base-metrics-port \$(( 8008 + EXECUTOR_NUMBER * 100 )) -- --verify-finalization --discv5:no
./scripts/launch_local_testnet.sh --testnet 1 --nodes 4 --stop-at-epoch 5 --log-level DEBUG --disable-htop --enable-logtrace --data-dir local_testnet1_data --base-port \$(( 9000 + EXECUTOR_NUMBER * 100 )) --base-rpc-port \$(( 7000 + EXECUTOR_NUMBER * 100 )) --base-metrics-port \$(( 8008 + EXECUTOR_NUMBER * 100 )) -- --verify-finalization --discv5:no
"""
}
// stage("testnet finalization - Miracl/Milagro fallback") {
// // EXECUTOR_NUMBER will be 0 or 1, since we have 2 executors per Jenkins node
// sh """#!/bin/bash
// set -e
// NIMFLAGS="-d:BLS_FORCE_BACKEND=miracl" ./scripts/launch_local_testnet.sh --testnet 0 --nodes 4 --stop-at-epoch 5 --log-level INFO --disable-htop --data-dir local_testnet0_data --base-port \$(( 9000 + EXECUTOR_NUMBER * 100 )) --base-rpc-port \$(( 7000 + EXECUTOR_NUMBER * 100 )) --base-metrics-port \$(( 8008 + EXECUTOR_NUMBER * 100 )) -- --verify-finalization
// NIMFLAGS="-d:BLS_FORCE_BACKEND=miracl" ./scripts/launch_local_testnet.sh --testnet 1 --nodes 4 --stop-at-epoch 5 --log-level INFO --disable-htop --data-dir local_testnet1_data --base-port \$(( 9000 + EXECUTOR_NUMBER * 100 )) --base-rpc-port \$(( 7000 + EXECUTOR_NUMBER * 100 )) --base-metrics-port \$(( 8008 + EXECUTOR_NUMBER * 100 )) -- --verify-finalization
// """
// }
}
)
}
@@ -100,4 +95,3 @@ parallel(
}
},
)

17 changes: 8 additions & 9 deletions beacon_chain.nimble
Original file line number Diff line number Diff line change
@@ -61,10 +61,17 @@ task test, "Run all tests":
# Just the part of minimal config which explicitly differs from mainnet
buildAndRunBinary "test_fixture_const_sanity_check", "tests/official/", """-d:const_preset=minimal -d:chronicles_sinks="json[file]""""

# Generic SSZ test, doesn't use consensus objects minimal/mainnet presets
buildAndRunBinary "test_fixture_ssz_generic_types", "tests/official/", """-d:chronicles_log_level=TRACE -d:chronicles_sinks="json[file]""""
# Consensus object SSZ tests
buildAndRunBinary "test_fixture_ssz_consensus_objects", "tests/official/", """-d:chronicles_log_level=TRACE -d:const_preset=mainnet -d:chronicles_sinks="json[file]""""
# EF tests
buildAndRunBinary "all_fixtures_require_ssz", "tests/official/", """-d:chronicles_log_level=TRACE -d:const_preset=mainnet -d:chronicles_sinks="json[file]""""

# Mainnet config
buildAndRunBinary "proto_array", "beacon_chain/fork_choice/", """-d:const_preset=mainnet -d:chronicles_sinks="json[file]""""
buildAndRunBinary "fork_choice", "beacon_chain/fork_choice/", """-d:const_preset=mainnet -d:chronicles_sinks="json[file]""""
buildAndRunBinary "all_tests", "tests/", """-d:chronicles_log_level=TRACE -d:const_preset=mainnet -d:chronicles_sinks="json[file]""""
buildAndRunBinary "all_tests", "tests/", """-d:UseSlashingProtection=true -d:chronicles_log_level=TRACE -d:const_preset=mainnet -d:chronicles_sinks="json[file]""""

# Check Miracl/Milagro fallback on select tests
buildAndRunBinary "test_interop", "tests/", """-d:chronicles_log_level=TRACE -d:const_preset=mainnet -d:BLS_FORCE_BACKEND=miracl -d:chronicles_sinks="json[file]""""
@@ -74,14 +81,6 @@ task test, "Run all tests":
buildAndRunBinary "test_attestation_pool", "tests/", """-d:chronicles_log_level=TRACE -d:const_preset=mainnet -d:BLS_FORCE_BACKEND=miracl -d:chronicles_sinks="json[file]""""
buildAndRunBinary "test_block_pool", "tests/", """-d:chronicles_log_level=TRACE -d:const_preset=mainnet -d:BLS_FORCE_BACKEND=miracl -d:chronicles_sinks="json[file]""""

# Generic SSZ test, doesn't use consensus objects minimal/mainnet presets
buildAndRunBinary "test_fixture_ssz_generic_types", "tests/official/", """-d:chronicles_log_level=TRACE -d:chronicles_sinks="json[file]""""

# Consensus object SSZ tests
buildAndRunBinary "test_fixture_ssz_consensus_objects", "tests/official/", """-d:chronicles_log_level=TRACE -d:const_preset=mainnet -d:chronicles_sinks="json[file]""""

buildAndRunBinary "all_fixtures_require_ssz", "tests/official/", """-d:chronicles_log_level=TRACE -d:const_preset=mainnet -d:chronicles_sinks="json[file]""""

# State and block sims; getting to 4th epoch triggers consensus checks
buildAndRunBinary "state_sim", "research/", "-d:const_preset=mainnet -d:chronicles_log_level=INFO", "--validators=3000 --slots=128"
# buildAndRunBinary "state_sim", "research/", "-d:const_preset=mainnet -d:BLS_FORCE_BACKEND=miracl -d:chronicles_log_level=INFO", "--validators=3000 --slots=128"
13 changes: 11 additions & 2 deletions beacon_chain/beacon_node.nim
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ import
mainchain_monitor, version, ssz/[merkleization], merkle_minimal,
sync_protocol, request_manager, keystore_management, interop, statusbar,
sync_manager, validator_duties, validator_api,
validator_slashing_protection,
./eth2_processor

const
@@ -258,7 +259,6 @@ proc init*(T: type BeaconNode,
netKeys: netKeys,
db: db,
config: conf,
attachedValidators: ValidatorPool.init(),
chainDag: chainDag,
quarantine: quarantine,
attestationPool: attestationPool,
@@ -271,6 +271,16 @@ proc init*(T: type BeaconNode,
topicAggregateAndProofs: topicAggregateAndProofs,
)

res.attachedValidators = ValidatorPool.init(
SlashingProtectionDB.init(
chainDag.headState.data.data.genesis_validators_root,
when UseSlashingProtection:
kvStore SqStoreRef.init(conf.validatorsDir(), "slashing_protection").tryGet()
else:
KvStoreRef()
)
)

proc getWallTime(): BeaconTime = res.beaconClock.now()

res.processor = Eth2Processor.new(
@@ -1312,4 +1322,3 @@ programMain:

of WalletsCmd.restore:
restoreWalletInteractively(rng[], config)

4 changes: 3 additions & 1 deletion beacon_chain/beacon_node_types.nim
Original file line number Diff line number Diff line change
@@ -5,7 +5,8 @@ import
stew/endians2,
spec/[datatypes, crypto],
block_pools/block_pools_types,
fork_choice/fork_choice_types
fork_choice/fork_choice_types,
validator_slashing_protection

export block_pools_types

@@ -105,5 +106,6 @@ type

ValidatorPool* = object
validators*: Table[ValidatorPubKey, AttachedValidator]
slashingProtection*: SlashingProtectionDB

func shortLog*(v: AttachedValidator): string = shortLog(v.pubKey)
87 changes: 64 additions & 23 deletions beacon_chain/validator_client.nim
Original file line number Diff line number Diff line change
@@ -16,14 +16,16 @@ import
json_serialization/std/[options, sets, net],

# Local modules
spec/[datatypes, digest, crypto, helpers, network],
spec/[datatypes, digest, crypto, helpers, network, signatures],
conf, time, version,
eth2_network, eth2_discovery, validator_pool, beacon_node_types,
nimbus_binary_common,
version, ssz/merkleization,
sync_manager, keystore_management,
spec/eth2_apis/callsigs_types,
eth2_json_rpc_serialization
eth2_json_rpc_serialization,
validator_slashing_protection,
eth/db/[kvstore, kvstore_sqlite3]

logScope: topics = "vc"

@@ -132,22 +134,35 @@ proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, a
# check if we have a validator which needs to propose on this slot
if vc.proposalsForCurrentEpoch.contains slot:
let public_key = vc.proposalsForCurrentEpoch[slot]
let validator = vc.attachedValidators.validators[public_key]

info "Proposing block", slot = slot, public_key = public_key

let randao_reveal = await validator.genRandaoReveal(
vc.fork, vc.beaconGenesis.genesis_validators_root, slot)

var newBlock = SignedBeaconBlock(
message: await vc.client.get_v1_validator_block(slot, vc.graffitiBytes, randao_reveal)
)

newBlock.root = hash_tree_root(newBlock.message)
newBlock.signature = await validator.signBlockProposal(
vc.fork, vc.beaconGenesis.genesis_validators_root, slot, newBlock.root)

discard await vc.client.post_v1_validator_block(newBlock)
let notSlashable = vc.attachedValidators
.slashingProtection
.checkSlashableBlockProposal(public_key, slot)
if notSlashable.isOk:
let validator = vc.attachedValidators.validators[public_key]
info "Proposing block", slot = slot, public_key = public_key
let randao_reveal = await validator.genRandaoReveal(
vc.fork, vc.beaconGenesis.genesis_validators_root, slot)
var newBlock = SignedBeaconBlock(
message: await vc.client.get_v1_validator_block(slot, vc.graffitiBytes, randao_reveal)
)
newBlock.root = hash_tree_root(newBlock.message)

# TODO: signing_root is recomputed in signBlockProposal just after
let signing_root = compute_block_root(vc.fork, vc.beaconGenesis.genesis_validators_root, slot, newBlock.root)
vc.attachedValidators
.slashingProtection
.registerBlock(public_key, slot, signing_root)

newBlock.signature = await validator.signBlockProposal(
vc.fork, vc.beaconGenesis.genesis_validators_root, slot, newBlock.root)

discard await vc.client.post_v1_validator_block(newBlock)
else:
warn "Slashing protection activated for block proposal",
validator = public_key,
slot = slot,
existingProposal = notSlashable.error

# https://github.com/ethereum/eth2.0-specs/blob/v0.12.2/specs/phase0/validator.md#attesting
# A validator should create and broadcast the attestation to the associated
@@ -167,12 +182,31 @@ proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, a
let validator = vc.attachedValidators.validators[a.public_key]
let ad = await vc.client.get_v1_validator_attestation(slot, a.committee_index)

# TODO I don't like these (u)int64-to-int conversions...
let attestation = await validator.produceAndSignAttestation(
ad, a.committee_length.int, a.validator_committee_index.int,
vc.fork, vc.beaconGenesis.genesis_validators_root)

discard await vc.client.post_v1_beacon_pool_attestations(attestation)
let notSlashable = vc.attachedValidators
.slashingProtection
.checkSlashableAttestation(
a.public_key,
ad.source.epoch,
ad.target.epoch)
if notSlashable.isOk():
# TODO signing_root is recomputed in produceAndSignAttestation/signAttestation just after
let signing_root = compute_attestation_root(
vc.fork, vc.beaconGenesis.genesis_validators_root, ad)
vc.attachedValidators
.slashingProtection
.registerAttestation(
a.public_key, ad.source.epoch, ad.target.epoch, signing_root)

# TODO I don't like these (u)int64-to-int conversions...
let attestation = await validator.produceAndSignAttestation(
ad, a.committee_length.int, a.validator_committee_index.int,
vc.fork, vc.beaconGenesis.genesis_validators_root)

discard await vc.client.post_v1_beacon_pool_attestations(attestation)
else:
warn "Slashing protection activated for attestation",
validator = a.public_key,
badVoteDetails = $notSlashable.error

except CatchableError as err:
warn "Caught an unexpected error", err = err.msg, slot = shortLog(slot)
@@ -230,6 +264,13 @@ programMain:
vc.beaconGenesis = waitFor vc.client.get_v1_beacon_genesis()
vc.beaconClock = BeaconClock.init(vc.beaconGenesis.genesis_time)

when UseSlashingProtection:
vc.attachedValidators.slashingProtection =
SlashingProtectionDB.init(
vc.beaconGenesis.genesis_validators_root,
kvStore SqStoreRef.init(config.validatorsDir(), "slashing_protection").tryGet()
)

let
curSlot = vc.beaconClock.now().slotOrZero()
nextSlot = curSlot + 1 # No earlier than GENESIS_SLOT + 1
45 changes: 39 additions & 6 deletions beacon_chain/validator_duties.nim
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@

import
# Standard library
std/[os, tables, strutils, sequtils, osproc, streams],
std/[os, tables, sequtils, osproc, streams],

# Nimble packages
stew/[objects], stew/shims/macros,
@@ -18,13 +18,14 @@ import
eth/[keys, async_utils], eth/p2p/discoveryv5/[protocol, enr],

# Local modules
spec/[datatypes, digest, crypto, helpers, validator, network],
spec/[datatypes, digest, crypto, helpers, validator, network, signatures],
spec/state_transition,
conf, time, validator_pool,
attestation_pool, block_pools/[spec_cache, chain_dag, clearance],
eth2_network, keystore_management, beacon_node_common, beacon_node_types,
nimbus_binary_common, mainchain_monitor, version, ssz/merkleization, interop,
attestation_aggregation, sync_manager, sszdump
attestation_aggregation, sync_manager, sszdump,
validator_slashing_protection

# Metrics for tracking attestation and beacon block loss
declareCounter beacon_attestations_sent,
@@ -120,6 +121,8 @@ proc isSynced*(node: BeaconNode, head: BlockRef): bool =
beaconTime = node.beaconClock.now()
wallSlot = beaconTime.toSlot()

# TODO: MaxEmptySlotCount should likely involve the weak subjectivity period.

# TODO if everyone follows this logic, the network will not recover from a
# halt: nobody will be producing blocks because everone expects someone
# else to do it
@@ -293,6 +296,16 @@ proc proposeBlock(node: BeaconNode,
slot = shortLog(slot)
return head

let notSlashable = node.attachedValidators
.slashingProtection
.checkSlashableBlockProposal(validator.pubkey, slot)
if notSlashable.isErr:
warn "Slashing protection activated",
validator = validator.pubkey,
slot = slot,
existingProposal = notSlashable.error
return head

let valInfo = ValidatorInfoForMakeBeaconBlock(kind: viValidator, validator: validator)
let beaconBlockTuple = await makeBeaconBlockForHeadAndSlot(
node, valInfo, validator_index, node.graffitiBytes, head, slot)
@@ -304,6 +317,14 @@ proc proposeBlock(node: BeaconNode,
)

newBlock.root = hash_tree_root(newBlock.message)

# TODO: recomputed in block proposal
let signing_root = compute_block_root(
beaconBlockTuple.fork, beaconBlockTuple.genesis_validators_root, slot, newBlock.root)
node.attachedValidators
.slashingProtection
.registerBlock(validator.pubkey, slot, signing_root)

newBlock.signature = await validator.signBlockProposal(
beaconBlockTuple.fork, beaconBlockTuple.genesis_validators_root, slot, newBlock.root)

@@ -368,9 +389,21 @@ proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
attestations.add((ad, committee.len, index_in_committee, validator))

for a in attestations:
traceAsyncErrors createAndSendAttestation(
node, fork, genesis_validators_root, a.validator, a.data,
a.committeeLen, a.indexInCommittee, num_active_validators)
let notSlashable = node.attachedValidators
.slashingProtection
.checkSlashableAttestation(
a.validator.pubkey,
a.data.source.epoch,
a.data.target.epoch)

if notSlashable.isOk():
traceAsyncErrors createAndSendAttestation(
node, fork, genesis_validators_root, a.validator, a.data,
a.committeeLen, a.indexInCommittee, num_active_validators)
else:
warn "Slashing protection activated for attestation",
validator = a.validator.pubkey,
badVoteDetails = $notSlashable.error

proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
Future[BlockRef] {.async.} =
12 changes: 10 additions & 2 deletions beacon_chain/validator_pool.nim
Original file line number Diff line number Diff line change
@@ -3,10 +3,18 @@ import
chronos, chronicles,
spec/[datatypes, crypto, digest, signatures, helpers],
beacon_node_types,
json_serialization/std/[sets, net]
json_serialization/std/[sets, net],
validator_slashing_protection,
eth/db/[kvstore, kvstore_sqlite3]

func init*(T: type ValidatorPool): T =
func init*(T: type ValidatorPool,
slashingProtectionDB: SlashingProtectionDB): T =
## Initialize the validator pool and the slashing protection service
## `genesis_validator_root` is used as an unique ID for the
## blockchain
## `backend` is the KeyValue Store backend
result.validators = initTable[ValidatorPubKey, AttachedValidator]()
result.slashingProtection = slashingProtectionDB

template count*(pool: ValidatorPool): int =
pool.validators.len
1,099 changes: 1,099 additions & 0 deletions beacon_chain/validator_slashing_protection.nim

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion tests/all_tests.nim
Original file line number Diff line number Diff line change
@@ -30,7 +30,9 @@ import # Unit test
./test_sync_manager,
./test_honest_validator,
./test_interop,
./fork_choice/tests_fork_choice
./fork_choice/tests_fork_choice,
./slashing_protection/test_slashing_interchange,
./slashing_protection/test_slashing_protection_db

import # Refactor state transition unit tests
# In mainnet these take 2 minutes and are empty TODOs
1 change: 1 addition & 0 deletions tests/slashing_protection/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.json
97 changes: 97 additions & 0 deletions tests/slashing_protection/test_slashing_interchange.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import
# Standard library
std/[unittest, os],
# Status lib
eth/db/kvstore,
stew/results,
nimcrypto/utils,
# Internal
../../beacon_chain/validator_slashing_protection,
../../beacon_chain/spec/[datatypes, digest, crypto, presets],
# Test utilies
../testutil

static: doAssert UseSlashingProtection, "The test was compiled without slashing protection, pass -d:UseSlashingProtection=true"

template wrappedTimedTest(name: string, body: untyped) =
# `check` macro takes a copy of whatever it's checking, on the stack!
block: # Symbol namespacing
proc wrappedTest() =
timedTest name:
body
wrappedTest()

func fakeRoot(index: SomeInteger): Eth2Digest =
## Create fake roots
## Those are just the value serialized in big-endian
## We prevent zero hash special case via a power of 2 prefix
result.data[0 ..< 8] = (1'u64 shl 32 + index.uint64).toBytesBE()

func fakeValidator(index: SomeInteger): ValidatorPubKey =
## Create fake validator public key
result = ValidatorPubKey(kind: OpaqueBlob)
result.blob[0 ..< 8] = (1'u64 shl 48 + index.uint64).toBytesBE()

func hexToDigest(hex: string): Eth2Digest =
result = Eth2Digest.fromHex(hex)

suiteReport "Slashing Protection DB - Interchange" & preset():
# https://hackmd.io/@sproul/Bk0Y0qdGD#Format-1-Complete
wrappedTimedTest "Smoke test - Complete format" & preset():
let genesis_validators_root = hexToDigest"0x04700007fabc8282644aed6d1c7c9e21d38a03a0c4ba193f3afe428824b3a673"
block: # export
let db = SlashingProtectionDB.init(genesis_validators_root, kvStore MemStoreRef.init())

let pubkey = ValidatorPubKey
.fromHex"0xb845089a1457f811bfc000588fbb4e713669be8ce060ea6be3c6ece09afc3794106c91ca73acda5e5457122d58723bed"
.get()
db.registerBlock(
pubkey,
Slot 81952,
hexToDigest"0x4ff6f743a43f3b4f95350831aeaf0a122a1a392922c45d804280284a69eb850b"
)
# db.registerBlock(
# pubkey,
# Slot 81951,
# fakeRoot(65535)
# )

db.registerAttestation(
pubkey,
source = Epoch 2290,
target = Epoch 3007,
hexToDigest"0x587d6a4f59a58fe24f406e0502413e77fe1babddee641fda30034ed37ecc884d"
)
db.registerAttestation(
pubkey,
source = Epoch 2290,
target = Epoch 3008,
fakeRoot(65535)
)

db.toSPDIF(currentSourcePath.parentDir/"test_complete_export_slashing_protection.json")

block: # import - zero root db
let db2 = SlashingProtectionDB.init(Eth2Digest(), kvStore MemStoreRef.init())

doAssert db2.fromSPDIF(currentSourcePath.parentDir/"test_complete_export_slashing_protection.json")
db2.toSPDIF(currentSourcePath.parentDir/"test_complete_export_slashing_protection_roundtrip1.json")

block: # import - same root db
let db3 = SlashingProtectionDB.init(genesis_validators_root, kvStore MemStoreRef.init())

doAssert db3.fromSPDIF(currentSourcePath.parentDir/"test_complete_export_slashing_protection.json")
db3.toSPDIF(currentSourcePath.parentDir/"test_complete_export_slashing_protection_roundtrip2.json")

block: # import - invalid root db
let invalid_genvalroot = hexToDigest"0x1234"
let db3 = SlashingProtectionDB.init(invalid_genvalroot, kvStore MemStoreRef.init())

doAssert not db3.fromSPDIF(currentSourcePath.parentDir/"test_complete_export_slashing_protection.json")
566 changes: 566 additions & 0 deletions tests/slashing_protection/test_slashing_protection_db.nim

Large diffs are not rendered by default.