Skip to content

Commit

Permalink
Merge pull request #8 from chainbound/naman/devnet
Browse files Browse the repository at this point in the history
working devnet
  • Loading branch information
thedevbirb authored Oct 17, 2024
2 parents 5b45570 + 40d50e5 commit 5898d70
Show file tree
Hide file tree
Showing 23 changed files with 862 additions and 396 deletions.
57 changes: 53 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ reth-primitives = { git = "https://github.com/paradigmxyz/reth", rev = "5dd5555c

# Logging
tracing = "0.1.37"
tracing-subscriber = "0.3.17"
tracing-subscriber = { version = "0.3.17", features = ["env-filter", "fmt"] }
tracing-appender = "0.2.2"

# Testing and Mocking
Expand Down
20 changes: 9 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,25 @@ RUN wget https://github.com/mozilla/sccache/releases/download/v0.3.1/sccache-v0.

ARG AWS_ACCESS_KEY_ID
ARG AWS_SECRET_ACCESS_KEY
ARG REPO_NAME
ARG REPO_NAME="helix"

RUN echo "REPO_NAME: $REPO_NAME"

# Test to make sure that aws access is set correctly
RUN test -n "$AWS_ACCESS_KEY_ID" || (echo "AWS_ACCESS_KEY_ID not set" && false)
RUN test -n "$AWS_SECRET_ACCESS_KEY" || (echo "AWS_SECRET_ACCESS_KEY not set" && false)
# RUN test -n "$AWS_ACCESS_KEY_ID" || (echo "AWS_ACCESS_KEY_ID not set" && false)
# RUN test -n "$AWS_SECRET_ACCESS_KEY" || (echo "AWS_SECRET_ACCESS_KEY not set" && false)

ENV SCCACHE_BUCKET=sccache-gtc
ENV SCCACHE_REGION=eu-west-1
ENV SCCACHE_S3_USE_SSL=true
# ENV SCCACHE_BUCKET=sccache-gtc
# ENV SCCACHE_REGION=eu-west-1
# ENV SCCACHE_S3_USE_SSL=true

# Copy necessary contents into the container at /app
ADD ./repos /app/
ADD ./ /app/

RUN ls -lah /app
RUN ls -lah /app/${REPO_NAME}

# Set the working directory to /app
WORKDIR /app/${REPO_NAME}
WORKDIR /app/

RUN --mount=type=cache,target=/root/.cargo \
--mount=type=cache,target=/usr/local/cargo/registry \
Expand All @@ -42,7 +41,7 @@ RUN --mount=type=cache,target=/root/.cargo \
RUSTC_WRAPPER=/usr/local/bin/sccache cargo build -p helix-cmd --release

# Copy binary into the workdir
RUN mv /app/$REPO_NAME/target/release/helix-cmd /app/helix-cmd
RUN mv /app/target/release/helix-cmd /app/helix-cmd

# our final base
FROM debian:stable-slim
Expand All @@ -58,4 +57,3 @@ COPY --from=helix /app/helix-cmd* ./

# set the startup command to run your binary
ENTRYPOINT ["/app/helix-cmd"]

9 changes: 6 additions & 3 deletions crates/api/src/builder/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ where
let stream = BroadcastStream::new(constraints_rx);

let filtered = stream.map(|result| match result {
Ok(constraint) => match serde_json::to_string(&constraint) {
Ok(constraint) => match serde_json::to_string(&vec![constraint]) {
Ok(json) => Ok(Event::default()
.data(json)
.event("signed_constraint")
Expand Down Expand Up @@ -1486,7 +1486,10 @@ where

match verify_multiproofs(&constraints, proofs, root) {
Ok(_) => Ok(()),
Err(_) => Err(BuilderApiError::InclusionProofVerificationFailed),
Err(e) => {
error!(error = %e, "failed to verify inclusion proofs");
Err(BuilderApiError::InclusionProofVerificationFailed)
},
}
}

Expand Down Expand Up @@ -2261,7 +2264,7 @@ fn sanity_check_block_submission(
payload_attributes: &PayloadAttributesUpdate,
chain_info: &ChainInfo,
) -> Result<(), BuilderApiError> {
let expected_timestamp = chain_info.genesis_time_in_secs + (bid_trace.slot * SECONDS_PER_SLOT);
let expected_timestamp = chain_info.genesis_time_in_secs + (bid_trace.slot * chain_info.seconds_per_slot);
if payload.timestamp() != expected_timestamp {
return Err(BuilderApiError::IncorrectTimestamp {
got: payload.timestamp(),
Expand Down
125 changes: 92 additions & 33 deletions crates/api/src/constraints/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use axum::{
body::{to_bytes, Body}, extract::ws::{Message, WebSocket, WebSocketUpgrade}, http::{request, Request, StatusCode}, response::{IntoResponse, Response}, Extension
};
use ethereum_consensus::{primitives::{BlsPublicKey, BlsSignature}, deneb::{verify_signed_data, Slot}, ssz};
use helix_common::{api::constraints_api::{SignableBLS, SignedDelegation, SignedRevocation, MAX_CONSTRAINTS_PER_SLOT}, bellatrix::List, chain_info::ChainInfo, proofs::{ConstraintsMessage, ConstraintsWithProofData, ProofError, SignedConstraints, SignedConstraintsWithProofData}, ConstraintSubmissionTrace};
use helix_common::{api::constraints_api::{SignableBLS, SignedDelegation, SignedRevocation, DELEGATION_ACTION, MAX_CONSTRAINTS_PER_SLOT, REVOCATION_ACTION}, bellatrix::List, chain_info::ChainInfo, proofs::{ConstraintsMessage, ConstraintsWithProofData, ProofError, SignedConstraints, SignedConstraintsWithProofData}, ConstraintSubmissionTrace};
use helix_database::DatabaseService;
use helix_datastore::Auctioneer;
use ethereum_consensus::signing::verify_signature;
use helix_utils::signing::{verify_signed_message, COMMIT_BOOST_DOMAIN};
use tracing::{info, warn, error};
use uuid::Uuid;
use std::{sync::Arc, time::{SystemTime, UNIX_EPOCH}};
Expand Down Expand Up @@ -82,14 +83,43 @@ where
return Err(ConstraintsApiError::NilConstraints);
}

// Add all the constraints to the cache
// check that all constraints are for the same slot and with the same pubkey
let Some(first_constraints) = signed_constraints.first().map(|c| c.message.clone()) else {
error!(request_id = %request_id, "No constraints found");
return Err(ConstraintsApiError::InvalidConstraints);
};
if !signed_constraints.iter().all(|c| c.message.slot == first_constraints.slot) {
error!(request_id = %request_id, "Constraints for different slots in the same batch");
return Err(ConstraintsApiError::InvalidConstraints);
}
if !signed_constraints.iter().all(|c| c.message.pubkey == first_constraints.pubkey) {
error!(request_id = %request_id, "Constraints for different pubkeys in the same batch");
return Err(ConstraintsApiError::InvalidConstraints);
}

// Perf: can we avoid calling the db?
let maybe_validator_pubkey = api.db.get_proposer_duties().await?.iter().find_map(|d| {
if d.slot == first_constraints.slot {
Some(d.entry.registration.message.public_key.clone())
} else {
None
}
});

let Some(validator_pubkey) = maybe_validator_pubkey else {
error!(request_id = %request_id, slot = first_constraints.slot, "Missing proposer info");
return Err(ConstraintsApiError::MissingProposerInfo);
};

// Fetch active delegations for the validator pubkey, if any
let delegations = api.auctioneer.get_validator_delegations(validator_pubkey.clone()).await?;
let delegatees = delegations.iter().map(|d| d.message.delegatee_pubkey.clone()).collect::<Vec<_>>();

// Add all the valid constraints to the cache
for constraint in signed_constraints {
let pubkey = constraint.message.pubkey.clone();
let message = &constraint.message;

// Check for conflicts in the constraints
let saved_constraints = api.auctioneer.get_constraints(message.slot).await?;
if let Some(conflict) = conflicts_with(&saved_constraints, message) {
let saved_constraints = api.auctioneer.get_constraints(constraint.message.slot).await?;
if let Some(conflict) = conflicts_with(&saved_constraints, &constraint.message) {
return Err(ConstraintsApiError::Conflict(conflict));
}

Expand All @@ -98,18 +128,29 @@ where
return Err(ConstraintsApiError::MaxConstraintsReached);
}

// Verify the signature.
if let Err(err) = verify_signature(
&pubkey,
&message.digest(),
// Check if the constraint pubkey is delegated to submit constraints for this validator.
// - If there are no delegations, only the validator pubkey can submit constraints
// - If there are delegations, only delegatees can submit constraints
let message_not_signed_by_validator = delegatees.is_empty() && constraint.message.pubkey != validator_pubkey;
let message_not_signed_by_delegatee = !delegatees.is_empty() && !delegatees.contains(&constraint.message.pubkey);

if message_not_signed_by_validator && message_not_signed_by_delegatee {
error!(request_id = %request_id, pubkey = %constraint.message.pubkey, "Pubkey unauthorized");
return Err(ConstraintsApiError::PubkeyNotAuthorized(constraint.message.pubkey))
}

// Verify the constraints message BLS signature
if let Err(e) = verify_signed_message(
&constraint.message.digest(),
&constraint.signature,
&constraint.message.pubkey,
COMMIT_BOOST_DOMAIN,
&api.chain_info.context,
) {
error!(err = ?e, request_id = %request_id, "Invalid constraints signature");
return Err(ConstraintsApiError::InvalidSignature);
};

// Once we support sending messages signed with correct validator pubkey on the sidecar,
// return error if invalid

// Send to the constraints channel
api.constraints_handle.send_constraints(constraint.clone());

Expand Down Expand Up @@ -160,21 +201,30 @@ where
let body_bytes = to_bytes(body, MAX_REQUEST_LENGTH).await?;

// Decode the incoming request body into a `SignedDelegation`.
let mut signed_delegation: SignedDelegation = match serde_json::from_slice(&body_bytes) {
Ok(delegation) => delegation,
Err(_) => return Err(ConstraintsApiError::InvalidDelegation),
let mut signed_delegation = match serde_json::from_slice::<SignedDelegation>(&body_bytes) {
Ok(delegation) => match delegation.message.action {
DELEGATION_ACTION => delegation,
other => {
warn!(request_id = %request_id, action = other, "Invalid delegation action. expected 0");
return Err(ConstraintsApiError::InvalidDelegation)
},
},
Err(e) => {
warn!(err = ?e, request_id = %request_id, "Failed to decode delegation");
return Err(ConstraintsApiError::InvalidDelegation)
},
};
trace.decode = get_nanos_timestamp()?;

let pubkey = signed_delegation.message.validator_pubkey.clone();
let message = &mut signed_delegation.message;

// Verify the delegation signature
if let Err(_) = verify_signature(
&pubkey,
&message.digest(),
if let Err(e) = verify_signed_message(
&signed_delegation.message.digest(),
&signed_delegation.signature,
&signed_delegation.message.validator_pubkey,
COMMIT_BOOST_DOMAIN,
&api.chain_info.context,
) {
warn!(err = ?e, request_id = %request_id, "Invalid delegation signature");
return Err(ConstraintsApiError::InvalidSignature);
};
trace.verify_signature = get_nanos_timestamp()?;
Expand Down Expand Up @@ -225,22 +275,31 @@ where
let body = req.into_body();
let body_bytes = to_bytes(body, MAX_REQUEST_LENGTH).await?;

// Decode the incoming request body into a `SignedDelegation`.
let mut signed_revocation: SignedRevocation = match serde_json::from_slice(&body_bytes) {
Ok(revocation ) => revocation,
Err(_) => return Err(ConstraintsApiError::InvalidRevocation),
// Decode the incoming request body into a `SignedRevocation`.
let mut signed_revocation = match serde_json::from_slice::<SignedRevocation>(&body_bytes) {
Ok(revocation ) => match revocation.message.action {
REVOCATION_ACTION => revocation,
other => {
warn!(request_id = %request_id, action = other, "Invalid revocation action. expected 1");
return Err(ConstraintsApiError::InvalidRevocation)
},
},
Err(e) => {
warn!(err = ?e, request_id = %request_id, "Failed to decode revocation");
return Err(ConstraintsApiError::InvalidRevocation)
},
};
trace.decode = get_nanos_timestamp()?;

let pubkey = signed_revocation.message.validator_pubkey.clone();
let message = &mut signed_revocation.message;

// Verify the revocation signature
if let Err(e) = verify_signature(
&pubkey,
&message.digest(),
if let Err(e) = verify_signed_message(
&signed_revocation.message.digest(),
&signed_revocation.signature,
&signed_revocation.message.validator_pubkey,
COMMIT_BOOST_DOMAIN,
&api.chain_info.context,
) {
warn!(err = ?e, request_id = %request_id, "Invalid revocation signature");
return Err(ConstraintsApiError::InvalidSignature);
};
trace.verify_signature = get_nanos_timestamp()?;
Expand Down
Loading

0 comments on commit 5898d70

Please sign in to comment.