nimbus-eth2/tests/slashing_protection/test_slashing_protection_db...

629 lines
16 KiB
Nim

# 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.
{.used.}
import
# Standard library
std/unittest,
# Status lib
eth/db/kvstore,
stew/results,
# 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()
suiteReport "Slashing Protection DB" & preset():
wrappedTimedTest "Empty database" & preset():
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
check:
db.checkSlashableBlockProposal(
fakeValidator(1234),
slot = Slot 1
).isOk()
db.checkSlashableAttestation(
fakeValidator(1234),
source = Epoch 1,
target = Epoch 2
).isOk()
db.checkSlashableAttestation(
fakeValidator(1234),
source = Epoch 2,
target = Epoch 1
).error.kind == TargetPrecedesSource
db.close()
wrappedTimedTest "SP for block proposal - linear append":
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
db.registerBlock(
fakeValidator(100),
Slot 10,
fakeRoot(100)
)
db.registerBlock(
fakeValidator(111),
Slot 15,
fakeRoot(111)
)
check:
# Slot occupied by same validator
db.checkSlashableBlockProposal(
fakeValidator(100),
slot = Slot 10
).isErr()
# Slot occupied by another validator
db.checkSlashableBlockProposal(
fakeValidator(111),
slot = Slot 10
).isOk()
# Slot occupied by another validator
db.checkSlashableBlockProposal(
fakeValidator(100),
slot = Slot 15
).isOk()
# Slot occupied by same validator
db.checkSlashableBlockProposal(
fakeValidator(111),
slot = Slot 15
).isErr()
# Slot inoccupied
db.checkSlashableBlockProposal(
fakeValidator(255),
slot = Slot 20
).isOk()
db.registerBlock(
fakeValidator(255),
slot = Slot 20,
fakeRoot(4321)
)
check:
# Slot now occupied
db.checkSlashableBlockProposal(
fakeValidator(255),
slot = Slot 20
).isErr()
wrappedTimedTest "SP for block proposal - backtracking append":
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
# last finalized block
db.registerBlock(
fakeValidator(0),
Slot 0,
fakeRoot(0)
)
db.registerBlock(
fakeValidator(100),
Slot 10,
fakeRoot(10)
)
db.registerBlock(
fakeValidator(100),
Slot 20,
fakeRoot(20)
)
for i in 0 ..< 30:
if i notin {10, 20}:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isOk()
else:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isErr()
db.registerBlock(
fakeValidator(100),
Slot 15,
fakeRoot(15)
)
for i in 0 ..< 30:
if i notin {10, 15, 20}:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isOk()
else:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isErr()
db.checkSlashableBlockProposal(
fakeValidator(0xDEADBEEF),
Slot i
).isOk()
db.registerBlock(
fakeValidator(100),
Slot 12,
fakeRoot(12)
)
db.registerBlock(
fakeValidator(100),
Slot 17,
fakeRoot(17)
)
for i in 0 ..< 30:
if i notin {10, 12, 15, 17, 20}:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isOk()
else:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isErr()
db.checkSlashableBlockProposal(
fakeValidator(0xDEADBEEF),
Slot i
).isOk()
db.registerBlock(
fakeValidator(100),
Slot 9,
fakeRoot(9)
)
db.registerBlock(
fakeValidator(100),
Slot 1,
fakeRoot(1)
)
db.registerBlock(
fakeValidator(100),
Slot 3,
fakeRoot(3)
)
for i in 0 ..< 30:
if i notin {1, 3, 9, 10, 12, 15, 17, 20}:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isOk()
else:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isErr()
db.checkSlashableBlockProposal(
fakeValidator(0xDEADBEEF),
Slot i
).isOk()
db.registerBlock(
fakeValidator(100),
Slot 29,
fakeRoot(29)
)
db.registerBlock(
fakeValidator(100),
Slot 2,
fakeRoot(2)
)
for i in 0 ..< 30:
if i notin {1, 2, 3, 9, 10, 12, 15, 17, 20, 29}:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isOk()
else:
check:
db.checkSlashableBlockProposal(
fakeValidator(100),
Slot i
).isErr()
db.checkSlashableBlockProposal(
fakeValidator(0xDEADBEEF),
Slot i
).isOk()
wrappedTimedTest "SP for same epoch attestation target - linear append":
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 10,
fakeRoot(100)
)
db.registerAttestation(
fakeValidator(111),
Epoch 0, Epoch 15,
fakeRoot(111)
)
check:
# Epoch occupied by same validator
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch 10,
).error.kind == DoubleVote
# Epoch occupied by another validator
db.checkSlashableAttestation(
fakeValidator(111),
Epoch 0, Epoch 10
).isOk()
# Epoch occupied by another validator
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch 15
).isOk()
# Epoch occupied by same validator
db.checkSlashableAttestation(
fakeValidator(111),
Epoch 0, Epoch 15
).error.kind == DoubleVote
# Epoch inoccupied
db.checkSlashableAttestation(
fakeValidator(255),
Epoch 0, Epoch 20
).isOk()
db.registerAttestation(
fakeValidator(255),
Epoch 0, Epoch 20,
fakeRoot(4321)
)
check:
# Epoch now occupied
db.checkSlashableAttestation(
fakeValidator(255),
Epoch 0, Epoch 20
).error.kind == DoubleVote
wrappedTimedTest "SP for same epoch attestation target - backtracking append":
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
# last finalized block
db.registerAttestation(
fakeValidator(0),
Epoch 0, Epoch 0,
fakeRoot(0)
)
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 10,
fakeRoot(10)
)
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 20,
fakeRoot(20)
)
for i in 0 ..< 30:
if i notin {10, 20}:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).isOk()
else:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).error.kind == DoubleVote
db.checkSlashableAttestation(
fakeValidator(0xDEADBEEF),
Epoch 0, Epoch i
).isOk()
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 15,
fakeRoot(15)
)
for i in 0 ..< 30:
if i notin {10, 15, 20}:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).isOk()
else:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).error.kind == DoubleVote
db.checkSlashableAttestation(
fakeValidator(0xDEADBEEF),
Epoch 0, Epoch i
).isOk()
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 12,
fakeRoot(12)
)
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 17,
fakeRoot(17)
)
for i in 0 ..< 30:
if i notin {10, 12, 15, 17, 20}:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).isOk()
else:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).error.kind == DoubleVote
db.checkSlashableAttestation(
fakeValidator(0xDEADBEEF),
Epoch 0, Epoch i
).isOk()
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 9,
fakeRoot(9)
)
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 1,
fakeRoot(1)
)
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 3,
fakeRoot(3)
)
for i in 0 ..< 30:
if i notin {1, 3, 9, 10, 12, 15, 17, 20}:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).isOk()
else:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).error.kind == DoubleVote
db.checkSlashableAttestation(
fakeValidator(0xDEADBEEF),
Epoch 0, Epoch i
).isOk()
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 29,
fakeRoot(29)
)
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 2,
fakeRoot(2)
)
for i in 0 ..< 30:
if i notin {1, 2, 3, 9, 10, 12, 15, 17, 20, 29}:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).isOk()
else:
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch i
).error.kind == DoubleVote
db.checkSlashableAttestation(
fakeValidator(0xDEADBEEF),
Epoch 0, Epoch i
).isOk()
wrappedTimedTest "SP for surrounded attestations":
block:
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
db.registerAttestation(
fakeValidator(100),
Epoch 10, Epoch 20,
fakeRoot(20)
)
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 11, Epoch 19
).error.kind == SurroundedVote
db.checkSlashableAttestation(
fakeValidator(200),
Epoch 11, Epoch 19
).isOk
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 11, Epoch 21
).isOk
# TODO: is that possible?
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 9, Epoch 19
).isOk
block:
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 1,
fakeRoot(0)
)
db.registerAttestation(
fakeValidator(100),
Epoch 10, Epoch 20,
fakeRoot(20)
)
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 11, Epoch 19
).error.kind == SurroundedVote
db.checkSlashableAttestation(
fakeValidator(200),
Epoch 11, Epoch 19
).isOk
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 11, Epoch 21
).isOk
# TODO: is that possible?
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 9, Epoch 19
).isOk
wrappedTimedTest "SP for surrounding attestations":
block:
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
db.registerAttestation(
fakeValidator(100),
Epoch 10, Epoch 20,
fakeRoot(20)
)
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 9, Epoch 21
).error.kind == SurroundingVote
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch 21
).error.kind == SurroundingVote
block:
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
db.registerAttestation(
fakeValidator(100),
Epoch 0, Epoch 1,
fakeRoot(20)
)
db.registerAttestation(
fakeValidator(100),
Epoch 10, Epoch 20,
fakeRoot(20)
)
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 9, Epoch 21
).error.kind == SurroundingVote
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 0, Epoch 21
).error.kind == SurroundingVote
wrappedTimedTest "Attestation ordering #1698":
block:
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
db.registerAttestation(
fakeValidator(100),
Epoch 1, Epoch 2,
fakeRoot(20)
)
db.registerAttestation(
fakeValidator(100),
Epoch 8, Epoch 10,
fakeRoot(20)
)
db.registerAttestation(
fakeValidator(100),
Epoch 14, Epoch 15,
fakeRoot(20)
)
# The current list is, 2 -> 10 -> 15
db.registerAttestation(
fakeValidator(100),
Epoch 3, Epoch 6,
fakeRoot(20)
)
# The current list is 2 -> 6 -> 10 -> 15
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 7, Epoch 11
).error.kind == SurroundingVote
wrappedTimedTest "Test valid attestation #1699":
block:
let db = SlashingProtectionDB.init(default(Eth2Digest), kvStore MemStoreRef.init())
db.registerAttestation(
fakeValidator(100),
Epoch 10, Epoch 20,
fakeRoot(20)
)
db.registerAttestation(
fakeValidator(100),
Epoch 40, Epoch 50,
fakeRoot(20)
)
check:
db.checkSlashableAttestation(
fakeValidator(100),
Epoch 20, Epoch 30
).isOk