Merge branch 'dev' into JustinDrake-patch-11

This commit is contained in:
Danny Ryan 2019-03-28 12:14:13 -06:00
commit 901478e148
No known key found for this signature in database
GPG Key ID: 2765A792E42CE07A
7 changed files with 469 additions and 70 deletions

View File

@ -28,7 +28,7 @@
- [`Eth1DataVote`](#eth1datavote)
- [`AttestationData`](#attestationdata)
- [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit)
- [`SlashableAttestation`](#slashableattestation)
- [`IndexedAttestation`](#indexedattestation)
- [`DepositData`](#depositdata)
- [`BeaconBlockHeader`](#beaconblockheader)
- [`Validator`](#validator)
@ -77,6 +77,7 @@
- [`generate_seed`](#generate_seed)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`verify_merkle_branch`](#verify_merkle_branch)
- [`get_crosslink_committee_for_attestation`](#get_crosslink_committee_for_attestation)
- [`get_attestation_participants`](#get_attestation_participants)
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
- [`bytes_to_int`](#bytes_to_int)
@ -86,7 +87,8 @@
- [`get_domain`](#get_domain)
- [`get_bitfield_bit`](#get_bitfield_bit)
- [`verify_bitfield`](#verify_bitfield)
- [`verify_slashable_attestation`](#verify_slashable_attestation)
- [`convert_to_indexed`](#convert_to_indexed)
- [`verify_indexed_attestation`](#verify_indexed_attestation)
- [`is_double_vote`](#is_double_vote)
- [`is_surround_vote`](#is_surround_vote)
- [`integer_squareroot`](#integer_squareroot)
@ -183,7 +185,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
| `SHARD_COUNT` | `2**10` (= 1,024) |
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) |
| `MAX_SLASHABLE_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
| `MAX_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
| `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) |
| `SHUFFLE_ROUND_COUNT` | 90 |
@ -369,16 +371,15 @@ The types are defined topologically to aid in facilitating an executable version
}
```
#### `SlashableAttestation`
#### `IndexedAttestation`
```python
{
# Validator indices
'validator_indices': ['uint64'],
'custody_bit_0_indices': ['uint64'],
'custody_bit_1_indices': ['uint64'],
# Attestation data
'data': AttestationData,
# Custody bitfield
'custody_bitfield': 'bytes',
# Aggregate signature
'aggregate_signature': 'bytes96',
}
@ -478,10 +479,10 @@ The types are defined topologically to aid in facilitating an executable version
```python
{
# First slashable attestation
'slashable_attestation_1': SlashableAttestation,
# Second slashable attestation
'slashable_attestation_2': SlashableAttestation,
# First attestation
'attestation_1': IndexedAttestation,
# Second attestation
'attestation_2': IndexedAttestation,
}
```
@ -1036,6 +1037,20 @@ def verify_merkle_branch(leaf: Bytes32, proof: List[Bytes32], depth: int, index:
return value == root
```
### `get_crosslink_committee_for_attestation`
```python
def get_crosslink_committee_for_attestation(state: BeaconState,
attestation_data: AttestationData) -> List[ValidatorIndex]:
# Find the committee in the list with the desired shard
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
return crosslink_committee
```
### `get_attestation_participants`
```python
@ -1043,13 +1058,9 @@ def get_attestation_participants(state: BeaconState,
attestation_data: AttestationData,
bitfield: bytes) -> List[ValidatorIndex]:
"""
Return the participant indices corresponding to ``attestation_data`` and ``bitfield``.
Return the sorted participant indices corresponding to ``attestation_data`` and ``bitfield``.
"""
# Find the committee in the list with the desired shard
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
assert verify_bitfield(bitfield, len(crosslink_committee))
@ -1059,7 +1070,7 @@ def get_attestation_participants(state: BeaconState,
aggregation_bit = get_bitfield_bit(bitfield, i)
if aggregation_bit == 0b1:
participants.append(validator_index)
return participants
return sorted(participants)
```
### `int_to_bytes1`, `int_to_bytes2`, ...
@ -1147,33 +1158,47 @@ def verify_bitfield(bitfield: bytes, committee_size: int) -> bool:
return True
```
### `verify_slashable_attestation`
### `convert_to_indexed`
```python
def verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation) -> bool:
def convert_to_indexed(state: BeaconState, attestation: Attestation):
"""
Verify validity of ``slashable_attestation`` fields.
Convert an attestation to (almost) indexed-verifiable form
"""
if slashable_attestation.custody_bitfield != b'\x00' * len(slashable_attestation.custody_bitfield): # [TO BE REMOVED IN PHASE 1]
attesting_indices = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
custody_bit_1_indices = get_attestation_participants(state, attestation.data, attestation.custody_bitfield)
custody_bit_0_indices = [index for index in attesting_indices if index not in custody_bit_1_indices]
return IndexedAttestation(
custody_bit_0_indices=custody_bit_0_indices,
custody_bit_1_indices=custody_bit_1_indices,
data=attestation.data,
aggregate_signature=attestation.aggregate_signature
)
```
### `verify_indexed_attestation`
```python
def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
"""
Verify validity of ``indexed_attestation`` fields.
"""
custody_bit_0_indices = indexed_attestation.custody_bit_0_indices
custody_bit_1_indices = indexed_attestation.custody_bit_1_indices
if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1]
return False
if not (1 <= len(slashable_attestation.validator_indices) <= MAX_SLASHABLE_ATTESTATION_PARTICIPANTS):
total_attesting_indices = len(custody_bit_0_indices + custody_bit_1_indices)
if not (1 <= total_attesting_indices <= MAX_ATTESTATION_PARTICIPANTS):
return False
for i in range(len(slashable_attestation.validator_indices) - 1):
if slashable_attestation.validator_indices[i] >= slashable_attestation.validator_indices[i + 1]:
return False
if not verify_bitfield(slashable_attestation.custody_bitfield, len(slashable_attestation.validator_indices)):
if custody_bit_0_indices != sorted(custody_bit_0_indices):
return False
custody_bit_0_indices = []
custody_bit_1_indices = []
for i, validator_index in enumerate(slashable_attestation.validator_indices):
if get_bitfield_bit(slashable_attestation.custody_bitfield, i) == 0b0:
custody_bit_0_indices.append(validator_index)
else:
custody_bit_1_indices.append(validator_index)
if custody_bit_1_indices != sorted(custody_bit_1_indices):
return False
return bls_verify_multiple(
pubkeys=[
@ -1181,11 +1206,11 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]),
],
message_hashes=[
hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b0)),
hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b1)),
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)),
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)),
],
signature=slashable_attestation.aggregate_signature,
domain=get_domain(state.fork, slot_to_epoch(slashable_attestation.data.slot), DOMAIN_ATTESTATION),
signature=indexed_attestation.aggregate_signature,
domain=get_domain(state.fork, slot_to_epoch(indexed_attestation.data.slot), DOMAIN_ATTESTATION),
)
```
@ -2255,16 +2280,17 @@ def process_attester_slashing(state: BeaconState,
Process ``AttesterSlashing`` transaction.
Note that this function mutates ``state``.
"""
attestation1 = attester_slashing.slashable_attestation_1
attestation2 = attester_slashing.slashable_attestation_2
attestation1 = attester_slashing.attestation_1
attestation2 = attester_slashing.attestation_2
# Check that the attestations are conflicting
assert attestation1.data != attestation2.data
assert (
is_double_vote(attestation1.data, attestation2.data) or
is_surround_vote(attestation1.data, attestation2.data)
)
assert verify_slashable_attestation(state, attestation1)
assert verify_slashable_attestation(state, attestation2)
assert verify_indexed_attestation(state, attestation1)
assert verify_indexed_attestation(state, attestation2)
slashable_indices = [
index for index in attestation1.validator_indices
if (
@ -2310,18 +2336,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
),
}
# Check custody bits [to be generalised in phase 1]
assert attestation.custody_bitfield == b'\x00' * len(attestation.custody_bitfield)
# Check aggregate signature [to be generalised in phase 1]
participants = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
assert len(participants) != 0
assert bls_verify(
pubkey=bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in participants]),
message_hash=hash_tree_root(AttestationDataAndCustodyBit(data=attestation.data, custody_bit=0b0)),
signature=attestation.aggregate_signature,
domain=get_domain(state.fork, target_epoch, DOMAIN_ATTESTATION),
)
# Check signature and bitfields
assert verify_indexed_attestation(state, convert_to_indexed(state, attestation))
# Cache pending attestation
pending_attestation = PendingAttestation(

View File

@ -0,0 +1,46 @@
ETH 2.0 Networking Spec - Messaging
===
# Abstract
This specification describes how individual Ethereum 2.0 messages are represented on the wire.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL”, NOT", “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
# Motivation
This specification seeks to define a messaging protocol that is flexible enough to be changed easily as the ETH 2.0 specification evolves.
Note that while `libp2p` is the chosen networking stack for Ethereum 2.0, as of this writing some clients do not have workable `libp2p` implementations. To allow those clients to communicate, we define a message envelope that includes the body's compression, encoding, and body length. Once `libp2p` is available across all implementations, this message envelope will be removed because `libp2p` will negotiate the values defined in the envelope upfront.
# Specification
## Message Structure
An ETH 2.0 message consists of an envelope that defines the message's compression, encoding, and length followed by the body itself.
Visually, a message looks like this:
```
+--------------------------+
| compression nibble |
+--------------------------+
| encoding nibble |
+--------------------------+
| body length (uint64) |
+--------------------------+
| |
| body |
| |
+--------------------------+
```
Clients MUST ignore messages with mal-formed bodies. The compression/encoding nibbles MUST be one of the following values:
## Compression Nibble Values
- `0x0`: no compression
## Encoding Nibble Values
- `0x1`: SSZ

View File

@ -0,0 +1,32 @@
ETH 2.0 Networking Spec - Node Identification
===
# Abstract
This specification describes how Ethereum 2.0 nodes identify and address each other on the network.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL", NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
# Specification
Clients use Ethereum Node Records (as described in [EIP-778](http://eips.ethereum.org/EIPS/eip-778)) to discover one another. Each ENR includes, among other things, the following keys:
- The node's IP.
- The node's TCP port.
- The node's public key.
For clients to be addressable, their ENR responses MUST contain all of the above keys. Client MUST verify the signature of any received ENRs, and disconnect from peers whose ENR signatures are invalid. Each node's public key MUST be unique.
The keys above are enough to construct a [multiaddr](https://github.com/multiformats/multiaddr) for use with the rest of the `libp2p` stack.
It is RECOMMENDED that clients set their TCP port to the default of `9000`.
## Peer ID Generation
The `libp2p` networking stack identifies peers via a "peer ID." Simply put, a node's Peer ID is the SHA2-256 `multihash` of the node's public key struct (serialized in protobuf, refer to the [Peer ID spec](https://github.com/libp2p/specs/pull/100)). `go-libp2p-crypto` contains the canonical implementation of how to hash `secp256k1` keys for use as a peer ID.
# See Also
- [multiaddr](https://github.com/multiformats/multiaddr)
- [multihash](https://multiformats.io/multihash/)
- [go-libp2p-crypto](https://github.com/libp2p/go-libp2p-crypto)

View File

@ -0,0 +1,292 @@
ETH 2.0 Networking Spec - RPC Interface
===
# Abstract
The Ethereum 2.0 networking stack uses two modes of communication: a broadcast protocol that gossips information to interested parties via GossipSub, and an RPC protocol that retrieves information from specific clients. This specification defines the RPC protocol.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL", NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
# Dependencies
This specification assumes familiarity with the [Messaging](./messaging.md), [Node Identification](./node-identification), and [Beacon Chain](../core/0_beacon-chain.md) specifications.
# Specification
## Message Schemas
Message body schemas are notated like this:
```
(
field_name_1: type
field_name_2: type
)
```
Embedded types are serialized as SSZ Containers unless otherwise noted.
All referenced data structures can be found in the [0-beacon-chain](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#data-structures) specification.
## `libp2p` Protocol Names
A "Protocol ID" in `libp2p` parlance refers to a human-readable identifier `libp2p` uses in order to identify sub-protocols and stream messages of different types over the same connection. Peers exchange supported protocol IDs via the `Identify` protocol upon connection. When opening a new stream, peers pin a particular protocol ID to it, and the stream remains contextualised thereafter. Since messages are sent inside a stream, they do not need to bear the protocol ID.
## RPC-Over-`libp2p`
To facilitate RPC-over-`libp2p`, a single protocol name is used: `/eth/serenity/beacon/rpc/1`. The version number in the protocol name is neither backwards or forwards compatible, and will be incremented whenever changes to the below structures are required.
Remote method calls are wrapped in a "request" structure:
```
(
id: uint64
method_id: uint16
body: Request
)
```
and their corresponding responses are wrapped in a "response" structure:
```
(
id: uint64
response_code: uint16
result: bytes
)
```
If an error occurs, a variant of the response structure is returned:
```
(
id: uint64
response_code: uint16
result: bytes
)
```
The details of the RPC-Over-`libp2p` protocol are similar to [JSON-RPC 2.0](https://www.jsonrpc.org/specification). Specifically:
1. The `id` member is REQUIRED.
2. The `id` member in the response MUST be the same as the value of the `id` in the request.
3. The `id` member MUST be unique within the context of a single connection. Monotonically increasing `id`s are RECOMMENDED.
4. The `method_id` member is REQUIRED.
5. The `result` member is REQUIRED on success.
6. The `result` member is OPTIONAL on errors, and MAY contain additional information about the error.
7. `response_code` MUST be `0` on success.
Structuring RPC requests in this manner allows multiple calls and responses to be multiplexed over the same stream without switching. Note that this implies that responses MAY arrive in a different order than requests.
The "method ID" fields in the below messages refer to the `method` field in the request structure above.
The first 1,000 values in `response_code` are reserved for system use. The following response codes are predefined:
1. `0`: No error.
2. `10`: Parse error.
2. `20`: Invalid request.
3. `30`: Method not found.
4. `40`: Server error.
### Alternative for Non-`libp2p` Clients
Since some clients are waiting for `libp2p` implementations in their respective languages. As such, they MAY listen for raw TCP messages on port `9000`. To distinguish RPC messages from other messages on that port, a byte prefix of `ETH` (`0x455448`) MUST be prepended to all messages. This option will be removed once `libp2p` is ready in all supported languages.
## Messages
### Hello
**Method ID:** `0`
**Body**:
```
(
network_id: uint8
chain_id: uint64
latest_finalized_root: bytes32
latest_finalized_epoch: uint64
best_root: bytes32
best_slot: uint64
)
```
Clients exchange `hello` messages upon connection, forming a two-phase handshake. The first message the initiating client sends MUST be the `hello` message. In response, the receiving client MUST respond with its own `hello` message.
Clients SHOULD immediately disconnect from one another following the handshake above under the following conditions:
1. If `network_id` belongs to a different chain, since the client definitionally cannot sync with this client.
2. If the `latest_finalized_root` shared by the peer is not in the client's chain at the expected epoch. For example, if Peer 1 in the diagram below has `(root, epoch)` of `(A, 5)` and Peer 2 has `(B, 3)`, Peer 1 would disconnect because it knows that `B` is not the root in their chain at epoch 3:
```
Root A
+---+
|xxx| +----+ Epoch 5
+-+-+
^
|
+-+-+
| | +----+ Epoch 4
+-+-+
Root B ^
|
+---+ +-+-+
|xxx+<---+--->+ | +----+ Epoch 3
+---+ | +---+
|
+-+-+
| | +-----------+ Epoch 2
+-+-+
^
|
+-+-+
| | +-----------+ Epoch 1
+---+
```
Once the handshake completes, the client with the higher `latest_finalized_epoch` or `best_slot` (if the clients have equal `latest_finalized_epoch`s) SHOULD request beacon block roots from its counterparty via `beacon_block_roots` (i.e., RPC method `10`).
### Goodbye
**Method ID:** `1`
**Body:**
```
(
reason: uint64
)
```
Client MAY send `goodbye` messages upon disconnection. The reason field MAY be one of the following values:
- `1`: Client shut down.
- `2`: Irrelevant network.
- `3`: Fault/error.
Clients MAY define custom goodbye reasons as long as the value is larger than `1000`.
### Get Status
**Method ID:** `2`
**Request Body:**
```
(
sha: bytes32
user_agent: bytes
timestamp: uint64
)
```
**Response Body:**
```
(
sha: bytes32
user_agent: bytes
timestamp: uint64
)
```
Returns metadata about the remote node.
### Request Beacon Block Roots
**Method ID:** `10`
**Request Body**
```
(
start_slot: uint64
count: uint64
)
```
**Response Body:**
```
# BlockRootSlot
(
block_root: bytes32
slot: uint64
)
(
roots: []BlockRootSlot
)
```
Requests a list of block roots and slots from the peer. The `count` parameter MUST be less than or equal to `32768`. The slots MUST be returned in ascending slot order.
### Beacon Block Headers
**Method ID:** `11`
**Request Body**
```
(
start_root: HashTreeRoot
start_slot: uint64
max_headers: uint64
skip_slots: uint64
)
```
**Response Body:**
```
(
headers: []BeaconBlockHeader
)
```
Requests beacon block headers from the peer starting from `(start_root, start_slot)`. The response MUST contain no more than `max_headers` headers. `skip_slots` defines the maximum number of slots to skip between blocks. For example, requesting blocks starting at slots `2` a `skip_slots` value of `1` would return the blocks at `[2, 4, 6, 8, 10]`. In cases where a slot is empty for a given slot number, the closest previous block MUST be returned. For example, if slot `4` were empty in the previous example, the returned array would contain `[2, 3, 6, 8, 10]`. If slot three were further empty, the array would contain `[2, 6, 8, 10]` - i.e., duplicate blocks MUST be collapsed. A `skip_slots` value of `0` returns all blocks.
The function of the `skip_slots` parameter helps facilitate light client sync - for example, in [#459](https://github.com/ethereum/eth2.0-specs/issues/459) - and allows clients to balance the peers from whom they request headers. Clients could, for instance, request every 10th block from a set of peers where each per has a different starting block in order to populate block data.
### Beacon Block Bodies
**Method ID:** `12`
**Request Body:**
```
(
block_roots: []HashTreeRoot
)
```
**Response Body:**
```
(
block_bodies: []BeaconBlockBody
)
```
Requests the `block_bodies` associated with the provided `block_roots` from the peer. Responses MUST return `block_roots` in the order provided in the request. If the receiver does not have a particular `block_root`, it must return a zero-value `block_body` (i.e., a `block_body` container with all zero fields).
### Beacon Chain State
**Note:** This section is preliminary, pending the definition of the data structures to be transferred over the wire during fast sync operations.
**Method ID:** `13`
**Request Body:**
```
(
hashes: []HashTreeRoot
)
```
**Response Body:** TBD
Requests contain the hashes of Merkle tree nodes that when merkelized yield the block's `state_root`.
The response will contain the values that, when hashed, yield the hashes inside the request body.

View File

@ -152,7 +152,7 @@ _Note:_ there might be "skipped" slots between the `parent` and `block`. These s
##### Parent root
Set `block.previous_block_root = hash_tree_root(parent)`.
Set `block.previous_block_root = signed_root(parent)`.
##### State root
@ -255,11 +255,11 @@ Set `attestation_data.shard = shard` where `shard` is the shard associated with
##### Beacon block root
Set `attestation_data.beacon_block_root = hash_tree_root(head_block)`.
Set `attestation_data.beacon_block_root = signed_root(head_block)`.
##### Target root
Set `attestation_data.target_root = hash_tree_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary.
Set `attestation_data.target_root = signed_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary.
_Note:_ This can be looked up in the state using:
* Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`.

View File

@ -135,7 +135,7 @@ def test_non_empty_custody_bitfield(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.custody_bitfield = b'\x01' + attestation.custody_bitfield[1:]
attestation.custody_bitfield = deepcopy(attestation.aggregation_bitfield)
pre_state, post_state = run_attestation_processing(state, attestation, False)

View File

@ -22,12 +22,14 @@ from build.phase0.spec import (
get_active_validator_indices,
get_attestation_participants,
get_block_root,
get_crosslink_committee_for_attestation,
get_crosslink_committees_at_slot,
get_current_epoch,
get_domain,
get_empty_block,
get_epoch_start_slot,
get_genesis_beacon_state,
slot_to_epoch,
verify_merkle_branch,
hash,
)
@ -248,12 +250,11 @@ def get_valid_attestation(state, slot=None):
shard = state.latest_start_shard
attestation_data = build_attestation_data(state, slot, shard)
crosslink_committees = get_crosslink_committees_at_slot(state, slot)
crosslink_committee = [committee for committee, _shard in crosslink_committees if _shard == attestation_data.shard][0]
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
committee_size = len(crosslink_committee)
bitfield_length = (committee_size + 7) // 8
aggregation_bitfield = b'\x01' + b'\x00' * (bitfield_length - 1)
aggregation_bitfield = b'\xC0' + b'\x00' * (bitfield_length - 1)
custody_bitfield = b'\x00' * bitfield_length
attestation = Attestation(
aggregation_bitfield=aggregation_bitfield,
@ -266,23 +267,35 @@ def get_valid_attestation(state, slot=None):
attestation.data,
attestation.aggregation_bitfield,
)
assert len(participants) == 1
assert len(participants) == 2
validator_index = participants[0]
privkey = privkeys[validator_index]
signatures = []
for validator_index in participants:
privkey = privkeys[validator_index]
signatures.append(
get_attestation_signature(
state,
attestation.data,
privkey
)
)
attestation.aggregation_signature = bls.aggregate_signatures(signatures)
return attestation
def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0):
message_hash = AttestationDataAndCustodyBit(
data=attestation.data,
custody_bit=0b0,
data=attestation_data,
custody_bit=custody_bit,
).hash_tree_root()
attestation.aggregation_signature = bls.sign(
return bls.sign(
message_hash=message_hash,
privkey=privkey,
domain=get_domain(
fork=state.fork,
epoch=get_current_epoch(state),
epoch=slot_to_epoch(attestation_data.slot),
domain_type=spec.DOMAIN_ATTESTATION,
)
)
return attestation