mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-09 13:56:23 +00:00
f5e9d3ffe4
Annotate the `research` and `test` files for which no further changes are needed to successfully compile them, to not interfere with periodic tasks such as spec reference bumps.
779 lines
21 KiB
Nim
779 lines
21 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2018-2024 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.
|
|
|
|
{.push raises: [].}
|
|
{.used.}
|
|
|
|
import
|
|
# Standard library
|
|
std/[os],
|
|
# Status lib
|
|
eth/db/[kvstore, kvstore_sqlite3],
|
|
stew/[results, endians2],
|
|
# Internal
|
|
../../beacon_chain/validators/slashing_protection,
|
|
../../beacon_chain/spec/[helpers],
|
|
../../beacon_chain/spec/datatypes/base,
|
|
# Test utilies
|
|
../testutil
|
|
|
|
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()
|
|
result.blob[0 ..< 8] = (1'u64 shl 48 + index.uint64).toBytesBE()
|
|
|
|
proc sqlite3db_delete(basepath, dbname: string) =
|
|
for extension in [".sqlite3-shm", ".sqlite3-wal", ".sqlite3"]:
|
|
try:
|
|
removeFile(basepath / dbname&extension)
|
|
except OSError:
|
|
discard
|
|
|
|
const TestDir = ""
|
|
const TestDbName = "test_slashprot"
|
|
|
|
# Reminder of SQLite constraints for fake data:
|
|
# attestations:
|
|
# - all fields are NOT NULL
|
|
# - attestation_root is unique
|
|
# - (validator_id, target_epoch)
|
|
# blocks:
|
|
# - all fields are NOT NULL
|
|
# - block_root is unique
|
|
# - (validator_id, slot)
|
|
|
|
suite "Slashing Protection DB" & preset():
|
|
test "Empty database" & preset():
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
check:
|
|
db.checkSlashableBlockProposal(
|
|
ValidatorIndex(1234),
|
|
fakeValidator(1234),
|
|
slot = Slot 1
|
|
).isOk()
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(1234),
|
|
fakeValidator(1234),
|
|
source = Epoch 1,
|
|
target = Epoch 2
|
|
).isOk()
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(1234),
|
|
fakeValidator(1234),
|
|
source = Epoch 2,
|
|
target = Epoch 1
|
|
).error.kind == TargetPrecedesSource
|
|
|
|
test "SP for block proposal - linear append":
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
check:
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 10,
|
|
fakeRoot(100)
|
|
).isOk()
|
|
db.registerBlock(
|
|
ValidatorIndex(111),
|
|
fakeValidator(111),
|
|
Slot 15,
|
|
fakeRoot(111)
|
|
).isOk()
|
|
|
|
# Slot occupied by same validator
|
|
db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
slot = Slot 10
|
|
).isErr()
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
slot = Slot 10,
|
|
fakeRoot(101)
|
|
).isErr()
|
|
# Slot occupied by another validator
|
|
db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
slot = Slot 15
|
|
).isOk()
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
slot = Slot 15,
|
|
fakeRoot(150)
|
|
).isOk()
|
|
# Slot occupied by same validator
|
|
db.checkSlashableBlockProposal(
|
|
ValidatorIndex(111),
|
|
fakeValidator(111),
|
|
slot = Slot 15
|
|
).isErr()
|
|
db.registerBlock(
|
|
ValidatorIndex(111),
|
|
fakeValidator(111),
|
|
slot = Slot 15,
|
|
fakeRoot(151)
|
|
).isErr()
|
|
|
|
# Slot inoccupied
|
|
db.checkSlashableBlockProposal(
|
|
ValidatorIndex(255),
|
|
fakeValidator(255),
|
|
slot = Slot 20
|
|
).isOk()
|
|
|
|
db.registerBlock(
|
|
ValidatorIndex(255),
|
|
fakeValidator(255),
|
|
slot = Slot 20,
|
|
fakeRoot(4321)
|
|
).isOk()
|
|
|
|
check:
|
|
# Slot now occupied
|
|
db.checkSlashableBlockProposal(
|
|
ValidatorIndex(255),
|
|
fakeValidator(255),
|
|
slot = Slot 20
|
|
).isErr()
|
|
db.registerBlock(
|
|
ValidatorIndex(255),
|
|
fakeValidator(255),
|
|
slot = Slot 20,
|
|
fakeRoot(4322)
|
|
).isErr()
|
|
|
|
test "SP for block proposal - backtracking append":
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
# last finalized block
|
|
check:
|
|
db.registerBlock(
|
|
ValidatorIndex(0),
|
|
fakeValidator(0),
|
|
Slot 0,
|
|
fakeRoot(0)
|
|
).isOk()
|
|
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 10,
|
|
fakeRoot(10)
|
|
).isOk()
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 20,
|
|
fakeRoot(20)
|
|
).isOk()
|
|
for i in 0 ..< 30:
|
|
let status = db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot i
|
|
)
|
|
if i > 10 and i != 20: # MinSlotViolation and DupSlot
|
|
doAssert status.isOk, "error: " & $status
|
|
else:
|
|
doAssert status.isErr, "error: " & $status
|
|
check:
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 15,
|
|
fakeRoot(15)
|
|
).isOk()
|
|
for i in 0 ..< 30:
|
|
if i > 10 and i notin {15, 20}: # MinSlotViolation and DupSlot
|
|
let status = db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot i
|
|
)
|
|
doAssert status.isOk, "error: " & $status
|
|
else:
|
|
let status = db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot i
|
|
)
|
|
doAssert status.isErr, "error: " & $status
|
|
check:
|
|
db.checkSlashableBlockProposal(
|
|
ValidatorIndex(0xDEADBEEF),
|
|
fakeValidator(0xDEADBEEF),
|
|
Slot i
|
|
).isOk()
|
|
check:
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 12,
|
|
fakeRoot(12)
|
|
).isOk()
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 17,
|
|
fakeRoot(17)
|
|
).isOk()
|
|
for i in 0 ..< 30:
|
|
if i > 10 and i notin {12, 15, 17, 20}:
|
|
let status = db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot i
|
|
)
|
|
doAssert status.isOk, "error: " & $status
|
|
else:
|
|
let status = db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot i
|
|
)
|
|
doAssert status.isErr, "error: " & $status
|
|
check:
|
|
db.checkSlashableBlockProposal(
|
|
ValidatorIndex(0xDEADBEEF),
|
|
fakeValidator(0xDEADBEEF),
|
|
Slot i
|
|
).isOk()
|
|
check:
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 29,
|
|
fakeRoot(29)
|
|
).isOk()
|
|
|
|
for i in 0 ..< 30:
|
|
if i > 10 and i notin {12, 15, 17, 20, 29}:
|
|
let status = db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot i
|
|
)
|
|
doAssert status.isOk, "error: " & $status
|
|
else:
|
|
let status = db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot i
|
|
)
|
|
doAssert status.isErr, "error: " & $status
|
|
check:
|
|
db.checkSlashableBlockProposal(
|
|
ValidatorIndex(0xDEADBEEF),
|
|
fakeValidator(0xDEADBEEF),
|
|
Slot i
|
|
).isOk()
|
|
|
|
test "SP for same epoch attestation target - linear append":
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
check:
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 0, Epoch 10,
|
|
fakeRoot(100)
|
|
).isOk()
|
|
db.registerAttestation(
|
|
ValidatorIndex(111),
|
|
fakeValidator(111),
|
|
Epoch 0, Epoch 15,
|
|
fakeRoot(111)
|
|
).isOk()
|
|
|
|
# Epoch occupied by same validator
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 0, Epoch 10,
|
|
).error.kind == DoubleVote
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 0, Epoch 10, fakeRoot(101)
|
|
).error.kind == DoubleVote
|
|
|
|
# Epoch occupied by another validator
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 0, Epoch 15
|
|
).isOk()
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 0, Epoch 15, fakeRoot(151)
|
|
).isOk()
|
|
|
|
# Epoch occupied by same validator
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(111),
|
|
fakeValidator(111),
|
|
Epoch 0, Epoch 15
|
|
).error.kind == DoubleVote
|
|
db.registerAttestation(
|
|
ValidatorIndex(111),
|
|
fakeValidator(111),
|
|
Epoch 0, Epoch 15, fakeRoot(161)
|
|
).error.kind == DoubleVote
|
|
|
|
# Epoch inoccupied
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(255),
|
|
fakeValidator(255),
|
|
Epoch 0, Epoch 20
|
|
).isOk()
|
|
db.registerAttestation(
|
|
ValidatorIndex(255),
|
|
fakeValidator(255),
|
|
Epoch 0, Epoch 20, fakeRoot(4321)
|
|
).isOk()
|
|
|
|
# Epoch now occupied
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(255),
|
|
fakeValidator(255),
|
|
Epoch 0, Epoch 20
|
|
).error.kind == DoubleVote
|
|
db.registerAttestation(
|
|
ValidatorIndex(255),
|
|
fakeValidator(255),
|
|
Epoch 0, Epoch 20, fakeRoot(4322)
|
|
).error.kind == DoubleVote
|
|
|
|
test "SP for surrounded attestations":
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
check:
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 10, Epoch 20,
|
|
fakeRoot(20)
|
|
).isOk()
|
|
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 11, Epoch 19
|
|
).error.kind == SurroundVote
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(200),
|
|
fakeValidator(200),
|
|
Epoch 11, Epoch 19
|
|
).isOk
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 11, Epoch 21
|
|
).isOk
|
|
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
check:
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 0, Epoch 1,
|
|
fakeRoot(1)
|
|
).isOk()
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 10, Epoch 20,
|
|
fakeRoot(20)
|
|
).isOk()
|
|
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 11, Epoch 19
|
|
).error.kind == SurroundVote
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(200),
|
|
fakeValidator(200),
|
|
Epoch 11, Epoch 19
|
|
).isOk
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 11, Epoch 21
|
|
).isOk
|
|
# TODO: is that possible?
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 9, Epoch 19
|
|
).isOk
|
|
|
|
test "SP for surrounding attestations":
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
check:
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 10, Epoch 20,
|
|
fakeRoot(20)
|
|
).isOk()
|
|
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 9, Epoch 21
|
|
).error.kind == SurroundVote
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 0, Epoch 21
|
|
).error.kind == SurroundVote
|
|
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
check:
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 0, Epoch 1,
|
|
fakeRoot(1)
|
|
).isOk()
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 10, Epoch 20,
|
|
fakeRoot(20)
|
|
).isOk()
|
|
|
|
check:
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 9, Epoch 21
|
|
).error.kind == SurroundVote
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 0, Epoch 21
|
|
).error.kind == SurroundVote
|
|
|
|
test "Attestation ordering #1698":
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
check:
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 1, Epoch 2,
|
|
fakeRoot(2)
|
|
).isOk()
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 8, Epoch 10,
|
|
fakeRoot(10)
|
|
).isOk()
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 14, Epoch 15,
|
|
fakeRoot(15)
|
|
).isOk()
|
|
|
|
# The current list is, 2 -> 10 -> 15
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 3, Epoch 6,
|
|
fakeRoot(6)
|
|
).isOk()
|
|
|
|
# The current list is 2 -> 6 -> 10 -> 15
|
|
|
|
check:
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 7, Epoch 11
|
|
).error.kind == SurroundVote
|
|
|
|
test "Test valid attestation #1699":
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
check:
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 10, Epoch 20,
|
|
fakeRoot(20)
|
|
).isOk()
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 40, Epoch 50,
|
|
fakeRoot(50)
|
|
).isOk()
|
|
|
|
check:
|
|
db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 20, Epoch 30
|
|
).isOk
|
|
|
|
test "Pruning blocks works":
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 10,
|
|
fakeRoot(10)
|
|
).expect("registered block")
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 1000,
|
|
fakeRoot(20)
|
|
).expect("registered block")
|
|
|
|
# After pruning, duplicate becomes a min slot violation
|
|
doAssert db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 10,
|
|
).error.kind == DoubleProposal
|
|
|
|
db.pruneAfterFinalization(
|
|
epoch(Slot 1000)
|
|
)
|
|
|
|
doAssert db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 10,
|
|
).error.kind == MinSlotViolation
|
|
|
|
test "Don't prune the very last block even by mistake":
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 10,
|
|
fakeRoot(10)
|
|
).expect("registered block")
|
|
db.registerBlock(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 1000,
|
|
fakeRoot(20)
|
|
).expect("registered block")
|
|
|
|
doAssert db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 1000,
|
|
).error.kind == DoubleProposal
|
|
|
|
# Pruning far in the future
|
|
db.pruneAfterFinalization(
|
|
epoch(Slot 10000)
|
|
)
|
|
|
|
# Last block is still there
|
|
doAssert db.checkSlashableBlockProposal(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Slot 1000,
|
|
).error.kind == DoubleProposal
|
|
|
|
test "Pruning attestations works":
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 10, Epoch 20,
|
|
fakeRoot(20)
|
|
).expect("registered block")
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 40, Epoch 50,
|
|
fakeRoot(50)
|
|
).expect("registered block")
|
|
|
|
# After pruning, duplicate becomes a min source epoch violation
|
|
doAssert db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 10, Epoch 20
|
|
).error.kind == DoubleVote
|
|
|
|
# After pruning, surrounding vote becomes a min source epoch violation
|
|
doAssert db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 9, Epoch 21
|
|
).error.kind == SurroundVote
|
|
|
|
# After pruning, surrounded vote becomes a min source epoch violation
|
|
doAssert db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 11, Epoch 19
|
|
).error.kind == SurroundVote
|
|
|
|
# --------------------------------
|
|
db.pruneAfterFinalization(
|
|
Epoch 40
|
|
)
|
|
# --------------------------------
|
|
|
|
doAssert db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 10, Epoch 20
|
|
).error.kind == MinSourceViolation
|
|
|
|
doAssert db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 9, Epoch 21
|
|
).error.kind == MinSourceViolation
|
|
|
|
doAssert db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 11, Epoch 19
|
|
).error.kind == MinSourceViolation
|
|
|
|
# TODO is it possible to actually trigger MinTargetViolation
|
|
# given all the other constraints?
|
|
|
|
test "Don't prune the very last attestation(s) even by mistake":
|
|
block:
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
let db = SlashingProtectionDB.init(ZERO_HASH, TestDir, TestDbName)
|
|
defer:
|
|
db.close()
|
|
sqlite3db_delete(TestDir, TestDbName)
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 10, Epoch 20,
|
|
fakeRoot(20)
|
|
).expect("registered block")
|
|
|
|
db.registerAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 40, Epoch 50,
|
|
fakeRoot(50)
|
|
).expect("registered block")
|
|
|
|
# --------------------------------
|
|
db.pruneAfterFinalization(
|
|
epoch(Slot 10000)
|
|
)
|
|
# --------------------------------
|
|
|
|
doAssert db.checkSlashableAttestation(
|
|
ValidatorIndex(100),
|
|
fakeValidator(100),
|
|
Epoch 40, Epoch 50
|
|
).error.kind == DoubleVote
|
|
|
|
# TODO is it possible to actually to have
|
|
# the MAX(SourceEpoch) and MAX(TargetEpoch)
|
|
# on 2 different attestations
|
|
# given all the other constraints?
|