2018-11-23 23:58:49 +00:00
|
|
|
import
|
2020-11-27 22:16:13 +00:00
|
|
|
std/[tables, json, streams],
|
|
|
|
chronos, chronicles, metrics,
|
2020-09-16 11:30:03 +00:00
|
|
|
json_serialization/std/[sets, net],
|
2020-11-27 22:16:13 +00:00
|
|
|
eth/db/[kvstore, kvstore_sqlite3],
|
|
|
|
./spec/[datatypes, crypto, digest, signatures, helpers],
|
2021-02-09 15:23:06 +00:00
|
|
|
./beacon_node_types, validator_protection/slashing_protection
|
2020-11-27 22:16:13 +00:00
|
|
|
|
|
|
|
declareGauge validators,
|
|
|
|
"Number of validators attached to the beacon node"
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2020-09-16 11:30:03 +00:00
|
|
|
func init*(T: type ValidatorPool,
|
|
|
|
slashingProtectionDB: SlashingProtectionDB): T =
|
|
|
|
## Initialize the validator pool and the slashing protection service
|
2021-02-09 15:23:06 +00:00
|
|
|
## `genesis_validators_root` is used as an unique ID for the
|
2020-09-16 11:30:03 +00:00
|
|
|
## blockchain
|
|
|
|
## `backend` is the KeyValue Store backend
|
2018-11-29 01:08:34 +00:00
|
|
|
result.validators = initTable[ValidatorPubKey, AttachedValidator]()
|
2020-09-16 11:30:03 +00:00
|
|
|
result.slashingProtection = slashingProtectionDB
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2018-12-19 12:58:53 +00:00
|
|
|
template count*(pool: ValidatorPool): int =
|
|
|
|
pool.validators.len
|
|
|
|
|
2018-11-23 23:58:49 +00:00
|
|
|
proc addLocalValidator*(pool: var ValidatorPool,
|
2018-11-29 01:08:34 +00:00
|
|
|
pubKey: ValidatorPubKey,
|
2020-11-27 23:34:25 +00:00
|
|
|
privKey: ValidatorPrivKey,
|
|
|
|
index: Option[ValidatorIndex]) =
|
2019-11-25 12:47:29 +00:00
|
|
|
let v = AttachedValidator(pubKey: pubKey,
|
2020-11-27 23:34:25 +00:00
|
|
|
index: index,
|
2018-12-08 14:17:47 +00:00
|
|
|
kind: inProcess,
|
2019-02-14 21:41:04 +00:00
|
|
|
privKey: privKey)
|
2018-12-08 14:17:47 +00:00
|
|
|
pool.validators[pubKey] = v
|
2020-10-01 18:56:42 +00:00
|
|
|
notice "Local validator attached", pubKey, validator = shortLog(v)
|
2019-04-06 07:46:07 +00:00
|
|
|
|
2020-11-27 22:16:13 +00:00
|
|
|
validators.set(pool.count().int64)
|
|
|
|
|
2020-09-01 13:44:40 +00:00
|
|
|
proc addRemoteValidator*(pool: var ValidatorPool,
|
|
|
|
pubKey: ValidatorPubKey,
|
|
|
|
v: AttachedValidator) =
|
|
|
|
pool.validators[pubKey] = v
|
2020-10-01 18:56:42 +00:00
|
|
|
notice "Remote validator attached", pubKey, validator = shortLog(v)
|
2020-09-01 13:44:40 +00:00
|
|
|
|
2020-11-27 22:16:13 +00:00
|
|
|
validators.set(pool.count().int64)
|
|
|
|
|
2020-08-10 13:21:31 +00:00
|
|
|
proc getValidator*(pool: ValidatorPool,
|
2018-11-29 01:08:34 +00:00
|
|
|
validatorKey: ValidatorPubKey): AttachedValidator =
|
performance fixes (#2259)
* performance fixes
* don't mark tree cache as dirty on read-only List accesses
* store only blob in memory for keys and signatures, parse blob lazily
* compare public keys by blob instead of parsing / converting to raw
* compare Eth2Digest using non-constant-time comparison
* avoid some unnecessary validator copying
This branch will in particular speed up deposit processing which has
been slowing down block replay.
Pre (mainnet, 1600 blocks):
```
All time are ms
Average, StdDev, Min, Max, Samples, Test
Validation is turned off meaning that no BLS operations are performed
3450.269, 0.000, 3450.269, 3450.269, 1, Initialize DB
0.417, 0.822, 0.036, 21.098, 1400, Load block from database
16.521, 0.000, 16.521, 16.521, 1, Load state from database
27.906, 50.846, 8.104, 1507.633, 1350, Apply block
52.617, 37.029, 20.640, 135.938, 50, Apply epoch block
```
Post:
```
3502.715, 0.000, 3502.715, 3502.715, 1, Initialize DB
0.080, 0.560, 0.035, 21.015, 1400, Load block from database
17.595, 0.000, 17.595, 17.595, 1, Load state from database
15.706, 11.028, 8.300, 107.537, 1350, Apply block
33.217, 12.622, 17.331, 60.580, 50, Apply epoch block
```
* more perf fixes
* load EpochRef cache into StateCache more aggressively
* point out security concern with public key cache
* reuse proposer index from state when processing block
* avoid genericAssign in a few more places
* don't parse key when signature is unparseable
* fix `==` overload for Eth2Digest
* preallocate validator list when getting active validators
* speed up proposer index calculation a little bit
* reuse cache when replaying blocks in ncli_db
* avoid a few more copying loops
```
Average, StdDev, Min, Max, Samples, Test
Validation is turned off meaning that no BLS operations are performed
3279.158, 0.000, 3279.158, 3279.158, 1, Initialize DB
0.072, 0.357, 0.035, 13.400, 1400, Load block from database
17.295, 0.000, 17.295, 17.295, 1, Load state from database
5.918, 9.896, 0.198, 98.028, 1350, Apply block
15.888, 10.951, 7.902, 39.535, 50, Apply epoch block
0.000, 0.000, 0.000, 0.000, 0, Database block store
```
* clear full balance cache before processing rewards and penalties
```
All time are ms
Average, StdDev, Min, Max, Samples, Test
Validation is turned off meaning that no BLS operations are performed
3947.901, 0.000, 3947.901, 3947.901, 1, Initialize DB
0.124, 0.506, 0.026, 202.370, 363345, Load block from database
97.614, 0.000, 97.614, 97.614, 1, Load state from database
0.186, 0.188, 0.012, 99.561, 357262, Advance slot, non-epoch
14.161, 5.966, 1.099, 395.511, 11524, Advance slot, epoch
1.372, 4.170, 0.017, 276.401, 363345, Apply block, no slot processing
0.000, 0.000, 0.000, 0.000, 0, Database block store
```
2021-01-25 12:04:18 +00:00
|
|
|
pool.validators.getOrDefault(validatorKey)
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2020-09-01 13:44:40 +00:00
|
|
|
proc signWithRemoteValidator(v: AttachedValidator, data: Eth2Digest):
|
|
|
|
Future[ValidatorSig] {.async.} =
|
|
|
|
v.connection.inStream.writeLine(v.connection.pubKeyStr, " ", $data)
|
|
|
|
v.connection.inStream.flush()
|
|
|
|
var line = newStringOfCap(120).TaintedString
|
|
|
|
discard v.connection.outStream.readLine(line)
|
|
|
|
result = ValidatorSig.fromHex(line).get()
|
|
|
|
# TODO this is an ugly hack to fake a delay and subsequent async reordering
|
|
|
|
# for the purpose of testing the external validator delay - to be
|
|
|
|
# replaced by something more sensible
|
|
|
|
await sleepAsync(chronos.milliseconds(1))
|
|
|
|
|
2020-11-09 14:18:55 +00:00
|
|
|
# TODO: Honest validator - https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/validator.md
|
2020-03-30 11:31:44 +00:00
|
|
|
proc signBlockProposal*(v: AttachedValidator, fork: Fork,
|
|
|
|
genesis_validators_root: Eth2Digest, slot: Slot,
|
2019-04-06 07:46:07 +00:00
|
|
|
blockRoot: Eth2Digest): Future[ValidatorSig] {.async.} =
|
2018-11-29 01:08:34 +00:00
|
|
|
if v.kind == inProcess:
|
2020-03-30 11:31:44 +00:00
|
|
|
result = get_block_signature(
|
|
|
|
fork, genesis_validators_root, slot, blockRoot, v.privKey)
|
2018-11-23 23:58:49 +00:00
|
|
|
else:
|
2020-09-01 13:44:40 +00:00
|
|
|
let root = compute_block_root(fork, genesis_validators_root, slot, blockRoot)
|
|
|
|
result = await signWithRemoteValidator(v, root)
|
2018-11-23 23:58:49 +00:00
|
|
|
|
|
|
|
proc signAttestation*(v: AttachedValidator,
|
2019-09-08 18:09:01 +00:00
|
|
|
attestation: AttestationData,
|
2020-03-30 11:31:44 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest):
|
|
|
|
Future[ValidatorSig] {.async.} =
|
2018-11-29 01:08:34 +00:00
|
|
|
if v.kind == inProcess:
|
2020-03-30 11:31:44 +00:00
|
|
|
result = get_attestation_signature(
|
|
|
|
fork, genesis_validators_root, attestation, v.privKey)
|
2018-11-23 23:58:49 +00:00
|
|
|
else:
|
2020-09-01 13:44:40 +00:00
|
|
|
let root = compute_attestation_root(fork, genesis_validators_root, attestation)
|
|
|
|
result = await signWithRemoteValidator(v, root)
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2020-06-05 09:57:40 +00:00
|
|
|
proc produceAndSignAttestation*(validator: AttachedValidator,
|
|
|
|
attestationData: AttestationData,
|
2020-11-16 16:10:51 +00:00
|
|
|
committeeLen: int, indexInCommittee: Natural,
|
2020-06-05 09:57:40 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest):
|
|
|
|
Future[Attestation] {.async.} =
|
|
|
|
let validatorSignature = await validator.signAttestation(attestationData,
|
|
|
|
fork, genesis_validators_root)
|
|
|
|
|
|
|
|
var aggregationBits = CommitteeValidatorsBits.init(committeeLen)
|
|
|
|
aggregationBits.setBit indexInCommittee
|
|
|
|
|
|
|
|
return Attestation(data: attestationData, signature: validatorSignature, aggregation_bits: aggregationBits)
|
|
|
|
|
2020-04-15 02:41:22 +00:00
|
|
|
proc signAggregateAndProof*(v: AttachedValidator,
|
|
|
|
aggregate_and_proof: AggregateAndProof,
|
2020-09-01 13:44:40 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest):
|
|
|
|
Future[ValidatorSig] {.async.} =
|
2020-04-15 02:41:22 +00:00
|
|
|
if v.kind == inProcess:
|
|
|
|
result = get_aggregate_and_proof_signature(
|
|
|
|
fork, genesis_validators_root, aggregate_and_proof, v.privKey)
|
|
|
|
else:
|
2020-09-01 13:44:40 +00:00
|
|
|
let root = compute_aggregate_and_proof_root(
|
|
|
|
fork, genesis_validators_root, aggregate_and_proof)
|
|
|
|
result = await signWithRemoteValidator(v, root)
|
2020-04-15 02:41:22 +00:00
|
|
|
|
2020-11-09 14:18:55 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/validator.md#randao-reveal
|
2020-03-30 11:31:44 +00:00
|
|
|
func genRandaoReveal*(k: ValidatorPrivKey, fork: Fork,
|
|
|
|
genesis_validators_root: Eth2Digest, slot: Slot): ValidatorSig =
|
2020-06-16 05:45:04 +00:00
|
|
|
get_epoch_signature(
|
|
|
|
fork, genesis_validators_root, slot.compute_epoch_at_slot, k)
|
2019-02-07 20:13:10 +00:00
|
|
|
|
2020-09-01 13:44:40 +00:00
|
|
|
proc genRandaoReveal*(v: AttachedValidator, fork: Fork,
|
|
|
|
genesis_validators_root: Eth2Digest, slot: Slot):
|
|
|
|
Future[ValidatorSig] {.async.} =
|
|
|
|
if v.kind == inProcess:
|
|
|
|
return genRandaoReveal(v.privKey, fork, genesis_validators_root, slot)
|
|
|
|
else:
|
|
|
|
let root = compute_epoch_root(
|
|
|
|
fork, genesis_validators_root, slot.compute_epoch_at_slot)
|
|
|
|
result = await signWithRemoteValidator(v, root)
|
|
|
|
|
|
|
|
proc getSlotSig*(v: AttachedValidator, fork: Fork,
|
|
|
|
genesis_validators_root: Eth2Digest, slot: Slot
|
|
|
|
): Future[ValidatorSig] {.async.} =
|
|
|
|
if v.kind == inProcess:
|
|
|
|
result = get_slot_signature(
|
|
|
|
fork, genesis_validators_root, slot, v.privKey)
|
|
|
|
else:
|
|
|
|
let root = compute_slot_root(fork, genesis_validators_root, slot)
|
|
|
|
result = await signWithRemoteValidator(v, root)
|