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

refactor: move validator api endpoints to standalone functions #6238

Closed
wants to merge 1 commit into from

Conversation

nazarhussain
Copy link
Contributor

@nazarhussain nazarhussain commented Dec 26, 2023

Motivation

  • Make the code readable and maintainable.
  • This will help to maintain over 1000 lines of Validator API code.
  • In next phase we need do a quite a refactoring in block production flow this refactor will make it easy to review and maintain.

Description

  • Extract Validator API endpoint implementation to separate functions

Steps to test or reproduce

  • Run all tests

@nazarhussain nazarhussain requested a review from a team as a code owner December 26, 2023 12:09
@nazarhussain nazarhussain self-assigned this Dec 26, 2023
Copy link

codecov bot commented Dec 26, 2023

Codecov Report

Merging #6238 (f6907c2) into unstable (1200c59) will not change coverage.
The diff coverage is n/a.

Additional details and impacted files
@@            Coverage Diff            @@
##           unstable    #6238   +/-   ##
=========================================
  Coverage     80.95%   80.95%           
=========================================
  Files           185      185           
  Lines         17935    17935           
  Branches       1078     1078           
=========================================
  Hits          14519    14519           
  Misses         3389     3389           
  Partials         27       27           

Copy link
Contributor

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: f994af5 Previous: 1200c59 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 671.27 us/op 773.76 us/op 0.87
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 51.136 us/op 80.228 us/op 0.64
BLS verify - blst-native 1.1088 ms/op 1.2735 ms/op 0.87
BLS verifyMultipleSignatures 3 - blst-native 2.3565 ms/op 2.6721 ms/op 0.88
BLS verifyMultipleSignatures 8 - blst-native 5.2077 ms/op 5.8582 ms/op 0.89
BLS verifyMultipleSignatures 32 - blst-native 19.161 ms/op 21.415 ms/op 0.89
BLS verifyMultipleSignatures 64 - blst-native 37.827 ms/op 42.169 ms/op 0.90
BLS verifyMultipleSignatures 128 - blst-native 75.182 ms/op 83.609 ms/op 0.90
BLS deserializing 10000 signatures 809.08 ms/op 909.90 ms/op 0.89
BLS deserializing 100000 signatures 8.1448 s/op 9.2158 s/op 0.88
BLS verifyMultipleSignatures - same message - 3 - blst-native 1.2034 ms/op 1.3252 ms/op 0.91
BLS verifyMultipleSignatures - same message - 8 - blst-native 1.2782 ms/op 1.4914 ms/op 0.86
BLS verifyMultipleSignatures - same message - 32 - blst-native 2.3841 ms/op 2.7992 ms/op 0.85
BLS verifyMultipleSignatures - same message - 64 - blst-native 3.1938 ms/op 3.6391 ms/op 0.88
BLS verifyMultipleSignatures - same message - 128 - blst-native 6.4771 ms/op 7.3249 ms/op 0.88
BLS aggregatePubkeys 32 - blst-native 23.821 us/op 26.291 us/op 0.91
BLS aggregatePubkeys 128 - blst-native 88.216 us/op 96.535 us/op 0.91
getAttestationsForBlock 28.847 ms/op 47.805 ms/op 0.60
getSlashingsAndExits - default max 90.647 us/op 184.56 us/op 0.49
getSlashingsAndExits - 2k 354.61 us/op 366.62 us/op 0.97
proposeBlockBody type=full, size=empty 3.7802 ms/op 5.0129 ms/op 0.75
isKnown best case - 1 super set check 346.00 ns/op 311.00 ns/op 1.11
isKnown normal case - 2 super set checks 339.00 ns/op 312.00 ns/op 1.09
isKnown worse case - 16 super set checks 331.00 ns/op 293.00 ns/op 1.13
CheckpointStateCache - add get delete 3.8880 us/op 5.6150 us/op 0.69
validate api signedAggregateAndProof - struct 2.2635 ms/op 2.7024 ms/op 0.84
validate gossip signedAggregateAndProof - struct 2.3552 ms/op 2.7430 ms/op 0.86
validate gossip attestation - vc 640000 1.1003 ms/op 1.3067 ms/op 0.84
batch validate gossip attestation - vc 640000 - chunk 32 128.67 us/op 155.73 us/op 0.83
batch validate gossip attestation - vc 640000 - chunk 64 117.21 us/op 138.52 us/op 0.85
batch validate gossip attestation - vc 640000 - chunk 128 108.23 us/op 128.37 us/op 0.84
batch validate gossip attestation - vc 640000 - chunk 256 109.45 us/op 127.53 us/op 0.86
pickEth1Vote - no votes 868.71 us/op 1.1709 ms/op 0.74
pickEth1Vote - max votes 9.1512 ms/op 9.5993 ms/op 0.95
pickEth1Vote - Eth1Data hashTreeRoot value x2048 19.209 ms/op 19.391 ms/op 0.99
pickEth1Vote - Eth1Data hashTreeRoot tree x2048 25.030 ms/op 28.342 ms/op 0.88
pickEth1Vote - Eth1Data fastSerialize value x2048 383.68 us/op 545.08 us/op 0.70
pickEth1Vote - Eth1Data fastSerialize tree x2048 6.0572 ms/op 6.3564 ms/op 0.95
bytes32 toHexString 420.00 ns/op 462.00 ns/op 0.91
bytes32 Buffer.toString(hex) 305.00 ns/op 270.00 ns/op 1.13
bytes32 Buffer.toString(hex) from Uint8Array 403.00 ns/op 397.00 ns/op 1.02
bytes32 Buffer.toString(hex) + 0x 295.00 ns/op 273.00 ns/op 1.08
Object access 1 prop 0.18300 ns/op 0.15100 ns/op 1.21
Map access 1 prop 0.17900 ns/op 0.14100 ns/op 1.27
Object get x1000 4.7370 ns/op 7.6270 ns/op 0.62
Map get x1000 0.69300 ns/op 0.71300 ns/op 0.97
Object set x1000 24.030 ns/op 47.615 ns/op 0.50
Map set x1000 16.470 ns/op 37.223 ns/op 0.44
Return object 10000 times 0.20940 ns/op 0.22890 ns/op 0.91
Throw Error 10000 times 2.5268 us/op 3.7261 us/op 0.68
fastMsgIdFn sha256 / 200 bytes 1.8560 us/op 3.1310 us/op 0.59
fastMsgIdFn h32 xxhash / 200 bytes 275.00 ns/op 258.00 ns/op 1.07
fastMsgIdFn h64 xxhash / 200 bytes 332.00 ns/op 321.00 ns/op 1.03
fastMsgIdFn sha256 / 1000 bytes 5.7610 us/op 10.979 us/op 0.52
fastMsgIdFn h32 xxhash / 1000 bytes 394.00 ns/op 379.00 ns/op 1.04
fastMsgIdFn h64 xxhash / 1000 bytes 398.00 ns/op 396.00 ns/op 1.01
fastMsgIdFn sha256 / 10000 bytes 49.331 us/op 100.45 us/op 0.49
fastMsgIdFn h32 xxhash / 10000 bytes 1.6830 us/op 1.8310 us/op 0.92
fastMsgIdFn h64 xxhash / 10000 bytes 1.1560 us/op 1.2520 us/op 0.92
send data - 1000 256B messages 11.348 ms/op 18.062 ms/op 0.63
send data - 1000 512B messages 14.539 ms/op 24.565 ms/op 0.59
send data - 1000 1024B messages 21.884 ms/op 38.574 ms/op 0.57
send data - 1000 1200B messages 25.865 ms/op 37.016 ms/op 0.70
send data - 1000 2048B messages 32.452 ms/op 42.468 ms/op 0.76
send data - 1000 4096B messages 30.297 ms/op 39.643 ms/op 0.76
send data - 1000 16384B messages 94.090 ms/op 114.91 ms/op 0.82
send data - 1000 65536B messages 370.45 ms/op 475.41 ms/op 0.78
enrSubnets - fastDeserialize 64 bits 1.0270 us/op 1.2000 us/op 0.86
enrSubnets - ssz BitVector 64 bits 420.00 ns/op 419.00 ns/op 1.00
enrSubnets - fastDeserialize 4 bits 194.00 ns/op 164.00 ns/op 1.18
enrSubnets - ssz BitVector 4 bits 421.00 ns/op 416.00 ns/op 1.01
prioritizePeers score -10:0 att 32-0.1 sync 2-0 66.950 us/op 97.417 us/op 0.69
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 95.184 us/op 121.65 us/op 0.78
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 100.23 us/op 154.93 us/op 0.65
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 172.61 us/op 284.36 us/op 0.61
prioritizePeers score 0:0 att 64-1 sync 4-1 206.71 us/op 341.18 us/op 0.61
array of 16000 items push then shift 1.2859 us/op 1.5627 us/op 0.82
LinkedList of 16000 items push then shift 5.9820 ns/op 8.6560 ns/op 0.69
array of 16000 items push then pop 60.550 ns/op 70.072 ns/op 0.86
LinkedList of 16000 items push then pop 5.7710 ns/op 8.4640 ns/op 0.68
array of 24000 items push then shift 1.8903 us/op 2.3788 us/op 0.79
LinkedList of 24000 items push then shift 5.9180 ns/op 8.7990 ns/op 0.67
array of 24000 items push then pop 81.346 ns/op 95.100 ns/op 0.86
LinkedList of 24000 items push then pop 5.6550 ns/op 8.5320 ns/op 0.66
intersect bitArray bitLen 8 4.9640 ns/op 6.1570 ns/op 0.81
intersect array and set length 8 48.051 ns/op 59.855 ns/op 0.80
intersect bitArray bitLen 128 27.650 ns/op 32.709 ns/op 0.85
intersect array and set length 128 725.49 ns/op 838.17 ns/op 0.87
bitArray.getTrueBitIndexes() bitLen 128 1.1150 us/op 1.4000 us/op 0.80
bitArray.getTrueBitIndexes() bitLen 248 1.8110 us/op 2.3410 us/op 0.77
bitArray.getTrueBitIndexes() bitLen 512 3.4550 us/op 4.5570 us/op 0.76
Buffer.concat 32 items 839.00 ns/op 943.00 ns/op 0.89
Uint8Array.set 32 items 1.5130 us/op 1.8140 us/op 0.83
Set add up to 64 items then delete first 1.7386 us/op 4.2640 us/op 0.41
OrderedSet add up to 64 items then delete first 2.6474 us/op 5.3503 us/op 0.49
Set add up to 64 items then delete last 1.9785 us/op 4.5285 us/op 0.44
OrderedSet add up to 64 items then delete last 2.9102 us/op 5.6544 us/op 0.51
Set add up to 64 items then delete middle 1.9775 us/op 4.5275 us/op 0.44
OrderedSet add up to 64 items then delete middle 4.2005 us/op 6.9260 us/op 0.61
Set add up to 128 items then delete first 3.8872 us/op 9.2688 us/op 0.42
OrderedSet add up to 128 items then delete first 6.0654 us/op 12.223 us/op 0.50
Set add up to 128 items then delete last 3.7742 us/op 9.0973 us/op 0.41
OrderedSet add up to 128 items then delete last 5.6152 us/op 11.466 us/op 0.49
Set add up to 128 items then delete middle 3.7335 us/op 8.9614 us/op 0.42
OrderedSet add up to 128 items then delete middle 10.411 us/op 16.683 us/op 0.62
Set add up to 256 items then delete first 7.5786 us/op 18.453 us/op 0.41
OrderedSet add up to 256 items then delete first 11.986 us/op 25.008 us/op 0.48
Set add up to 256 items then delete last 7.3130 us/op 18.147 us/op 0.40
OrderedSet add up to 256 items then delete last 11.167 us/op 23.167 us/op 0.48
Set add up to 256 items then delete middle 7.2860 us/op 17.996 us/op 0.40
OrderedSet add up to 256 items then delete middle 29.927 us/op 45.472 us/op 0.66
transfer serialized Status (84 B) 1.4880 us/op 1.7040 us/op 0.87
copy serialized Status (84 B) 1.3250 us/op 1.4150 us/op 0.94
transfer serialized SignedVoluntaryExit (112 B) 1.6580 us/op 1.8360 us/op 0.90
copy serialized SignedVoluntaryExit (112 B) 1.4110 us/op 1.5000 us/op 0.94
transfer serialized ProposerSlashing (416 B) 1.9770 us/op 2.1780 us/op 0.91
copy serialized ProposerSlashing (416 B) 2.0940 us/op 2.2880 us/op 0.92
transfer serialized Attestation (485 B) 1.9830 us/op 2.6650 us/op 0.74
copy serialized Attestation (485 B) 2.0260 us/op 2.6910 us/op 0.75
transfer serialized AttesterSlashing (33232 B) 2.2450 us/op 2.8910 us/op 0.78
copy serialized AttesterSlashing (33232 B) 5.1720 us/op 5.8700 us/op 0.88
transfer serialized Small SignedBeaconBlock (128000 B) 2.4300 us/op 2.8870 us/op 0.84
copy serialized Small SignedBeaconBlock (128000 B) 11.972 us/op 14.891 us/op 0.80
transfer serialized Avg SignedBeaconBlock (200000 B) 2.4710 us/op 3.3150 us/op 0.75
copy serialized Avg SignedBeaconBlock (200000 B) 17.254 us/op 20.901 us/op 0.83
transfer serialized BlobsSidecar (524380 B) 2.4630 us/op 3.1380 us/op 0.78
copy serialized BlobsSidecar (524380 B) 113.48 us/op 116.22 us/op 0.98
transfer serialized Big SignedBeaconBlock (1000000 B) 2.5780 us/op 3.2810 us/op 0.79
copy serialized Big SignedBeaconBlock (1000000 B) 201.36 us/op 152.15 us/op 1.32
pass gossip attestations to forkchoice per slot 2.6875 ms/op 3.8168 ms/op 0.70
forkChoice updateHead vc 100000 bc 64 eq 0 438.13 us/op 669.74 us/op 0.65
forkChoice updateHead vc 600000 bc 64 eq 0 3.2209 ms/op 4.4347 ms/op 0.73
forkChoice updateHead vc 1000000 bc 64 eq 0 4.3916 ms/op 6.9767 ms/op 0.63
forkChoice updateHead vc 600000 bc 320 eq 0 2.5868 ms/op 4.1915 ms/op 0.62
forkChoice updateHead vc 600000 bc 1200 eq 0 2.6830 ms/op 4.3422 ms/op 0.62
forkChoice updateHead vc 600000 bc 7200 eq 0 3.6443 ms/op 5.3508 ms/op 0.68
forkChoice updateHead vc 600000 bc 64 eq 1000 9.2819 ms/op 11.201 ms/op 0.83
forkChoice updateHead vc 600000 bc 64 eq 10000 9.6176 ms/op 11.766 ms/op 0.82
forkChoice updateHead vc 600000 bc 64 eq 300000 11.639 ms/op 15.797 ms/op 0.74
computeDeltas 500000 validators 300 proto nodes 3.0928 ms/op 6.4779 ms/op 0.48
computeDeltas 500000 validators 1200 proto nodes 2.9419 ms/op 6.4296 ms/op 0.46
computeDeltas 500000 validators 7200 proto nodes 2.9543 ms/op 6.2796 ms/op 0.47
computeDeltas 750000 validators 300 proto nodes 4.4671 ms/op 9.9190 ms/op 0.45
computeDeltas 750000 validators 1200 proto nodes 4.4531 ms/op 9.7638 ms/op 0.46
computeDeltas 750000 validators 7200 proto nodes 4.5013 ms/op 9.8104 ms/op 0.46
computeDeltas 1400000 validators 300 proto nodes 8.6721 ms/op 19.224 ms/op 0.45
computeDeltas 1400000 validators 1200 proto nodes 8.5944 ms/op 19.975 ms/op 0.43
computeDeltas 1400000 validators 7200 proto nodes 8.7355 ms/op 19.938 ms/op 0.44
computeDeltas 2100000 validators 300 proto nodes 13.292 ms/op 28.405 ms/op 0.47
computeDeltas 2100000 validators 1200 proto nodes 13.224 ms/op 28.749 ms/op 0.46
computeDeltas 2100000 validators 7200 proto nodes 13.254 ms/op 27.122 ms/op 0.49
computeProposerBoostScoreFromBalances 500000 validators 3.0998 ms/op 3.6909 ms/op 0.84
computeProposerBoostScoreFromBalances 750000 validators 3.1031 ms/op 3.6891 ms/op 0.84
computeProposerBoostScoreFromBalances 1400000 validators 3.1030 ms/op 3.6315 ms/op 0.85
computeProposerBoostScoreFromBalances 2100000 validators 3.1022 ms/op 3.6951 ms/op 0.84
altair processAttestation - 250000 vs - 7PWei normalcase 1.6229 ms/op 2.2976 ms/op 0.71
altair processAttestation - 250000 vs - 7PWei worstcase 2.3198 ms/op 3.4650 ms/op 0.67
altair processAttestation - setStatus - 1/6 committees join 92.945 us/op 174.66 us/op 0.53
altair processAttestation - setStatus - 1/3 committees join 198.91 us/op 337.44 us/op 0.59
altair processAttestation - setStatus - 1/2 committees join 281.50 us/op 457.31 us/op 0.62
altair processAttestation - setStatus - 2/3 committees join 361.14 us/op 571.63 us/op 0.63
altair processAttestation - setStatus - 4/5 committees join 483.04 us/op 770.10 us/op 0.63
altair processAttestation - setStatus - 100% committees join 579.90 us/op 894.23 us/op 0.65
altair processBlock - 250000 vs - 7PWei normalcase 6.6661 ms/op 9.7272 ms/op 0.69
altair processBlock - 250000 vs - 7PWei normalcase hashState 27.540 ms/op 39.148 ms/op 0.70
altair processBlock - 250000 vs - 7PWei worstcase 30.438 ms/op 36.169 ms/op 0.84
altair processBlock - 250000 vs - 7PWei worstcase hashState 74.452 ms/op 95.194 ms/op 0.78
phase0 processBlock - 250000 vs - 7PWei normalcase 2.7087 ms/op 2.4537 ms/op 1.10
phase0 processBlock - 250000 vs - 7PWei worstcase 25.612 ms/op 29.315 ms/op 0.87
altair processEth1Data - 250000 vs - 7PWei normalcase 374.13 us/op 556.86 us/op 0.67
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:15 9.6120 us/op 8.8090 us/op 1.09
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:219 32.789 us/op 57.586 us/op 0.57
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:42 5.1970 us/op 14.672 us/op 0.35
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:18 3.4100 us/op 9.6140 us/op 0.35
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1020 89.711 us/op 158.67 us/op 0.57
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11777 1.0479 ms/op 1.0226 ms/op 1.02
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.4380 ms/op 1.4569 ms/op 0.99
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 863.89 us/op 1.7823 ms/op 0.48
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 2.3441 ms/op 3.0851 ms/op 0.76
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 1.7283 ms/op 2.1766 ms/op 0.79
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 3.6825 ms/op 5.0731 ms/op 0.73
Tree 40 250000 create 249.17 ms/op 337.58 ms/op 0.74
Tree 40 250000 get(125000) 110.66 ns/op 185.54 ns/op 0.60
Tree 40 250000 set(125000) 738.74 ns/op 990.76 ns/op 0.75
Tree 40 250000 toArray() 9.4685 ms/op 18.397 ms/op 0.51
Tree 40 250000 iterate all - toArray() + loop 9.4491 ms/op 18.398 ms/op 0.51
Tree 40 250000 iterate all - get(i) 38.492 ms/op 63.738 ms/op 0.60
MutableVector 250000 create 8.8708 ms/op 11.663 ms/op 0.76
MutableVector 250000 get(125000) 5.3840 ns/op 6.5010 ns/op 0.83
MutableVector 250000 set(125000) 206.09 ns/op 268.93 ns/op 0.77
MutableVector 250000 toArray() 2.5379 ms/op 3.1295 ms/op 0.81
MutableVector 250000 iterate all - toArray() + loop 2.4295 ms/op 3.2592 ms/op 0.75
MutableVector 250000 iterate all - get(i) 1.3274 ms/op 1.5257 ms/op 0.87
Array 250000 create 2.1344 ms/op 3.2357 ms/op 0.66
Array 250000 clone - spread 1.0451 ms/op 1.2026 ms/op 0.87
Array 250000 get(125000) 1.0010 ns/op 0.99000 ns/op 1.01
Array 250000 set(125000) 1.2080 ns/op 3.9880 ns/op 0.30
Array 250000 iterate all - loop 152.72 us/op 162.19 us/op 0.94
effectiveBalanceIncrements clone Uint8Array 300000 13.210 us/op 23.957 us/op 0.55
effectiveBalanceIncrements clone MutableVector 300000 391.00 ns/op 335.00 ns/op 1.17
effectiveBalanceIncrements rw all Uint8Array 300000 182.01 us/op 195.90 us/op 0.93
effectiveBalanceIncrements rw all MutableVector 300000 60.956 ms/op 78.874 ms/op 0.77
phase0 afterProcessEpoch - 250000 vs - 7PWei 75.708 ms/op 111.37 ms/op 0.68
phase0 beforeProcessEpoch - 250000 vs - 7PWei 32.910 ms/op 53.056 ms/op 0.62
altair processEpoch - mainnet_e81889 387.66 ms/op 473.93 ms/op 0.82
mainnet_e81889 - altair beforeProcessEpoch 71.383 ms/op 85.708 ms/op 0.83
mainnet_e81889 - altair processJustificationAndFinalization 6.0490 us/op 14.236 us/op 0.42
mainnet_e81889 - altair processInactivityUpdates 3.8053 ms/op 5.7833 ms/op 0.66
mainnet_e81889 - altair processRewardsAndPenalties 62.073 ms/op 40.335 ms/op 1.54
mainnet_e81889 - altair processRegistryUpdates 1.8490 us/op 2.4680 us/op 0.75
mainnet_e81889 - altair processSlashings 570.00 ns/op 437.00 ns/op 1.30
mainnet_e81889 - altair processEth1DataReset 604.00 ns/op 468.00 ns/op 1.29
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.7638 ms/op 1.4155 ms/op 1.25
mainnet_e81889 - altair processSlashingsReset 2.5150 us/op 3.6480 us/op 0.69
mainnet_e81889 - altair processRandaoMixesReset 2.7280 us/op 3.9140 us/op 0.70
mainnet_e81889 - altair processHistoricalRootsUpdate 677.00 ns/op 728.00 ns/op 0.93
mainnet_e81889 - altair processParticipationFlagUpdates 1.1850 us/op 1.3640 us/op 0.87
mainnet_e81889 - altair processSyncCommitteeUpdates 770.00 ns/op 766.00 ns/op 1.01
mainnet_e81889 - altair afterProcessEpoch 78.229 ms/op 117.03 ms/op 0.67
capella processEpoch - mainnet_e217614 1.8547 s/op 2.1229 s/op 0.87
mainnet_e217614 - capella beforeProcessEpoch 430.48 ms/op 508.69 ms/op 0.85
mainnet_e217614 - capella processJustificationAndFinalization 12.783 us/op 19.084 us/op 0.67
mainnet_e217614 - capella processInactivityUpdates 13.499 ms/op 22.746 ms/op 0.59
mainnet_e217614 - capella processRewardsAndPenalties 399.46 ms/op 423.24 ms/op 0.94
mainnet_e217614 - capella processRegistryUpdates 7.5650 us/op 15.091 us/op 0.50
mainnet_e217614 - capella processSlashings 333.00 ns/op 549.00 ns/op 0.61
mainnet_e217614 - capella processEth1DataReset 325.00 ns/op 589.00 ns/op 0.55
mainnet_e217614 - capella processEffectiveBalanceUpdates 4.0795 ms/op 4.5279 ms/op 0.90
mainnet_e217614 - capella processSlashingsReset 2.1630 us/op 2.8050 us/op 0.77
mainnet_e217614 - capella processRandaoMixesReset 2.9610 us/op 3.8930 us/op 0.76
mainnet_e217614 - capella processHistoricalRootsUpdate 639.00 ns/op 508.00 ns/op 1.26
mainnet_e217614 - capella processParticipationFlagUpdates 973.00 ns/op 1.3440 us/op 0.72
mainnet_e217614 - capella afterProcessEpoch 202.20 ms/op 297.78 ms/op 0.68
phase0 processEpoch - mainnet_e58758 393.17 ms/op 433.37 ms/op 0.91
mainnet_e58758 - phase0 beforeProcessEpoch 127.92 ms/op 130.84 ms/op 0.98
mainnet_e58758 - phase0 processJustificationAndFinalization 11.988 us/op 15.624 us/op 0.77
mainnet_e58758 - phase0 processRewardsAndPenalties 53.939 ms/op 55.248 ms/op 0.98
mainnet_e58758 - phase0 processRegistryUpdates 7.5100 us/op 8.8390 us/op 0.85
mainnet_e58758 - phase0 processSlashings 585.00 ns/op 552.00 ns/op 1.06
mainnet_e58758 - phase0 processEth1DataReset 497.00 ns/op 414.00 ns/op 1.20
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.3783 ms/op 1.1546 ms/op 1.19
mainnet_e58758 - phase0 processSlashingsReset 2.2650 us/op 2.6470 us/op 0.86
mainnet_e58758 - phase0 processRandaoMixesReset 2.7610 us/op 3.9840 us/op 0.69
mainnet_e58758 - phase0 processHistoricalRootsUpdate 667.00 ns/op 513.00 ns/op 1.30
mainnet_e58758 - phase0 processParticipationRecordUpdates 2.4320 us/op 3.0810 us/op 0.79
mainnet_e58758 - phase0 afterProcessEpoch 63.883 ms/op 93.998 ms/op 0.68
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.0200 ms/op 1.3423 ms/op 0.76
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.4131 ms/op 1.4540 ms/op 0.97
altair processInactivityUpdates - 250000 normalcase 20.334 ms/op 19.719 ms/op 1.03
altair processInactivityUpdates - 250000 worstcase 21.043 ms/op 20.381 ms/op 1.03
phase0 processRegistryUpdates - 250000 normalcase 5.6920 us/op 7.7090 us/op 0.74
phase0 processRegistryUpdates - 250000 badcase_full_deposits 359.71 us/op 324.44 us/op 1.11
phase0 processRegistryUpdates - 250000 worstcase 0.5 104.00 ms/op 136.58 ms/op 0.76
altair processRewardsAndPenalties - 250000 normalcase 62.204 ms/op 54.865 ms/op 1.13
altair processRewardsAndPenalties - 250000 worstcase 62.531 ms/op 52.677 ms/op 1.19
phase0 getAttestationDeltas - 250000 normalcase 5.0910 ms/op 8.8107 ms/op 0.58
phase0 getAttestationDeltas - 250000 worstcase 5.5764 ms/op 8.7546 ms/op 0.64
phase0 processSlashings - 250000 worstcase 77.717 us/op 80.714 us/op 0.96
altair processSyncCommitteeUpdates - 250000 106.65 ms/op 153.30 ms/op 0.70
BeaconState.hashTreeRoot - No change 294.00 ns/op 249.00 ns/op 1.18
BeaconState.hashTreeRoot - 1 full validator 141.38 us/op 141.25 us/op 1.00
BeaconState.hashTreeRoot - 32 full validator 1.4106 ms/op 1.4439 ms/op 0.98
BeaconState.hashTreeRoot - 512 full validator 18.373 ms/op 15.542 ms/op 1.18
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 163.03 us/op 178.55 us/op 0.91
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.8904 ms/op 1.9376 ms/op 0.98
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 26.535 ms/op 24.513 ms/op 1.08
BeaconState.hashTreeRoot - 1 balances 157.37 us/op 114.10 us/op 1.38
BeaconState.hashTreeRoot - 32 balances 1.5356 ms/op 1.2865 ms/op 1.19
BeaconState.hashTreeRoot - 512 balances 11.713 ms/op 12.754 ms/op 0.92
BeaconState.hashTreeRoot - 250000 balances 192.19 ms/op 195.30 ms/op 0.98
aggregationBits - 2048 els - zipIndexesInBitList 11.111 us/op 17.936 us/op 0.62
byteArrayEquals 32 64.101 ns/op 74.637 ns/op 0.86
Buffer.compare 32 38.638 ns/op 55.750 ns/op 0.69
byteArrayEquals 1024 1.7478 us/op 2.0238 us/op 0.86
Buffer.compare 1024 46.986 ns/op 71.613 ns/op 0.66
byteArrayEquals 16384 27.771 us/op 32.407 us/op 0.86
Buffer.compare 16384 229.00 ns/op 269.58 ns/op 0.85
byteArrayEquals 123687377 207.98 ms/op 250.99 ms/op 0.83
Buffer.compare 123687377 3.6635 ms/op 6.0576 ms/op 0.60
byteArrayEquals 32 - diff last byte 62.041 ns/op 72.948 ns/op 0.85
Buffer.compare 32 - diff last byte 38.946 ns/op 57.415 ns/op 0.68
byteArrayEquals 1024 - diff last byte 1.7398 us/op 2.0577 us/op 0.85
Buffer.compare 1024 - diff last byte 47.067 ns/op 74.485 ns/op 0.63
byteArrayEquals 16384 - diff last byte 27.340 us/op 32.743 us/op 0.83
Buffer.compare 16384 - diff last byte 230.12 ns/op 246.12 ns/op 0.93
byteArrayEquals 123687377 - diff last byte 206.72 ms/op 244.31 ms/op 0.85
Buffer.compare 123687377 - diff last byte 5.4274 ms/op 6.1137 ms/op 0.89
byteArrayEquals 32 - random bytes 4.2890 ns/op 5.2610 ns/op 0.82
Buffer.compare 32 - random bytes 34.618 ns/op 58.940 ns/op 0.59
byteArrayEquals 1024 - random bytes 4.0460 ns/op 7.1830 ns/op 0.56
Buffer.compare 1024 - random bytes 33.336 ns/op 58.367 ns/op 0.57
byteArrayEquals 16384 - random bytes 4.0420 ns/op 5.0540 ns/op 0.80
Buffer.compare 16384 - random bytes 33.664 ns/op 58.420 ns/op 0.58
byteArrayEquals 123687377 - random bytes 7.0900 ns/op 8.2100 ns/op 0.86
Buffer.compare 123687377 - random bytes 35.860 ns/op 61.670 ns/op 0.58
regular array get 100000 times 37.176 us/op 43.019 us/op 0.86
wrappedArray get 100000 times 37.355 us/op 43.036 us/op 0.87
arrayWithProxy get 100000 times 9.2329 ms/op 14.372 ms/op 0.64
ssz.Root.equals 50.859 ns/op 52.394 ns/op 0.97
byteArrayEquals 49.607 ns/op 51.289 ns/op 0.97
Buffer.compare 8.4860 ns/op 10.448 ns/op 0.81
shuffle list - 16384 els 4.1722 ms/op 6.8176 ms/op 0.61
shuffle list - 250000 els 61.205 ms/op 99.671 ms/op 0.61
processSlot - 1 slots 14.418 us/op 16.120 us/op 0.89
processSlot - 32 slots 3.3349 ms/op 3.2216 ms/op 1.04
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 58.522 ms/op 58.393 ms/op 1.00
getCommitteeAssignments - req 1 vs - 250000 vc 2.1309 ms/op 2.4537 ms/op 0.87
getCommitteeAssignments - req 100 vs - 250000 vc 3.2405 ms/op 3.6239 ms/op 0.89
getCommitteeAssignments - req 1000 vs - 250000 vc 3.4827 ms/op 4.0692 ms/op 0.86
findModifiedValidators - 10000 modified validators 410.05 ms/op 530.92 ms/op 0.77
findModifiedValidators - 1000 modified validators 322.03 ms/op 421.67 ms/op 0.76
findModifiedValidators - 100 modified validators 306.53 ms/op 420.65 ms/op 0.73
findModifiedValidators - 10 modified validators 291.44 ms/op 388.59 ms/op 0.75
findModifiedValidators - 1 modified validators 292.89 ms/op 395.21 ms/op 0.74
findModifiedValidators - no difference 296.18 ms/op 409.10 ms/op 0.72
compare ViewDUs 3.8781 s/op 4.3091 s/op 0.90
compare each validator Uint8Array 1.6075 s/op 1.8792 s/op 0.86
compare ViewDU to Uint8Array 754.96 ms/op 1.1009 s/op 0.69
migrate state 1000000 validators, 24 modified, 0 new 763.50 ms/op 766.10 ms/op 1.00
migrate state 1000000 validators, 1700 modified, 1000 new 1.0259 s/op 1.0694 s/op 0.96
migrate state 1000000 validators, 3400 modified, 2000 new 1.2280 s/op 1.3239 s/op 0.93
migrate state 1500000 validators, 24 modified, 0 new 755.82 ms/op 791.93 ms/op 0.95
migrate state 1500000 validators, 1700 modified, 1000 new 1.0098 s/op 1.1054 s/op 0.91
migrate state 1500000 validators, 3400 modified, 2000 new 1.2513 s/op 1.3488 s/op 0.93
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 4.2500 ns/op 4.3000 ns/op 0.99
state getBlockRootAtSlot - 250000 vs - 7PWei 745.19 ns/op 535.22 ns/op 1.39
computeProposers - vc 250000 5.8813 ms/op 9.0298 ms/op 0.65
computeEpochShuffling - vc 250000 64.206 ms/op 103.55 ms/op 0.62
getNextSyncCommittee - vc 250000 105.22 ms/op 153.41 ms/op 0.69
computeSigningRoot for AttestationData 24.128 us/op 23.317 us/op 1.03
hash AttestationData serialized data then Buffer.toString(base64) 1.2076 us/op 2.3464 us/op 0.51
toHexString serialized data 829.92 ns/op 1.1035 us/op 0.75
Buffer.toString(base64) 141.68 ns/op 217.37 ns/op 0.65

