Skip to content
Closed
Show file tree
Hide file tree
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
12 changes: 10 additions & 2 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1565,12 +1565,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
&self,
randao_reveal: Signature,
slot: Slot,
validator_graffiti: Option<Graffiti>,
) -> Result<BeaconBlockAndState<T::EthSpec>, BlockProductionError> {
let state = self
.state_at_slot(slot - 1, StateSkipConfig::WithStateRoots)
.map_err(|_| BlockProductionError::UnableToProduceAtSlot(slot))?;

self.produce_block_on_state(state, slot, randao_reveal)
self.produce_block_on_state(state, slot, randao_reveal, validator_graffiti)
}

/// Produce a block for some `slot` upon the given `state`.
Expand All @@ -1586,6 +1587,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
mut state: BeaconState<T::EthSpec>,
produce_at_slot: Slot,
randao_reveal: Signature,
validator_graffiti: Option<Graffiti>,
) -> Result<BeaconBlockAndState<T::EthSpec>, BlockProductionError> {
metrics::inc_counter(&metrics::BLOCK_PRODUCTION_REQUESTS);
let timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_TIMES);
Expand Down Expand Up @@ -1653,6 +1655,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
}

// Override the beacon node's graffiti with graffiti from the validator, if present.
let graffiti = match validator_graffiti {
Some(graffiti) => graffiti,
None => self.graffiti,
};

