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>
This commit is contained in:
Pawan Dhananjay
2020-02-14 17:05:18 +05:30
committed by GitHub
parent 7a880dd23c
commit 74c34d1602
6 changed files with 627 additions and 180 deletions

View File

@@ -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<T: BeaconChainTypes>(
) -> ApiResult {
ResponseBuilder::new(&req)?.body(&beacon_chain.head()?.beacon_state.genesis_time)
}
pub fn proposer_slashing<T: BeaconChainTypes>(
req: Request<Body>,
beacon_chain: Arc<BeaconChain<T>>,
) -> 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::<ProposerSlashing>(&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<T: BeaconChainTypes>(
req: Request<Body>,
beacon_chain: Arc<BeaconChain<T>>,
) -> 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::<AttesterSlashing<T::EthSpec>>(&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)
}

View File

@@ -104,6 +104,12 @@ pub fn route<T: BeaconChainTypes>(
(&Method::GET, "/beacon/committees") => {
into_boxfut(beacon::get_committees::<T>(req, beacon_chain))
}
(&Method::POST, "/beacon/proposer_slashing") => {
into_boxfut(beacon::proposer_slashing::<T>(req, beacon_chain))
}
(&Method::POST, "/beacon/attester_slashing") => {
into_boxfut(beacon::attester_slashing::<T>(req, beacon_chain))
}
// Methods for Validator
(&Method::POST, "/validator/duties") => {

View File

@@ -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<T: EthSpec>(
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::<E>(
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::<E>(
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]);
}