From 74c34d1602cd133289d7ff4cff6c1fb0da4463cf Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Fri, 14 Feb 2020 17:05:18 +0530 Subject: [PATCH] Add attester/proposer slashing endpoints (#856) * Remove deprecated api_spec.yaml * add prototype for proposer slashing * remove clippy warnings * Add proposer_slashing API * Prototype for attester slashing API call * Fix logic error in operation pool * Finish test for attester_slashing api call * Clean proposer_slashing test * Cargo fmt * Remove useless to_string after format! macro * Cargo fmt * Update book with new api calls * Re-enable proposer slashing verification * Update book with appropriate test example * Fix proposer_slashing test * Update comments and tests for clearer code * Remove extraneous comments * Fix test * Minor fix * Address reviewer comments Co-authored-by: pscott <30843220+pscott@users.noreply.github.com> --- beacon_node/rest_api/src/beacon.rs | 76 ++- beacon_node/rest_api/src/router.rs | 6 + beacon_node/rest_api/tests/test.rs | 164 +++++- book/src/http_beacon.md | 521 ++++++++++++------ .../builders/testing_beacon_block_builder.rs | 4 +- eth2/utils/remote_beacon_node/src/lib.rs | 36 +- 6 files changed, 627 insertions(+), 180 deletions(-) diff --git a/beacon_node/rest_api/src/beacon.rs b/beacon_node/rest_api/src/beacon.rs index e3f7367ea7..3b5fcf3821 100644 --- a/beacon_node/rest_api/src/beacon.rs +++ b/beacon_node/rest_api/src/beacon.rs @@ -10,8 +10,8 @@ use ssz_derive::{Decode, Encode}; use std::sync::Arc; use store::Store; use types::{ - BeaconState, CommitteeIndex, EthSpec, Hash256, PublicKeyBytes, RelativeEpoch, - SignedBeaconBlock, Slot, Validator, + AttesterSlashing, BeaconState, CommitteeIndex, EthSpec, Hash256, ProposerSlashing, + PublicKeyBytes, RelativeEpoch, SignedBeaconBlock, Slot, Validator, }; /// Information about the block and state that are at head of the beacon chain. @@ -496,3 +496,75 @@ pub fn get_genesis_time( ) -> ApiResult { ResponseBuilder::new(&req)?.body(&beacon_chain.head()?.beacon_state.genesis_time) } + +pub fn proposer_slashing( + req: Request, + beacon_chain: Arc>, +) -> BoxFut { + let response_builder = ResponseBuilder::new(&req); + + let future = req + .into_body() + .concat2() + .map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e))) + .and_then(|chunks| { + serde_json::from_slice::(&chunks).map_err(|e| { + ApiError::BadRequest(format!( + "Unable to parse JSON into ProposerSlashing: {:?}", + e + )) + }) + }) + .and_then(move |proposer_slashing| { + let spec = &beacon_chain.spec; + let state = &beacon_chain.head().unwrap().beacon_state; + beacon_chain + .op_pool + .insert_proposer_slashing(proposer_slashing, state, spec) + .map_err(|e| { + ApiError::BadRequest(format!( + "Error while inserting proposer slashing: {:?}", + e + )) + }) + }) + .and_then(|_| response_builder?.body(&true)); + + Box::new(future) +} + +pub fn attester_slashing( + req: Request, + beacon_chain: Arc>, +) -> BoxFut { + let response_builder = ResponseBuilder::new(&req); + + let future = req + .into_body() + .concat2() + .map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e))) + .and_then(|chunks| { + serde_json::from_slice::>(&chunks).map_err(|e| { + ApiError::BadRequest(format!( + "Unable to parse JSON into AttesterSlashing: {:?}", + e + )) + }) + }) + .and_then(move |attester_slashing| { + let spec = &beacon_chain.spec; + let state = &beacon_chain.head().unwrap().beacon_state; + beacon_chain + .op_pool + .insert_attester_slashing(attester_slashing, state, spec) + .map_err(|e| { + ApiError::BadRequest(format!( + "Error while inserting attester slashing: {:?}", + e + )) + }) + }) + .and_then(|_| response_builder?.body(&true)); + + Box::new(future) +} diff --git a/beacon_node/rest_api/src/router.rs b/beacon_node/rest_api/src/router.rs index 0e629d6359..970bcb2c44 100644 --- a/beacon_node/rest_api/src/router.rs +++ b/beacon_node/rest_api/src/router.rs @@ -104,6 +104,12 @@ pub fn route( (&Method::GET, "/beacon/committees") => { into_boxfut(beacon::get_committees::(req, beacon_chain)) } + (&Method::POST, "/beacon/proposer_slashing") => { + into_boxfut(beacon::proposer_slashing::(req, beacon_chain)) + } + (&Method::POST, "/beacon/attester_slashing") => { + into_boxfut(beacon::attester_slashing::(req, beacon_chain)) + } // Methods for Validator (&Method::POST, "/validator/duties") => { diff --git a/beacon_node/rest_api/tests/test.rs b/beacon_node/rest_api/tests/test.rs index 81b8a6fb0f..37ed420922 100644 --- a/beacon_node/rest_api/tests/test.rs +++ b/beacon_node/rest_api/tests/test.rs @@ -12,9 +12,12 @@ use remote_beacon_node::{ use std::convert::TryInto; use std::sync::Arc; use types::{ - test_utils::generate_deterministic_keypair, BeaconBlock, BeaconState, ChainSpec, Domain, Epoch, - EthSpec, MinimalEthSpec, PublicKey, RelativeEpoch, Signature, SignedBeaconBlock, SignedRoot, - Slot, Validator, + test_utils::{ + build_double_vote_attester_slashing, build_proposer_slashing, + generate_deterministic_keypair, AttesterSlashingTestTask, ProposerSlashingTestTask, + }, + BeaconBlock, BeaconState, ChainSpec, Domain, Epoch, EthSpec, MinimalEthSpec, PublicKey, + RelativeEpoch, Signature, SignedBeaconBlock, SignedRoot, Slot, Validator, }; use version; @@ -862,3 +865,158 @@ fn compare_validator_response( assert_eq!(state.balances[i], balance, "balances"); assert_eq!(state.validators[i], *validator, "validator index"); } + +#[test] +fn proposer_slashing() { + let mut env = build_env(); + + let node = build_node(&mut env, testing_client_config()); + let remote_node = node.remote_node().expect("should produce remote node"); + let chain = node + .client + .beacon_chain() + .expect("node should have beacon chain"); + + let state = chain + .head() + .expect("should have retrieved state") + .beacon_state; + + let spec = &chain.spec; + + // Check that there are no proposer slashings before insertion + let (proposer_slashings, _attester_slashings) = chain.op_pool.get_slashings(&state, spec); + assert_eq!(proposer_slashings.len(), 0); + + let slot = state.slot; + let proposer_index = chain + .block_proposer(slot) + .expect("should get proposer index"); + let keypair = generate_deterministic_keypair(proposer_index); + let key = &keypair.sk; + let fork = &state.fork; + let proposer_slashing = build_proposer_slashing::( + ProposerSlashingTestTask::Valid, + proposer_index as u64, + &key, + fork, + spec, + ); + + let result = env + .runtime() + .block_on( + remote_node + .http + .beacon() + .proposer_slashing(proposer_slashing.clone()), + ) + .expect("should fetch from http api"); + assert!(result, true); + + // Length should be just one as we've inserted only one proposer slashing + let (proposer_slashings, _attester_slashings) = chain.op_pool.get_slashings(&state, spec); + assert_eq!(proposer_slashings.len(), 1); + assert_eq!(proposer_slashing.clone(), proposer_slashings[0]); + + let mut invalid_proposer_slashing = build_proposer_slashing::( + ProposerSlashingTestTask::Valid, + proposer_index as u64, + &key, + fork, + spec, + ); + invalid_proposer_slashing.signed_header_2 = invalid_proposer_slashing.signed_header_1.clone(); + + let result = env.runtime().block_on( + remote_node + .http + .beacon() + .proposer_slashing(invalid_proposer_slashing), + ); + assert!(result.is_err()); + + // Length should still be one as we've inserted nothing since last time. + let (proposer_slashings, _attester_slashings) = chain.op_pool.get_slashings(&state, spec); + assert_eq!(proposer_slashings.len(), 1); + assert_eq!(proposer_slashing, proposer_slashings[0]); +} + +#[test] +fn attester_slashing() { + let mut env = build_env(); + + let node = build_node(&mut env, testing_client_config()); + let remote_node = node.remote_node().expect("should produce remote node"); + let chain = node + .client + .beacon_chain() + .expect("node should have beacon chain"); + + let state = chain + .head() + .expect("should have retrieved state") + .beacon_state; + let slot = state.slot; + let spec = &chain.spec; + + let proposer_index = chain + .block_proposer(slot) + .expect("should get proposer index"); + let keypair = generate_deterministic_keypair(proposer_index); + + let secret_keys = vec![&keypair.sk]; + let validator_indices = vec![proposer_index as u64]; + let fork = &state.fork; + + // Checking there are no attester slashings before insertion + let (_proposer_slashings, attester_slashings) = chain.op_pool.get_slashings(&state, spec); + assert_eq!(attester_slashings.len(), 0); + + let attester_slashing = build_double_vote_attester_slashing( + AttesterSlashingTestTask::Valid, + &validator_indices[..], + &secret_keys[..], + fork, + spec, + ); + + let result = env + .runtime() + .block_on( + remote_node + .http + .beacon() + .attester_slashing(attester_slashing.clone()), + ) + .expect("should fetch from http api"); + assert!(result, true); + + // Length should be just one as we've inserted only one attester slashing + let (_proposer_slashings, attester_slashings) = chain.op_pool.get_slashings(&state, spec); + assert_eq!(attester_slashings.len(), 1); + assert_eq!(attester_slashing, attester_slashings[0]); + + // Building an invalid attester slashing + let mut invalid_attester_slashing = build_double_vote_attester_slashing( + AttesterSlashingTestTask::Valid, + &validator_indices[..], + &secret_keys[..], + fork, + spec, + ); + invalid_attester_slashing.attestation_2 = invalid_attester_slashing.attestation_1.clone(); + + let result = env.runtime().block_on( + remote_node + .http + .beacon() + .attester_slashing(invalid_attester_slashing), + ); + assert!(result.is_err()); + + // Length should still be one as we've failed to insert the attester slashing. + let (_proposer_slashings, attester_slashings) = chain.op_pool.get_slashings(&state, spec); + assert_eq!(attester_slashings.len(), 1); + assert_eq!(attester_slashing, attester_slashings[0]); +} diff --git a/book/src/http_beacon.md b/book/src/http_beacon.md index 309ed6437d..2ad5e7839f 100644 --- a/book/src/http_beacon.md +++ b/book/src/http_beacon.md @@ -7,19 +7,283 @@ beacon chain and also historical information about beacon blocks and states. HTTP Path | Description | | --- | -- | +[`/beacon/attester_slashing`](#beaconattester_slashing) | Insert an attester slashing +[`/beacon/block`](#beaconblock) | Get a `BeaconBlock` by slot or root. +[`/beacon/block_root`](#beaconblock_root) | Resolve a slot to a block root. +[`/beacon/committees`](#beaconcommittees) | Get the shuffling for an epoch. [`/beacon/head`](#beaconhead) | Info about the block at the head of the chain. [`/beacon/heads`](#beaconheads) | Returns a list of all known chain heads. -[`/beacon/block_root`](#beaconblock_root) | Resolve a slot to a block root. -[`/beacon/block`](#beaconblock) | Get a `SignedBeaconBlock` by slot or root. -[`/beacon/state_root`](#beaconstate_root) | Resolve a slot to a state root. +[`/beacon/proposer_slashing`](#beaconproposer_slashing) | Insert a proposer slashing [`/beacon/state`](#beaconstate) | Get a `BeaconState` by slot or root. +[`/beacon/state_root`](#beaconstate_root) | Resolve a slot to a state root. [`/beacon/state/genesis`](#beaconstategenesis) | Get a `BeaconState` at genesis. [`/beacon/genesis_time`](#beacongenesis_time) | Get the genesis time from the beacon state. [`/beacon/fork`](#beaconfork) | Get the fork of the head of the chain. [`/beacon/validators`](#beaconvalidators) | Query for one or more validators. -[`/beacon/validators/all`](#beaconvalidatorsall) | Get all validators. [`/beacon/validators/active`](#beaconvalidatorsactive) | Get all active validators. -[`/beacon/committees`](#beaconcommittees) | Get the shuffling for an epoch. +[`/beacon/validators/all`](#beaconvalidatorsall) | Get all validators. + +## `/beacon/attester_slashing` + +Accepts an `attester_slashing` and verifies it. If it is valid, it is added to the operations pool for potential inclusion in a future block. Returns a 400 error if the `attester_slashing` is invalid. + +### HTTP Specification + +| Property | Specification | +| --- |--- | +Path | `/beacon/attester_slashing` +Method | POST +JSON Encoding | Object +Query Parameters | None +Typical Responses | 200/400 + +### Parameters + +Expects the following object in the POST request body: + +``` +{ + attestation_1: { + attesting_indices: [u64], + data: { + slot: Slot, + index: u64, + beacon_block_root: Bytes32, + source: { + epoch: Epoch, + root: Bytes32 + }, + target: { + epoch: Epoch, + root: Bytes32 + } + } + signature: Bytes32 + }, + attestation_2: { + attesting_indices: [u64], + data: { + slot: Slot, + index: u64, + beacon_block_root: Bytes32, + source: { + epoch: Epoch, + root: Bytes32 + }, + target: { + epoch: Epoch, + root: Bytes32 + } + } + signature: Bytes32 + } +} +``` + +### Returns + +Returns `true` if the attester slashing was inserted successfully, or the corresponding error if it failed. + +### Example + +### Request Body + +```json +{ + "attestation_1": { + "attesting_indices": [0], + "data": { + "slot": 1, + "index": 0, + "beacon_block_root": "0x0000000000000000000000000000000000000000000000000100000000000000", + "source": { + "epoch": 1, + "root": "0x0000000000000000000000000000000000000000000000000100000000000000" + }, + "target": { + "epoch": 1, + "root": "0x0000000000000000000000000000000000000000000000000100000000000000" + } + }, + "signature": "0xb47f7397cd944b8d5856a13352166bbe74c85625a45b14b7347fc2c9f6f6f82acee674c65bc9ceb576fcf78387a6731c0b0eb3f8371c70db2da4e7f5dfbc451730c159d67263d3db56b6d0e009e4287a8ba3efcacac30b3ae3447e89dc71b5b9" + }, + "attestation_2": { + "attesting_indices": [0], + "data": { + "slot": 1, + "index": 0, + "beacon_block_root": "0x0000000000000000000000000000000000000000000000000100000000000000", + "source": { + "epoch": 1, + "root": "0x0000000000000000000000000000000000000000000000000100000000000000" + }, + "target": { + "epoch": 1, + "root": "0x0000000000000000000000000000000000000000000000000200000000000000" + } + }, + "signature": "0x93fef587a63acf72aaf8df627718fd43cb268035764071f802ffb4370a2969d226595cc650f4c0bf2291ae0c0a41fcac1700f318603d75d34bcb4b9f4a8368f61eeea0e1f5d969d92d5073ba5fbadec102b45ec87d418d25168d2e3c74b9fcbb" + } +} +``` + +_Note: data sent here is for demonstration purposes only_ + +## `/beacon/block` + +Request that the node return a beacon chain block that matches the provided +criteria (a block `root` or beacon chain `slot`). Only one of the parameters +should be provided as a criteria. + +### HTTP Specification + +| Property | Specification | +| --- |--- | +Path | `/beacon/block` +Method | GET +JSON Encoding | Object +Query Parameters | `slot`, `root` +Typical Responses | 200, 404 + +### Parameters + +Accepts **only one** of the following parameters: + +- `slot` (`Slot`): Query by slot number. Any block returned must be in the canonical chain (i.e., +either the head or an ancestor of the head). +- `root` (`Bytes32`): Query by tree hash root. A returned block is not required to be in the +canonical chain. + +### Returns + +Returns an object containing a single [`SignedBeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.0/specs/phase0/beacon-chain.md#signedbeaconblock) and the block root of the inner [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.0/specs/phase0/beacon-chain.md#beaconblock). + +### Example Response + +```json +{ + "root": "0xc35ddf4e71c31774e0594bd7eb32dfe50b54dbc40abd594944254b4ec8895196", + "beacon_block": { + "message": { + "slot": 0, + "parent_root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "state_root": "0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f", + "body": { + "randao_reveal": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "eth1_data": { + "deposit_root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "deposit_count": 0, + "block_hash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "graffiti": "0x0000000000000000000000000000000000000000000000000000000000000000", + "proposer_slashings": [], + "attester_slashings": [], + "attestations": [], + "deposits": [], + "voluntary_exits": [] + } + }, + "signature": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } +} +``` + +## `/beacon/block_root` + +Returns the block root for the given slot in the canonical chain. If there +is a re-org, the same slot may return a different root. + +### HTTP Specification + +| Property | Specification | +| --- |--- | +Path | `/beacon/block_root` +Method | GET +JSON Encoding | Object +Query Parameters | `slot` +Typical Responses | 200, 404 + +## Parameters + +- `slot` (`Slot`): the slot to be resolved to a root. + +### Example Response + +```json +"0xc35ddf4e71c31774e0594bd7eb32dfe50b54dbc40abd594944254b4ec8895196" +``` + +## `/beacon/committees` + +Request the committees (a.k.a. "shuffling") for all slots and committee indices +in a given `epoch`. + +### HTTP Specification + +| Property | Specification | +| --- |--- | +Path | `/beacon/committees` +Method | GET +JSON Encoding | Object +Query Parameters | `epoch` +Typical Responses | 200/500 + +### Parameters + +The `epoch` (`Epoch`) query parameter is required and defines the epoch for +which the committees will be returned. All slots contained within the response will +be inside this epoch. + +### Returns + +A list of beacon committees. + +### Example Response + +```json +[ + { + "slot": 4768, + "index": 0, + "committee": [ + 1154, + 492, + 9667, + 3089, + 8987, + 1421, + 224, + 11243, + 2127, + 2329, + 188, + 482, + 486 + ] + }, + { + "slot": 4768, + "index": 1, + "committee": [ + 5929, + 8482, + 5528, + 6130, + 14343, + 9777, + 10808, + 12739, + 15234, + 12819, + 5423, + 6320, + 9991 + ] + } +] +``` + +_Truncated for brevity._ ## `/beacon/head` @@ -82,115 +346,76 @@ Typical Responses | 200 ] ``` -## `/beacon/block_root` +## `/beacon/proposer_slashing` -Returns the block root for the given slot in the canonical chain. If there -is a re-org, the same slot may return a different root. +Accepts a `proposer_slashing` and verifies it. If it is valid, it is added to the operations pool for potential inclusion in a future block. Returns an 400 error if the `proposer_slashing` is invalid. ### HTTP Specification | Property | Specification | | --- |--- | -Path | `/beacon/block_root` -Method | GET +Path | `/beacon/proposer_slashing` +Method | POST JSON Encoding | Object -Query Parameters | `slot` -Typical Responses | 200, 404 +Query Parameters | None +Typical Responses | 200/400 -## Parameters +### Request Body -- `slot` (`Slot`): the slot to be resolved to a root. +Expects the following object in the POST request body: -### Example Response - -```json -"0xc35ddf4e71c31774e0594bd7eb32dfe50b54dbc40abd594944254b4ec8895196" ``` - -## `/beacon/block` - -Request that the node return a beacon chain block that matches the provided -criteria (a block `root` or beacon chain `slot`). Only one of the parameters -should be provided as a criteria. - -### HTTP Specification - -| Property | Specification | -| --- |--- | -Path | `/beacon/block` -Method | GET -JSON Encoding | Object -Query Parameters | `slot`, `root` -Typical Responses | 200, 404 - -### Parameters - -Accepts **only one** of the following parameters: - -- `slot` (`Slot`): Query by slot number. Any block returned must be in the canonical chain (i.e., -either the head or an ancestor of the head). -- `root` (`Bytes32`): Query by tree hash root. A returned block is not required to be in the -canonical chain. - -### Returns - -Returns an object containing a single [`SignedBeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.0/specs/phase0/beacon-chain.md#signedbeaconblock) and the block root of the inner [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.0/specs/phase0/beacon-chain.md#beaconblock). - -### Example Response - -```json { - "root": "0xc35ddf4e71c31774e0594bd7eb32dfe50b54dbc40abd594944254b4ec8895196", - "beacon_block": { - "message": { - "slot": 0, - "parent_root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "state_root": "0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f", - "body": { - "randao_reveal": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "eth1_data": { - "deposit_root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "deposit_count": 0, - "block_hash": "0x0000000000000000000000000000000000000000000000000000000000000000" - }, - "graffiti": "0x0000000000000000000000000000000000000000000000000000000000000000", - "proposer_slashings": [], - "attester_slashings": [], - "attestations": [], - "deposits": [], - "voluntary_exits": [] - } - }, - "signature": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + proposer_index: u64, + header_1: { + slot: Slot, + parent_root: Bytes32, + state_root: Bytes32, + body_root: Bytes32, + signature: Bytes32 + }, + header_2: { + slot: Slot, + parent_root: Bytes32, + state_root: Bytes32, + body_root: Bytes32, + signature: Bytes32 } } ``` -## `/beacon/state_root` +### Returns -Returns the state root for the given slot in the canonical chain. If there -is a re-org, the same slot may return a different root. +Returns `true` if the proposer slashing was inserted successfully, or the corresponding error if it failed. -### HTTP Specification +### Example -| Property | Specification | -| --- |--- | -Path | `/beacon/state_root` -Method | GET -JSON Encoding | Object -Query Parameters | `slot` -Typical Responses | 200, 404 - -## Parameters - -- `slot` (`Slot`): the slot to be resolved to a root. - -### Example Response +### Request Body ```json -"0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f" +{ + "proposer_index": 0, + "header_1": { + "slot": 0, + "parent_root": "0x0101010101010101010101010101010101010101010101010101010101010101", + "state_root": "0x0101010101010101010101010101010101010101010101010101010101010101", + "body_root": "0x0101010101010101010101010101010101010101010101010101010101010101", + "signature": "0xb8970d1342c6d5779c700ec366efd0ca819937ca330960db3ca5a55eb370a3edd83f4cbb2f74d06e82f934fcbd4bb80609a19c2254cc8b3532a4efff9e80edf312ac735757c059d77126851e377f875593e64ba50d1dffe69a809a409202dd12" + }, + "header_2": { + "slot": 0, + "parent_root": "0x0202020202020202020202020202020202020202020202020202020202020202", + "state_root": "0x0101010101010101010101010101010101010101010101010101010101010101", + "body_root": "0x0101010101010101010101010101010101010101010101010101010101010101", + "signature": "0xb60e6b348698a34e59b22e0af96f8809f977f00f95d52375383ade8d22e9102270a66c6d52b0434214897e11ca4896871510c01b3fd74d62108a855658d5705fcfc4ced5136264a1c6496f05918576926aa191b1ad311b7e27f5aa2167aba294" + } +} ``` +_Note: data sent here is for demonstration purposes only_ + + + ## `/beacon/state` Request that the node return a beacon chain state that matches the provided @@ -236,6 +461,32 @@ and its tree hash root. _Truncated for brevity._ +## `/beacon/state_root` + +Returns the state root for the given slot in the canonical chain. If there +is a re-org, the same slot may return a different root. + + +### HTTP Specification + +| Property | Specification | +| --- |--- | +Path | `/beacon/state_root` +Method | GET +JSON Encoding | Object +Query Parameters | `slot` +Typical Responses | 200, 404 + +## Parameters + +- `slot` (`Slot`): the slot to be resolved to a root. + +### Example Response + +```json +"0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f" +``` + ## `/beacon/state/genesis` Request that the node return a beacon chain state at genesis (slot 0). @@ -405,30 +656,6 @@ _Note: for demonstration purposes the second pubkey is some unknown pubkey._ ] ``` -## `/beacon/validators/all` - -Returns all validators. - -### HTTP Specification - -| Property | Specification | -| --- |--- | -Path | `/beacon/validators/all` -Method | GET -JSON Encoding | Object -Query Parameters | `state_root` (optional) -Typical Responses | 200 - -### Parameters - -The optional `state_root` (`Bytes32`) query parameter indicates which -`BeaconState` should be used to collect the information. When omitted, the -canonical head state will be used. - -### Returns - -The return format is identical to the [`/beacon/validators`](#beaconvalidators) response body. - ## `/beacon/validators/active` Returns all validators that are active in the state defined by `state_root`. @@ -453,74 +680,26 @@ canonical head state will be used. The return format is identical to the [`/beacon/validators`](#beaconvalidators) response body. -## `/beacon/committees` +## `/beacon/validators/all` -Request the committees (a.k.a. "shuffling") for all slots and committee indices -in a given `epoch`. +Returns all validators. ### HTTP Specification | Property | Specification | | --- |--- | -Path | `/beacon/committees` +Path | `/beacon/validators/all` Method | GET JSON Encoding | Object -Query Parameters | `epoch` +Query Parameters | `state_root` (optional) Typical Responses | 200 ### Parameters -The `epoch` (`Epoch`) query parameter is required and defines the epoch for -which the committees will be returned. All slots contained within the response will -be inside this epoch. +The optional `state_root` (`Bytes32`) query parameter indicates which +`BeaconState` should be used to collect the information. When omitted, the +canonical head state will be used. ### Returns -A list of beacon committees. - -### Example Response - -```json -[ - { - "slot": 4768, - "index": 0, - "committee": [ - 1154, - 492, - 9667, - 3089, - 8987, - 1421, - 224, - 11243, - 2127, - 2329, - 188, - 482, - 486 - ] - }, - { - "slot": 4768, - "index": 1, - "committee": [ - 5929, - 8482, - 5528, - 6130, - 14343, - 9777, - 10808, - 12739, - 15234, - 12819, - 5423, - 6320, - 9991 - ] - } -] -``` - -_Truncated for brevity._ +The return format is identical to the [`/beacon/validators`](#beaconvalidators) response body. \ No newline at end of file diff --git a/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs b/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs index 9454922017..46d901beba 100644 --- a/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs @@ -377,7 +377,7 @@ impl TestingBeaconBlockBuilder { /// Builds an `ProposerSlashing` for some `validator_index`. /// /// Signs the message using a `BeaconChainHarness`. -fn build_proposer_slashing( +pub fn build_proposer_slashing( test_task: ProposerSlashingTestTask, validator_index: u64, secret_key: &SecretKey, @@ -396,7 +396,7 @@ fn build_proposer_slashing( /// Builds an `AttesterSlashing` for some `validator_indices`. /// /// Signs the message using a `BeaconChainHarness`. -fn build_double_vote_attester_slashing( +pub fn build_double_vote_attester_slashing( test_task: AttesterSlashingTestTask, validator_indices: &[u64], secret_keys: &[&SecretKey], diff --git a/eth2/utils/remote_beacon_node/src/lib.rs b/eth2/utils/remote_beacon_node/src/lib.rs index dff969b385..64791e8ad0 100644 --- a/eth2/utils/remote_beacon_node/src/lib.rs +++ b/eth2/utils/remote_beacon_node/src/lib.rs @@ -14,8 +14,8 @@ use ssz::Encode; use std::marker::PhantomData; use std::time::Duration; use types::{ - Attestation, BeaconBlock, BeaconState, CommitteeIndex, Epoch, EthSpec, Fork, Hash256, - PublicKey, Signature, SignedBeaconBlock, Slot, + Attestation, AttesterSlashing, BeaconBlock, BeaconState, CommitteeIndex, Epoch, EthSpec, Fork, + Hash256, ProposerSlashing, PublicKey, Signature, SignedBeaconBlock, Slot, }; use url::Url; @@ -500,6 +500,38 @@ impl Beacon { client.json_get(url, vec![("epoch".into(), format!("{}", epoch.as_u64()))]) }) } + + pub fn proposer_slashing( + &self, + proposer_slashing: ProposerSlashing, + ) -> impl Future { + let client = self.0.clone(); + + self.url("proposer_slashing") + .into_future() + .and_then(move |url| { + client + .json_post::<_>(url, proposer_slashing) + .and_then(|response| error_for_status(response).map_err(Error::from)) + .and_then(|mut success| success.json().map_err(Error::from)) + }) + } + + pub fn attester_slashing( + &self, + attester_slashing: AttesterSlashing, + ) -> impl Future { + let client = self.0.clone(); + + self.url("attester_slashing") + .into_future() + .and_then(move |url| { + client + .json_post::<_>(url, attester_slashing) + .and_then(|response| error_for_status(response).map_err(Error::from)) + .and_then(|mut success| success.json().map_err(Error::from)) + }) + } } /// Provides the functions on the `/spec` endpoint of the node.