You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This document describes the extensions made to the Phase 0 design of The Beacon Chain to support data sharding,
based on the ideas here and more broadly here,
using KZG10 commitments to commit to data to remove any need for fraud proofs (and hence, safety-critical synchrony assumptions) in the design.
Glossary
Data: A list of KZG points, to translate a byte string into
Blob: Data with commitments and meta-data, like a flattened bundle of L2 transactions.
Constants
The following values are (non-configurable) constants used throughout the specification.
Misc
Name
Value
Notes
FIELD_ELEMENTS_PER_SAMPLE
uint64(2**4) (= 16)
31 * 16 = 496 bytes
Domain types
Name
Value
DOMAIN_SHARD_SAMPLE
DomainType('0x10000000')
Preset
Misc
Name
Value
Notes
MAX_SHARDS
uint64(2**12) (= 4,096)
Theoretical max shard count (used to determine data structure sizes)
ACTIVE_SHARDS
uint64(2**8) (= 256)
Initial shard count
MAX_PROPOSER_BLOCKS_BETWEEN_BUILDER_BLOCKS
uint64(2**4) (= 16)
TODO: Need to define what happens if there were more blocks without builder blocks
Time parameters
With the introduction of builder blocks the number of slots per epoch is doubled (it counts beacon blocks and builder blocks).
Name
Value
Unit
Duration
SLOTS_PER_EPOCH
uint64(2**6) (= 64)
slots
8:32 minutes
Shard blob samples
Name
Value
Notes
SAMPLES_PER_BLOB
uint64(2**9) (= 512)
248 * 512 = 126,976 bytes
Configuration
Note: Some preset variables may become run-time configurable for testnets, but default to a preset while the spec is unstable.
E.g. ACTIVE_SHARDS and SAMPLES_PER_BLOB.
Time parameters
Name
Value
Unit
Duration
SECONDS_PER_SLOT
uint64(8)
seconds
8 seconds
Containers
New Containers
BuilderBlockBid
classBuilderBlockBid(Container):
slot: Slotparent_block_root: Rootexecution_payload_root: Rootsharded_data_commitment_root: Root# Root of the sharded data (only data, not beacon/builder block commitments)sharded_data_commitment_count: uint64# Count of sharded data commitmentsbid: Gwei# Block builder bid paid to proposervalidator_index: ValidatorIndex# Validator index for this bid# Block builders use an Eth1 address -- need signature as# block bid and data gas base fees will be charged to this addresssignature_y_parity: boolsignature_r: uint256signature_s: uint256
BuilderBlockBidWithRecipientAddress
classBuilderBlockBidWithRecipientAddress(Container):
builder_block_bid: Union[None, BuilderBlockBid]
recipient_address: ExecutionAddress# Address to receive the block builder bid
ShardedCommitmentsContainer
classShardedCommitmentsContainer(Container):
sharded_commitments: List[KZGCommitment, 2*MAX_SHARDS]
# Aggregate degree proof for all sharded_commitmentsdegree_proof: KZGCommitment# The sizes of the blocks encoded in the commitments (last builder and all beacon blocks since)included_block_sizes: List[uint64, MAX_PROPOSER_BLOCKS_BETWEEN_BUILDER_BLOCKS+1]
# Number of commitments that are for sharded data (no blocks)included_sharded_data_commitments: uint64# Random evaluation of beacon blocks + execution payload (this helps with quick verification)block_verification_kzg_proof: KZGCommitment
defget_active_shard_count(state: BeaconState, epoch: Epoch) ->uint64:
""" Return the number of active shards. Note that this puts an upper bound on the number of committees per slot. """returnACTIVE_SHARDS
defprocess_block_header(state: BeaconState, block: BeaconBlock) ->None:
# Verify that the slots matchassertblock.slot==state.slot# Verify that the block is newer than latest block headerassertblock.slot>state.latest_block_header.slot# Verify that proposer index is the correct indexifnotis_builder_block_slot(block.slot):
assertblock.proposer_index==get_beacon_proposer_index(state)
# Verify that the parent matchesassertblock.parent_root==hash_tree_root(state.latest_block_header)
# Cache current block as the new latest blockstate.latest_block_header=BeaconBlockHeader(
slot=block.slot,
proposer_index=block.proposer_index,
parent_root=block.parent_root,
state_root=Bytes32(), # Overwritten in the next process_slot callbody_root=hash_tree_root(block.body),
)
# Verify proposer is not slashedproposer=state.validators[block.proposer_index]
assertnotproposer.slashed
Builder Block Bid
defverify_builder_block_bid(state: BeaconState, block: BeaconBlock) ->None:
ifis_builder_block_slot(block.slot):
# Get last builder block bidassertstate.blocks_since_builder_block[-1].body.payload_data.selector==0builder_block_bid=state.blocks_since_builder_block[-1].body.payload_data.value.builder_block_bidassertbuilder_block_bid.slot+1==block.slotassertblock.body.payload_data.selector==1# Verify that builder block does not contain bidbuilder_block_data=block.body.payload_data.valueassertbuilder_block_bid.execution_payload_root==hash_tree_root(builder_block_data.execution_payload)
assertbuilder_block_bid.sharded_data_commitment_count==builder_block_data.included_sharded_data_commitmentsassertbuilder_block_bid.sharded_data_commitment_root==hash_tree_root(builder_block_data.sharded_commitments[-builder_block_bid.included_sharded_data_commitments:])
assertbuilder_block_bid.validator_index==block.proposer_indexelse:
assertblock.body.payload_data.selector==0builder_block_bid=block.body.payload_data.value.builder_block_bidassertbuilder_block_bid.slot==block.slotassertbuilder_block_bid.parent_block_root==block.parent_root# We do not check that the builder address exists or has sufficient balance here.# If it does not have sufficient balance, the block proposer loses out, so it is their# responsibility to check.# Check that the builder is a slashable validator. We can probably reduce this requirement and only# ensure that they have 1 ETH in their account as a DOS protection.builder=state.validators[builder_block_bid.validator_index]
assertis_slashable_validator(builder, get_current_epoch(state))
Sharded data
defprocess_sharded_data(state: BeaconState, block: BeaconBlock) ->None:
ifis_builder_block_slot(block.slot):
assertblock.body.payload_data.selector==1sharded_commitments_container=block.body.payload_data.value.sharded_commitments_container# Verify not too many commitmentsassertlen(sharded_commitments_container.sharded_commitments) //2<=get_active_shard_count(state, get_current_epoch(state))
# Verify the degree proofr=hash_to_bls_field(sharded_commitments_container.sharded_commitments, 0)
r_powers=compute_powers(r, len(sharded_commitments_container.sharded_commitments))
combined_commitment=elliptic_curve_lincomb(sharded_commitments_container.sharded_commitments, r_powers)
payload_field_elements_per_blob=SAMPLES_PER_BLOB*FIELD_ELEMENTS_PER_SAMPLE//2verify_degree_proof(combined_commitment, payload_field_elements_per_blob, sharded_commitments_container.degree_proof)
# Verify that the 2*N commitments lie on a degree < N polynomiallow_degree_check(sharded_commitments_container.sharded_commitments)
# Verify that blocks since the last builder block have been includedblocks_chunked= [bytes_to_field_elements(ssz_serialize(block)) forblockinstate.blocks_since_builder_block]
block_vectors= []
forblock_chunkedinblocks_chunked:
foriinrange(0, len(block_chunked), payload_field_elements_per_blob):
block_vectors.append(block_chunked[i:i+payload_field_elements_per_blob])
number_of_blobs=len(block_vectors)
r=hash_to_bls_field(sharded_commitments_container.sharded_commitments[:number_of_blobs], 0)
x=hash_to_bls_field(sharded_commitments_container.sharded_commitments[:number_of_blobs], 1)
r_powers=compute_powers(r, number_of_blobs)
combined_vector=vector_lincomb(block_vectors, r_powers)
combined_commitment=elliptic_curve_lincomb(sharded_commitments_container.sharded_commitments[:number_of_blobs], r_powers)
y=evaluate_polynomial_in_evaluation_form(combined_vector, x)
verify_kzg_proof(combined_commitment, x, y, sharded_commitments_container.block_verification_kzg_proof)
# Verify that number of sharded data commitments is correctly indicatedassert2* (number_of_blobs+included_sharded_data_commitments) ==len(sharded_commitments_container.sharded_commitments)
Execution payload
defprocess_execution_payload(state: BeaconState, block: BeaconBlock, execution_engine: ExecutionEngine) ->None:
ifis_builder_block_slot(block.slot):
assertblock.body.payload_data.selector==1payload=block.body.payload_data.value.execution_payload# Verify consistency of the parent hash with respect to the previous execution payload headerassertpayload.parent_hash==state.latest_execution_payload_header.block_hash# Verify randomassertpayload.random==get_randao_mix(state, get_current_epoch(state))
# Verify timestampassertpayload.timestamp==compute_timestamp_at_slot(state, state.slot)
# Get sharded data commitmentssharded_commitments_container=block.body.sharded_commitments_containersharded_data_commitments=sharded_commitments_container.sharded_commitments[-sharded_commitments_container.included_sharded_data_commitments:]
# Get all unprocessed builder block bidsunprocessed_builder_block_bid_with_recipient_addresses= []
forblockinstate.blocks_since_builder_block[1:]:
unprocessed_builder_block_bid_with_recipient_addresses.append(block.body.builder_block_bid_with_recipient_address.value)
# Verify the execution payload is valid# The execution engine gets two extra payloads: One for the sharded data commitments (these are needed to verify type 3 transactions)# and one for all so far unprocessed builder block bids:# * The execution engine needs to transfer the balance from the bidder to the proposer.# * The execution engine needs to deduct data gas fees from the bidder balancesassertexecution_engine.execute_payload(payload,
sharded_data_commitments,
unprocessed_builder_block_bid_with_recipient_addresses)
# Cache execution payload headerstate.latest_execution_payload_header=ExecutionPayloadHeader(
parent_hash=payload.parent_hash,
fee_recipient=payload.fee_recipient,
state_root=payload.state_root,
receipt_root=payload.receipt_root,
logs_bloom=payload.logs_bloom,
random=payload.random,
block_number=payload.block_number,
gas_limit=payload.gas_limit,
gas_used=payload.gas_used,
timestamp=payload.timestamp,
extra_data=payload.extra_data,
base_fee_per_gas=payload.base_fee_per_gas,
block_hash=payload.block_hash,
transactions_root=hash_tree_root(payload.transactions),
)