mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 02:12:33 +00:00
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:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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") => {
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user