by benchmarkbot/action

Copy link
Member

@nflaig nflaig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how this makes the code more maintainable. Splitting up code just because the file has above X amount of lines of code does not make sense to me. After this PR a lot more boilerplate code is required to add a new api.

We also lose the git history / blame on the validator api code which is always something we should avoid unless it is really required for a refactoring with strong reasoning behind it (which I don't see from the Motivation in PR description)

@g11tech
Copy link
Contributor

g11tech commented Dec 26, 2023

i think we can have file splits functionalty wise i.e. attestations related, synccommittee related, block related (produce and publish, and hence please no function renaming, also build on top of this PR: #6236 as this and its base PR: #6227 are high priority and need to be in the next milestone for deneb release)

boiler plate build... wrapper isnt too much imo but yea we don't need to go a super deep endpoint level split

@nazarhussain
Copy link
Contributor Author

If I spin up there is tons of literature on the topic, but the basic rule of thumb have a global agreement.

More readable code is more maintainable code.

And readability of a 1000 lines of single function nested upto 4-5 levels is less than of a 100 lines of function. Consider the fact that here we are not taking about index.ts file as 1000 lines, rather getValidatorApi of 1000 lines function.

Few references: A very common used practices is Airbnb Style Guide, which suggest 50 lines per function and 300 lines per file. An other industry best practices guide for Ruby even go strict and suggest to have 10 lines per method. If I remember correctly Bob Martin author of Clean Code suggested to have it upto 100 lines per function.

So we can debate on number of lines but bloating a function to unlimited number lines is not something even debatable.

Git history is well preserved and can be traced back even if code is moved to different files, you just have to follow a different tree path on some level. If we really make git history a blocker for refactoring, then we can't really improve any product.

The other reference point to split by behavior is not gonna work because each team member may have different understanding of it. So splitting per endpoint is straight forward for everyone to understand. And having idiomatic smaller higher order functions have a lot of text book benefits that I don't want to list here.

@nflaig
Copy link
Member

nflaig commented Dec 26, 2023

rather getValidatorApi of 1000 lines function

That's not a single function, you could use a class instead and would achieve the same result. If indentations are a concern could use a class + methods which would reduce the depth by one but honestly better to review each function see if improvements can be done there.

Few references

I don't think either of those are helpful for this argument.

So we can debate on number of lines but bloating a function to unlimited number lines is not something even debatable.

I am not debating this, my points are

  • more boilerplate code (less maintainable)
  • git history lost for no good reason
  • weak reasoning behind refactoring: "Make the code readable and maintainable", I would argue it is less readable if you want to review the validator api as you have to navigate through a bunch of files.

Git history is well preserved and can be traced back even if code is moved to different files

Just look at the git diff of this PR (+1,193 −937), it's not reviewable. I would have to trust that you properly copy-pasted the code and made no mistakes.

If we really make git history a blocker for refactoring, then we can't really improve any product

Again, not part of my argument, never said it is a blocker but there should be strong reasoning to move code around like that which this PR does not have.


This is my subjective opinion which can probably only be changed if there is a good and objective argument for this refactoring which I think is something we should have for every proposed code change.

@ensi321
Copy link
Contributor

ensi321 commented Dec 29, 2023

As a contributor who recently picked up the validator api code, let me put in my two cents. I think while it seems large and overwhelming (>1000 lines of code), its organization is good but a little messy.

Something I observed:

  1. There are some "utility" functions like getGenesisBlockRoot and notOnOptimisticBlockRoot and they are mixed with the endpoint functions.
  2. Some endpoint functions are directly defined in the return statement, some are defined in variables and later be fed inside the return statement (eg. produceBlockV3)

For 1, I think they can be managed separately and they are trivial enough that losing git history might not be a big deal compare to endpoint functions. (I could be wrong). ValidatorEndpointDependencies makes sense to me and the extra amount of boilerplates doesn't impact too much of readability and reviewability.

For 2, it is not clear to me why some are defined in the former way and some are defined in the latter way. My thought was functions with complex logic like produceBlockV3 warrants a separate variable. But then getProposerDuties which looks complex to me, is directly defined in the return statement.

My humble suggestion is moving forward, any new endpoint with complex logic should be placed in a standalone file under endpoints folder as illustrated in this PR. New endpoints with simple logic can be directly defined in the return statement in index.ts. Existed endpoints should be left as is to avoid sudden large change to the codebase, and loss of git history.

For the purpose of this PR, I think produceBlockV3 can be the first endpoint to be placed in a standalone file to set an example for the future complex endpoints. And the loss of git history might be mild since it's one of the newer endpoints.

@nflaig
Copy link
Member

nflaig commented Dec 29, 2023

Moving everything that is defined before the return statement out of the file would be a good first step. Everything block production related could definitely deserve it's own file, not a big fan of endpoints folder for that but maybe just a produceBlock.ts file in the same folder?

What I don't like in this PR are cases were we add 12 lines of boilerplate code for 1 line of actual code.

): ServerApi<routes.validator.Api>["submitBeaconCommitteeSelections"] {
return async function submitBeaconCommitteeSelections() {
throw new OnlySupportedByDVT();

Previously, the api impl type was also inferred when updating the route definition, now it requires a manual step to pick the correct type and clue it together in index file.

And the last thing is that the validator impl now deviates from how we define other routes, do we move the others into separate file as well and why is it not part of this PR then?

I feel like moving everything into separate files should be a last resort if all other attempts of refactoring failed or are insufficient.

@dapplion
Copy link
Contributor

dapplion commented Jan 8, 2024

Strongly in favour of keeping git history and not moving existing code around

@philknows
Copy link
Member

I don’t work with the codebase as much as you guys but would like to see a decision about it today if possible. There should be a decision on how to organize the existing functionality in which @dapplion is strongly against and also a decision on how to organize this going forward. Some of the newer guys opinions should give a fresh perspective here on what to do.

@jeluard
Copy link
Contributor

jeluard commented Jan 8, 2024

My 2 cents: generally in favor of improving readability. What could improve it in this specific scenario is having shorter functions, supported by extracted utility functions. It would also help with improving code comprehension (and removing the need for some comments) and testability.
No strong opinions on having one file per endpoints. git history is generally important in old enough codebase but might also be an indication of poor code readability :)

@wemeetagain
Copy link
Member

I've pulled the code locally to give a fair review. Tried to navigate and weigh the pros and cons here and in unstable.
I tend to agree with Nico and Lion for the reasons they mentioned.

I don't really read this file as "a single 1000 line function", rather the file is an implementation of a single interface, routes.validator.Api. I tend to read the file as a list of functions, the functions related to the validator namespace of the beacon-apis. The structure we have now is like a prototypical class, with the chain, metrics, etc acting as the class state.

I think some of the utility functions could be pulled out, and either made pure or pulled into methods.

As far as how we should structure code going forward, whether to combine functionality in a single file or move things across files is, imo, somewhat subjective, and shouldn't necessarily be reduced to "smaller file is more readable, so smallest file is most readable".
Some questions I think about:

  • What is the conceptual unit of code in this file? A conceptual unit may exist at the code level, as an interface or practical grouping, at the application level, as some domain-specific idea.
  • How is this function / module used? Are there multiple consumers of this function / module? If not, what other concept is this code related to?
  • How can this conceptual unit of code be simplified or its purpose highlighted? Is there a way to simplify the algorithms? More or better documentation?

Block production may warrant its own file, given the importance in the application. It seems that this was also the original catalyst for this PR.

@dapplion
Copy link
Contributor

dapplion commented Jan 9, 2024

A bit tangential, but relevant IMO:

I want to comment on a common trend in JS community which is the phobia to long files. Long may indicate opportunities to split code, but they are not intrinsically bad. Splitting a long file because it's long doesn't always help the overall project.

I past example I personally fought to keep as long file is the gossipsub/index.ts file (https://github.com/ChainSafe/js-libp2p-gossipsub/blob/master/src/index.ts). Splitting it always resulted in harder to follow code. We have been doing just fine with it. Even git is smart enough to almost never trigger conflicts even if you edit the same file.

@wemeetagain
Copy link
Member

Closing this PR now. Feel free to open a PR to refactor utility functions and / or a PR to refactor the block production code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants