Home | About John | Resume/CV | References | Writing | Research |
---|
With the Introduction of Ethereum 2.01 2 3 block production and consensus were separated4 into execution clients5 and consensus clients6 7.
The execution chain implemented a simplified Proof of Work1 reducing difficutly to zero and removing the need for omners (uncles) which would now be handled by the beacon chain3 which is responsible for providing consensus 2.
Light Clients11 were also introduced. To facilate this a sync committee of 512 current validators is elected every 255 epochs, approximately every 27 hours (see sample sync comittee data), they are responsible for signing each block.
As at December 11th, 2022 Ethereum has 487,920 validators8 with a sample epoch (166581) and slot (5,330,592) having 139 transactions with 19,227 votes from 63 committees and 126 aggregated committe attestations.
Here is more information on the upgrade9 and the roadmap10
Ethreum uses Proof of Stake (PoS). Here we give an overview of Proof of Stake Followed by a deep dive into synch committees. Following is an excerpt from Ethereum Orgs Proof of Stake document
What is proof-of-stake (PoS)?
Proof-of-stake underlies certain consensus mechanisms used by blockchains to achieve distributed consensus. In proof-of-work, miners prove they have capital at risk by expending energy. Ethereum uses proof-of-stake, where validators explicitly stake capital in the form of ETH into a smart contract on Ethereum. This staked ETH then acts as collateral that can be destroyed if the validator behaves dishonestly or lazily. The validator is then responsible for checking that new blocks propagated over the network are valid and occasionally creating and propagating new blocks themselves.
Proof-of-stake comes with a number of improvements to the now-deprecated proof-of-work system:
- better energy efficiency – there is no need to use lots of energy on proof-of-work computations
- lower barriers to entry, reduced hardware requirements – there is no need for elite hardware to stand a chance of creating new blocks
- reduced centralization risk – proof-of-stake should lead to more nodes securing the network
- because of the low energy requirement less ETH issuance is required to incentivize participation
- economic penalties for misbehaviour make 51% style attacks exponentially more costly for an attacker compared to proof-of-work
- the community can resort to social recovery of an honest chain if a 51% attack were to overcome the crypto-economic defenses.
Validators run both an Ethereum 1 client (e.g. geth) and a Beacon Chain Client (e.g. prysm). The geth client recieves transactions and places them into blocks. For additional details see the Ethereum Builder Specs12. The following diagrams give an overview of how blocks are proposed and how MEV Boost13 could be integrated. For simplification we can replace mev_boost and relay with geth in the block proposal diagram as the majority of validators simply run a geth node.
The Beacon Chain provides the heartbeat to Ethereum’s consensus. Each slot is 12 seconds and an epoch is 32 slots: 6.4 minutes.
When a validator is nominated as a proposer for a slot in an Epoch they propose a block gathered from there Ethereum 1 client.
This proposed block is attested to by other validators who have been assigned as committe members for this slot6.
A block proposer is a validator that has been pseudorandomly selected to build a block.
Most of the time, validators are attesters that vote on blocks. These votes are recorded in the Beacon Chain and determine the head of the Beacon Chain.
A committee is a group of validators. For security, each slot has committees of at least 128 validators. An attacker has less than a one in a trillion probability of controlling ⅔ of a committee.
The concept of a randomness beacon that emits random numbers for the public, lends its name to the Ethereum Beacon Chain. The Beacon Chain enforces consensus on a pseudorandom process called RANDAO.
The attestation lifecyle9 involves
When an epoch ends, if its checkpoint has garnered a ⅔ supermajority, the checkpoint gets justified.
If a checkpoint B is justified and the checkpoint in the immediate next epoch becomes justified, then B becomes finalized. Typically, a checkpoint is finalized in two epochs, 12.8 minutes.
A sync committee of 512 current validators is elected every 255 epochs, approximately every 27 hours (see sample sync comittee data).
They are responsible for signing each block which records which sync committee members (validtors) signed the block, held in syncaggregate_bits
, and creates a bls aggregate signature held in syncaggregate_signature
(see block-data).
"syncaggregate_bits": "0xdffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffffffffffffffdffffffffffffffffffffffff",
"syncaggregate_participation": 0.9921875,
"syncaggregate_signature": "0x95332c55790018eed3d17eada01cb4045348d09137505bc8697eeedaa3800a830ee2c138251850a9577f62a5488419ef0a722579156a177fb3a147017f1077af5d778f46a4cdf815fc450129d135fe5286e16df68333592e4aa45821bde780dd",
This is used in Altair Light Client – Sync Protocol11 which enables the beacon chain to be light client friendly for constrained environments to access Ethereum.
Following is an overview of statuses for validators in Ethereum 2.0 phase 0 14.
- Deposited: the validator has made a deposit and has registered in BeaconState.
- Eligible to be activated (Pending): the validator is eligible to be activated.
- Activated: the validator is activated
- Note that the validator may be “eligible to be activated, but has not been activated yet”.
- Slashed: the validator has been slashed
- Exited: the validator is exited
- Withdrawable: the validator is withdrawable
- Note that the validator will be able to withdraw to EEs in phase 2
Note that in some cases, a validator can be in multiple statuses at the same time, e.g., an active validator may be “activated and slashed”.
Light client state updates
LightClientUpdate
, LightClientFinalityUpdate
and LightClientOptimisticUpdate
:
update: LightClientUpdate
: Every update
triggers process_light_client_update(store, update, current_slot, genesis_validators_root)
where current_slot
is the current slot based on a local clock.finality_update: LightClientFinalityUpdate
: Every finality_update
triggers process_light_client_finality_update(store, finality_update, current_slot, genesis_validators_root)
.optimistic_update: LightClientOptimisticUpdate
: Every optimistic_update
triggers process_light_client_optimistic_update(store, optimistic_update, current_slot, genesis_validators_root)
.process_light_client_store_force_update
MAY be called based on use case dependent heuristics if light client sync appears stuck.validate_light_client_update
def validate_light_client_update(store: LightClientStore,
update: LightClientUpdate,
current_slot: Slot,
genesis_validators_root: Root) -> None:
# Verify sync committee has sufficient participants
sync_aggregate = update.sync_aggregate
assert sum(sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
# Verify update does not skip a sync committee period
assert is_valid_light_client_header(update.attested_header)
update_attested_slot = update.attested_header.beacon.slot
update_finalized_slot = update.finalized_header.beacon.slot
assert current_slot >= update.signature_slot > update_attested_slot >= update_finalized_slot
store_period = compute_sync_committee_period_at_slot(store.finalized_header.beacon.slot)
update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot)
if is_next_sync_committee_known(store):
assert update_signature_period in (store_period, store_period + 1)
else:
assert update_signature_period == store_period
# Verify update is relevant
update_attested_period = compute_sync_committee_period_at_slot(update_attested_slot)
update_has_next_sync_committee = not is_next_sync_committee_known(store) and (
is_sync_committee_update(update) and update_attested_period == store_period
)
assert (
update_attested_slot > store.finalized_header.beacon.slot
or update_has_next_sync_committee
)
# Verify that the `finality_branch`, if present, confirms `finalized_header`
# to match the finalized checkpoint root saved in the state of `attested_header`.
# Note that the genesis finalized checkpoint root is represented as a zero hash.
if not is_finality_update(update):
assert update.finalized_header == LightClientHeader()
else:
if update_finalized_slot == GENESIS_SLOT:
assert update.finalized_header == LightClientHeader()
finalized_root = Bytes32()
else:
assert is_valid_light_client_header(update.finalized_header)
finalized_root = hash_tree_root(update.finalized_header.beacon)
assert is_valid_merkle_branch(
leaf=finalized_root,
branch=update.finality_branch,
depth=floorlog2(FINALIZED_ROOT_INDEX),
index=get_subtree_index(FINALIZED_ROOT_INDEX),
root=update.attested_header.beacon.state_root,
)
# Verify that the `next_sync_committee`, if present, actually is the next sync committee saved in the
# state of the `attested_header`
if not is_sync_committee_update(update):
assert update.next_sync_committee == SyncCommittee()
else:
if update_attested_period == store_period and is_next_sync_committee_known(store):
assert update.next_sync_committee == store.next_sync_committee
assert is_valid_merkle_branch(
leaf=hash_tree_root(update.next_sync_committee),
branch=update.next_sync_committee_branch,
depth=floorlog2(NEXT_SYNC_COMMITTEE_INDEX),
index=get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX),
root=update.attested_header.beacon.state_root,
)
# Verify sync committee aggregate signature
if update_signature_period == store_period:
sync_committee = store.current_sync_committee
else:
sync_committee = store.next_sync_committee
participant_pubkeys = [
pubkey for (bit, pubkey) in zip(sync_aggregate.sync_committee_bits, sync_committee.pubkeys)
if bit
]
fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot))
domain = compute_domain(DOMAIN_SYNC_COMMITTEE, fork_version, genesis_validators_root)
signing_root = compute_signing_root(update.attested_header.beacon, domain)
assert bls.FastAggregateVerify(participant_pubkeys, signing_root, sync_aggregate.sync_committee_signature)
apply_light_client_update
def apply_light_client_update(store: LightClientStore, update: LightClientUpdate) -> None:
store_period = compute_sync_committee_period_at_slot(store.finalized_header.beacon.slot)
update_finalized_period = compute_sync_committee_period_at_slot(update.finalized_header.beacon.slot)
if not is_next_sync_committee_known(store):
assert update_finalized_period == store_period
store.next_sync_committee = update.next_sync_committee
elif update_finalized_period == store_period + 1:
store.current_sync_committee = store.next_sync_committee
store.next_sync_committee = update.next_sync_committee
store.previous_max_active_participants = store.current_max_active_participants
store.current_max_active_participants = 0
if update.finalized_header.beacon.slot > store.finalized_header.beacon.slot:
store.finalized_header = update.finalized_header
if store.finalized_header.beacon.slot > store.optimistic_header.beacon.slot:
store.optimistic_header = store.finalized_header
Sample Implementation: NEAR Rainbow Bridge Ethereum Light Client Deployed on NEAR
Bridging support was implemented by NEAR under Eth2-to-Near-relay: prototype implementation #762
When we send light client update for finality block inside light client update, we also send Eth1 execution block hash with the Merkle proof of include to Beacon Block Body. Execution block hash you can find at BeaconBlockBody.execution_payload.block_hash.
So, for creating Merkle proof, we need two levels of Merkle Tree, as shown in the picture. Both Merkle trees you can find in beacon_block_body_merkle_tree.rs The first level Merkle tree for beacon block body and the second level Merkle tree for execution payload.
The execution block hash proof creation you can find in execution_block_proof.rs First, we build two Merkle trees and concatenate together the Merkle proof for block_hash in execution_payload and the Merkle proof of execution_payload in beacon_block_body. The final Merkle proof is shown by the orange vertices on the picture; the orange numbers in the picture are the order of hashes in the proof.
beacon_block_header_with_execution_data.rs contain a structure which consists of beacon_block_header and correspondent execution_block_hash with Merkle proof. This structure is created for finality blocks in a light client update.
Consensus
[1] EIP-3675: Upgrade consensus to Proof-of-Stake: Specification of the consensus mechanism upgrade on Ethereum Mainnet that introduces Proof-of-Stake.
[2] EIP-2982: Serenity Phase 0: Phase 0 of the release schedule of Serenity, a series of updates to Ethereum a scalable, proof-of-stake consensus.
[3] Ethreum Consensus Specs Phase 0: Specifications for Ethereum 2.0 Phase 0 including beacon-chain, deposit-contract, fork-choice, p2p-interface, validator and weak-subjectivity
[6] Ethereum Consensus and Execution Client Distribution: Percentages of nodes running client types for both Consensus (Prysm, Lighthours, Nimbus, Teku) and Execution (Geth, Erigon, Besu, Nethermind) clients.
[5] go-ethereum go documentation: Documentation for Go Ethereum, Official Golang implementation of the Ethereum protocol. Which is an execution chain implementation.
[6] prysm go documentation: Documentation for prysm, An Ethereum Consensus Implementation Written in Go. A beacon-chain immplementation. Also see Prysm Documentation
[7] lighthouse documentation: Documentation for lighthouse, written in Rust. A beacon-chain immplementation.
[8] Etherum 2.0 Validators Overview: Live Monitoring of Ethreum 2.0 Validators from beachoncha.in
[9] Upgrading Ethereum: A technical handbook on Ethereum’s move to proof of stake and beyond Edition 0.2: Bellatrix [WIP] by Ben Edgington.
[9] Annotated Ethereum Roadmap: an entry point for the various items on the Ethereum roadmap, with a quick summary along with links for those who want to dive deeper.
[10] Shanghai/Capella Upgrade: the first simultaneous upgrade of Ethereum’s execution layer and consensus layer, and is highly anticipated because it will enable staked ETH withdrawals.
Signing
Staking
References Technical Summary
[1] Ethereum EVM illustrated: A technical overview of Ethereum including state, accounts, transactions and messages as well as the EVM. Appendix E has links to type definitions for blocks, transactions, state etc in geth.
[2] Blocks: Block data definitions including attestations from ethereum.org
[3] eth1 block proposal: Technical walkthrough of how blocks are proposed and potential MEV opportunities from FlashBots.
[4] Assemble Block: Ethereum Specification for block Assembly as part of Rayonism – The Merge spec.
[5] Prysm running a node: Operational procedures for Validators by Prysm. Note validators run both the beacon chain(consensus) and a geth node(execution)
[6]The Beacon Chain Ethereum 2.0 explainer you need to read first: Detailed walk through og Ethereum 2.0 block production including slots, epochs, validators, commitees and finality.
[7] Etherum 2.0 Validators Overview: Live Monitoring of Ethreum 2.0 Validators from beachoncha.in
[8]BLS Signatures: Detailed walkthrough of BLS Signatures and how they can be used in aggregation.
[8]Attestation Inclusion Lifecycle: High Level overview of the attestation life cycle including geeration, propogation, aggregation and inclusion.Attest
[ts10] Beacon Chain Proposal: Sync Comittees: For each period (~27 hours), 1024 validators are randomly selected to be part of the sync committee during that period. Validators in the sync committee would publish signatures attesting to the current head. These signatures would be broadcasted as part of a LightClientUpdate object that could help light clients find the head, and would be included in the beacon chain to be rewarded.
[ts11] Altair Light Client – Sync Protocol: This document suggests a minimal light client design for the beacon chain that uses sync committees.
[ts12] Ethereum Builder Specifications: Honest Validator: explains the way in which a beacon chain validator is expected to use the Builder spec to participate in an external builder network.
[ts13] Flashbots: mev-boost: open source middleware run by validators to access a competitive block-building market.
[ts14] A note on Ethereum 2.0 phase 0 validator lifecycle: describes the concept of validator status epochs and the cases of validator lifecycle in the view of “validator status transition” in phase 0.
Additional
Process Flow
Attestation Process Flow
Attestations Block(LMD Ghost Vote) and Epoch Checkpoints (FFG Votes)
From Attestations, ethereum.org
Every epoch (6.4 minutes) a validator proposes an attestation to the network. The attestation is for a specific slot in the epoch. The purpose of the attestation is to vote in favor of the validator’s view of the chain, in particular the most recent justified block and the first block in the current epoch (known as source and target checkpoints). This information is combined for all participating validators, enabling the network to reach consensus about the state of the blockchain.
The attestation contains the following components:
- aggregation_bits: a bitlist of validators where the position maps to the validator index in their committee; the value (0/1) indicates whether the validator signed the data (i.e. whether they are active and agree with the block proposer)
- data: details relating to the attestation, as defined below
- signature: a BLS signature that aggregates the signatures of individual validators
The first task for an attesting validator is to build the data. The data contains the following information:
- slot: The slot number that the attestation refers to
- index: A number that identifies which committee the validator belongs to in a given slot
- beacon_block_root: Root hash of the block the validator sees at the head of the chain (the result of applying the fork-choice algorithm)
- source: Part of the finality vote indicating what the validators see as the most recent justified block
- target: Part of the finality vote indicating what the validators see as the first block in the current epoch
Once the data is built, the validator can flip the bit in aggregation_bits corresponding to their own validator index from 0 to 1 to show that they participated.
Finally, the validator signs the attestation and broadcasts it to the network.
Technical Details
Following is an overview of the state structure and logic for generating committees and aggregating attestations. For data structures, please see Beacon State Data Structures from Prysm and web3signer_types from prysm.
BeaconState contains both a ReadOnlyBeaconState and a WriteOnlyBeaconState wich contain ReadOnlyValidators and ReadOnlyRandaoMixes and WriteOnlyValidators and WriteOnlyRandaoMixes respectively.
At the beginning of each epoch func ProcessRandaoMixesReset processes the final updates to RANDAO mix during epoch processing. This calls RandaoMix which returns the randao mix (xor’ed seed) of a given slot. It is used to shuffle validators.
Following are sample mixes generated from func TestRandaoMix_OK by adding the statement fmt.Printf("mix: %v\n", mix)
mix: [10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
mix: [40 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
mix: [159 134 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
The shuffle functions consist of
func ShuffleList: returns list of shuffled indexes in a pseudorandom permutation p
of 0...list_size - 1
with “seed“ as entropy. We utilize ‘swap or not’ shuffling in this implementation; we are allocating the memory with the seed that stays constant between iterations instead of reallocating it each iteration as in the spec. This implementation is based on the original implementation from protolambda, https://github.com/protolambda/eth2-shuffle
Following is an example of a shuffled list generated from TestShuffleList_OK
list1: [0 1 2 3 4 5 6 7 8 9]
seed1: [1 128 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
shuffledList1: [0 7 8 6 3 9 4 5 2 1]
func ShuffleIndex: returns p(index)
in a pseudorandom permutation p
of 0...list_size - 1
with “seed“ as entropy. We utilize ‘swap or not’ shuffling in this implementation; we are allocating the memory with the seed that stays constant between iterations instead of reallocating it each iteration as in the spec. This implementation is based on the original implementation from protolambda, https://github.com/protolambda/eth2-shuffle
func ShuffleIndices: ShuffledIndices uses input beacon state and returns the shuffled indices of the input epoch, the shuffled indices then can be used to break up into committees.
Committes are formed using functions from beacon_comittee.go
func BeaconComittee: returns the beacon committee of a given slot and committee index. The validator indices and seed are provided as an argument rather than an imported implementation from the spec definition. Having them as an argument allows for cheaper computation run time. (This is an optomized version of func BeaconComitteFromState)
Following is an example of a beaconComittee
generated by adding the following lines to TestBeaconCommitteeFromState_UpdateCacheForPreviousEpoch:
```
var beaconCommittee []types.ValidatorIndex beaconCommittee, err = BeaconCommitteeFromState(context.Background(), state, 1 /previous epoch/, 0) fmt.Printf(“beaconComittee: %+v\n”, beaconCommittee) ```
Result
```
beaconComittee: [160 338 313 307 320 324 45 469 196 303 23 14 97 312 126 488]
```
Following is an example of commitees
generated by adding the following lines to TestComputeCommittee_WithoutCache
``` committees, err := computeCommittee(indices, seed, 0, 1 /* Total committee*/)
fmt.Printf(“committees: %+v\n”, committees) ```
<details>
<summary>result</summary>
```
committees: [799 45 913 1 631 654 417 244 1270 918 798 719 426 164 1171 863 848 522 828 359 713 972 284 680 203 832 453 75 979 468 667 540 180 729 1137 156 624 434 655 974 108 321 641 750 1150 356 933 870 650 984 869 95 975 510 563 1002 821 819 599 597 593 635 982 915 693 910 1030 845 461 887 936 354 1075 1253 1238 1011 395 773 670 54 389 765 1183 912 866 1230 1279 521 898 598 1038 814 377 1209 1226 19 1087 775 820 401 640 1028 673 174 493 857 931 288 475 1115 139 429 353 295 412 1136 1166 1191 496 677 1039 629 826 528 769 289 856 147 1227 243 731 297 924 89 644 557 1027 1239 1109 447 323 716 764 669 903 612 350 1046 392 768 1257 1083 216 294 606 971 103 902 1015 801 674 1099 49 484 995 1012 146 879 1156 548 1081 844 873 1246 1107 115 121 1018 387 751 941 1259 183 916 937 71 163 287 706 421 304 247 310 113 1032 776 502 1276 274 1214 418 271 307 1224 332 1222 240 657 1060 479 874 14 1147 627 122 448 1082 458 371 214 64 487 1263 34 172 497 880 555 1091 839 507 530 1170 498 999 727 950 317 1266 423 134 364 1092 1128 155 362 419 1219 1019 379 1163 483 917 318 804 336 985 463 584 210 1255 26 583 850 365 723 433 1073 1141 735 922 1035 893 774 1114 256 358 1044 997 546 679 1024 699 1096 663 1066 499 366 1256 883 566 17 717 393 422 622 795 1181 554 1212 736 1064 106 1050 72 1153 1210 198 943 818 518 309 101 471 0 38 688 107 718 1077 1021 648 1236 891 969 39 481 1159 660 686 450 990 1045 1213 756 900 849 355 119 1135 623 878 44 596 262 553 1013 290 269 691 18 207 454 620 221 983 852 430 843 1272 209 526 1100 865 402 437 278 976 1185 784 128 906 536 608 683 1205 574 1251 562 344 930 440 758 472 239 369 73 1235 478 724 373 399 1142 375 490 966 1203 1093 403 74 65 1247 579 145 1090 143 80 190 187 449 1160 194 959 533 671 442 136 158 665 79 253 226 1076 572 1130 227 909 940 275 43 342 182 126 967 700 267 1070 171 1000 658 876 1120 424 141 1164 328 1277 1220 1245 314 335 886 249 638 836 104 527 1057 1179 1111 551 334 749 754 237 1232 495 549 672 250 547 1132 427 346 935 515 452 184 739 77 689 744 831 281 76 48 2 327 542 351 47 1079 661 585 746 709 260 486 1242 932 303 435 1061 282 1217 390 996 457 470 40 592 785 1065 24 160 991 920 858 978 616 934 586 601 939 730 501 859 482 1207 386 1037 78 1184 947 861 643 231 22 397 1126 1215 265 1145 864 942 809 398 715 890 385 559 232 777 185 410 131 112 192 632 1124 302 1025 904 1047 94 1175 516 474 1122 568 617 894 733 1074 1252 264 263 851 124 1258 1023 1121 283 901 1225 923 464 193 1140 810 604 1108 740 1157 368 853 199 270 8 752 529 973 90 246 896 11 960 6 734 285 299 1042 152 732 965 469 161 609 1234 467 1084 780 1069 466 816 588 50 1194 1127 5 1010 31 712 766 1049 813 157 27 259 1055 343 793 1005 127 558 1036 794 1006 1178 767 1168 537 254 1218 590 361 531 186 567 605 4 255 618 37 1216 1134 337 223 811 962 67 587 1001 1187 842 455 1228 1248 1056 300 613 396 1152 830 329 61 1155 439 1188 807 1182 268 662 1101 1026 82 847 755 757 148 1244 778 664 1059 1197 301 1117 1274 743 840 316 123 634 272 1237 326 1041 1068 372 1003 1190 1243 630 298 215 166 445 513 838 363 1085 854 639 503 129 1029 1196 219 325 1161 70 165 564 1206 111 1078 1233 970 444 12 400 211 742 191 41 760 506 196 988 1173 125 177 420 805 957 862 1088 1144 1267 1265 994 380 1250 505 235 1089 451 120 762 867 1167 117 675 16 711 575 1009 85 577 550 1116 895 438 822 138 308 13 349 233 197 404 142 1123 589 614 251 411 1007 228 151 911 105 1162 738 140 892 1110 607 511 802 580 459 293 619 927 488 378 60 1020 236 212 279 980 322 1052 29 720 173 812 1043 882 797 159 926 1261 58 726 492 494 242 3 725 800 524 1062 1195 504 1016 808 168 436 682 383 952 615 179 57 921 370 394 945 489 1254 154 938 789 1229 339 684 806 525 539 787 1268 698 1008 621 225 408 32 964 357 188 477 114 581 144 745 701 110 391 460 381 181 1231 63 206 1264 480 538 561 591 1113 1202 825 348 704 33 625 783 681 1063 1080 1240 217 28 1176 928 582 914 229 252 1102 552 280 728 594 1017 35 406 137 175 162 1118 176 66 296 837 56 508 786 602 102 443 1095 868 696 899 692 1086 1223 907 834 1241 1172 118 1221 855 266 556 1098 384 948 55 340 178 1249 150 781 642 514 771 291 877 519 100 919 224 376 1125 987 645 1169 305 1133 319 201 611 956 42 189 238 908 703 88 981 954 1139 1174 881 576 1105 1186 1201 414 545 741 407 313 23 653 1051 509 872 195 649 1208 1165 1014 595 222 697 1112 1033 234 748 823 570 476 1198 1180 1154 248 257 905 306 1269 676 116 135 51 208 68 202 646 1177 312 86 388 1200 833 779 791 153 347 230 1158 565 543 261 986 875 1193 415 889 273 20 258 600 860 573 636 149 759 374 1072 1053 610 286 656 1119 1260 500 637 702 97 951 628 170 491 944 747 99 714 1278 721 69 571 83 520 473 569 989 98 245 929 1106 961 431 955 1004 884 998 446 544 949 220 535 1031 311 93 1262 871 763 1273 485 647 352 803 205 652 1034 687 958 888 753 792 456 782 59 462 441 796 708 1192 360 96 1148 678 428 277 1189 1071 633 1151 1103 25 993 835 241 1211 320 968 788 338 925 7 9 668 84 330 204 690 133 405 1094 1138 1097 1275 761 1104 10 897 315 517 694 416 685 560 62 772 382 977 87 651 532 659 827 1204 737 841 331 213 1040 132 846 963 695 130 292 91 1022 324 81 992 1199 770 790 465 523 425 1146 21 1054 815 345 829 666 603 1067 109 167 722 432 1149 953 512 413 707 1058 885 218 626 341 409 824 30 705 1048 578 367 710 946 36 1131 46 200 534 15 92 1129 276 817 169 53 52 541 333 1143 1271]
```
</details>
Attestations are managed using functions from attestation.go
ValidateNilAttestation
first to ensure data.Target
can’t be nil.func AggregateSignature: returns the aggregated signature of the input attestations.
Spec pseudocode definition:
def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature:
signatures = [attestation.signature for attestation in attestations]
return bls.Aggregate(signatures)
Following is an example aggregrated signature by adding the following lines to TestAttestation_AggregateSignature
``` aggSig, err := helpers.AggregateSignature(atts)
fmt.Printf(“aggSig: %+v\n”, aggSig) ```
Result
```
aggSig: &{s:0xc0003fe000}
```
func IsAggregated: IsAggregated returns true if the attestation is an aggregated attestation, false otherwise.
func ComputeSubnetForAttestation: returns the subnet for which the provided attestation will be broadcasted to.This differs from the spec definition by instead passing in the active validators indices in the attestation’s given epoch.
Spec pseudocode definition:
def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64:
"""
Compute the correct subnet for an attestation for Phase 0.
Note, this mimics expected future behavior where attestations will be mapped to their shard subnet.
"""
slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
func ComputeSubnetFromCommitteeAndSlot: is a flattened version of ComputeSubnetForAttestation where we only pass in the relevant fields from the attestation as function arguments.
Spec pseudocode definition:
def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64:
"""
Compute the correct subnet for an attestation for Phase 0.
Note, this mimics expected future behavior where attestations will be mapped to their shard subnet.
"""
slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
func ValidateAttestationTime: Validates that the incoming attestation is in the desired time range. An attestation is valid only if received within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots.
Example:
ATTESTATION_PROPAGATION_SLOT_RANGE = 5
clockDisparity = 24 seconds
current_slot = 100
invalid_attestation_slot = 92
invalid_attestation_slot = 103
valid_attestation_slot = 98
valid_attestation_slot = 101
In the attestation must be within the range of 95 to 102 in the example above.
func VerifyCheckpointEpoch: VerifyCheckpointEpoch is within current epoch and previous epoch with respect to current time. Returns true if it’s within, false if it’s not.
Note: Sample command for running tests in Prysm: bazel test //beacon-chain/core/helpers:go_default_test --test_output=streamed --test_filter=TestAttestation_AggregateSignature
.
Consensus Committee Selection
Syncing to Current state
LightClientFinalityUpdate
to light clients, allowing them to keep track of the latest finalized_header
.LightClientOptimisticUpdate
to light clients, allowing them to keep track of the latest optimistic_header
.Note: Time on Ethereum 2.0 Proof of Stake is divided into slots and epochs. One slot is 12 seconds. One epoch is 6.4 minutes, consisting of 32 slots. One block can be created for each slot.
Altair Light Client – Sync Protocol: The beacon chain is designed to be light client friendly for constrained environments to access Ethereum with reasonable safety and liveness.
Such environments include resource-constrained devices (e.g. phones for trust-minimized wallets)and metered VMs (e.g. blockchain VMs for cross-chain bridges).
This document suggests a minimal light client design for the beacon chain thatuses sync committees introduced in this beacon chain extension.
Additional documents describe how the light client sync protocol can be used:
Light client sync process: explains how light clients MAY obtain light client data to sync with the network.
genesis_state
(including genesis_time
and genesis_validators_root
), and with a trusted block root. The trusted block SHOULD be within the weak subjectivity period, and its root SHOULD be from a finalized Checkpoint
.genesis_time
, and the current fork digest is determined to browse for and connect to relevant light client data providers.LightClientBootstrap
object for the configured trusted block root. The bootstrap
object is passed to initialize_light_client_store
to obtain a local LightClientStore
.finalized_period
from store.finalized_header.slot
, optimistic_period
from store.optimistic_header.slot
, and current_period
from current_slot
based on the local clock.
finalized_period == optimistic_period
and is_next_sync_committee_known
indicates False
, the light client fetches a LightClientUpdate
for finalized_period
. If finalized_period == current_period
, this fetch SHOULD be scheduled at a random time before current_period
advances.finalized_period + 1 < current_period
, the light client fetches a LightClientUpdate
for each sync committee period in range [finalized_period + 1, current_period)
(current period excluded)finalized_period + 1 >= current_period
, the light client keeps observing LightClientFinalityUpdate
and LightClientOptimisticUpdate
. Received objects are passed to process_light_client_finality_update
and process_light_client_optimistic_update
. This ensures that finalized_header
and optimistic_header
reflect the latest blocks.process_light_client_store_force_update
MAY be called based on use case dependent heuristics if light client sync appears stuck. If available, falling back to an alternative syncing mechanism to cover the affected sync committee period is preferred.The Portal Network: The Portal Network is an in progess effort to enable lightweight protocol access by resource constrained devices. The term “portal” is used to indicate that these networks provide a view into the protocol but are not critical to the operation of the core Ethereum protocol.
The Portal Network is comprised of multiple peer-to-peer networks which together provide the data and functionality necessary to expose the standard JSON-RPC API. These networks are specially designed to ensure that clients participating in these networks can do so with minimal expenditure of networking bandwidth, CPU, RAM, and HDD resources.
The term ‘Portal Client’ describes a piece of software which participates in these networks. Portal Clients typically expose the standard JSON-RPC API.
Motivation: The Portal Network is focused on delivering reliable, lightweight, and decentralized access to the Ethereum protocol.
Prior Work on the “Light Ethereum Subprotocol” (LES): The term “light client” has historically refered to a client of the existing DevP2P based LES network. This network is designed using a client/server architecture. The LES network has a total capacity dictated by the number of “servers” on the network. In order for this network to scale, the “server” capacity has to increase. This also means that at any point in time the network has some total capacity which if exceeded will cause service degradation across the network. Because of this the LES network is unreliable when operating near capacity.
Block Relay
Beacon State: A client has a trusted beacon state root, and it wants to access some parts of the state. Each of the access request corresponds to some leave nodes of the beacon state. The request is a content lookup on a DHT. The response is a Merkle proof.
A Distributed Hash Table (DHT) allows network participants to have retrieve data on-demand based on a content
Syncing Block Headers: A beacon chain client could sync committee to perform state updates. The data object LightClientSkipSyncUpdate allows a client to quickly sync to a recent header with the appropriate sync committee. Once the client establishes a recent header, it could sync to other headers by processing LightClientUpdates. These two data types allow a client to stay up-to-date with the beacon chain.
Advance Block Headers: A beacon chain client could sync committee to perform state updates. The data object LightClientSkipSyncUpdate allows a client to quickly sync to a recent header with the appropriate sync committee. Once the client establishes a recent header, it could sync to other headers by processing LightClientUpdates. These two data types allow a client to stay up-to-date with the beacon chain.
These two data types are placed into separate sub-networks. A light client make find-content requests on `skip-sync-network` at start of the sync to get a header with the same `SyncCommittee` object as in the current sync period. The client uses messages in the gossip topic `bc-light-client-update` to advance its header.
The gossip topics described in this document is part of a [proposal](https://ethresear.ch/t/a-beacon-chain-light-client-proposal/11064) for a beacon chain light client.
Retrieving Beacon State: A client has a trusted beacon state root, and it wants to access some parts of the state. Each of the access request corresponds to some leave nodes of the beacon state. The request is a content lookup on a DHT. The response is a Merkle proof.
A Distributed Hash Table (DHT) allows network participants to have retrieve data on-demand based on a content key. A portal-network DHT is different than a traditional one in that each participant could selectively limit its workload by choosing a small interest radius r. A participants only process messages that are within its chosen radius boundary.
Wire Protocol: For a subprotocol, we need to further define the following to be able to instantiate the wire format of each message type.
1. content_key
2. content_id
3. payload
The content of the message is a Merkle proof contains multiple leave nodes for a [BeaconState](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate).
Finally, we define the necessary encodings. A light client only knows the root of the beacon state. The client wants to know the details of some leave nodes. The client has to be able to construct the `content_key` only knowing the root and which leave nodes it wants see. The `content_key` is the ssz serialization of the paths. The paths represent the part of the beacon state that one wants to know about. The paths are represented by generalized indices. Note that `hash_tree_root` and `serialize` are the same as those defined in [sync-gossip](https://github.com/ethereum/portal-network-specs/blob/master/beacon-chain/sync-gossip.md).
TODO: Review of Retrieving a transaction proof not just retrieving data on-demand
Circuits
Verification
Beacon Chain Topics Prysm
package p2p
const (
// GossipProtocolAndDigest represents the protocol and fork digest prefix in a gossip topic.
GossipProtocolAndDigest = "/eth2/%x/"
// Message Types
//
// GossipAttestationMessage is the name for the attestation message type. It is
// specially extracted so as to determine the correct message type from an attestation
// subnet.
GossipAttestationMessage = "beacon_attestation"
// GossipSyncCommitteeMessage is the name for the sync committee message type. It is
// specially extracted so as to determine the correct message type from a sync committee
// subnet.
GossipSyncCommitteeMessage = "sync_committee"
// GossipBlockMessage is the name for the block message type.
GossipBlockMessage = "beacon_block"
// GossipExitMessage is the name for the voluntary exit message type.
GossipExitMessage = "voluntary_exit"
// GossipProposerSlashingMessage is the name for the proposer slashing message type.
GossipProposerSlashingMessage = "proposer_slashing"
// GossipAttesterSlashingMessage is the name for the attester slashing message type.
GossipAttesterSlashingMessage = "attester_slashing"
// GossipAggregateAndProofMessage is the name for the attestation aggregate and proof message type.
GossipAggregateAndProofMessage = "beacon_aggregate_and_proof"
// GossipContributionAndProofMessage is the name for the sync contribution and proof message type.
GossipContributionAndProofMessage = "sync_committee_contribution_and_proof"
// GossipBlsToExecutionChangeMessage is the name for the bls to execution change message type.
GossipBlsToExecutionChangeMessage = "bls_to_execution_change"
// Topic Formats
//
// AttestationSubnetTopicFormat is the topic format for the attestation subnet.
AttestationSubnetTopicFormat = GossipProtocolAndDigest + GossipAttestationMessage + "_%d"
// SyncCommitteeSubnetTopicFormat is the topic format for the sync committee subnet.
SyncCommitteeSubnetTopicFormat = GossipProtocolAndDigest + GossipSyncCommitteeMessage + "_%d"
// BlockSubnetTopicFormat is the topic format for the block subnet.
BlockSubnetTopicFormat = GossipProtocolAndDigest + GossipBlockMessage
// ExitSubnetTopicFormat is the topic format for the voluntary exit subnet.
ExitSubnetTopicFormat = GossipProtocolAndDigest + GossipExitMessage
// ProposerSlashingSubnetTopicFormat is the topic format for the proposer slashing subnet.
ProposerSlashingSubnetTopicFormat = GossipProtocolAndDigest + GossipProposerSlashingMessage
// AttesterSlashingSubnetTopicFormat is the topic format for the attester slashing subnet.
AttesterSlashingSubnetTopicFormat = GossipProtocolAndDigest + GossipAttesterSlashingMessage
// AggregateAndProofSubnetTopicFormat is the topic format for the aggregate and proof subnet.
AggregateAndProofSubnetTopicFormat = GossipProtocolAndDigest + GossipAggregateAndProofMessage
// SyncContributionAndProofSubnetTopicFormat is the topic format for the sync aggregate and proof subnet.
SyncContributionAndProofSubnetTopicFormat = GossipProtocolAndDigest + GossipContributionAndProofMessage
// BlsToExecutionChangeSubnetTopicFormat is the topic format for the bls to execution change subnet.
BlsToExecutionChangeSubnetTopicFormat = GossipProtocolAndDigest + GossipBlsToExecutionChangeMessage
)
// SealHash returns the hash of a block prior to it being sealed.
func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
hasher := sha3.NewLegacyKeccak256()
rlp.Encode(hasher, []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
header.Root,
header.TxHash,
header.ReceiptHash,
header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra,
})
hasher.Sum(hash[:0])
return hash
}
BeaconBlockBody from prysm (golang)
// BeaconBlockBody is the main beacon block body structure. It can represent any block type.
type BeaconBlockBody struct {
version int
isBlinded bool
randaoReveal [field_params.BLSSignatureLength]byte
eth1Data *eth.Eth1Data
graffiti [field_params.RootLength]byte
proposerSlashings []*eth.ProposerSlashing
attesterSlashings []*eth.AttesterSlashing
attestations []*eth.Attestation
deposits []*eth.Deposit
voluntaryExits []*eth.SignedVoluntaryExit
syncAggregate *eth.SyncAggregate
executionPayload *engine.ExecutionPayload
executionPayloadHeader *engine.ExecutionPayloadHeader
}
// BeaconBlock is the main beacon block structure. It can represent any block type.
type BeaconBlock struct {
version int
slot types.Slot
proposerIndex types.ValidatorIndex
parentRoot [field_params.RootLength]byte
stateRoot [field_params.RootLength]byte
body *BeaconBlockBody
}
// SignedBeaconBlock is the main signed beacon block structure. It can represent any block type.
type SignedBeaconBlock struct {
version int
block *BeaconBlock
signature [field_params.BLSSignatureLength]byte
}
Eth1Data from prysm (golang)
type ETH1ChainData struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
CurrentEth1Data *LatestETH1Data `protobuf:"bytes,1,opt,name=current_eth1_data,json=currentEth1Data,proto3" json:"current_eth1_data,omitempty"`
ChainstartData *ChainStartData `protobuf:"bytes,2,opt,name=chainstart_data,json=chainstartData,proto3" json:"chainstart_data,omitempty"`
BeaconState *BeaconState `protobuf:"bytes,3,opt,name=beacon_state,json=beaconState,proto3" json:"beacon_state,omitempty"`
Trie *SparseMerkleTrie `protobuf:"bytes,4,opt,name=trie,proto3" json:"trie,omitempty"`
DepositContainers []*DepositContainer `protobuf:"bytes,5,rep,name=deposit_containers,json=depositContainers,proto3" json:"deposit_containers,omitempty"`
}
type LatestETH1Data struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
BlockHeight uint64 `protobuf:"varint,2,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"`
BlockTime uint64 `protobuf:"varint,3,opt,name=block_time,json=blockTime,proto3" json:"block_time,omitempty"`
BlockHash []byte `protobuf:"bytes,4,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"`
LastRequestedBlock uint64 `protobuf:"varint,5,opt,name=last_requested_block,json=lastRequestedBlock,proto3" json:"last_requested_block,omitempty"`
}
BeaconBlockAltair from lighthouse rust
/// Return an Altair block where the block has maximum size.
pub fn full(spec: &ChainSpec) -> Self {
let base_block: BeaconBlockBase<_, Payload> = BeaconBlockBase::full(spec);
let sync_aggregate = SyncAggregate {
sync_committee_signature: AggregateSignature::empty(),
sync_committee_bits: BitVector::default(),
};
BeaconBlockAltair {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyAltair {
proposer_slashings: base_block.body.proposer_slashings,
attester_slashings: base_block.body.attester_slashings,
attestations: base_block.body.attestations,
deposits: base_block.body.deposits,
voluntary_exits: base_block.body.voluntary_exits,
sync_aggregate,
randao_reveal: Signature::empty(),
eth1_data: Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
deposit_count: 0,
},
graffiti: Graffiti::default(),
_phantom: PhantomData,
},
}
}
}
type BeaconState interface {
SpecParametersProvider
ReadOnlyBeaconState
WriteOnlyBeaconState
Copy() BeaconState
HashTreeRoot(ctx context.Context) ([32]byte, error)
FutureForkStub
StateProver
}
type ReadOnlyBeaconState interface {
ReadOnlyBlockRoots
ReadOnlyStateRoots
ReadOnlyRandaoMixes
ReadOnlyEth1Data
ReadOnlyValidators
ReadOnlyBalances
ReadOnlyCheckpoint
ReadOnlyAttestations
ToProtoUnsafe() interface{}
ToProto() interface{}
GenesisTime() uint64
GenesisValidatorsRoot() []byte
Slot() types.Slot
Fork() *ethpb.Fork
LatestBlockHeader() *ethpb.BeaconBlockHeader
HistoricalRoots() [][]byte
Slashings() []uint64
FieldReferencesCount() map[string]uint64
MarshalSSZ() ([]byte, error)
IsNil() bool
Version() int
LatestExecutionPayloadHeader() (interfaces.ExecutionData, error)
}
type ReadOnlyValidators interface {
Validators() []*ethpb.Validator
ValidatorAtIndex(idx types.ValidatorIndex) (*ethpb.Validator, error)
ValidatorAtIndexReadOnly(idx types.ValidatorIndex) (ReadOnlyValidator, error)
ValidatorIndexByPubkey(key [fieldparams.BLSPubkeyLength]byte) (types.ValidatorIndex, bool)
PubkeyAtIndex(idx types.ValidatorIndex) [fieldparams.BLSPubkeyLength]byte
NumValidators() int
ReadFromEveryValidator(f func(idx int, val ReadOnlyValidator) error) error
}
type ReadOnlyRandaoMixes interface {
RandaoMixes() [][]byte
RandaoMixAtIndex(idx uint64) ([]byte, error)
RandaoMixesLength() int
}
type WriteOnlyBeaconState interface {
WriteOnlyBlockRoots
WriteOnlyStateRoots
WriteOnlyRandaoMixes
WriteOnlyEth1Data
WriteOnlyValidators
WriteOnlyBalances
WriteOnlyCheckpoint
WriteOnlyAttestations
SetGenesisTime(val uint64) error
SetGenesisValidatorsRoot(val []byte) error
SetSlot(val types.Slot) error
SetFork(val *ethpb.Fork) error
SetLatestBlockHeader(val *ethpb.BeaconBlockHeader) error
SetHistoricalRoots(val [][]byte) error
SetSlashings(val []uint64) error
UpdateSlashingsAtIndex(idx, val uint64) error
AppendHistoricalRoots(root [32]byte) error
SetLatestExecutionPayloadHeader(payload interfaces.ExecutionData) error
SetWithdrawalQueue(val []*enginev1.Withdrawal) error
AppendWithdrawal(val *enginev1.Withdrawal) error
SetNextWithdrawalIndex(i uint64) error
SetNextPartialWithdrawalValidatorIndex(i types.ValidatorIndex) error
}
type WriteOnlyValidators interface {
SetValidators(val []*ethpb.Validator) error
ApplyToEveryValidator(f func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error)) error
UpdateValidatorAtIndex(idx types.ValidatorIndex, val *ethpb.Validator) error
AppendValidator(val *ethpb.Validator) error
}
type WriteOnlyRandaoMixes interface {
SetRandaoMixes(val [][]byte) error
UpdateRandaoMixesAtIndex(idx uint64, val []byte) error
}
Validator information
type Validator struct {
PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" spec-name:"pubkey" ssz-size:"48"`
WithdrawalCredentials []byte `` /* 138-byte string literal not displayed */
EffectiveBalance uint64 `protobuf:"varint,3,opt,name=effective_balance,json=effectiveBalance,proto3" json:"effective_balance,omitempty"`
Slashed bool `protobuf:"varint,4,opt,name=slashed,proto3" json:"slashed,omitempty"`
ActivationEligibilityEpoch github_com_prysmaticlabs_prysm_v3_consensus_types_primitives.Epoch `` /* 221-byte string literal not displayed */
ActivationEpoch github_com_prysmaticlabs_prysm_v3_consensus_types_primitives.Epoch `` /* 186-byte string literal not displayed */
ExitEpoch github_com_prysmaticlabs_prysm_v3_consensus_types_primitives.Epoch `` /* 168-byte string literal not displayed */
WithdrawableEpoch github_com_prysmaticlabs_prysm_v3_consensus_types_primitives.Epoch `` /* 192-byte string literal not displayed */
// contains filtered or unexported fields
}
////////////////////////////////////////////////////////////////////////////////
// sub properties of Sign Requests /////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// ForkInfo a sub property object of the Sign request
type ForkInfo struct {
Fork *Fork `json:"fork"`
GenesisValidatorsRoot hexutil.Bytes `json:"genesis_validators_root"`
}
// Fork a sub property of ForkInfo.
type Fork struct {
PreviousVersion hexutil.Bytes `json:"previous_version"`
CurrentVersion hexutil.Bytes `json:"current_version"`
Epoch string `json:"epoch"` /*uint64*/
}
// AggregationSlot a sub property of AggregationSlotSignRequest.
type AggregationSlot struct {
Slot string `json:"slot"`
}
// AggregateAndProof a sub property of AggregateAndProofSignRequest.
type AggregateAndProof struct {
AggregatorIndex string `json:"aggregator_index"` /* uint64 */
Aggregate *Attestation `json:"aggregate"`
SelectionProof hexutil.Bytes `json:"selection_proof"` /* 96 bytes */
}
// Attestation a sub property of AggregateAndProofSignRequest.
type Attestation struct {
AggregationBits hexutil.Bytes `json:"aggregation_bits"` /*hex bitlist*/
Data *AttestationData `json:"data"`
Signature hexutil.Bytes `json:"signature"`
}
// AttestationData a sub property of Attestation.
type AttestationData struct {
Slot string `json:"slot"` /* uint64 */
Index string `json:"index"` /* uint64 */ // Prysm uses CommitteeIndex but web3signer uses index.
BeaconBlockRoot hexutil.Bytes `json:"beacon_block_root"`
Source *Checkpoint `json:"source"`
Target *Checkpoint `json:"target"`
}
// Checkpoint a sub property of AttestationData.
type Checkpoint struct {
Epoch string `json:"epoch"`
Root string `json:"root"`
}
attestation.proto from prysm (Message Structure)
message AttestationData {
// Attestation data includes information on Casper the Friendly Finality Gadget's votes
// See: https://arxiv.org/pdf/1710.09437.pdf
// Slot of the attestation attesting for.
uint64 slot = 1 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives.Slot"];
// The committee index that submitted this attestation.
uint64 committee_index = 2 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives.CommitteeIndex"];
// 32 byte root of the LMD GHOST block vote.
bytes beacon_block_root = 3 [(ethereum.eth.ext.ssz_size) = "32"];
// The most recent justified checkpoint in the beacon state
Checkpoint source = 4;
// The checkpoint attempting to be justified for the current epoch and its epoch boundary block
Checkpoint target = 5;
}
Following is the Epoch Data for 167040 It can be retrieved from here or by using this curl command
curl -X 'GET' \
'https://beaconcha.in/api/v1/epoch/167040' \
-H 'accept: application/json'
Response
{
"status": "OK",
"data": {
"attestationscount": 3457,
"attesterslashingscount": 0,
"averagevalidatorbalance": 33899775551,
"blockscount": 32,
"depositscount": 0,
"eligibleether": 15596542000000000,
"epoch": 167040,
"finalized": true,
"globalparticipationrate": 0.9963188171386719,
"missedblocks": 0,
"orphanedblocks": 0,
"proposedblocks": 32,
"proposerslashingscount": 0,
"scheduledblocks": 0,
"totalvalidatorbalance": 16522615004645864,
"validatorscount": 487396,
"voluntaryexitscount": 0,
"votedether": 15539128000000000
}
}
Following is the Block Data for Slot 5,330,592 It can be retrieved from here or by using this curl command
curl -X 'GET' 'https://beaconcha.in/api/v1/block/5330592' -H 'accept: application/json'
Response
{
"status": "OK",
"data": {
"attestationscount": 126,
"attesterslashingscount": 0,
"blockroot": "0xaebe891086c79ab79b325f474dc1150f1223e567337bff815cc318f14c64c233",
"depositscount": 0,
"epoch": 166581,
"eth1data_blockhash": "0xd346f84ffe7c600b7714d6411c8bea988d9d64dbdb432f26db58e72946337954",
"eth1data_depositcount": 498785,
"eth1data_depositroot": "0x9a5603a34aa60f299384679bf4bfc267e99b68278a81f343bde8cb5650bf1d60",
"exec_base_fee_per_gas": 12376913565,
"exec_block_hash": "0x26239efe09f51b24bdf7c518b1aa925a3b0b6453682408ec8a5c906d5038a6e7",
"exec_block_number": 16163905,
"exec_extra_data": "0x496c6c756d696e61746520446d6f63726174697a6520447374726962757465",
"exec_fee_recipient": "0xdafea492d9c6733ae3d56b7ed1adb60692c98bc5",
"exec_gas_limit": 30000000,
"exec_gas_used": 9901267,
"exec_logs_bloom": "0x8c21554815843b4084a999b2901917a52c58004a82a8440d94919a77f9241181388a0c404f000a8c0321ab024800bf899610e60ec801fb4b0352e34f147626192648619065381ded6b9d92bcd0861120adc1ec01064e7a016ea91c478d01b81316462d2d622a60010bc0139f6fb8ccf200499c0e211a85c042047d1601aa0c2ea2833902a2a3091528492dad09f6dc064529c455d328413b78c680c4699815ac9a91610f19e66542edca45a10518ee65b02cf02241a124232d5958b6004cd0a5846c5703d00b5e4d8353221015f7d38c1429074e34aaa11f3804f933082860c401152088251479918297a1a9237d9ac35539f6d069cca07a005819494a653913",
"exec_parent_hash": "0x06746d5ff105e96a1b8961c2490c0261b474604fbcbf934e86295c0030e26ce2",
"exec_random": "0xc2861c72cf4d34b37ec73519dbc20b690742b5cc119ed3738f1dd67d8ca52723",
"exec_receipts_root": "0x33cdf5c6e03dd341f282d02d3c354c2361a6212692b2a3c06b520397045313f4",
"exec_state_root": "0x517304bade8d83337c9a52f8ceeb13f924b64486b3b8033f7c348c176922104a",
"exec_timestamp": 1670791127,
"exec_transactions_count": 139,
"graffiti": "0x0000000000000000000000000000000000000000000000000000000000000000",
"graffiti_text": "",
"parentroot": "0x0cab36616bbcbbc67c343ddce00241c27d0df2c367c5fa82fc7c0fdf0ed37405",
"proposer": 4345,
"proposerslashingscount": 0,
"randaoreveal": "0x83950cb64781aff91f4bd14aa6abb0f5fdb7e08e4e81c264f0754c93d7672c4a9615de196491fdb53eafdeb8f49e9cf515f1bd3dc05bb5dc0e2dd8bff5a8d783b503e3385e80b61485f0ddac1caa9361132a863db84e7e234df5815e6908e4e7",
"signature": "0x84865a9480ae6313b0e5fcadfa294b35f5963e06c66ad1c7613dc081e9700c07f82a2583ba4b62b2483b4a1b9d49aafe0690f22fcf4d0072f9f44a5ce3067ef4fda560d171001cc6bf5dc84e09d9055d92894b86b27695c297f25530cd8db7a0",
"slot": 5330592,
"stateroot": "0x9e7e40d844c3b229cd9497d662a6d94276d285945073849995aba93c7e73cfe7",
"status": "1",
"syncaggregate_bits": "0xdffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffffffffffffffdffffffffffffffffffffffff",
"syncaggregate_participation": 0.9921875,
"syncaggregate_signature": "0x95332c55790018eed3d17eada01cb4045348d09137505bc8697eeedaa3800a830ee2c138251850a9577f62a5488419ef0a722579156a177fb3a147017f1077af5d778f46a4cdf815fc450129d135fe5286e16df68333592e4aa45821bde780dd",
"voluntaryexitscount": 0,
"votes": 19227
}
}
Following is the execution block data for 16163905 It can be retrieved from here or by using this curl command
curl -X 'GET' 'https://beaconcha.in/api/v1/execution/block/16163905' -H 'accept: application/json'
Result
{
"status": "OK",
"data": [
{
"blockHash": "0x26239efe09f51b24bdf7c518b1aa925a3b0b6453682408ec8a5c906d5038a6e7",
"blockNumber": 16163905,
"timestamp": 1670791127,
"blockReward": 37343826945103810,
"blockMevReward": 37083911760238810,
"producerReward": 37083911760238810,
"feeRecipient": "0xdafea492d9c6733ae3d56b7ed1adb60692c98bc5",
"gasLimit": 30000000,
"gasUsed": 9901267,
"baseFee": 12376913565,
"txCount": 139,
"internalTxCount": 54,
"uncleCount": 0,
"parentHash": "0x06746d5ff105e96a1b8961c2490c0261b474604fbcbf934e86295c0030e26ce2",
"uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"difficulty": 0,
"posConsensus": {
"executionBlockNumber": 16163905,
"proposerIndex": 4345,
"slot": 5330592,
"epoch": 166581,
"finalized": true
},
"relay": {
"tag": "flashbots-relay",
"builderPubkey": "0x81beef03aafd3dd33ffd7deb337407142c80fea2690e5b3190cfc01bde5753f28982a7857c96172a75a234cb7bcb994f",
"producerFeeRecipient": "0x60987e0d8b5e0095869ca6f0e642828e3f258bb5"
},
"consensusAlgorithm": "pos"
}
]
}
Following is a sample Sync Committee It can be retrieved from here or by using this curl command
curl -X 'GET' 'https://beaconcha.in/api/v1/sync_committee/latest' -H 'accept: application/json'
Abbrieviated Result
{
"status": "OK",
"data": {
"end_epoch": 167167,
"period": 652,
"start_epoch": 166912,
"validators": [
328781,
184949,
...
]
}
}
Sync Committee Configuration
EPOCHS_PER_SYNC_COMMITTEE_PERIOD
is set in config.go currently 255 epochs per synch comittee (approx 27 hrs) for Ethreum Mainnet.
Sync Committee Update Process
onBlock
which is called when a gossip block is received. It also has function handleEpochBoundary
which calls ProcessSlots
in beacon-chain/core/transition and calls function UpdateCommitteeCache
in beacon-chain/core/helpersProcessSlots
calls ProcessEpoch
in beacon-chain/core/altair/transition.goProcessEpoch
which calls ProcessSyncCommitteeUpdates
in epoch_spec.goProcessSyncCommitteeUpdates
which calls NextSyncCommittee
it also persists beacon state syncCommittee by calling beaconState.SetNextSyncCommittee(nextSyncCommittee)
in setters_sync_committee.goNextSyncCommittee
which calls NextSyncCommittee
to return the sync committee indices, with possible duplicates, for the next sync committee.VerifySyncCommitteeSig
UpdateCommitteeCache
which gets called at the beginning of every epoch to cache the committee shuffled indices list with committee index and epoch number. It caches the shuffled indices for current epoch and next epoch. it calls UpdatePositionsInCommittee
in sync_committee.goUpdatePositionsInCommittee
which updates caching of validators position in sync committee in respect to current epoch and next epoch. This should be called when current_sync_committee
and next_sync_committee
change and that happens every EPOCHS_PER_SYNC_COMMITTEE_PERIOD
.Sync Committee Retrieval gRPC and API methods
GetDuties
and StreamDuties
which calls function duties
to compute the validator duties from the head state’s corresponding epoch for validators public key / indices requested.which manages sync committee duties every EPOCHS_PER_SYNC_COMMITTEE_PERIOD - 1
which is set in config.go currently 255 epochs per synch comittee (approx 27 hrs) for Ethreum Mainnet.
func registerSyncSubnetNextPeriod(s beaconState.BeaconState, epoch types.Epoch, pubKey []byte, status ethpb.ValidatorStatus) error {
committee, err := s.NextSyncCommittee()
if err != nil {
return err
}
syncCommPeriod := slots.SyncCommitteePeriod(epoch)
registerSyncSubnet(epoch, syncCommPeriod+1, pubKey, committee, status)
return nil
}
Sync Committee Storage
Persistence Mechanism
SyncCommitteeDuty
// BeaconState defines a struct containing utilities for the Ethereum Beacon Chain state, defining
// getters and setters for its respective values and helpful functions such as HashTreeRoot().
type BeaconState struct {
version int
genesisTime uint64
genesisValidatorsRoot [32]byte
slot eth2types.Slot
fork *ethpb.Fork
latestBlockHeader *ethpb.BeaconBlockHeader
blockRoots *customtypes.BlockRoots
stateRoots *customtypes.StateRoots
historicalRoots customtypes.HistoricalRoots
eth1Data *ethpb.Eth1Data
eth1DataVotes []*ethpb.Eth1Data
eth1DepositIndex uint64
validators []*ethpb.Validator
balances []uint64
randaoMixes *customtypes.RandaoMixes
slashings []uint64
previousEpochAttestations []*ethpb.PendingAttestation
currentEpochAttestations []*ethpb.PendingAttestation
previousEpochParticipation []byte
currentEpochParticipation []byte
justificationBits bitfield.Bitvector4
previousJustifiedCheckpoint *ethpb.Checkpoint
currentJustifiedCheckpoint *ethpb.Checkpoint
finalizedCheckpoint *ethpb.Checkpoint
inactivityScores []uint64
currentSyncCommittee *ethpb.SyncCommittee
nextSyncCommittee *ethpb.SyncCommittee
latestExecutionPayloadHeader *enginev1.ExecutionPayloadHeader
latestExecutionPayloadHeaderCapella *enginev1.ExecutionPayloadHeaderCapella
nextWithdrawalIndex uint64
nextWithdrawalValidatorIndex eth2types.ValidatorIndex
lock sync.RWMutex
dirtyFields map[nativetypes.FieldIndex]bool
dirtyIndices map[nativetypes.FieldIndex][]uint64
stateFieldLeaves map[nativetypes.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[nativetypes.FieldIndex]bool
valMapHandler *stateutil.ValidatorMapHandler
merkleLayers [][][]byte
sharedFieldReferences map[nativetypes.FieldIndex]*stateutil.Reference
}
type SyncCommittee struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Pubkeys [][]byte `protobuf:"bytes,1,rep,name=pubkeys,proto3" json:"pubkeys,omitempty" ssz-size:"512,48"`
AggregatePubkey []byte `protobuf:"bytes,2,opt,name=aggregate_pubkey,json=aggregatePubkey,proto3" json:"aggregate_pubkey,omitempty" ssz-size:"48"`
}
// BeaconState has read and write access to beacon state methods.
type BeaconState interface {
SpecParametersProvider
ReadOnlyBeaconState
ReadOnlyWithdrawals
WriteOnlyBeaconState
Copy() BeaconState
HashTreeRoot(ctx context.Context) ([32]byte, error)
FutureForkStub
StateProver
}
// StateProver defines the ability to create Merkle proofs for beacon state fields.
type StateProver interface {
FinalizedRootProof(ctx context.Context) ([][]byte, error)
CurrentSyncCommitteeProof(ctx context.Context) ([][]byte, error)
NextSyncCommitteeProof(ctx context.Context) ([][]byte, error)
}