let mut block = SignedBeaconBlock {
message: BeaconBlock {
slot: state.slot,
Expand All @@ -1662,7 +1670,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
body: BeaconBlockBody {
randao_reveal,
eth1_data,
graffiti: self.graffiti,
graffiti,
proposer_slashings: proposer_slashings.into(),
attester_slashings: attester_slashings.into(),
attestations: self
Expand Down
2 changes: 1 addition & 1 deletion beacon_node/beacon_chain/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ where

let (block, state) = self
.chain
.produce_block_on_state(state, slot, randao_reveal)
.produce_block_on_state(state, slot, randao_reveal, None)
.expect("should produce block");

let signed_block = block.sign(sk, &state.fork, state.genesis_validators_root, &self.spec);
Expand Down
12 changes: 10 additions & 2 deletions beacon_node/rest_api/src/validator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::helpers::{check_content_type_for_json, publish_beacon_block_to_network};
use crate::helpers::{
check_content_type_for_json, parse_hex_ssz_bytes, publish_beacon_block_to_network,
};
use crate::response_builder::ResponseBuilder;
use crate::{ApiError, ApiResult, NetworkChannel, UrlQuery};
use beacon_chain::{
Expand Down Expand Up @@ -288,8 +290,14 @@ pub fn get_new_beacon_block<T: BeaconChainTypes>(
let slot = query.slot()?;
let randao_reveal = query.randao_reveal()?;

let validator_graffiti = if let Some((_key, value)) = query.first_of_opt(&["graffiti"]) {
Some(parse_hex_ssz_bytes(&value)?)
} else {
None
};

let (new_block, _state) = beacon_chain
.produce_block(randao_reveal, slot)
.produce_block(randao_reveal, slot, validator_graffiti)
.map_err(|e| {
error!(
log,
Expand Down
49 changes: 46 additions & 3 deletions beacon_node/rest_api/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ fn validator_block_post() {
remote_node
.http
.validator()
.produce_block(slot, randao_reveal),
.produce_block(slot, randao_reveal, None),
)
.expect("should fetch block from http api");

Expand Down Expand Up @@ -547,15 +547,58 @@ fn validator_block_get() {
remote_node
.http
.validator()
.produce_block(slot, randao_reveal.clone()),
.produce_block(slot, randao_reveal.clone(), None),
)
.expect("should fetch block from http api");

let (expected_block, _state) = node
.client
.beacon_chain()
.expect("client should have beacon chain")
.produce_block(randao_reveal, slot)
.produce_block(randao_reveal, slot, None)
.expect("should produce block");

assert_eq!(
block, expected_block,
"the block returned from the API should be as expected"
);
}

#[test]
fn validator_block_get_with_graffiti() {
let mut env = build_env();

let spec = &E::default_spec();

let node = build_node(&mut env, testing_client_config());
let remote_node = node.remote_node().expect("should produce remote node");

let beacon_chain = node
.client
.beacon_chain()
.expect("client should have beacon chain");

let slot = Slot::new(1);
let randao_reveal = get_randao_reveal(beacon_chain, slot, spec);

let block = env
.runtime()
.block_on(remote_node.http.validator().produce_block(
slot,
randao_reveal.clone(),
Some(*b"test-graffiti-test-graffiti-test"),
))
.expect("should fetch block from http api");

let (expected_block, _state) = node
.client
.beacon_chain()
.expect("client should have beacon chain")
.produce_block(
randao_reveal,
slot,
Some(*b"test-graffiti-test-graffiti-test"),
)
.expect("should produce block");

assert_eq!(
Expand Down
25 changes: 14 additions & 11 deletions common/remote_beacon_node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use std::marker::PhantomData;
use std::time::Duration;
use types::{
Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, CommitteeIndex,
Epoch, EthSpec, Fork, Hash256, ProposerSlashing, PublicKey, PublicKeyBytes, Signature,
SignedAggregateAndProof, SignedBeaconBlock, Slot, SubnetId,
Epoch, EthSpec, Fork, Graffiti, Hash256, ProposerSlashing, PublicKey, PublicKeyBytes,
Signature, SignedAggregateAndProof, SignedBeaconBlock, Slot, SubnetId,
};
use url::Url;

Expand Down Expand Up @@ -313,18 +313,21 @@ impl<E: EthSpec> Validator<E> {
&self,
slot: Slot,
randao_reveal: Signature,
graffiti: Option<Graffiti>,
) -> Result<BeaconBlock<E>, Error> {
let client = self.0.clone();
let url = self.url("block")?;
client
.json_get::<BeaconBlock<E>>(
url,
vec![
("slot".into(), format!("{}", slot.as_u64())),
("randao_reveal".into(), as_ssz_hex_string(&randao_reveal)),
],
)
.await

let mut query_pairs = vec![
("slot".into(), format!("{}", slot.as_u64())),
("randao_reveal".into(), as_ssz_hex_string(&randao_reveal)),
];

if let Some(graffiti_bytes) = graffiti {
query_pairs.push(("graffiti".into(), as_ssz_hex_string(&graffiti_bytes)));
}

client.json_get::<BeaconBlock<E>>(url, query_pairs).await
}

/// Subscribes a list of validators to particular slots for attestation production/publication.
Expand Down
13 changes: 11 additions & 2 deletions validator_client/src/block_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ use slog::{crit, debug, error, info, trace, warn};
use slot_clock::SlotClock;
use std::ops::Deref;
use std::sync::Arc;
use types::{EthSpec, PublicKey, Slot};
use types::{EthSpec, Graffiti, PublicKey, Slot};

/// Builds a `BlockService`.
pub struct BlockServiceBuilder<T, E: EthSpec> {
validator_store: Option<ValidatorStore<T, E>>,
slot_clock: Option<Arc<T>>,
beacon_node: Option<RemoteBeaconNode<E>>,
context: Option<RuntimeContext<E>>,
graffiti: Option<Graffiti>,
}

impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
Expand All @@ -24,6 +25,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
slot_clock: None,
beacon_node: None,
context: None,
graffiti: None,
}
}

Expand All @@ -47,6 +49,11 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
self
}

pub fn graffiti(mut self, graffiti: Option<Graffiti>) -> Self {
self.graffiti = graffiti;
self
}

pub fn build(self) -> Result<BlockService<T, E>, String> {
Ok(BlockService {
inner: Arc::new(Inner {
Expand All @@ -62,6 +69,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
context: self
.context
.ok_or_else(|| "Cannot build BlockService without runtime_context")?,
graffiti: self.graffiti,
}),
})
}
Expand All @@ -73,6 +81,7 @@ pub struct Inner<T, E: EthSpec> {
slot_clock: Arc<T>,
beacon_node: RemoteBeaconNode<E>,
context: RuntimeContext<E>,
graffiti: Option<Graffiti>,
}

/// Attempts to produce attestations for any block producer(s) at the start of the epoch.
Expand Down Expand Up @@ -214,7 +223,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
.beacon_node
.http
.validator()
.produce_block(slot, randao_reveal)
.produce_block(slot, randao_reveal, self.graffiti)
.await
.map_err(|e| format!("Error from beacon node when producing block: {:?}", e))?;

Expand Down
8 changes: 8 additions & 0 deletions validator_client/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
node is not synced.",
),
)
// This overwrites the graffiti configured in the beacon node.
.arg(
Arg::with_name("graffiti")
.long("graffiti")
.help("Specify your custom graffiti to be included in blocks.")
.value_name("GRAFFITI")
.takes_value(true)
)
}
24 changes: 24 additions & 0 deletions validator_client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::ArgMatches;
use clap_utils::{parse_optional, parse_path_with_default_in_home_dir};
use serde_derive::{Deserialize, Serialize};
use std::path::PathBuf;
use types::{Graffiti, GRAFFITI_BYTES_LEN};

pub const DEFAULT_HTTP_SERVER: &str = "http://localhost:5052/";
pub const DEFAULT_DATA_DIR: &str = ".lighthouse/validators";
Expand All @@ -27,6 +28,8 @@ pub struct Config {
pub strict_lockfiles: bool,
/// If true, don't scan the validators dir for new keystores.
pub disable_auto_discover: bool,
/// Graffiti to be inserted everytime we create a block.
pub graffiti: Option<Graffiti>,
}

impl Default for Config {
Expand All @@ -45,6 +48,7 @@ impl Default for Config {
allow_unsynced_beacon_node: false,
strict_lockfiles: false,
disable_auto_discover: false,
graffiti: None,
}
}
}
Expand Down Expand Up @@ -80,6 +84,26 @@ impl Config {
config.secrets_dir = secrets_dir;
}

if let Some(input_graffiti) = cli_args.value_of("graffiti") {
let graffiti_bytes = input_graffiti.as_bytes();
if graffiti_bytes.len() > GRAFFITI_BYTES_LEN {
return Err(format!(
"Your graffiti is too long! {} bytes maximum!",
GRAFFITI_BYTES_LEN
));
} else {
// Default graffiti to all 0 bytes.
let mut graffiti = Graffiti::default();

// Copy the provided bytes over.
//
// Panic-free because `graffiti_bytes.len()` <= `GRAFFITI_BYTES_LEN`.
graffiti[..graffiti_bytes.len()].copy_from_slice(&graffiti_bytes);

config.graffiti = Some(graffiti);
}
}

Ok(config)
}
}
1 change: 1 addition & 0 deletions validator_client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
.validator_store(validator_store.clone())
.beacon_node(beacon_node.clone())
.runtime_context(context.service_context("block".into()))
.graffiti(config.graffiti)
.build()?;

let attestation_service = AttestationServiceBuilder::new()
Expand Down