-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(api): constraints getter functions #4
feat(api): constraints getter functions #4
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job! Some minor comments
crates/api/src/constraints/api.rs
Outdated
// TODO: Move this to appropriate place | ||
#[derive(Clone)] | ||
pub struct ConstraintsHandle { | ||
pub(crate) constraints_tx: mpsc::Sender<ConstraintsMessage>, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where would you like to put this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought I think this might be right place itself. lmk if any suggestions
crates/api/src/relay_data/api.rs
Outdated
// TODO: Return err if the slot is outside expiry bounds i.e. before an epoch | ||
// For this we need a beacon state tracker | ||
let slot = params as u64; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to return an error in such case. Anyway, I'd consider to make the slot
parameter optional (and reflect it in the docs), and when it is not sent we should consider the current slot (from the beacon state) the default one. It is most likely the most requested slot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point
crates/api/src/relay_data/api.rs
Outdated
/// Implements this API: <https://chainbound.github.io/bolt-docs/api/relay#constraints-stream> | ||
pub async fn constraints_stream( | ||
Extension(data_api): Extension<Arc<DataApi<A, DB>>>, | ||
) -> Sse<impl Stream<Item = Result<Event, DataApiError>>> { | ||
let constraints_rx = data_api.constraints_rx.read().await; | ||
|
||
// pub async fn blocks_with_proofs() { | ||
// unimplemented!() | ||
// } | ||
let filtered = constraints_rx.map(|constraint| match serde_json::to_string(&constraint) { | ||
Ok(json) => Ok(Event::default().data(json).event("constraint").retry(Duration::from_millis(50))), | ||
Err(err) => { | ||
warn!(error=%err, "Failed to serialize constraint"); | ||
Err(DataApiError::InternalServerError) | ||
} | ||
}); | ||
|
||
Sse::new(filtered).keep_alive(KeepAlive::default()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great and very short, however I have never implemented SSE in Rust. I have two questions:
- can you please add some unit tests for this?
- what did you use as a reference for this implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes will add unit tests for this.
Referred this -
https://github.com/chainbound/dato/blob/1f6999dd01bf34698c926fde84373527c589b03e/src/client/api.rs#L138
crates/api/src/relay_data/api.rs
Outdated
/// Implements this API: <https://chainbound.github.io/bolt-docs/api/relay#constraints-stream> | ||
pub async fn constraints_stream( | ||
Extension(data_api): Extension<Arc<DataApi<A, DB>>>, | ||
) -> Sse<impl Stream<Item = Result<Event, DataApiError>>> { | ||
let constraints_rx = data_api.constraints_rx.read().await; | ||
|
||
// pub async fn blocks_with_proofs() { | ||
// unimplemented!() | ||
// } | ||
let filtered = constraints_rx.map(|constraint| match serde_json::to_string(&constraint) { | ||
Ok(json) => Ok(Event::default().data(json).event("constraint").retry(Duration::from_millis(50))), | ||
Err(err) => { | ||
warn!(error=%err, "Failed to serialize constraint"); | ||
Err(DataApiError::InternalServerError) | ||
} | ||
}); | ||
|
||
Sse::new(filtered).keep_alive(KeepAlive::default()) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this SSE stay open? I don't fully understand how long we are keeping the read() lock for when opening a stream. What happens if we want to write in the constraints while also streaming contents to clients?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing out, resolved
Testing WIP |
crates/api/src/relay_data/api.rs
Outdated
} | ||
|
||
impl<A: Auctioneer + 'static, DB: DatabaseService + 'static> DataApi<A, DB> { | ||
pub fn new(validator_preferences: Arc<ValidatorPreferences>, auctioneer:Arc<A>, db: Arc<DB>) -> (Self, ConstraintsHandle) { | ||
let (constraints_tx, constraints_rx) = mpsc::channel(100); // Should we have buffer? | ||
let (constraints_tx, constraints_rx ) = broadcast::channel(100); // Should we have buffer? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to address the comment, I would leave the channel to something like 128 constraint messages - it's not likely to ever get filled anyway
crates/api/src/relay_data/api.rs
Outdated
let constraints_rx = data_api.constraints_rx.resubscribe(); | ||
let stream = BroadcastStream::new(constraints_rx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lovely
feat(builder-api): delegations getter fn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job! Few nits
crates/api/src/builder/api.rs
Outdated
let slot = slot.slot; | ||
let head_slot = api.curr_slot_info.read().await.0; | ||
|
||
if slot > head_slot || slot < head_slot - 32 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be if slot < head_slot || slot > head_slot + 32
?
Presumably we want to fetch the constraints for a future slot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right! mb
crates/api/src/builder/api.rs
Outdated
let duty_bytes = api.proposer_duties_response.read().await.clone(); | ||
let proposer_duties: Vec<BuilderGetValidatorsResponse> = serde_json::from_slice(&duty_bytes.unwrap()).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion
let Some(duty_bytes) = api.proposer_duties_response.read().await else {
return Err(...)
}
let Ok(proposer_duties) = serde_json::from_slice::<Vec<BuilderGetValidatorsResponse>>(duty_bytes) else {
return Err(...)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point
crates/api/src/builder/api.rs
Outdated
Err(err) => { | ||
match err { | ||
DatabaseError::ValidatorDelegationNotFound => { | ||
warn!("No delegations found for validator"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warn!("No delegations found for validator"); | |
debug!("No delegations found for validator"); |
crates/api/src/builder/api.rs
Outdated
let mut tx_clone = payload.transactions().clone(); | ||
let root = tx_clone.hash_tree_root().expect("failed to hash tree root"); | ||
let root = root.to_vec().as_slice().try_into().expect("failed to convert to hash32"); | ||
|
||
let proofs = payload.proofs().expect("proofs not found"); | ||
match verify_multiproofs(constraints, proofs, root) { | ||
let constraints: Vec<ConstraintsWithProofData> = constraints.iter().map(|c| ConstraintsWithProofData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be able to avoid clone() inside by using into_iter()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I use into_iter() and remove clone it still gives move occurs, copy trait not implemented
let mut tx_clone = payload.transactions().clone(); | ||
let root = tx_clone.hash_tree_root().expect("failed to hash tree root"); | ||
let root = root.to_vec().as_slice().try_into().expect("failed to convert to hash32"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great workaround of the types :)
|
||
#[tokio::test] | ||
#[serial] | ||
async fn test_constraints_stream_ok() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great test
crates/api/src/constraints/api.rs
Outdated
) { | ||
return Err(ConstraintsApiError::InvalidSignature); | ||
}; | ||
|
||
// Once we support sending messages signed with correct validator pubkey on the sidecar, | ||
// return error if invalid | ||
|
||
let message = signed_constraints.message.clone(); | ||
let message = constraint.message.clone(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can probably do this and use slot
below to avoid clone
let message = constraint.message.clone(); | |
let slot = constraint.message.slot; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
man I seriously need to start avoiding clone haha. I remember this was needed before but after making some changes to the verification part, I forgot to remove this clone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, let's move on to testing on devnet
fix: return signed delegations
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
This PR adds the following -
ConstraintWithProofData
toSignedConstraintWithProofData