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

Updated phase 1: commitments #579

Merged
merged 2 commits into from
Feb 10, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 deletions specs/core/1_shard-data-chains.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_BLOCK_SIZE` | 2**14 (= 16384) | bytes | |
| `CROSSLINK_LOOKBACK` | 2**5 (= 32) | slots | |

### Flags, domains, etc.

Expand Down Expand Up @@ -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).

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
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)):
values = values + [SHARD_BLOCK_SIZE]
return values
```

def mk_combined_data_root(roots):
data = roots + [ZERO_ROOT for _ in range(len(roots), next_power_of_2(len(roots)))]
return compute_merkle_root(data)
```python
def merkle_root_of_bytes(data: bytes) -> bytes:
return merkle_root([data[i:i+32] for i in range(0, len(data), 32)])
```

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]:
We define the function for computing the commitment as follows:

```python
def mk_combined_data_root(depths, bodies):
data = b''.join(bodies)
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)])
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]))
)
```

Verify that the `shard_block_combined_data_root` is the output of these functions.
The `shard_chain_commitment` is only valid if it equals `compute_commitment(headers, bodies)`.


### Shard block fork choice rule

Expand Down