parent
abed5ffdae
commit
1f97206dcf
|
@ -20,6 +20,7 @@ Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md
|
||||||
|------------------------|-----------------|-------|---------------|
|
|------------------------|-----------------|-------|---------------|
|
||||||
| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | |
|
| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | |
|
||||||
| `SHARD_BLOCK_SIZE` | 2**14 (= 16384) | bytes | |
|
| `SHARD_BLOCK_SIZE` | 2**14 (= 16384) | bytes | |
|
||||||
|
| `CROSSLINK_LOOKBACK` | 2**5 (= 32) | slots | |
|
||||||
|
|
||||||
### Flags, domains, etc.
|
### Flags, domains, etc.
|
||||||
|
|
||||||
|
@ -98,26 +99,39 @@ A node should sign a crosslink only if the following conditions hold. **If a nod
|
||||||
|
|
||||||
First, the conditions must recursively apply to the crosslink referenced in `last_crosslink_root` for the same shard (unless `last_crosslink_root` equals zero, in which case we are at the genesis).
|
First, the conditions must recursively apply to the crosslink referenced in `last_crosslink_root` for the same shard (unless `last_crosslink_root` equals zero, in which case we are at the genesis).
|
||||||
|
|
||||||
Second, we verify the `shard_block_combined_data_root`. Let `h` be the slot _immediately after_ the slot of the shard block included by the last crosslink, and `h+n-1` be the slot number of the block directly referenced by the current `shard_block_root`. Let `B[i]` be the block at slot `h+i` in the shard chain. Let `bodies[0] .... bodies[n-1]` be the bodies of these blocks and `roots[0] ... roots[n-1]` the data roots. If there is a missing slot in the shard chain at position `h+i`, then `bodies[i] == b'\x00' * shard_block_maxbytes(state[i])` and `roots[i]` be the Merkle root of the empty data. Define `compute_merkle_root` be a simple Merkle root calculating function that takes as input a list of objects, where the list's length must be an exact power of two. We define the function for computing the combined data root as follows:
|
Second, we verify the `shard_chain_commitment`.
|
||||||
|
* Let `start_slot = state.latest_crosslinks[shard].epoch * EPOCH_LENGTH + EPOCH_LENGTH - CROSSLINK_LOOKBACK`.
|
||||||
|
* Let `end_slot = attestation.data.slot - attestation.data.slot % EPOCH_LENGTH - CROSSLINK_LOOKBACK`.
|
||||||
|
* Let `length = end_slot - start_slot`, `headers[0] .... headers[length-1]` be the serialized block headers in the canonical shard chain from the verifer's point of view (note that this implies that `headers` and `bodies` have been checked for validity).
|
||||||
|
* Let `bodies[0] ... bodies[length-1]` be the bodies of the blocks.
|
||||||
|
* Note: If there is a missing slot, then the header and body are the same as that of the block at the most recent slot that has a block.
|
||||||
|
|
||||||
|
We define two helpers:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
ZERO_ROOT = merkle_root(bytes([0] * SHARD_BLOCK_SIZE))
|
def pad_to_power_of_2(values: List[bytes]) -> List[bytes]:
|
||||||
|
while not is_power_of_two(len(values)):
|
||||||
def mk_combined_data_root(roots):
|
values = values + [SHARD_BLOCK_SIZE]
|
||||||
data = roots + [ZERO_ROOT for _ in range(len(roots), next_power_of_2(len(roots)))]
|
return values
|
||||||
return compute_merkle_root(data)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This outputs the root of a tree of the data roots, with the data roots all adjusted to have the same height if needed. The tree can also be viewed as a tree of all of the underlying data concatenated together, appropriately padded. Here is an equivalent definition that uses bodies instead of roots [TODO: check equivalence]:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def mk_combined_data_root(depths, bodies):
|
def merkle_root_of_bytes(data: bytes) -> bytes:
|
||||||
data = b''.join(bodies)
|
return merkle_root([data[i:i+32] for i in range(0, len(data), 32)])
|
||||||
data += bytes([0] * (next_power_of_2(len(data)) - len(data))
|
|
||||||
return compute_merkle_root([data[pos:pos+SHARD_CHUNK_SIZE] for pos in range(0, len(data), SHARD_CHUNK_SIZE)])
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Verify that the `shard_block_combined_data_root` is the output of these functions.
|
We define the function for computing the commitment as follows:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def compute_commitment(headers: List[ShardBlock], bodies: List[bytes]) -> Bytes32:
|
||||||
|
return hash(
|
||||||
|
merkle_root(pad_to_power_of_2([merkle_root_of_bytes(zpad(serialize(h), SHARD_BLOCK_SIZE)) for h in headers])),
|
||||||
|
merkle_root(pad_to_power_of_2([merkle_root_of_bytes(h) for h in bodies]))
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
The `shard_chain_commitment` is only valid if it equals `compute_commitment(headers, bodies)`.
|
||||||
|
|
||||||
|
|
||||||
### Shard block fork choice rule
|
### Shard block fork choice rule
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue