From e15c44b39df226bad687d80b0105fdc157f919f5 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 4 May 2022 08:33:03 -0600 Subject: [PATCH 001/153] init commit --- .gitignore | 2 + Cargo.toml | 19 ++ src/api_client.rs | 492 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + 4 files changed, 516 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/api_client.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..4fffb2f89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..5138c8a3e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "beacon-api-client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = { version = "1.0", features = ["full"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +reqwest = "0.11.10" + +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.79" + +thiserror = "1.0.30" + +ethereum_consensus = { git = "https://github.com/ralexstokes/ethereum_consensus" } \ No newline at end of file diff --git a/src/api_client.rs b/src/api_client.rs new file mode 100644 index 000000000..d446aaf94 --- /dev/null +++ b/src/api_client.rs @@ -0,0 +1,492 @@ +use ethereum_consensus::altair::mainnet::{ + SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, +}; +use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; +use ethereum_consensus::networking::{Enr, MetaData, Multiaddr, PeerId}; +use ethereum_consensus::phase0::mainnet::{ + Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Checkpoint, Fork, + ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, + SignedVoluntaryExit, Validator, +}; +use ethereum_consensus::primitives::{ + BlsPublicKey, Bytes32, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, + RandaoReveal, Root, Slot, ValidatorIndex, Version, +}; +use std::collections::HashMap; + +pub enum Error {} + +pub struct Client {} + +pub struct GenesisDetails { + pub genesis_time: u64, + pub genesis_validators_root: Root, + pub genesis_fork_version: Version, +} + +pub enum StateId { + Head, + Genesis, + Finalized, + Justified, + Slot(Slot), + Root(Root), +} + +pub enum BlockId { + Head, + Genesis, + Finalized, + Slot(Slot), + Root(Root), +} + +pub enum ExecutionStatus { + Default, + Optimistic, +} + +pub struct FinalityCheckpoints { + pub previous_justified: Checkpoint, + pub current_justified: Checkpoint, + pub finalized: Checkpoint, +} + +pub enum ValidatorStatus { + PendingInitialized, + PendingQueued, + ActiveOngoing, + ActiveExiting, + ActiveSlashed, + ExitedUnslashed, + ExitedSlashed, + WithdrawalPossible, + WithdrawalDone, + // TODO what are these? + Active, + Pending, + Exited, + Withdrawal, +} + +pub enum PubkeyOrIndex { + Pubkey(BlsPublicKey), + Index(ValidatorIndex), +} + +pub struct ValidatorDescriptor { + pub pubkey_or_index: PubkeyOrIndex, + pub status: ValidatorStatus, +} + +pub struct ValidatorSummary { + pub index: ValidatorIndex, + pub balance: Gwei, + pub status: ValidatorStatus, + pub validator: Validator, +} + +pub struct BalanceSummary { + pub index: ValidatorIndex, + pub balance: Gwei, +} + +pub struct CommitteeFilter { + pub epoch: Option, + pub index: Option, + pub slot: Option, +} + +pub struct CommitteeSummary { + pub index: CommitteeIndex, + pub slot: Slot, + pub validators: Vec, +} + +pub struct SyncCommitteeSummary { + pub validators: Vec, + pub validator_aggregates: Vec>, +} + +pub struct BeaconHeaderSummary { + pub root: Root, + pub canonical: bool, + pub signed_header: SignedBeaconBlockHeader, +} + +pub enum EventTopic { + Head, + Block, + Attestation, + VoluntaryExit, + FinalizedCheckpoint, + ChainReorg, + ContributionAndProof, +} + +pub struct NetworkIdentity { + pub peer_id: PeerId, + pub enr: Enr, + pub p2p_addresses: Vec, + pub discovery_addresses: Vec, + pub metadata: MetaData, +} + +pub enum PeerState { + Disconnected, + Connecting, + Connected, + Disconnecting, +} + +pub enum ConnectionOrientation { + Inbound, + Outbound, +} + +pub struct PeerDescriptor { + pub state: PeerState, + pub direction: ConnectionOrientation, +} + +pub struct PeerDescription { + pub peer_id: PeerId, + pub enr: Enr, + pub last_seen_p2p_address: Multiaddr, + pub state: PeerState, + pub direction: ConnectionOrientation, +} + +pub struct PeerSummary { + pub disconnected: usize, + pub connecting: usize, + pub connected: usize, + pub disconnecting: usize, +} + +pub struct SyncStatus { + pub head_slot: Slot, + pub sync_distance: usize, + pub is_syncing: bool, +} + +pub enum HealthStatus { + Ready, + Syncing, + NotInitialized, +} + +pub struct AttestationDuty { + pub pubkey: BlsPublicKey, + pub validator_index: ValidatorIndex, + pub committee_index: CommitteeIndex, + pub committee_length: usize, + pub committees_at_slot: usize, + pub validator_committee_index: usize, + pub slot: Slot, +} + +pub struct ProposerDuty { + pub pubkey: BlsPublicKey, + pub validator_index: ValidatorIndex, + pub slot: Slot, +} + +pub struct SyncCommitteeDuty { + pub pubkey: BlsPublicKey, + pub validator_index: ValidatorIndex, + pub validator_sync_committee_indices: Vec, +} + +pub struct CommitteeDescriptor { + pub validator_index: ValidatorIndex, + pub committee_index: CommitteeIndex, + pub committees_at_slot: usize, + pub slot: Slot, + pub is_aggregator: bool, +} + +pub struct SyncCommitteeDescriptor { + pub validator_index: ValidatorIndex, + pub sync_committee_indices: Vec, + pub until_epoch: Epoch, +} + +pub struct BeaconProposerRegistration { + pub validator_index: ValidatorIndex, + pub fee_recipient: ExecutionAddress, +} + +impl Client { + /* beacon namespace */ + pub async fn get_genesis_details(&self) -> GenesisDetails { + unimplemented!("") + } + + pub async fn get_state_root(id: StateId) -> Root { + unimplemented!("") + } + + pub async fn get_fork(id: StateId) -> Fork { + unimplemented!("") + } + + pub async fn get_finality_checkpoints(id: StateId) -> FinalityCheckpoints { + unimplemented!("") + } + + pub async fn get_validators( + id: StateId, + filters: &[ValidatorDescriptor], + ) -> Vec { + unimplemented!("") + } + + pub async fn get_validator(id: StateId, validator_id: PubkeyOrIndex) -> ValidatorSummary { + unimplemented!("") + } + + pub async fn get_balances(id: StateId, filters: &[PubkeyOrIndex]) -> Vec { + unimplemented!("") + } + + pub async fn get_all_committees(id: StateId) -> Vec { + unimplemented!("") + } + + pub async fn get_committees(id: StateId, filter: CommitteeFilter) -> Vec { + unimplemented!("") + } + + pub async fn get_sync_committees( + id: StateId, + epoch: Option, + ) -> Vec { + unimplemented!("") + } + + pub async fn get_beacon_header_at_head() -> BeaconHeaderSummary { + unimplemented!("") + } + + pub async fn get_beacon_header_for_slot(slot: Slot) -> BeaconHeaderSummary { + unimplemented!("") + } + + pub async fn get_beacon_header_for_parent_root(parent_root: Root) -> BeaconHeaderSummary { + unimplemented!("") + } + + pub async fn get_beacon_header(id: BlockId) -> BeaconHeaderSummary { + unimplemented!("") + } + + pub async fn post_signed_beacon_block(block: &SignedBeaconBlock) -> Result<(), Error> { + unimplemented!("") + } + + pub async fn post_signed_blinded_beacon_block( + block: &SignedBlindedBeaconBlock, + ) -> Result<(), Error> { + unimplemented!("") + } + + // v2 endpoint + pub async fn get_beacon_block(id: BlockId) -> SignedBeaconBlock { + unimplemented!("") + } + + pub async fn get_beacon_block_root(id: BlockId) -> Root { + unimplemented!("") + } + + pub async fn get_attestations_from_beacon_block(id: BlockId) -> Vec { + unimplemented!("") + } + + pub async fn get_attestations_from_pool( + slot: Option, + committee_index: Option, + ) -> Vec { + unimplemented!("") + } + + pub async fn post_attestations(attestations: &[Attestation]) -> Result<(), Error> { + unimplemented!("") + } + + pub async fn get_attester_slashings_from_pool() -> Vec { + unimplemented!("") + } + + pub async fn post_attester_slashing(attester_slashing: &AttesterSlashing) -> Result<(), Error> { + unimplemented!("") + } + + pub async fn get_proposer_slashings_from_pool() -> Vec { + unimplemented!("") + } + + pub async fn post_proposer_slashing(proposer_slashing: &ProposerSlashing) -> Result<(), Error> { + unimplemented!("") + } + + pub async fn post_sync_committee_messages( + messages: &[SyncCommitteeMessage], + ) -> Result<(), Error> { + unimplemented!("") + } + + pub async fn get_voluntary_exits_from_pool() -> Vec { + unimplemented!("") + } + + pub async fn post_signed_voluntary_exit(exit: &SignedVoluntaryExit) -> Result<(), Error> { + unimplemented!("") + } + + /* config namespace */ + pub async fn get_fork_schedule() -> Vec { + unimplemented!("") + } + + pub async fn get_spec() -> HashMap { + unimplemented!("") + } + + pub async fn get_deposit_contract_address() -> (ChainId, ExecutionAddress) { + unimplemented!("") + } + + /* debug namespace */ + // v2 endpoint + pub async fn get_state(id: StateId) -> BeaconState { + unimplemented!("") + } + + // v2 endpoint + pub async fn get_heads() -> Vec { + unimplemented!("") + } + + /* events namespace */ + // TODO: figure out return type + pub async fn get_events(topics: &[EventTopic]) -> T { + // get back "event: TOPIC, data: T" + unimplemented!("") + } + + /* node namespace */ + pub async fn get_node_identity() -> NetworkIdentity { + unimplemented!("") + } + + pub async fn get_node_peers(filters: &[PeerDescriptor]) -> Vec { + unimplemented!("") + } + + pub async fn get_peer(peer_id: Multiaddr) -> PeerDescription { + unimplemented!("") + } + + pub async fn get_peer_count() -> PeerSummary { + unimplemented!("") + } + + pub async fn get_node_version() -> String { + unimplemented!("") + } + + pub async fn get_sync_status() -> SyncStatus { + unimplemented!("") + } + + pub async fn get_health() -> HealthStatus { + unimplemented!("") + } + + /* validator namespace */ + pub async fn get_attester_duties( + epoch: Epoch, + indices: &[ValidatorIndex], + ) -> (Root, Vec) { + unimplemented!("") + } + + pub async fn get_proposer_duties(epoch: Epoch) -> (Root, Vec) { + unimplemented!("") + } + + pub async fn get_sync_committee_duties( + epoch: Epoch, + indices: &[ValidatorIndex], + ) -> (Root, Vec) { + unimplemented!("") + } + + // v2 endpoint + pub async fn get_block( + slot: Slot, + randao_reveal: RandaoReveal, + graffiti: Bytes32, + ) -> BeaconBlock { + unimplemented!("") + } + + pub async fn get_blinded_block( + slot: Slot, + randao_reveal: RandaoReveal, + graffiti: Bytes32, + ) -> BlindedBeaconBlock { + unimplemented!("") + } + + pub async fn get_attestation_data( + slot: Slot, + committee_index: CommitteeIndex, + ) -> AttestationData { + unimplemented!("") + } + + pub async fn get_attestation_aggregate(attestation_data_root: Root, slot: Slot) -> Attestation { + unimplemented!("") + } + + pub async fn post_aggregates_with_proofs( + aggregates_with_proofs: &[SignedAggregateAndProof], + ) -> Result<(), Error> { + Ok(()) + } + + pub async fn subscribe_subnets_for_attestation( + committee_descriptors: &[CommitteeDescriptor], + ) -> Result<(), Error> { + Ok(()) + } + + pub async fn subscribe_subnets_for_sync_committee( + sync_committee_descriptors: &[SyncCommitteeDescriptor], + ) -> Result<(), Error> { + Ok(()) + } + + pub async fn get_sync_committee_contribution( + slot: Slot, + subcommittee_index: usize, + beacon_block_root: Root, + ) -> Result { + unimplemented!("") + } + + pub async fn post_contributions_with_proofs( + contributions_with_proofs: &[SignedContributionAndProof], + ) -> Result<(), Error> { + Ok(()) + } + + pub async fn register_proposers( + registrations: &[BeaconProposerRegistration], + ) -> Result<(), Error> { + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..da111a4b7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +mod api_client; + +pub use api_client::*; From baea66edb1414bca74c51767d640d3d60ffb495e Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 4 May 2022 10:16:22 -0600 Subject: [PATCH 002/153] Create README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..e1546e391 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# beacon-api-client +A client for the Ethereum beacon node APIs From 3ec9f90f3ca5a3acf5ce90c1142e3053e4c71204 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 4 May 2022 10:17:57 -0600 Subject: [PATCH 003/153] Create LICENSE-APACHE --- LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE-APACHE diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 4ab2adc70015c5cb93c626e97bb807090edf4308 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 4 May 2022 10:20:10 -0600 Subject: [PATCH 004/153] update licensing --- Cargo.toml | 1 + src/LICENSE-MIT | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/LICENSE-MIT diff --git a/Cargo.toml b/Cargo.toml index 5138c8a3e..d2367ac87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "beacon-api-client" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/LICENSE-MIT b/src/LICENSE-MIT new file mode 100644 index 000000000..101aa4202 --- /dev/null +++ b/src/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Alex Stokes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From dbacb3039cb2af5b4e0f0a1a5e9e7f7fcc716fa9 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 4 May 2022 10:41:34 -0600 Subject: [PATCH 005/153] missed file in last commit --- LICENSE-MIT | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE-MIT diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..101aa4202 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Alex Stokes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From bfe0c597ce6795457cda80c83a98c80118c1d2a9 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 4 May 2022 10:42:31 -0600 Subject: [PATCH 006/153] clean up file --- src/LICENSE-MIT | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/LICENSE-MIT diff --git a/src/LICENSE-MIT b/src/LICENSE-MIT deleted file mode 100644 index 101aa4202..000000000 --- a/src/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Alex Stokes - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file From 65c3724b89da198b852375965ecaf097e670fcc4 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 4 May 2022 10:50:09 -0600 Subject: [PATCH 007/153] sketch out return types --- src/api_client.rs | 98 +++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index d446aaf94..319c8cfa7 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -219,65 +219,76 @@ pub struct BeaconProposerRegistration { impl Client { /* beacon namespace */ - pub async fn get_genesis_details(&self) -> GenesisDetails { + pub async fn get_genesis_details(&self) -> Result { unimplemented!("") } - pub async fn get_state_root(id: StateId) -> Root { + pub async fn get_state_root(id: StateId) -> Result { unimplemented!("") } - pub async fn get_fork(id: StateId) -> Fork { + pub async fn get_fork(id: StateId) -> Result { unimplemented!("") } - pub async fn get_finality_checkpoints(id: StateId) -> FinalityCheckpoints { + pub async fn get_finality_checkpoints(id: StateId) -> Result { unimplemented!("") } pub async fn get_validators( id: StateId, filters: &[ValidatorDescriptor], - ) -> Vec { + ) -> Result, Error> { unimplemented!("") } - pub async fn get_validator(id: StateId, validator_id: PubkeyOrIndex) -> ValidatorSummary { + pub async fn get_validator( + id: StateId, + validator_id: PubkeyOrIndex, + ) -> Result { unimplemented!("") } - pub async fn get_balances(id: StateId, filters: &[PubkeyOrIndex]) -> Vec { + pub async fn get_balances( + id: StateId, + filters: &[PubkeyOrIndex], + ) -> Result, Error> { unimplemented!("") } - pub async fn get_all_committees(id: StateId) -> Vec { + pub async fn get_all_committees(id: StateId) -> Result, Error> { unimplemented!("") } - pub async fn get_committees(id: StateId, filter: CommitteeFilter) -> Vec { + pub async fn get_committees( + id: StateId, + filter: CommitteeFilter, + ) -> Result, Error> { unimplemented!("") } pub async fn get_sync_committees( id: StateId, epoch: Option, - ) -> Vec { + ) -> Result, Error> { unimplemented!("") } - pub async fn get_beacon_header_at_head() -> BeaconHeaderSummary { + pub async fn get_beacon_header_at_head() -> Result { unimplemented!("") } - pub async fn get_beacon_header_for_slot(slot: Slot) -> BeaconHeaderSummary { + pub async fn get_beacon_header_for_slot(slot: Slot) -> Result { unimplemented!("") } - pub async fn get_beacon_header_for_parent_root(parent_root: Root) -> BeaconHeaderSummary { + pub async fn get_beacon_header_for_parent_root( + parent_root: Root, + ) -> Result { unimplemented!("") } - pub async fn get_beacon_header(id: BlockId) -> BeaconHeaderSummary { + pub async fn get_beacon_header(id: BlockId) -> Result { unimplemented!("") } @@ -292,22 +303,24 @@ impl Client { } // v2 endpoint - pub async fn get_beacon_block(id: BlockId) -> SignedBeaconBlock { + pub async fn get_beacon_block(id: BlockId) -> Result { unimplemented!("") } - pub async fn get_beacon_block_root(id: BlockId) -> Root { + pub async fn get_beacon_block_root(id: BlockId) -> Result { unimplemented!("") } - pub async fn get_attestations_from_beacon_block(id: BlockId) -> Vec { + pub async fn get_attestations_from_beacon_block( + id: BlockId, + ) -> Result, Error> { unimplemented!("") } pub async fn get_attestations_from_pool( slot: Option, committee_index: Option, - ) -> Vec { + ) -> Result, Error> { unimplemented!("") } @@ -315,7 +328,7 @@ impl Client { unimplemented!("") } - pub async fn get_attester_slashings_from_pool() -> Vec { + pub async fn get_attester_slashings_from_pool() -> Result, Error> { unimplemented!("") } @@ -323,7 +336,7 @@ impl Client { unimplemented!("") } - pub async fn get_proposer_slashings_from_pool() -> Vec { + pub async fn get_proposer_slashings_from_pool() -> Result, Error> { unimplemented!("") } @@ -337,7 +350,7 @@ impl Client { unimplemented!("") } - pub async fn get_voluntary_exits_from_pool() -> Vec { + pub async fn get_voluntary_exits_from_pool() -> Result, Error> { unimplemented!("") } @@ -346,62 +359,62 @@ impl Client { } /* config namespace */ - pub async fn get_fork_schedule() -> Vec { + pub async fn get_fork_schedule() -> Result, Error> { unimplemented!("") } - pub async fn get_spec() -> HashMap { + pub async fn get_spec() -> Result, Error> { unimplemented!("") } - pub async fn get_deposit_contract_address() -> (ChainId, ExecutionAddress) { + pub async fn get_deposit_contract_address() -> Result<(ChainId, ExecutionAddress), Error> { unimplemented!("") } /* debug namespace */ // v2 endpoint - pub async fn get_state(id: StateId) -> BeaconState { + pub async fn get_state(id: StateId) -> Result { unimplemented!("") } // v2 endpoint - pub async fn get_heads() -> Vec { + pub async fn get_heads() -> Result, Error> { unimplemented!("") } /* events namespace */ // TODO: figure out return type - pub async fn get_events(topics: &[EventTopic]) -> T { + pub async fn get_events(topics: &[EventTopic]) -> Result { // get back "event: TOPIC, data: T" unimplemented!("") } /* node namespace */ - pub async fn get_node_identity() -> NetworkIdentity { + pub async fn get_node_identity() -> Result { unimplemented!("") } - pub async fn get_node_peers(filters: &[PeerDescriptor]) -> Vec { + pub async fn get_node_peers(filters: &[PeerDescriptor]) -> Result, Error> { unimplemented!("") } - pub async fn get_peer(peer_id: Multiaddr) -> PeerDescription { + pub async fn get_peer(peer_id: Multiaddr) -> Result { unimplemented!("") } - pub async fn get_peer_count() -> PeerSummary { + pub async fn get_peer_count() -> Result { unimplemented!("") } - pub async fn get_node_version() -> String { + pub async fn get_node_version() -> Result { unimplemented!("") } - pub async fn get_sync_status() -> SyncStatus { + pub async fn get_sync_status() -> Result { unimplemented!("") } - pub async fn get_health() -> HealthStatus { + pub async fn get_health() -> Result { unimplemented!("") } @@ -409,18 +422,18 @@ impl Client { pub async fn get_attester_duties( epoch: Epoch, indices: &[ValidatorIndex], - ) -> (Root, Vec) { + ) -> Result<(Root, Vec), Error> { unimplemented!("") } - pub async fn get_proposer_duties(epoch: Epoch) -> (Root, Vec) { + pub async fn get_proposer_duties(epoch: Epoch) -> Result<(Root, Vec), Error> { unimplemented!("") } pub async fn get_sync_committee_duties( epoch: Epoch, indices: &[ValidatorIndex], - ) -> (Root, Vec) { + ) -> Result<(Root, Vec), Error> { unimplemented!("") } @@ -429,7 +442,7 @@ impl Client { slot: Slot, randao_reveal: RandaoReveal, graffiti: Bytes32, - ) -> BeaconBlock { + ) -> Result { unimplemented!("") } @@ -437,18 +450,21 @@ impl Client { slot: Slot, randao_reveal: RandaoReveal, graffiti: Bytes32, - ) -> BlindedBeaconBlock { + ) -> Result { unimplemented!("") } pub async fn get_attestation_data( slot: Slot, committee_index: CommitteeIndex, - ) -> AttestationData { + ) -> Result { unimplemented!("") } - pub async fn get_attestation_aggregate(attestation_data_root: Root, slot: Slot) -> Attestation { + pub async fn get_attestation_aggregate( + attestation_data_root: Root, + slot: Slot, + ) -> Result { unimplemented!("") } From a8d02b48c565720069513d5af89090e5b06e7e2e Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 5 May 2022 17:48:14 -0600 Subject: [PATCH 008/153] update deps --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d2367ac87..bcab1cf5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } reqwest = "0.11.10" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.79" +serde_json = "1.0.81" thiserror = "1.0.30" From af4fe0c2b863eb847497dbf7809d093a357f1aed Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 5 May 2022 18:02:21 -0600 Subject: [PATCH 009/153] add `serde` support for types --- src/api_client.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/serde.rs | 1 + 3 files changed, 54 insertions(+) create mode 100644 src/serde.rs diff --git a/src/api_client.rs b/src/api_client.rs index 319c8cfa7..a439c4fcd 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -12,18 +12,23 @@ use ethereum_consensus::primitives::{ BlsPublicKey, Bytes32, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, RandaoReveal, Root, Slot, ValidatorIndex, Version, }; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; pub enum Error {} pub struct Client {} +#[derive(Serialize, Deserialize)] pub struct GenesisDetails { + #[serde(with = "crate::serde::as_string")] pub genesis_time: u64, pub genesis_validators_root: Root, + #[serde(with = "crate::serde::as_hex")] pub genesis_fork_version: Version, } +#[derive(Serialize, Deserialize)] pub enum StateId { Head, Genesis, @@ -33,6 +38,7 @@ pub enum StateId { Root(Root), } +#[derive(Serialize, Deserialize)] pub enum BlockId { Head, Genesis, @@ -41,17 +47,20 @@ pub enum BlockId { Root(Root), } +#[derive(Serialize, Deserialize)] pub enum ExecutionStatus { Default, Optimistic, } +#[derive(Serialize, Deserialize)] pub struct FinalityCheckpoints { pub previous_justified: Checkpoint, pub current_justified: Checkpoint, pub finalized: Checkpoint, } +#[derive(Serialize, Deserialize)] pub enum ValidatorStatus { PendingInitialized, PendingQueued, @@ -79,15 +88,21 @@ pub struct ValidatorDescriptor { pub status: ValidatorStatus, } +#[derive(Serialize, Deserialize)] pub struct ValidatorSummary { + #[serde(with = "crate::serde::as_string")] pub index: ValidatorIndex, + #[serde(with = "crate::serde::as_string")] pub balance: Gwei, pub status: ValidatorStatus, pub validator: Validator, } +#[derive(Serialize, Deserialize)] pub struct BalanceSummary { + #[serde(with = "crate::serde::as_string")] pub index: ValidatorIndex, + #[serde(with = "crate::serde::as_string")] pub balance: Gwei, } @@ -97,17 +112,25 @@ pub struct CommitteeFilter { pub slot: Option, } +#[derive(Serialize, Deserialize)] pub struct CommitteeSummary { + #[serde(with = "crate::serde::as_string")] pub index: CommitteeIndex, + #[serde(with = "crate::serde::as_string")] pub slot: Slot, + #[serde(with = "crate::serde::collection_over_string")] pub validators: Vec, } +#[derive(Serialize, Deserialize)] pub struct SyncCommitteeSummary { + #[serde(with = "crate::serde::collection_over_string")] pub validators: Vec, + // TODO fix serde here pub validator_aggregates: Vec>, } +#[derive(Serialize, Deserialize)] pub struct BeaconHeaderSummary { pub root: Root, pub canonical: bool, @@ -124,6 +147,7 @@ pub enum EventTopic { ContributionAndProof, } +#[derive(Serialize, Deserialize)] pub struct NetworkIdentity { pub peer_id: PeerId, pub enr: Enr, @@ -132,6 +156,7 @@ pub struct NetworkIdentity { pub metadata: MetaData, } +#[derive(Serialize, Deserialize)] pub enum PeerState { Disconnected, Connecting, @@ -139,16 +164,19 @@ pub enum PeerState { Disconnecting, } +#[derive(Serialize, Deserialize)] pub enum ConnectionOrientation { Inbound, Outbound, } +#[derive(Serialize, Deserialize)] pub struct PeerDescriptor { pub state: PeerState, pub direction: ConnectionOrientation, } +#[derive(Serialize, Deserialize)] pub struct PeerDescription { pub peer_id: PeerId, pub enr: Enr, @@ -157,44 +185,66 @@ pub struct PeerDescription { pub direction: ConnectionOrientation, } +#[derive(Serialize, Deserialize)] pub struct PeerSummary { + #[serde(with = "crate::serde::as_string")] pub disconnected: usize, + #[serde(with = "crate::serde::as_string")] pub connecting: usize, + #[serde(with = "crate::serde::as_string")] pub connected: usize, + #[serde(with = "crate::serde::as_string")] pub disconnecting: usize, } +#[derive(Serialize, Deserialize)] pub struct SyncStatus { + #[serde(with = "crate::serde::as_string")] pub head_slot: Slot, + #[serde(with = "crate::serde::as_string")] pub sync_distance: usize, pub is_syncing: bool, } +#[derive(Serialize, Deserialize)] pub enum HealthStatus { Ready, Syncing, NotInitialized, } +#[derive(Serialize, Deserialize)] pub struct AttestationDuty { pub pubkey: BlsPublicKey, + #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, + #[serde(with = "crate::serde::as_string")] pub committee_index: CommitteeIndex, + #[serde(with = "crate::serde::as_string")] pub committee_length: usize, + #[serde(with = "crate::serde::as_string")] pub committees_at_slot: usize, + #[serde(with = "crate::serde::as_string")] pub validator_committee_index: usize, + #[serde(with = "crate::serde::as_string")] pub slot: Slot, } +#[derive(Serialize, Deserialize)] pub struct ProposerDuty { pub pubkey: BlsPublicKey, + #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, + #[serde(with = "crate::serde::as_string")] pub slot: Slot, } +#[derive(Serialize, Deserialize)] pub struct SyncCommitteeDuty { pub pubkey: BlsPublicKey, + #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, + #[serde(with = "crate::serde::collection_over_string")] pub validator_sync_committee_indices: Vec, } @@ -212,7 +262,9 @@ pub struct SyncCommitteeDescriptor { pub until_epoch: Epoch, } +#[derive(Serialize, Deserialize)] pub struct BeaconProposerRegistration { + #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, pub fee_recipient: ExecutionAddress, } diff --git a/src/lib.rs b/src/lib.rs index da111a4b7..6231945ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ mod api_client; +mod serde; pub use api_client::*; diff --git a/src/serde.rs b/src/serde.rs new file mode 100644 index 000000000..58c79833d --- /dev/null +++ b/src/serde.rs @@ -0,0 +1 @@ +pub(crate) use ethereum_consensus::serde::{as_hex, as_string, collection_over_string}; From 4cd48364ad102554914a403b67ae5c73665ff712 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Mon, 9 May 2022 12:52:14 -0700 Subject: [PATCH 010/153] add support for API "envelopes" --- examples/sketch.rs | 18 +++ src/api_client.rs | 269 ++----------------------------------------- src/lib.rs | 2 + src/types.rs | 278 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 310 insertions(+), 257 deletions(-) create mode 100644 examples/sketch.rs create mode 100644 src/types.rs diff --git a/examples/sketch.rs b/examples/sketch.rs new file mode 100644 index 000000000..67fc3bf75 --- /dev/null +++ b/examples/sketch.rs @@ -0,0 +1,18 @@ +use beacon_api_client::{ApiResponse, ConsensusVersion, VersionedApiResponse}; +use ethereum_consensus::bellatrix::mainnet::BlindedBeaconBlock; +use serde_json; + +fn main() { + let block: ApiResponse = ApiResponse { + data: BlindedBeaconBlock::default(), + }; + let block_repr = serde_json::to_string(&block).unwrap(); + println!("{block_repr}"); + + let block_with_version: VersionedApiResponse = VersionedApiResponse { + version: ConsensusVersion::Bellatrix, + data: BlindedBeaconBlock::default(), + }; + let block_with_version_repr = serde_json::to_string(&block_with_version).unwrap(); + println!("{block_with_version_repr}"); +} diff --git a/src/api_client.rs b/src/api_client.rs index a439c4fcd..9b544a680 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -1,274 +1,29 @@ +use crate::types::{ + AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, BlockId, + CommitteeDescriptor, CommitteeFilter, CommitteeSummary, EventTopic, FinalityCheckpoints, + GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, PeerDescriptor, PeerSummary, + ProposerDuty, PubkeyOrIndex, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, + SyncCommitteeSummary, SyncStatus, ValidatorDescriptor, ValidatorSummary, +}; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, }; use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; -use ethereum_consensus::networking::{Enr, MetaData, Multiaddr, PeerId}; +use ethereum_consensus::networking::Multiaddr; use ethereum_consensus::phase0::mainnet::{ - Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Checkpoint, Fork, - ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, - SignedVoluntaryExit, Validator, + Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, + ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, }; use ethereum_consensus::primitives::{ - BlsPublicKey, Bytes32, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, - RandaoReveal, Root, Slot, ValidatorIndex, Version, + Bytes32, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, RandaoReveal, Root, + Slot, ValidatorIndex, }; -use serde::{Deserialize, Serialize}; use std::collections::HashMap; pub enum Error {} pub struct Client {} -#[derive(Serialize, Deserialize)] -pub struct GenesisDetails { - #[serde(with = "crate::serde::as_string")] - pub genesis_time: u64, - pub genesis_validators_root: Root, - #[serde(with = "crate::serde::as_hex")] - pub genesis_fork_version: Version, -} - -#[derive(Serialize, Deserialize)] -pub enum StateId { - Head, - Genesis, - Finalized, - Justified, - Slot(Slot), - Root(Root), -} - -#[derive(Serialize, Deserialize)] -pub enum BlockId { - Head, - Genesis, - Finalized, - Slot(Slot), - Root(Root), -} - -#[derive(Serialize, Deserialize)] -pub enum ExecutionStatus { - Default, - Optimistic, -} - -#[derive(Serialize, Deserialize)] -pub struct FinalityCheckpoints { - pub previous_justified: Checkpoint, - pub current_justified: Checkpoint, - pub finalized: Checkpoint, -} - -#[derive(Serialize, Deserialize)] -pub enum ValidatorStatus { - PendingInitialized, - PendingQueued, - ActiveOngoing, - ActiveExiting, - ActiveSlashed, - ExitedUnslashed, - ExitedSlashed, - WithdrawalPossible, - WithdrawalDone, - // TODO what are these? - Active, - Pending, - Exited, - Withdrawal, -} - -pub enum PubkeyOrIndex { - Pubkey(BlsPublicKey), - Index(ValidatorIndex), -} - -pub struct ValidatorDescriptor { - pub pubkey_or_index: PubkeyOrIndex, - pub status: ValidatorStatus, -} - -#[derive(Serialize, Deserialize)] -pub struct ValidatorSummary { - #[serde(with = "crate::serde::as_string")] - pub index: ValidatorIndex, - #[serde(with = "crate::serde::as_string")] - pub balance: Gwei, - pub status: ValidatorStatus, - pub validator: Validator, -} - -#[derive(Serialize, Deserialize)] -pub struct BalanceSummary { - #[serde(with = "crate::serde::as_string")] - pub index: ValidatorIndex, - #[serde(with = "crate::serde::as_string")] - pub balance: Gwei, -} - -pub struct CommitteeFilter { - pub epoch: Option, - pub index: Option, - pub slot: Option, -} - -#[derive(Serialize, Deserialize)] -pub struct CommitteeSummary { - #[serde(with = "crate::serde::as_string")] - pub index: CommitteeIndex, - #[serde(with = "crate::serde::as_string")] - pub slot: Slot, - #[serde(with = "crate::serde::collection_over_string")] - pub validators: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct SyncCommitteeSummary { - #[serde(with = "crate::serde::collection_over_string")] - pub validators: Vec, - // TODO fix serde here - pub validator_aggregates: Vec>, -} - -#[derive(Serialize, Deserialize)] -pub struct BeaconHeaderSummary { - pub root: Root, - pub canonical: bool, - pub signed_header: SignedBeaconBlockHeader, -} - -pub enum EventTopic { - Head, - Block, - Attestation, - VoluntaryExit, - FinalizedCheckpoint, - ChainReorg, - ContributionAndProof, -} - -#[derive(Serialize, Deserialize)] -pub struct NetworkIdentity { - pub peer_id: PeerId, - pub enr: Enr, - pub p2p_addresses: Vec, - pub discovery_addresses: Vec, - pub metadata: MetaData, -} - -#[derive(Serialize, Deserialize)] -pub enum PeerState { - Disconnected, - Connecting, - Connected, - Disconnecting, -} - -#[derive(Serialize, Deserialize)] -pub enum ConnectionOrientation { - Inbound, - Outbound, -} - -#[derive(Serialize, Deserialize)] -pub struct PeerDescriptor { - pub state: PeerState, - pub direction: ConnectionOrientation, -} - -#[derive(Serialize, Deserialize)] -pub struct PeerDescription { - pub peer_id: PeerId, - pub enr: Enr, - pub last_seen_p2p_address: Multiaddr, - pub state: PeerState, - pub direction: ConnectionOrientation, -} - -#[derive(Serialize, Deserialize)] -pub struct PeerSummary { - #[serde(with = "crate::serde::as_string")] - pub disconnected: usize, - #[serde(with = "crate::serde::as_string")] - pub connecting: usize, - #[serde(with = "crate::serde::as_string")] - pub connected: usize, - #[serde(with = "crate::serde::as_string")] - pub disconnecting: usize, -} - -#[derive(Serialize, Deserialize)] -pub struct SyncStatus { - #[serde(with = "crate::serde::as_string")] - pub head_slot: Slot, - #[serde(with = "crate::serde::as_string")] - pub sync_distance: usize, - pub is_syncing: bool, -} - -#[derive(Serialize, Deserialize)] -pub enum HealthStatus { - Ready, - Syncing, - NotInitialized, -} - -#[derive(Serialize, Deserialize)] -pub struct AttestationDuty { - pub pubkey: BlsPublicKey, - #[serde(with = "crate::serde::as_string")] - pub validator_index: ValidatorIndex, - #[serde(with = "crate::serde::as_string")] - pub committee_index: CommitteeIndex, - #[serde(with = "crate::serde::as_string")] - pub committee_length: usize, - #[serde(with = "crate::serde::as_string")] - pub committees_at_slot: usize, - #[serde(with = "crate::serde::as_string")] - pub validator_committee_index: usize, - #[serde(with = "crate::serde::as_string")] - pub slot: Slot, -} - -#[derive(Serialize, Deserialize)] -pub struct ProposerDuty { - pub pubkey: BlsPublicKey, - #[serde(with = "crate::serde::as_string")] - pub validator_index: ValidatorIndex, - #[serde(with = "crate::serde::as_string")] - pub slot: Slot, -} - -#[derive(Serialize, Deserialize)] -pub struct SyncCommitteeDuty { - pub pubkey: BlsPublicKey, - #[serde(with = "crate::serde::as_string")] - pub validator_index: ValidatorIndex, - #[serde(with = "crate::serde::collection_over_string")] - pub validator_sync_committee_indices: Vec, -} - -pub struct CommitteeDescriptor { - pub validator_index: ValidatorIndex, - pub committee_index: CommitteeIndex, - pub committees_at_slot: usize, - pub slot: Slot, - pub is_aggregator: bool, -} - -pub struct SyncCommitteeDescriptor { - pub validator_index: ValidatorIndex, - pub sync_committee_indices: Vec, - pub until_epoch: Epoch, -} - -#[derive(Serialize, Deserialize)] -pub struct BeaconProposerRegistration { - #[serde(with = "crate::serde::as_string")] - pub validator_index: ValidatorIndex, - pub fee_recipient: ExecutionAddress, -} - impl Client { /* beacon namespace */ pub async fn get_genesis_details(&self) -> Result { diff --git a/src/lib.rs b/src/lib.rs index 6231945ea..a9fe3b91b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ mod api_client; mod serde; +mod types; pub use api_client::*; +pub use types::*; diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 000000000..82f41775b --- /dev/null +++ b/src/types.rs @@ -0,0 +1,278 @@ +use ethereum_consensus::networking::{Enr, MetaData, Multiaddr, PeerId}; +use ethereum_consensus::phase0::mainnet::{Checkpoint, SignedBeaconBlockHeader, Validator}; +use ethereum_consensus::primitives::{ + BlsPublicKey, CommitteeIndex, Epoch, ExecutionAddress, Gwei, Root, Slot, ValidatorIndex, + Version, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct GenesisDetails { + #[serde(with = "crate::serde::as_string")] + pub genesis_time: u64, + pub genesis_validators_root: Root, + #[serde(with = "crate::serde::as_hex")] + pub genesis_fork_version: Version, +} + +#[derive(Serialize, Deserialize)] +pub enum StateId { + Head, + Genesis, + Finalized, + Justified, + Slot(Slot), + Root(Root), +} + +#[derive(Serialize, Deserialize)] +pub enum BlockId { + Head, + Genesis, + Finalized, + Slot(Slot), + Root(Root), +} + +#[derive(Serialize, Deserialize)] +enum ExecutionStatus { + Default, + Optimistic, +} + +#[derive(Serialize, Deserialize)] +pub struct FinalityCheckpoints { + pub previous_justified: Checkpoint, + pub current_justified: Checkpoint, + pub finalized: Checkpoint, +} + +#[derive(Serialize, Deserialize)] +pub enum ValidatorStatus { + PendingInitialized, + PendingQueued, + ActiveOngoing, + ActiveExiting, + ActiveSlashed, + ExitedUnslashed, + ExitedSlashed, + WithdrawalPossible, + WithdrawalDone, + // TODO what are these? + Active, + Pending, + Exited, + Withdrawal, +} + +pub enum PubkeyOrIndex { + Pubkey(BlsPublicKey), + Index(ValidatorIndex), +} + +pub struct ValidatorDescriptor { + pub pubkey_or_index: PubkeyOrIndex, + pub status: ValidatorStatus, +} + +#[derive(Serialize, Deserialize)] +pub struct ValidatorSummary { + #[serde(with = "crate::serde::as_string")] + pub index: ValidatorIndex, + #[serde(with = "crate::serde::as_string")] + pub balance: Gwei, + pub status: ValidatorStatus, + pub validator: Validator, +} + +#[derive(Serialize, Deserialize)] +pub struct BalanceSummary { + #[serde(with = "crate::serde::as_string")] + pub index: ValidatorIndex, + #[serde(with = "crate::serde::as_string")] + pub balance: Gwei, +} + +pub struct CommitteeFilter { + pub epoch: Option, + pub index: Option, + pub slot: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct CommitteeSummary { + #[serde(with = "crate::serde::as_string")] + pub index: CommitteeIndex, + #[serde(with = "crate::serde::as_string")] + pub slot: Slot, + #[serde(with = "crate::serde::collection_over_string")] + pub validators: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct SyncCommitteeSummary { + #[serde(with = "crate::serde::collection_over_string")] + pub validators: Vec, + // TODO fix serde here + pub validator_aggregates: Vec>, +} + +#[derive(Serialize, Deserialize)] +pub struct BeaconHeaderSummary { + pub root: Root, + pub canonical: bool, + pub signed_header: SignedBeaconBlockHeader, +} + +pub enum EventTopic { + Head, + Block, + Attestation, + VoluntaryExit, + FinalizedCheckpoint, + ChainReorg, + ContributionAndProof, +} + +#[derive(Serialize, Deserialize)] +pub struct NetworkIdentity { + pub peer_id: PeerId, + pub enr: Enr, + pub p2p_addresses: Vec, + pub discovery_addresses: Vec, + pub metadata: MetaData, +} + +#[derive(Serialize, Deserialize)] +pub enum PeerState { + Disconnected, + Connecting, + Connected, + Disconnecting, +} + +#[derive(Serialize, Deserialize)] +pub enum ConnectionOrientation { + Inbound, + Outbound, +} + +#[derive(Serialize, Deserialize)] +pub struct PeerDescriptor { + pub state: PeerState, + pub direction: ConnectionOrientation, +} + +#[derive(Serialize, Deserialize)] +pub struct PeerDescription { + pub peer_id: PeerId, + pub enr: Enr, + pub last_seen_p2p_address: Multiaddr, + pub state: PeerState, + pub direction: ConnectionOrientation, +} + +#[derive(Serialize, Deserialize)] +pub struct PeerSummary { + #[serde(with = "crate::serde::as_string")] + pub disconnected: usize, + #[serde(with = "crate::serde::as_string")] + pub connecting: usize, + #[serde(with = "crate::serde::as_string")] + pub connected: usize, + #[serde(with = "crate::serde::as_string")] + pub disconnecting: usize, +} + +#[derive(Serialize, Deserialize)] +pub struct SyncStatus { + #[serde(with = "crate::serde::as_string")] + pub head_slot: Slot, + #[serde(with = "crate::serde::as_string")] + pub sync_distance: usize, + pub is_syncing: bool, +} + +#[derive(Serialize, Deserialize)] +pub enum HealthStatus { + Ready, + Syncing, + NotInitialized, +} + +#[derive(Serialize, Deserialize)] +pub struct AttestationDuty { + pub pubkey: BlsPublicKey, + #[serde(with = "crate::serde::as_string")] + pub validator_index: ValidatorIndex, + #[serde(with = "crate::serde::as_string")] + pub committee_index: CommitteeIndex, + #[serde(with = "crate::serde::as_string")] + pub committee_length: usize, + #[serde(with = "crate::serde::as_string")] + pub committees_at_slot: usize, + #[serde(with = "crate::serde::as_string")] + pub validator_committee_index: usize, + #[serde(with = "crate::serde::as_string")] + pub slot: Slot, +} + +#[derive(Serialize, Deserialize)] +pub struct ProposerDuty { + pub pubkey: BlsPublicKey, + #[serde(with = "crate::serde::as_string")] + pub validator_index: ValidatorIndex, + #[serde(with = "crate::serde::as_string")] + pub slot: Slot, +} + +#[derive(Serialize, Deserialize)] +pub struct SyncCommitteeDuty { + pub pubkey: BlsPublicKey, + #[serde(with = "crate::serde::as_string")] + pub validator_index: ValidatorIndex, + #[serde(with = "crate::serde::collection_over_string")] + pub validator_sync_committee_indices: Vec, +} + +pub struct CommitteeDescriptor { + pub validator_index: ValidatorIndex, + pub committee_index: CommitteeIndex, + pub committees_at_slot: usize, + pub slot: Slot, + pub is_aggregator: bool, +} + +pub struct SyncCommitteeDescriptor { + pub validator_index: ValidatorIndex, + pub sync_committee_indices: Vec, + pub until_epoch: Epoch, +} + +#[derive(Serialize, Deserialize)] +pub struct BeaconProposerRegistration { + #[serde(with = "crate::serde::as_string")] + pub validator_index: ValidatorIndex, + pub fee_recipient: ExecutionAddress, +} + +#[derive(Serialize, Deserialize)] +#[serde(bound = "T: Serialize + serde::de::DeserializeOwned")] +pub struct ApiResponse { + pub data: T, +} + +#[derive(Serialize, Deserialize)] +#[serde(bound = "T: Serialize + serde::de::DeserializeOwned")] +pub struct VersionedApiResponse { + pub version: ConsensusVersion, + pub data: T, +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ConsensusVersion { + Phase0, + Altair, + Bellatrix, +} From a290f94b160b0bc1d3482c525fcf1f91850b35a9 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 6 May 2022 11:59:11 -0700 Subject: [PATCH 011/153] implement skeleton to support the builder API --- src/api_client.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 9b544a680..de4aab283 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -9,6 +9,7 @@ use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, }; use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; +use ethereum_consensus::builder::SignedValidatorRegistration; use ethereum_consensus::networking::Multiaddr; use ethereum_consensus::phase0::mainnet::{ Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, @@ -307,9 +308,16 @@ impl Client { Ok(()) } - pub async fn register_proposers( + pub async fn prepare_proposers( registrations: &[BeaconProposerRegistration], ) -> Result<(), Error> { Ok(()) } + + // endpoint for builder registrations + pub async fn register_validators_with_builders( + registrations: &[SignedValidatorRegistration], + ) -> Result<(), Error> { + Ok(()) + } } From dad260eae8954ca4bc7f8facb34798630a5fd9da Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 10 May 2022 14:34:39 -0700 Subject: [PATCH 012/153] add API error types --- examples/sketch.rs | 22 +++++++++++++++++++--- src/error.rs | 16 ++++++++++++++++ src/lib.rs | 2 ++ src/types.rs | 19 ++++++++++++++----- 4 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/error.rs diff --git a/examples/sketch.rs b/examples/sketch.rs index 67fc3bf75..184748983 100644 --- a/examples/sketch.rs +++ b/examples/sketch.rs @@ -1,18 +1,34 @@ -use beacon_api_client::{ApiResponse, ConsensusVersion, VersionedApiResponse}; +use beacon_api_client::{ApiError, ApiResult, ConsensusVersion, Value, VersionedValue}; use ethereum_consensus::bellatrix::mainnet::BlindedBeaconBlock; use serde_json; fn main() { - let block: ApiResponse = ApiResponse { + let block = Value { data: BlindedBeaconBlock::default(), }; let block_repr = serde_json::to_string(&block).unwrap(); println!("{block_repr}"); - let block_with_version: VersionedApiResponse = VersionedApiResponse { + let block_with_version = VersionedValue { version: ConsensusVersion::Bellatrix, data: BlindedBeaconBlock::default(), }; let block_with_version_repr = serde_json::to_string(&block_with_version).unwrap(); println!("{block_with_version_repr}"); + + let full_success_response = ApiResult::Ok(block_with_version); + let str_repr = serde_json::to_string(&full_success_response).unwrap(); + println!("{}", str_repr); + + let recovered_success: ApiResult> = + serde_json::from_str(&str_repr).unwrap(); + println!("{:#?}", recovered_success); + + let full_error_response: ApiResult> = + ApiResult::Err(ApiError::from((404, "some failure"))); + let str_repr = serde_json::to_string(&full_error_response).unwrap(); + println!("{str_repr}"); + + let recovered_error: ApiResult = serde_json::from_str(&str_repr).unwrap(); + println!("{:#?}", recovered_error); } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 000000000..6aae11497 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct ApiError { + code: u16, + message: String, +} + +impl<'a> From<(u16, &'a str)> for ApiError { + fn from((code, message): (u16, &'a str)) -> Self { + Self { + code, + message: message.to_string(), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index a9fe3b91b..5cb8b86c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ mod api_client; +mod error; mod serde; mod types; pub use api_client::*; +pub use error::ApiError; pub use types::*; diff --git a/src/types.rs b/src/types.rs index 82f41775b..596295143 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,3 +1,4 @@ +use crate::error::ApiError; use ethereum_consensus::networking::{Enr, MetaData, Multiaddr, PeerId}; use ethereum_consensus::phase0::mainnet::{Checkpoint, SignedBeaconBlockHeader, Validator}; use ethereum_consensus::primitives::{ @@ -256,23 +257,31 @@ pub struct BeaconProposerRegistration { pub fee_recipient: ExecutionAddress, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] #[serde(bound = "T: Serialize + serde::de::DeserializeOwned")] -pub struct ApiResponse { +pub struct Value { pub data: T, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] #[serde(bound = "T: Serialize + serde::de::DeserializeOwned")] -pub struct VersionedApiResponse { +pub struct VersionedValue { pub version: ConsensusVersion, pub data: T, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "lowercase")] pub enum ConsensusVersion { Phase0, Altair, Bellatrix, } + +#[derive(Serialize, Deserialize, Debug)] +#[serde(bound = "T: Serialize + serde::de::DeserializeOwned")] +#[serde(untagged)] +pub enum ApiResult { + Ok(T), + Err(ApiError), +} From d0b940f24955a7f9b3599956380693268fb6257e Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 10 May 2022 17:08:46 -0700 Subject: [PATCH 013/153] allow user extension for foreign methods --- Cargo.toml | 3 +- src/api_client.rs | 72 +++++++++++++++++++++++++++++++++++++++++------ src/error.rs | 14 +++++++-- 3 files changed, 77 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bcab1cf5a..130519b1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,8 @@ license = "MIT OR Apache-2.0" tokio = { version = "1.0", features = ["full"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } -reqwest = "0.11.10" +reqwest = { version = "0.11.10", features = ["json"] } +url = "2.2.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" diff --git a/src/api_client.rs b/src/api_client.rs index de4aab283..50e54ffcb 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -1,9 +1,10 @@ +use crate::error::ApiError; use crate::types::{ - AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, BlockId, - CommitteeDescriptor, CommitteeFilter, CommitteeSummary, EventTopic, FinalityCheckpoints, - GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, PeerDescriptor, PeerSummary, - ProposerDuty, PubkeyOrIndex, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, - SyncCommitteeSummary, SyncStatus, ValidatorDescriptor, ValidatorSummary, + ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, + BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, EventTopic, + FinalityCheckpoints, GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, + PeerDescriptor, PeerSummary, ProposerDuty, PubkeyOrIndex, StateId, SyncCommitteeDescriptor, + SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorDescriptor, ValidatorSummary, }; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, @@ -19,16 +20,69 @@ use ethereum_consensus::primitives::{ Bytes32, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, RandaoReveal, Root, Slot, ValidatorIndex, }; +use reqwest; use std::collections::HashMap; +use thiserror::Error; +use url::{ParseError, Url}; + +#[derive(Debug, Error)] +pub enum Error { + #[error("could not parse URL: {0}")] + Url(#[from] ParseError), + #[error("could not send request: {0}")] + Http(#[from] reqwest::Error), + #[error("error from API: {0}")] + Api(#[from] ApiError), +} -pub enum Error {} - -pub struct Client {} +pub struct Client { + http: reqwest::Client, + endpoint: Url, +} impl Client { + pub fn new>(client: &reqwest::Client, endpoint: U) -> Self { + Self { + http: client.clone(), + endpoint: endpoint.into(), + } + } + + pub async fn get( + &self, + path: &str, + ) -> Result { + let target = self.endpoint.join(path)?; + let result: ApiResult = self.http.get(target).send().await?.json().await?; + match result { + ApiResult::Ok(result) => Ok(result), + ApiResult::Err(err) => Err(err.into()), + } + } + + pub async fn post( + &self, + path: &str, + argument: &T, + ) -> Result { + let target = self.endpoint.join(path)?; + let result: ApiResult = self + .http + .post(target) + .json(argument) + .send() + .await? + .json() + .await?; + match result { + ApiResult::Ok(result) => Ok(result), + ApiResult::Err(err) => Err(err.into()), + } + } + /* beacon namespace */ pub async fn get_genesis_details(&self) -> Result { - unimplemented!("") + self.get("/eth/v1/beacon/genesis").await } pub async fn get_state_root(id: StateId) -> Result { diff --git a/src/error.rs b/src/error.rs index 6aae11497..8ba6b405f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,11 +1,21 @@ use serde::{Deserialize, Serialize}; +use std::error::Error; +use std::fmt; #[derive(Serialize, Deserialize, Debug)] pub struct ApiError { - code: u16, - message: String, + pub code: u16, + pub message: String, } +impl fmt::Display for ApiError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "status {}: {}", self.code, self.message) + } +} + +impl Error for ApiError {} + impl<'a> From<(u16, &'a str)> for ApiError { fn from((code, message): (u16, &'a str)) -> Self { Self { From 050edc3dc2b3479f5d2fe82a2ddeaba88f4515e7 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 10 May 2022 17:14:16 -0700 Subject: [PATCH 014/153] add convenience method with default http client --- src/api_client.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 50e54ffcb..e57fd7935 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -41,13 +41,18 @@ pub struct Client { } impl Client { - pub fn new>(client: &reqwest::Client, endpoint: U) -> Self { + pub fn new_with_client>(client: reqwest::Client, endpoint: U) -> Self { Self { http: client.clone(), endpoint: endpoint.into(), } } + pub fn new>(endpoint: U) -> Self { + let client = reqwest::Client::new(); + Self::new_with_client(client, endpoint) + } + pub async fn get( &self, path: &str, From 7445971a3301d5bebed8e9d0da0e5985a94f9dbc Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 10 May 2022 18:21:43 -0700 Subject: [PATCH 015/153] let in and out types differ for `post` --- src/api_client.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index e57fd7935..42c1e8089 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -65,13 +65,16 @@ impl Client { } } - pub async fn post( + pub async fn post< + T: serde::Serialize + serde::de::DeserializeOwned, + U: serde::Serialize + serde::de::DeserializeOwned, + >( &self, path: &str, argument: &T, - ) -> Result { + ) -> Result { let target = self.endpoint.join(path)?; - let result: ApiResult = self + let result: ApiResult = self .http .post(target) .json(argument) From 46eb96fa7898245b3480115423d77aab5f3756a9 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 11 May 2022 20:39:35 -0600 Subject: [PATCH 016/153] expose underlying HTTP client --- src/api_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 42c1e8089..3e995f9be 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -36,7 +36,7 @@ pub enum Error { } pub struct Client { - http: reqwest::Client, + pub http: reqwest::Client, endpoint: Url, } From cf11b240eb72999e1632a540bb6a576afb5fdb79 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 11 May 2022 20:47:36 -0600 Subject: [PATCH 017/153] expose HTTP method instead of client --- src/api_client.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 3e995f9be..d37093f8f 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -36,7 +36,7 @@ pub enum Error { } pub struct Client { - pub http: reqwest::Client, + http: reqwest::Client, endpoint: Url, } @@ -73,21 +73,23 @@ impl Client { path: &str, argument: &T, ) -> Result { - let target = self.endpoint.join(path)?; - let result: ApiResult = self - .http - .post(target) - .json(argument) - .send() - .await? - .json() - .await?; + let result: ApiResult = self.http_post(path, argument).await?.json().await?; match result { ApiResult::Ok(result) => Ok(result), ApiResult::Err(err) => Err(err.into()), } } + pub async fn http_post( + &self, + path: &str, + argument: &T, + ) -> Result { + let target = self.endpoint.join(path)?; + let response = self.http.post(target).json(argument).send().await?; + Ok(response) + } + /* beacon namespace */ pub async fn get_genesis_details(&self) -> Result { self.get("/eth/v1/beacon/genesis").await From 3f8e5a47b13888d3fc0f66cc817d9ef881c621d0 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 11 May 2022 20:51:57 -0600 Subject: [PATCH 018/153] expose HTTP GET as well --- src/api_client.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index d37093f8f..42c011610 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -57,14 +57,19 @@ impl Client { &self, path: &str, ) -> Result { - let target = self.endpoint.join(path)?; - let result: ApiResult = self.http.get(target).send().await?.json().await?; + let result: ApiResult = self.http_get(path).await?.json().await?; match result { ApiResult::Ok(result) => Ok(result), ApiResult::Err(err) => Err(err.into()), } } + pub async fn http_get(&self, path: &str) -> Result { + let target = self.endpoint.join(path)?; + let response = self.http.get(target).send().await?; + Ok(response) + } + pub async fn post< T: serde::Serialize + serde::de::DeserializeOwned, U: serde::Serialize + serde::de::DeserializeOwned, From ae8c05a6d550312e0ef570cf4052d0d88b38bb75 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 13 May 2022 14:11:53 -0600 Subject: [PATCH 019/153] use `http::StatusCode` type over `u16` type --- Cargo.toml | 1 + src/error.rs | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 130519b1a..5451d54a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } reqwest = { version = "0.11.10", features = ["json"] } url = "2.2.2" +http = "0.2.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" diff --git a/src/error.rs b/src/error.rs index 8ba6b405f..ebf7ff3f1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,10 +1,32 @@ +use http::StatusCode; use serde::{Deserialize, Serialize}; use std::error::Error; use std::fmt; +mod as_u16 { + use http::StatusCode; + use serde::{de::Deserializer, Deserialize, Serializer}; + + pub fn serialize(x: &StatusCode, s: S) -> Result + where + S: Serializer, + { + s.serialize_u16(x.as_u16()) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value: u16 = Deserialize::deserialize(deserializer)?; + StatusCode::from_u16(value).map_err(serde::de::Error::custom) + } +} + #[derive(Serialize, Deserialize, Debug)] pub struct ApiError { - pub code: u16, + #[serde(with = "as_u16")] + pub code: StatusCode, pub message: String, } @@ -16,11 +38,14 @@ impl fmt::Display for ApiError { impl Error for ApiError {} -impl<'a> From<(u16, &'a str)> for ApiError { - fn from((code, message): (u16, &'a str)) -> Self { - Self { +impl<'a> TryFrom<(u16, &'a str)> for ApiError { + type Error = http::status::InvalidStatusCode; + + fn try_from((code, message): (u16, &'a str)) -> Result { + let code = StatusCode::from_u16(code)?; + Ok(Self { code, message: message.to_string(), - } + }) } } From c395e0faa7a5a632e3aa132cc8a8727deaedda6a Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 13 May 2022 15:22:48 -0600 Subject: [PATCH 020/153] turn API errors into crate errors --- src/api_client.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 42c011610..eb8b060a8 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -66,7 +66,7 @@ impl Client { pub async fn http_get(&self, path: &str) -> Result { let target = self.endpoint.join(path)?; - let response = self.http.get(target).send().await?; + let response = self.http.get(target).send().await?.error_for_status()?; Ok(response) } @@ -91,7 +91,13 @@ impl Client { argument: &T, ) -> Result { let target = self.endpoint.join(path)?; - let response = self.http.post(target).json(argument).send().await?; + let response = self + .http + .post(target) + .json(argument) + .send() + .await? + .error_for_status()?; Ok(response) } From 1c61087c42fd5c9c6f30a772eb2901f045907c3b Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 14 May 2022 08:20:00 -0600 Subject: [PATCH 021/153] rollback previous commit --- src/api_client.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index eb8b060a8..42c011610 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -66,7 +66,7 @@ impl Client { pub async fn http_get(&self, path: &str) -> Result { let target = self.endpoint.join(path)?; - let response = self.http.get(target).send().await?.error_for_status()?; + let response = self.http.get(target).send().await?; Ok(response) } @@ -91,13 +91,7 @@ impl Client { argument: &T, ) -> Result { let target = self.endpoint.join(path)?; - let response = self - .http - .post(target) - .json(argument) - .send() - .await? - .error_for_status()?; + let response = self.http.post(target).json(argument).send().await?; Ok(response) } From 66e07dc86a2563af1b1a7d860d8cde8935de7dd8 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 14 May 2022 08:34:06 -0600 Subject: [PATCH 022/153] add error utility --- src/api_client.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/api_client.rs b/src/api_client.rs index 42c011610..2c1edad35 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -35,6 +35,13 @@ pub enum Error { Api(#[from] ApiError), } +pub async fn api_error_or_ok(response: reqwest::Response, ok: T) -> Result { + match response.json::().await { + Ok(err) => Err(Error::Api(err)), + Err(_) => Ok(ok), + } +} + pub struct Client { http: reqwest::Client, endpoint: Url, From 19807935c1d50e3e7ce76e2f0414a4bbe1832339 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 14 May 2022 08:43:04 -0600 Subject: [PATCH 023/153] clean up implementation of `api_error_or_ok` --- src/api_client.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 2c1edad35..80f0a1df4 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -35,10 +35,12 @@ pub enum Error { Api(#[from] ApiError), } -pub async fn api_error_or_ok(response: reqwest::Response, ok: T) -> Result { - match response.json::().await { - Ok(err) => Err(Error::Api(err)), - Err(_) => Ok(ok), +pub async fn api_error_or_ok(response: reqwest::Response) -> Result<(), Error> { + if response.status() == reqwest::StatusCode::OK { + Ok(()) + } else { + let api_err = response.json::().await?; + Err(Error::Api(api_err)) } } From 16297e65b4da65844c0841cc7fb1d731b522ad83 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 14 May 2022 16:28:23 -0600 Subject: [PATCH 024/153] expose `http::StatusCode` to avoid spilling dep --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5cb8b86c9..d14900344 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,5 @@ mod types; pub use api_client::*; pub use error::ApiError; pub use types::*; + +pub use http::StatusCode; From f8c8d10cb27ed4edf7958e0458460802bcafda13 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 14 May 2022 17:16:35 -0600 Subject: [PATCH 025/153] rollback previous commit --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d14900344..5cb8b86c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,5 +6,3 @@ mod types; pub use api_client::*; pub use error::ApiError; pub use types::*; - -pub use http::StatusCode; From b426d13c3816b4b4cc99acbe1b0799f129314537 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 14 May 2022 17:17:03 -0600 Subject: [PATCH 026/153] some refactoring --- src/error.rs | 24 ++---------------------- src/serde.rs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/error.rs b/src/error.rs index ebf7ff3f1..6dede418a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,36 +3,16 @@ use serde::{Deserialize, Serialize}; use std::error::Error; use std::fmt; -mod as_u16 { - use http::StatusCode; - use serde::{de::Deserializer, Deserialize, Serializer}; - - pub fn serialize(x: &StatusCode, s: S) -> Result - where - S: Serializer, - { - s.serialize_u16(x.as_u16()) - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let value: u16 = Deserialize::deserialize(deserializer)?; - StatusCode::from_u16(value).map_err(serde::de::Error::custom) - } -} - #[derive(Serialize, Deserialize, Debug)] pub struct ApiError { - #[serde(with = "as_u16")] + #[serde(with = "crate::serde::as_u16")] pub code: StatusCode, pub message: String, } impl fmt::Display for ApiError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "status {}: {}", self.code, self.message) + write!(f, "{}", self.message) } } diff --git a/src/serde.rs b/src/serde.rs index 58c79833d..87403fa79 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1 +1,21 @@ pub(crate) use ethereum_consensus::serde::{as_hex, as_string, collection_over_string}; + +pub(crate) mod as_u16 { + use http::StatusCode; + use serde::{de::Deserializer, Deserialize, Serializer}; + + pub fn serialize(x: &StatusCode, s: S) -> Result + where + S: Serializer, + { + s.serialize_u16(x.as_u16()) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value: u16 = Deserialize::deserialize(deserializer)?; + StatusCode::from_u16(value).map_err(serde::de::Error::custom) + } +} From 92f976ee8ae592f925a2405bea88640edf185330 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 14 May 2022 17:17:10 -0600 Subject: [PATCH 027/153] add example for `http_post` --- examples/post.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 examples/post.rs diff --git a/examples/post.rs b/examples/post.rs new file mode 100644 index 000000000..5a73df425 --- /dev/null +++ b/examples/post.rs @@ -0,0 +1,16 @@ +use beacon_api_client::Client; +use ethereum_consensus::builder::SignedValidatorRegistration; +use url::Url; + +#[tokio::main] +async fn main() { + let client = Client::new(Url::parse("http://localhost:8080").unwrap()); + let data = SignedValidatorRegistration::default(); + let response = client + .http_post("/eth/v1/builder/validators", &data) + .await + .unwrap(); + dbg!(&response); + dbg!(&response.status()); + dbg!(&response.text().await); +} From c2b8999614e1abad4c830185cf81568a2e119a01 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 14 May 2022 17:18:34 -0600 Subject: [PATCH 028/153] fix example --- examples/sketch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sketch.rs b/examples/sketch.rs index 184748983..8860c044f 100644 --- a/examples/sketch.rs +++ b/examples/sketch.rs @@ -25,7 +25,7 @@ fn main() { println!("{:#?}", recovered_success); let full_error_response: ApiResult> = - ApiResult::Err(ApiError::from((404, "some failure"))); + ApiResult::Err(ApiError::try_from((404, "some failure")).unwrap()); let str_repr = serde_json::to_string(&full_error_response).unwrap(); println!("{str_repr}"); From c13609df4218598399345cad58ac2dc94f064213 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 14 May 2022 17:20:14 -0600 Subject: [PATCH 029/153] add justfile --- justfile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 justfile diff --git a/justfile b/justfile new file mode 100644 index 000000000..1ebb930bd --- /dev/null +++ b/justfile @@ -0,0 +1,9 @@ +test: + cargo test +fmt: + cargo fmt +lint: fmt + cargo clippy +build: + cargo build +run-ci: lint build test From f906b9a53e65867dc94e2b6b83a7a1f654b4d71c Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 20 May 2022 09:32:36 -0600 Subject: [PATCH 030/153] add CI and whitespace in README --- .github/workflows/rust.yml | 28 ++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 29 insertions(+) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 000000000..fba840e7e --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,28 @@ +name: Rust + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Install + run: rustup update stable + - name: Format + run: cargo fmt -- --check + - name: Run clippy + run: cargo clippy --verbose -- -D warnings + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/README.md b/README.md index e1546e391..f307a4285 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # beacon-api-client + A client for the Ethereum beacon node APIs From 0b4088230426371df086e95076e229d8b2006205 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 20 May 2022 09:41:59 -0600 Subject: [PATCH 031/153] address clippy feedback --- src/api_client.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 80f0a1df4..b9b67e1dc 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -1,3 +1,6 @@ +// TODO: remove once things are all used +#![allow(unused_variables)] + use crate::error::ApiError; use crate::types::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, @@ -20,7 +23,6 @@ use ethereum_consensus::primitives::{ Bytes32, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, RandaoReveal, Root, Slot, ValidatorIndex, }; -use reqwest; use std::collections::HashMap; use thiserror::Error; use url::{ParseError, Url}; @@ -52,7 +54,7 @@ pub struct Client { impl Client { pub fn new_with_client>(client: reqwest::Client, endpoint: U) -> Self { Self { - http: client.clone(), + http: client, endpoint: endpoint.into(), } } From cfb641a62f9099b12cb8e9b305edc842b9759a2b Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Mon, 23 May 2022 10:08:07 +0530 Subject: [PATCH 032/153] Fix dependency name --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5451d54a1..ee6d75de1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ serde_json = "1.0.81" thiserror = "1.0.30" -ethereum_consensus = { git = "https://github.com/ralexstokes/ethereum_consensus" } \ No newline at end of file +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum_consensus" } \ No newline at end of file From 8d3decff24a4a29377b06749d85e311532d446c4 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 22 May 2022 23:50:31 -0600 Subject: [PATCH 033/153] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ee6d75de1..fa4fe3ad0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ serde_json = "1.0.81" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum_consensus" } \ No newline at end of file +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus" } \ No newline at end of file From dd810e4adac2326e76930f308a2d0ac0243232cc Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 27 May 2022 16:27:38 +0100 Subject: [PATCH 034/153] implement get_state_root() and get_fork() --- src/api_client.rs | 37 +++++++++++++++++++++++++++++++++---- src/main.rs | 25 +++++++++++++++++++++++++ src/types.rs | 8 ++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 src/main.rs diff --git a/src/api_client.rs b/src/api_client.rs index b9b67e1dc..116507af1 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -8,6 +8,7 @@ use crate::types::{ FinalityCheckpoints, GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, PeerDescriptor, PeerSummary, ProposerDuty, PubkeyOrIndex, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorDescriptor, ValidatorSummary, + Value, RootData }; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, @@ -111,12 +112,40 @@ impl Client { self.get("/eth/v1/beacon/genesis").await } - pub async fn get_state_root(id: StateId) -> Result { - unimplemented!("") + pub async fn get_state_root(&self, state_id: StateId) -> Result { + let mut stub = ""; + + match state_id { + StateId::Finalized => stub = "finalized", + StateId::Head => stub = "head", + StateId::Genesis => stub = "genesis", + StateId::Root(_) => stub = "not implemented", + StateId::Slot(_) => stub = "not implemented", + StateId::Justified => stub = "justified", + }; + + let path = format!("eth/v1/beacon/states/{}/root", &stub); + + let root: Value = self.get(&path).await?; + + Ok(root.data.root) } - pub async fn get_fork(id: StateId) -> Result { - unimplemented!("") + pub async fn get_fork(&self, state_id: StateId) -> Result { + let mut stub = ""; + + match state_id { + StateId::Finalized => stub = "finalized", + StateId::Head => stub = "head", + StateId::Genesis => stub = "genesis", + StateId::Root(_) => stub = "not implemented", + StateId::Slot(_) => stub = "not implemented", + StateId::Justified => stub = "justified", + }; + + let path = format!("eth/v1/beacon/states/{}/fork", &stub); + let result: Value = self.get(&path).await?; + Ok(result.data) } pub async fn get_finality_checkpoints(id: StateId) -> Result { diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 000000000..1ff30b4c9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,25 @@ +use beacon_api_client::Client; +use beacon_api_client::StateId; +use url::Url; + +#[tokio::main] +async fn main() { + let s = "http://127.0.0.1:8003/"; + let url: Url = Url::parse(s).unwrap(); + let client = Client::new(url); + + let root = client.get_state_root(StateId::Finalized).await; + let fork = client.get_fork(StateId::Finalized).await; + + // unwrap fork and extract fields + let fk = fork.unwrap(); + let pv = fk.previous_version; + let cv = fk.current_version; + let ep = fk.epoch; + + println!("\nroot:\n{:?}\n", root.unwrap()); + + println!("fork.previous_version = {:?}", pv); + println!("fork.current_version = {:?}", cv); + println!("fork.epoch = {:?}", ep); +} diff --git a/src/types.rs b/src/types.rs index 596295143..62f07bdff 100644 --- a/src/types.rs +++ b/src/types.rs @@ -26,6 +26,14 @@ pub enum StateId { Root(Root), } + +#[derive(Serialize, Deserialize)] +pub struct RootData { + pub root: Root, +} + + + #[derive(Serialize, Deserialize)] pub enum BlockId { Head, From 94a1c0d4634db80bfd6c3c77a0f530a5045a0332 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 27 May 2022 12:19:44 -0600 Subject: [PATCH 035/153] whitespace --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fa4fe3ad0..59021fb35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ serde_json = "1.0.81" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus" } \ No newline at end of file +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus" } From 8b50aac570657027130164f0f51c60fe156fed1f Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 27 May 2022 12:19:50 -0600 Subject: [PATCH 036/153] remove unnecessary bounds --- src/api_client.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index b9b67e1dc..6a13c0019 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -81,10 +81,7 @@ impl Client { Ok(response) } - pub async fn post< - T: serde::Serialize + serde::de::DeserializeOwned, - U: serde::Serialize + serde::de::DeserializeOwned, - >( + pub async fn post( &self, path: &str, argument: &T, @@ -96,7 +93,7 @@ impl Client { } } - pub async fn http_post( + pub async fn http_post( &self, path: &str, argument: &T, From 2f724c18d7e9184dc0f499d4781d8b8086e4f5b6 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 27 May 2022 21:50:08 -0600 Subject: [PATCH 037/153] allow `Client` to be clone-able --- src/api_client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api_client.rs b/src/api_client.rs index 6a13c0019..7ea44691c 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -46,6 +46,7 @@ pub async fn api_error_or_ok(response: reqwest::Response) -> Result<(), Error> { } } +#[derive(Clone)] pub struct Client { http: reqwest::Client, endpoint: Url, From d731ea8385ff97536ba25821071ab9225afc2f23 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Sat, 28 May 2022 09:48:18 +0100 Subject: [PATCH 038/153] fix whitespace Co-authored-by: Alex Stokes --- src/api_client.rs | 2 -- src/types.rs | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 116507af1..b4f40777c 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -125,9 +125,7 @@ impl Client { }; let path = format!("eth/v1/beacon/states/{}/root", &stub); - let root: Value = self.get(&path).await?; - Ok(root.data.root) } diff --git a/src/types.rs b/src/types.rs index 62f07bdff..ddca688f4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -26,14 +26,11 @@ pub enum StateId { Root(Root), } - #[derive(Serialize, Deserialize)] pub struct RootData { pub root: Root, } - - #[derive(Serialize, Deserialize)] pub enum BlockId { Head, From bcbfc473942f364e00de7a3fef6e967d87d58515 Mon Sep 17 00:00:00 2001 From: Joe Date: Sat, 28 May 2022 09:55:10 +0100 Subject: [PATCH 039/153] move matching logic to enum --- src/api_client.rs | 34 +++++++--------------------------- src/types.rs | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index b4f40777c..063d1c202 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -6,9 +6,9 @@ use crate::types::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, - PeerDescriptor, PeerSummary, ProposerDuty, PubkeyOrIndex, StateId, SyncCommitteeDescriptor, - SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorDescriptor, ValidatorSummary, - Value, RootData + PeerDescriptor, PeerSummary, ProposerDuty, PubkeyOrIndex, RootData, StateId, + SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, + ValidatorDescriptor, ValidatorSummary, Value, }; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, @@ -113,35 +113,15 @@ impl Client { } pub async fn get_state_root(&self, state_id: StateId) -> Result { - let mut stub = ""; - - match state_id { - StateId::Finalized => stub = "finalized", - StateId::Head => stub = "head", - StateId::Genesis => stub = "genesis", - StateId::Root(_) => stub = "not implemented", - StateId::Slot(_) => stub = "not implemented", - StateId::Justified => stub = "justified", - }; - - let path = format!("eth/v1/beacon/states/{}/root", &stub); + let path = format!("eth/v1/beacon/states/{state_id}/root"); + let root: Value = self.get(&path).await?; + Ok(root.data.root) } pub async fn get_fork(&self, state_id: StateId) -> Result { - let mut stub = ""; - - match state_id { - StateId::Finalized => stub = "finalized", - StateId::Head => stub = "head", - StateId::Genesis => stub = "genesis", - StateId::Root(_) => stub = "not implemented", - StateId::Slot(_) => stub = "not implemented", - StateId::Justified => stub = "justified", - }; - - let path = format!("eth/v1/beacon/states/{}/fork", &stub); + let path = format!("eth/v1/beacon/states/{state_id}/fork"); let result: Value = self.get(&path).await?; Ok(result.data) } diff --git a/src/types.rs b/src/types.rs index ddca688f4..17b93870f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -6,6 +6,7 @@ use ethereum_consensus::primitives::{ Version, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use std::fmt; #[derive(Serialize, Deserialize)] pub struct GenesisDetails { @@ -26,6 +27,20 @@ pub enum StateId { Root(Root), } +impl fmt::Display for StateId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + StateId::Finalized => "finalized", + StateId::Justified => "justified", + StateId::Head => "head", + StateId::Genesis => "genesis", + StateId::Slot(_) => "not yet implemented", + StateId::Root(_) => "not yet implemented", + }; + write!(f, "{}", printable) + } +} + #[derive(Serialize, Deserialize)] pub struct RootData { pub root: Root, From 1dddca03897c6a488cec0ef4e96a84d8c22901f5 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 31 May 2022 20:24:42 +0100 Subject: [PATCH 040/153] tidy up and standardize whitespace --- src/api_client.rs | 2 -- src/main.rs | 1 - 2 files changed, 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 063d1c202..641432f88 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -114,9 +114,7 @@ impl Client { pub async fn get_state_root(&self, state_id: StateId) -> Result { let path = format!("eth/v1/beacon/states/{state_id}/root"); - let root: Value = self.get(&path).await?; - Ok(root.data.root) } diff --git a/src/main.rs b/src/main.rs index 1ff30b4c9..5f2c99496 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,6 @@ async fn main() { let ep = fk.epoch; println!("\nroot:\n{:?}\n", root.unwrap()); - println!("fork.previous_version = {:?}", pv); println!("fork.current_version = {:?}", cv); println!("fork.epoch = {:?}", ep); From 1f83d568e87fd0c5b12327fdd86b1de71843e10e Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 1 Jun 2022 19:03:09 +0100 Subject: [PATCH 041/153] inline write statement for StateId::slot & root --- src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.rs b/src/types.rs index 17b93870f..03276a348 100644 --- a/src/types.rs +++ b/src/types.rs @@ -34,8 +34,8 @@ impl fmt::Display for StateId { StateId::Justified => "justified", StateId::Head => "head", StateId::Genesis => "genesis", - StateId::Slot(_) => "not yet implemented", - StateId::Root(_) => "not yet implemented", + StateId::Slot(slot) => return write!(f, "{}", slot), + StateId::Root(root) => return write!(f, "{}", root), }; write!(f, "{}", printable) } From 832e472b5b1fb28b722b2dcfb325ebbf3340d65d Mon Sep 17 00:00:00 2001 From: "lightclient@protonmail.com" Date: Wed, 1 Jun 2022 15:13:23 -0600 Subject: [PATCH 042/153] impl get_validator method --- src/api_client.rs | 7 +++++-- src/types.rs | 11 +++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index a702753dc..b29e80395 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -134,10 +134,13 @@ impl Client { } pub async fn get_validator( - id: StateId, + &self, + state_id: StateId, validator_id: PubkeyOrIndex, ) -> Result { - unimplemented!("") + let path = format!("eth/v1/beacon/states/{state_id}/validators/{validator_id}"); + let result: Value = self.get(&path).await?; + Ok(result.data) } pub async fn get_balances( diff --git a/src/types.rs b/src/types.rs index 03276a348..47807c8f0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -69,6 +69,7 @@ pub struct FinalityCheckpoints { } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] pub enum ValidatorStatus { PendingInitialized, PendingQueued, @@ -91,6 +92,16 @@ pub enum PubkeyOrIndex { Index(ValidatorIndex), } +impl fmt::Display for PubkeyOrIndex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + PubkeyOrIndex::Pubkey(ref pk) => pk.to_string(), + PubkeyOrIndex::Index(i) => i.to_string(), + }; + write!(f, "{}", printable) + } +} + pub struct ValidatorDescriptor { pub pubkey_or_index: PubkeyOrIndex, pub status: ValidatorStatus, From 8c6ec91c551678fb7b0171f6e09faec761a00acc Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 2 Jun 2022 12:17:02 -0600 Subject: [PATCH 043/153] implement `get_validators` endpoint --- Cargo.toml | 1 + src/api_client.rs | 30 +++++++++++++++++++++++++----- src/types.rs | 44 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 59021fb35..7de101044 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ http = "0.2.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" +itertools = "0.10.3" thiserror = "1.0.30" diff --git a/src/api_client.rs b/src/api_client.rs index b29e80395..66545ed1d 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -7,8 +7,8 @@ use crate::types::{ BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, PeerDescriptor, PeerSummary, ProposerDuty, PubkeyOrIndex, RootData, StateId, - SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, - ValidatorDescriptor, ValidatorSummary, Value, + SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, + ValidatorSummary, Value, }; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, @@ -24,6 +24,7 @@ use ethereum_consensus::primitives::{ Bytes32, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, RandaoReveal, Root, Slot, ValidatorIndex, }; +use itertools::Itertools; use std::collections::HashMap; use thiserror::Error; use url::{ParseError, Url}; @@ -127,10 +128,29 @@ impl Client { } pub async fn get_validators( - id: StateId, - filters: &[ValidatorDescriptor], + &self, + state_id: StateId, + validator_ids: &[PubkeyOrIndex], + filters: &[ValidatorStatus], ) -> Result, Error> { - unimplemented!("") + let path = format!("eth/v1/beacon/states/{state_id}/validators"); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + if !validator_ids.is_empty() { + let validator_ids = validator_ids.iter().join(", "); + request = request.query(&[("id", validator_ids)]); + } + if !filters.is_empty() { + let filters = filters.iter().join(", "); + request = request.query(&[("status", filters)]); + } + let response = request.send().await?; + + let result: ApiResult>> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn get_validator( diff --git a/src/types.rs b/src/types.rs index 47807c8f0..8493fb9ca 100644 --- a/src/types.rs +++ b/src/types.rs @@ -68,7 +68,7 @@ pub struct FinalityCheckpoints { pub finalized: Checkpoint, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ValidatorStatus { PendingInitialized, @@ -80,13 +80,34 @@ pub enum ValidatorStatus { ExitedSlashed, WithdrawalPossible, WithdrawalDone, - // TODO what are these? Active, Pending, Exited, Withdrawal, } +impl fmt::Display for ValidatorStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + Self::PendingInitialized => "pending_initialized", + Self::PendingQueued => "pending_queued", + Self::ActiveOngoing => "active_ongoing", + Self::ActiveExiting => "active_exiting", + Self::ActiveSlashed => "active_slashed", + Self::ExitedUnslashed => "exited_unslashed", + Self::ExitedSlashed => "exited_slashed", + Self::WithdrawalPossible => "withdrawal_possible", + Self::WithdrawalDone => "withdrawal_done", + Self::Active => "active", + Self::Pending => "pending", + Self::Exited => "exited", + Self::Withdrawal => "withdrawal", + }; + write!(f, "{}", printable) + } +} + +#[derive(Debug)] pub enum PubkeyOrIndex { Pubkey(BlsPublicKey), Index(ValidatorIndex), @@ -95,19 +116,26 @@ pub enum PubkeyOrIndex { impl fmt::Display for PubkeyOrIndex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let printable = match *self { - PubkeyOrIndex::Pubkey(ref pk) => pk.to_string(), - PubkeyOrIndex::Index(i) => i.to_string(), + Self::Pubkey(ref pk) => pk.to_string(), + Self::Index(i) => i.to_string(), }; write!(f, "{}", printable) } } -pub struct ValidatorDescriptor { - pub pubkey_or_index: PubkeyOrIndex, - pub status: ValidatorStatus, +impl From for PubkeyOrIndex { + fn from(index: ValidatorIndex) -> Self { + Self::Index(index) + } } -#[derive(Serialize, Deserialize)] +impl From for PubkeyOrIndex { + fn from(public_key: BlsPublicKey) -> Self { + Self::Pubkey(public_key) + } +} + +#[derive(Debug, Serialize, Deserialize)] pub struct ValidatorSummary { #[serde(with = "crate::serde::as_string")] pub index: ValidatorIndex, From a3fac0abae15afb3cddb16fa806993c4acc8b389 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 4 Jun 2022 09:11:16 -0600 Subject: [PATCH 044/153] impl `Clone`, `Copy` for `ValidatorStatus` --- src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index 8493fb9ca..ef0e10525 100644 --- a/src/types.rs +++ b/src/types.rs @@ -68,7 +68,7 @@ pub struct FinalityCheckpoints { pub finalized: Checkpoint, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ValidatorStatus { PendingInitialized, From d464ff9c00a69557367d0ab7f6596705b0322216 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 31 May 2022 20:43:09 +0100 Subject: [PATCH 045/153] implement `finalized_checkpoints` endpoint --- src/api_client.rs | 9 +++++++-- src/main.rs | 19 +++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 66545ed1d..3db97b4ad 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -123,8 +123,13 @@ impl Client { Ok(result.data) } - pub async fn get_finality_checkpoints(id: StateId) -> Result { - unimplemented!("") + pub async fn get_finality_checkpoints( + &self, + id: StateId, + ) -> Result { + let path = format!("eth/v1/beacon/states/{id}/finality_checkpoints"); + let result: Value = self.get(&path).await?; + Ok(result.data) } pub async fn get_validators( diff --git a/src/main.rs b/src/main.rs index 5f2c99496..19e81ed2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,17 +8,12 @@ async fn main() { let url: Url = Url::parse(s).unwrap(); let client = Client::new(url); - let root = client.get_state_root(StateId::Finalized).await; - let fork = client.get_fork(StateId::Finalized).await; + let checkpoints = client + .get_finality_checkpoints(StateId::Finalized) + .await + .unwrap(); - // unwrap fork and extract fields - let fk = fork.unwrap(); - let pv = fk.previous_version; - let cv = fk.current_version; - let ep = fk.epoch; - - println!("\nroot:\n{:?}\n", root.unwrap()); - println!("fork.previous_version = {:?}", pv); - println!("fork.current_version = {:?}", cv); - println!("fork.epoch = {:?}", ep); + println!("previous checkpoint: {:?}", checkpoints.previous_justified); + println!("current checkpoint: {:?}", checkpoints.current_justified); + println!("finalized checkpoint: {:?}", checkpoints.finalized); } From dd8d2a8f7d79a0dc96195917668224d0b1e0fa0d Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 8 Jun 2022 16:20:44 +0100 Subject: [PATCH 046/153] implement `get_node`version()` --- src/api_client.rs | 7 ++++--- src/types.rs | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 3db97b4ad..42db26f8b 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -8,7 +8,7 @@ use crate::types::{ FinalityCheckpoints, GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, PeerDescriptor, PeerSummary, ProposerDuty, PubkeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, - ValidatorSummary, Value, + ValidatorSummary, Value, VersionData, }; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, @@ -325,8 +325,9 @@ impl Client { unimplemented!("") } - pub async fn get_node_version() -> Result { - unimplemented!("") + pub async fn get_node_version(self) -> Result { + let result: Value = self.get("eth/v1/node/version").await?; + Ok(result.data.version) } pub async fn get_sync_status() -> Result { diff --git a/src/types.rs b/src/types.rs index ef0e10525..7fc86cb76 100644 --- a/src/types.rs +++ b/src/types.rs @@ -8,6 +8,11 @@ use ethereum_consensus::primitives::{ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::fmt; +#[derive(Serialize, Deserialize)] +pub struct VersionData { + pub version: String, +} + #[derive(Serialize, Deserialize)] pub struct GenesisDetails { #[serde(with = "crate::serde::as_string")] From 72835ec495a090d12b5fcef260536305ef411abb Mon Sep 17 00:00:00 2001 From: Joe Date: Thu, 9 Jun 2022 20:46:44 +0100 Subject: [PATCH 047/153] impl get_block() and get_block_root() --- src/api_client.rs | 11 +++++++---- src/types.rs | 13 +++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 42db26f8b..376f7a990 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -222,12 +222,15 @@ impl Client { } // v2 endpoint - pub async fn get_beacon_block(id: BlockId) -> Result { - unimplemented!("") + pub async fn get_beacon_block(self, id: BlockId) -> Result { + let result: Value = + self.get(&format!("eth/v2/beacon/blocks/{id}")).await?; + Ok(result.data) } - pub async fn get_beacon_block_root(id: BlockId) -> Result { - unimplemented!("") + pub async fn get_beacon_block_root(self, id: BlockId) -> Result { + let result: Value = self.get(&format!("eth/v1/beacon/blocks/{id}/root")).await?; + Ok(result.data.root) } pub async fn get_attestations_from_beacon_block( diff --git a/src/types.rs b/src/types.rs index 7fc86cb76..d78508687 100644 --- a/src/types.rs +++ b/src/types.rs @@ -60,6 +60,19 @@ pub enum BlockId { Root(Root), } +impl fmt::Display for BlockId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + BlockId::Finalized => "finalized", + BlockId::Head => "head", + BlockId::Genesis => "genesis", + BlockId::Slot(slot) => return write!(f, "{}", slot), + BlockId::Root(root) => return write!(f, "{}", root), + }; + write!(f, "{}", printable) + } +} + #[derive(Serialize, Deserialize)] enum ExecutionStatus { Default, From 288b48e600bdd79573ce2d85bae97fd67a6b197d Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 12 Jun 2022 10:37:39 -0600 Subject: [PATCH 048/153] extra `Value` with partially-types `meta` field --- src/types.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/types.rs b/src/types.rs index 7fc86cb76..85b0ecc78 100644 --- a/src/types.rs +++ b/src/types.rs @@ -6,6 +6,7 @@ use ethereum_consensus::primitives::{ Version, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use std::collections::HashMap; use std::fmt; #[derive(Serialize, Deserialize)] @@ -325,13 +326,8 @@ pub struct BeaconProposerRegistration { #[serde(bound = "T: Serialize + serde::de::DeserializeOwned")] pub struct Value { pub data: T, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(bound = "T: Serialize + serde::de::DeserializeOwned")] -pub struct VersionedValue { - pub version: ConsensusVersion, - pub data: T, + #[serde(flatten)] + pub meta: HashMap, } #[derive(Serialize, Deserialize, Debug)] From 2887ab08fedab370ab9232d76ab35c372f99f164 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 12 Jun 2022 10:55:37 -0600 Subject: [PATCH 049/153] impl `get_proposer_duties` --- src/api_client.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 42db26f8b..ba4ae129c 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -37,6 +37,10 @@ pub enum Error { Http(#[from] reqwest::Error), #[error("error from API: {0}")] Api(#[from] ApiError), + #[error("missing expected data in response: {0}")] + MissingExpectedData(String), + #[error("json error: {0}")] + Json(#[from] serde_json::Error), } pub async fn api_error_or_ok(response: reqwest::Response) -> Result<(), Error> { @@ -346,8 +350,17 @@ impl Client { unimplemented!("") } - pub async fn get_proposer_duties(epoch: Epoch) -> Result<(Root, Vec), Error> { - unimplemented!("") + pub async fn get_proposer_duties( + &self, + epoch: Epoch, + ) -> Result<(Root, Vec), Error> { + let endpoint = format!("eth/v1/validator/duties/proposer/{epoch}"); + let mut result: Value> = self.get(&endpoint).await?; + let dependent_root_value = result.meta.remove("dependent_root").ok_or_else(|| { + Error::MissingExpectedData("missing `dependent_root` in response".to_string()) + })?; + let dependent_root: Root = serde_json::from_value(dependent_root_value)?; + Ok((dependent_root, result.data)) } pub async fn get_sync_committee_duties( From 33fbb4a56f64f176f1c98df5e9c754e8c4ff8c57 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 12 Jun 2022 11:11:22 -0600 Subject: [PATCH 050/153] fix example --- examples/sketch.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/sketch.rs b/examples/sketch.rs index 8860c044f..2a760597e 100644 --- a/examples/sketch.rs +++ b/examples/sketch.rs @@ -1,16 +1,19 @@ -use beacon_api_client::{ApiError, ApiResult, ConsensusVersion, Value, VersionedValue}; +use beacon_api_client::{ApiError, ApiResult, ConsensusVersion, Value}; use ethereum_consensus::bellatrix::mainnet::BlindedBeaconBlock; use serde_json; +use std::collections::HashMap; fn main() { let block = Value { + meta: HashMap::new(), data: BlindedBeaconBlock::default(), }; let block_repr = serde_json::to_string(&block).unwrap(); println!("{block_repr}"); - let block_with_version = VersionedValue { - version: ConsensusVersion::Bellatrix, + let version = serde_json::to_value(ConsensusVersion::Bellatrix).unwrap(); + let block_with_version = Value { + meta: HashMap::from_iter([("version".to_string(), version)]), data: BlindedBeaconBlock::default(), }; let block_with_version_repr = serde_json::to_string(&block_with_version).unwrap(); @@ -20,7 +23,7 @@ fn main() { let str_repr = serde_json::to_string(&full_success_response).unwrap(); println!("{}", str_repr); - let recovered_success: ApiResult> = + let recovered_success: ApiResult> = serde_json::from_str(&str_repr).unwrap(); println!("{:#?}", recovered_success); From c45eac562adf385cd08a167cd5a2e6c4c434c4ec Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 12 Jun 2022 11:13:24 -0600 Subject: [PATCH 051/153] add github action cache --- .github/workflows/rust.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fba840e7e..b8115b1fe 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,6 +18,8 @@ jobs: - uses: actions/checkout@v2 - name: Install run: rustup update stable + - name: Rust Cache + uses: Swatinem/rust-cache@v1.3.0 - name: Format run: cargo fmt -- --check - name: Run clippy From aafe1ed645f55ae32ae0349706a55e1c044b7ee9 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 12 Jun 2022 11:42:29 -0600 Subject: [PATCH 052/153] s/pubkey/public_key/ --- src/api_client.rs | 8 ++++---- src/types.rs | 23 +++++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index ba4ae129c..e55d15388 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -6,7 +6,7 @@ use crate::types::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, - PeerDescriptor, PeerSummary, ProposerDuty, PubkeyOrIndex, RootData, StateId, + PeerDescriptor, PeerSummary, ProposerDuty, PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, }; @@ -139,7 +139,7 @@ impl Client { pub async fn get_validators( &self, state_id: StateId, - validator_ids: &[PubkeyOrIndex], + validator_ids: &[PublicKeyOrIndex], filters: &[ValidatorStatus], ) -> Result, Error> { let path = format!("eth/v1/beacon/states/{state_id}/validators"); @@ -165,7 +165,7 @@ impl Client { pub async fn get_validator( &self, state_id: StateId, - validator_id: PubkeyOrIndex, + validator_id: PublicKeyOrIndex, ) -> Result { let path = format!("eth/v1/beacon/states/{state_id}/validators/{validator_id}"); let result: Value = self.get(&path).await?; @@ -174,7 +174,7 @@ impl Client { pub async fn get_balances( id: StateId, - filters: &[PubkeyOrIndex], + filters: &[PublicKeyOrIndex], ) -> Result, Error> { unimplemented!("") } diff --git a/src/types.rs b/src/types.rs index 85b0ecc78..73376d2f3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -114,30 +114,30 @@ impl fmt::Display for ValidatorStatus { } #[derive(Debug)] -pub enum PubkeyOrIndex { - Pubkey(BlsPublicKey), +pub enum PublicKeyOrIndex { + PublicKey(BlsPublicKey), Index(ValidatorIndex), } -impl fmt::Display for PubkeyOrIndex { +impl fmt::Display for PublicKeyOrIndex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let printable = match *self { - Self::Pubkey(ref pk) => pk.to_string(), + Self::PublicKey(ref pk) => pk.to_string(), Self::Index(i) => i.to_string(), }; write!(f, "{}", printable) } } -impl From for PubkeyOrIndex { +impl From for PublicKeyOrIndex { fn from(index: ValidatorIndex) -> Self { Self::Index(index) } } -impl From for PubkeyOrIndex { +impl From for PublicKeyOrIndex { fn from(public_key: BlsPublicKey) -> Self { - Self::Pubkey(public_key) + Self::PublicKey(public_key) } } @@ -268,7 +268,8 @@ pub enum HealthStatus { #[derive(Serialize, Deserialize)] pub struct AttestationDuty { - pub pubkey: BlsPublicKey, + #[serde(rename = "pubkey")] + pub public_key: BlsPublicKey, #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, #[serde(with = "crate::serde::as_string")] @@ -285,7 +286,8 @@ pub struct AttestationDuty { #[derive(Serialize, Deserialize)] pub struct ProposerDuty { - pub pubkey: BlsPublicKey, + #[serde(rename = "pubkey")] + pub public_key: BlsPublicKey, #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, #[serde(with = "crate::serde::as_string")] @@ -294,7 +296,8 @@ pub struct ProposerDuty { #[derive(Serialize, Deserialize)] pub struct SyncCommitteeDuty { - pub pubkey: BlsPublicKey, + #[serde(rename = "pubkey")] + pub public_key: BlsPublicKey, #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, #[serde(with = "crate::serde::collection_over_string")] From 7a9a89470c3b5837ef219835f01f2745ff4173b6 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 12 Jun 2022 11:49:28 -0600 Subject: [PATCH 053/153] impl `Debug` for `ProposerDuty` --- src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index 73376d2f3..b76535c39 100644 --- a/src/types.rs +++ b/src/types.rs @@ -284,7 +284,7 @@ pub struct AttestationDuty { pub slot: Slot, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct ProposerDuty { #[serde(rename = "pubkey")] pub public_key: BlsPublicKey, From aefc7d0d971bc56f624c9d41523d9be39fdcf46a Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 12 Jun 2022 12:14:51 -0600 Subject: [PATCH 054/153] bugfix: `get_genesis_details` --- src/api_client.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index e55d15388..8db8e1815 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -112,7 +112,8 @@ impl Client { /* beacon namespace */ pub async fn get_genesis_details(&self) -> Result { - self.get("/eth/v1/beacon/genesis").await + let details: Value = self.get("/eth/v1/beacon/genesis").await?; + Ok(details.data) } pub async fn get_state_root(&self, state_id: StateId) -> Result { From b22e736528e24c83dbeef96cd02624624bfde496 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 12 Jun 2022 12:20:32 -0600 Subject: [PATCH 055/153] impl `prepare_proposers` --- src/api_client.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 8db8e1815..9a9d14354 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -435,9 +435,13 @@ impl Client { } pub async fn prepare_proposers( + &self, registrations: &[BeaconProposerRegistration], ) -> Result<(), Error> { - Ok(()) + let response = self + .http_post("/eth/v1/validator/prepare_beacon_proposer", ®istrations) + .await?; + api_error_or_ok(response).await } // endpoint for builder registrations From e3172e0639b29d45b2d58fad84164aa03286c894 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 12 Jun 2022 12:25:29 -0600 Subject: [PATCH 056/153] impl `Debug` for `BeaconProposerRegistration` --- src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index b76535c39..d46925c77 100644 --- a/src/types.rs +++ b/src/types.rs @@ -318,7 +318,7 @@ pub struct SyncCommitteeDescriptor { pub until_epoch: Epoch, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct BeaconProposerRegistration { #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, From 92b9ac975946496496dfac09a40a1165e55a73bc Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 10 Jun 2022 17:33:36 +0100 Subject: [PATCH 057/153] implement get_all_committees and get_balances borrow self --- src/api_client.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 42db26f8b..348df1624 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -169,14 +169,32 @@ impl Client { } pub async fn get_balances( + &self, id: StateId, filters: &[PubkeyOrIndex], ) -> Result, Error> { - unimplemented!("") + let path = format!("eth/v1/beacon/states/{id}/validator_balances"); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + + if !filters.is_empty() { + let filters = filters.iter().join(", "); + request = request.query(&[("id", filters)]); + } + let response = request.send().await?; + + let result: ApiResult>> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } - pub async fn get_all_committees(id: StateId) -> Result, Error> { - unimplemented!("") + pub async fn get_all_committees(&self, id: StateId) -> Result, Error> { + let result: Value> = self + .get(&format!("eth/v1/beacon/states/{id}/committees")) + .await?; + Ok(result.data) } pub async fn get_committees( From 1cd88be670b63043d9da7f1f2f3190b777a7baa4 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Sun, 12 Jun 2022 20:15:52 +0100 Subject: [PATCH 058/153] Apply suggestions from code review Co-authored-by: Alex Stokes --- src/api_client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 376f7a990..d3f0bf2cc 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -222,13 +222,13 @@ impl Client { } // v2 endpoint - pub async fn get_beacon_block(self, id: BlockId) -> Result { + pub async fn get_beacon_block(&self, id: BlockId) -> Result { let result: Value = self.get(&format!("eth/v2/beacon/blocks/{id}")).await?; Ok(result.data) } - pub async fn get_beacon_block_root(self, id: BlockId) -> Result { + pub async fn get_beacon_block_root(&self, id: BlockId) -> Result { let result: Value = self.get(&format!("eth/v1/beacon/blocks/{id}/root")).await?; Ok(result.data.root) } From 19920e5131e7e6f59666804cfd1a513eb8e6de07 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 10 Jun 2022 10:42:40 +0100 Subject: [PATCH 059/153] `get_block_attestns()` & `get_pool_attestns()` fmt --- src/api_client.rs | 26 ++++++++++++++++++++++++-- src/types.rs | 12 ++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 09be1853a..699c81c52 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -257,16 +257,38 @@ impl Client { } pub async fn get_attestations_from_beacon_block( + self, id: BlockId, ) -> Result, Error> { - unimplemented!("") + let result: Value> = self + .get(&format!("eth/v1/beacon/blocks/{id}/attestations")) + .await?; + Ok(result.data) } pub async fn get_attestations_from_pool( + self, slot: Option, committee_index: Option, ) -> Result, Error> { - unimplemented!("") + let path: String = "eth/v1/beacon/pool/attestations".to_string(); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + + if slot.and(committee_index).is_some() { + request = request.query(&[("slot", slot.unwrap())]); + request = request.query(&[("committee_index", committee_index.unwrap())]); + } else if slot.is_some() && committee_index.is_none() { + request = request.query(&[("slot", slot.unwrap())]); + } else if slot.is_none() && committee_index.is_some() { + request = request.query(&[("committee_index", committee_index.unwrap())]); + } + let response = request.send().await?; + let result: ApiResult>> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn post_attestations(attestations: &[Attestation]) -> Result<(), Error> { diff --git a/src/types.rs b/src/types.rs index b85415657..02cdcf9bd 100644 --- a/src/types.rs +++ b/src/types.rs @@ -60,6 +60,18 @@ pub enum BlockId { Slot(Slot), Root(Root), } +impl fmt::Display for BlockId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + BlockId::Finalized => "finalized", + BlockId::Head => "head", + BlockId::Genesis => "genesis", + BlockId::Slot(slot) => return write!(f, "{}", slot), + BlockId::Root(root) => return write!(f, "{}", root), + }; + write!(f, "{}", printable) + } +} impl fmt::Display for BlockId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { From 795886bb63a26e3cae5c0c2ac372611348508f12 Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 13 Jun 2022 09:19:54 +0100 Subject: [PATCH 060/153] use pattern matching instead of if/else borrow self --- src/api_client.rs | 21 +++++++++------------ src/types.rs | 12 ------------ 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 699c81c52..aeec10f85 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -257,7 +257,7 @@ impl Client { } pub async fn get_attestations_from_beacon_block( - self, + &self, id: BlockId, ) -> Result, Error> { let result: Value> = self @@ -267,21 +267,18 @@ impl Client { } pub async fn get_attestations_from_pool( - self, + &self, slot: Option, committee_index: Option, ) -> Result, Error> { - let path: String = "eth/v1/beacon/pool/attestations".to_string(); - let target = self.endpoint.join(&path)?; + let path = "eth/v1/beacon/pool/attestations"; + let target = self.endpoint.join(path)?; let mut request = self.http.get(target); - - if slot.and(committee_index).is_some() { - request = request.query(&[("slot", slot.unwrap())]); - request = request.query(&[("committee_index", committee_index.unwrap())]); - } else if slot.is_some() && committee_index.is_none() { - request = request.query(&[("slot", slot.unwrap())]); - } else if slot.is_none() && committee_index.is_some() { - request = request.query(&[("committee_index", committee_index.unwrap())]); + if let Some(slot) = slot { + request = request.query(&[("slot", slot)]); + } + if let Some(committee_index) = committee_index { + request = request.query(&[("committe_index", committee_index)]); } let response = request.send().await?; let result: ApiResult>> = response.json().await?; diff --git a/src/types.rs b/src/types.rs index 02cdcf9bd..b85415657 100644 --- a/src/types.rs +++ b/src/types.rs @@ -60,18 +60,6 @@ pub enum BlockId { Slot(Slot), Root(Root), } -impl fmt::Display for BlockId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let printable = match *self { - BlockId::Finalized => "finalized", - BlockId::Head => "head", - BlockId::Genesis => "genesis", - BlockId::Slot(slot) => return write!(f, "{}", slot), - BlockId::Root(root) => return write!(f, "{}", root), - }; - write!(f, "{}", printable) - } -} impl fmt::Display for BlockId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { From 2f05ed186e887e4c4031be23a3d22add88a41032 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 8 Jul 2022 16:03:56 +0100 Subject: [PATCH 061/153] implement get_committee add default to committeefilter Apply suggestions from code review Co-authored-by: Alex Stokes refactor `get_all_committees()` now calls `get_committees()` with default `CommitteeFilter` --- src/api_client.rs | 30 ++++++++++++++++++++++++------ src/types.rs | 1 + 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index aeec10f85..09d8c462b 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -196,27 +196,45 @@ impl Client { } pub async fn get_all_committees(&self, id: StateId) -> Result, Error> { - let result: Value> = self - .get(&format!("eth/v1/beacon/states/{id}/committees")) - .await?; - Ok(result.data) + let result: Vec = + self.get_committees(id, CommitteeFilter::default()).await?; + Ok(result) } pub async fn get_committees( + &self, id: StateId, filter: CommitteeFilter, ) -> Result, Error> { - unimplemented!("") + let path = format!("eth/v1/beacon/states/{id}/committees"); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + if let Some(epoch) = filter.epoch { + request = request.query(&[("epoch", epoch)]); + } + if let Some(index) = filter.index { + request = request.query(&[("index", index)]); + } + if let Some(slot) = filter.slot { + request = request.query(&[("slot", slot)]); + } + let response = request.send().await?; + let result: ApiResult>> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn get_sync_committees( + &self, id: StateId, epoch: Option, ) -> Result, Error> { unimplemented!("") } - pub async fn get_beacon_header_at_head() -> Result { + pub async fn get_beacon_header_at_head(&self) -> Result { unimplemented!("") } diff --git a/src/types.rs b/src/types.rs index b85415657..567ed10f6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -172,6 +172,7 @@ pub struct BalanceSummary { pub balance: Gwei, } +#[derive(Default)] pub struct CommitteeFilter { pub epoch: Option, pub index: Option, From ad8e03d836c8a1dcee006ff4bb3555d6a937e7c8 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Mon, 11 Jul 2022 09:03:28 +0100 Subject: [PATCH 062/153] Update src/api_client.rs Co-authored-by: Alex Stokes --- src/api_client.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 09d8c462b..624ffd244 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -196,9 +196,7 @@ impl Client { } pub async fn get_all_committees(&self, id: StateId) -> Result, Error> { - let result: Vec = - self.get_committees(id, CommitteeFilter::default()).await?; - Ok(result) + self.get_committees(id, CommitteeFilter::default()).await } pub async fn get_committees( From 5aba4c7a26fe080a898fd0d19384da3713f9f772 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 13 Jul 2022 19:13:12 +0100 Subject: [PATCH 063/153] implement get_node_health() --- src/api_client.rs | 16 ++++++++++++++-- src/types.rs | 13 +++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 624ffd244..979c79702 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -25,6 +25,7 @@ use ethereum_consensus::primitives::{ Slot, ValidatorIndex, }; use itertools::Itertools; +use reqwest::StatusCode; use std::collections::HashMap; use thiserror::Error; use url::{ParseError, Url}; @@ -395,8 +396,19 @@ impl Client { unimplemented!("") } - pub async fn get_health() -> Result { - unimplemented!("") + pub async fn get_health(&self) -> Result { + let path = "eth/v1/node/health"; + let target = self.endpoint.join(path)?; + let request = self.http.get(target); + let response = request.send().await?; + let status: u16 = StatusCode::as_u16(&response.status()); + let result = match status { + 200 => HealthStatus::Ready, + 206 => HealthStatus::Syncing, + 503 => HealthStatus::NotInitialized, + _ => HealthStatus::Unknown, + }; + Ok(result) } /* validator namespace */ diff --git a/src/types.rs b/src/types.rs index 567ed10f6..117351d25 100644 --- a/src/types.rs +++ b/src/types.rs @@ -278,6 +278,19 @@ pub enum HealthStatus { Ready, Syncing, NotInitialized, + Unknown, +} + +impl fmt::Display for HealthStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + HealthStatus::Ready => "Ready", + HealthStatus::Syncing => "Syncing", + HealthStatus::NotInitialized => "Not Initialized", + HealthStatus::Unknown => "Unknown", + }; + write!(f, "{}", printable) + } } #[derive(Serialize, Deserialize)] From 333657e07fca2af129b6e132544a8681322e2b3d Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 13 Jul 2022 21:19:41 +0100 Subject: [PATCH 064/153] rm Display and u16 conversion --- src/api_client.rs | 9 ++++----- src/types.rs | 14 +------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 979c79702..a5b9033e0 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -401,11 +401,10 @@ impl Client { let target = self.endpoint.join(path)?; let request = self.http.get(target); let response = request.send().await?; - let status: u16 = StatusCode::as_u16(&response.status()); - let result = match status { - 200 => HealthStatus::Ready, - 206 => HealthStatus::Syncing, - 503 => HealthStatus::NotInitialized, + let result = match response.status() { + http::StatusCode::OK => HealthStatus::Ready, + http::StatusCode::PARTIAL_CONTENT => HealthStatus::Syncing, + http::StatusCode::SERVICE_UNAVAILABLE => HealthStatus::NotInitialized, _ => HealthStatus::Unknown, }; Ok(result) diff --git a/src/types.rs b/src/types.rs index 117351d25..d07ecd06a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -273,7 +273,7 @@ pub struct SyncStatus { pub is_syncing: bool, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub enum HealthStatus { Ready, Syncing, @@ -281,18 +281,6 @@ pub enum HealthStatus { Unknown, } -impl fmt::Display for HealthStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let printable = match *self { - HealthStatus::Ready => "Ready", - HealthStatus::Syncing => "Syncing", - HealthStatus::NotInitialized => "Not Initialized", - HealthStatus::Unknown => "Unknown", - }; - write!(f, "{}", printable) - } -} - #[derive(Serialize, Deserialize)] pub struct AttestationDuty { #[serde(rename = "pubkey")] From 7b8ddb8617acb448b28fea8dcef8bccc91b6d2e1 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 13 Jul 2022 21:23:13 +0100 Subject: [PATCH 065/153] rm unused import --- src/api_client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index a5b9033e0..967798286 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -25,7 +25,6 @@ use ethereum_consensus::primitives::{ Slot, ValidatorIndex, }; use itertools::Itertools; -use reqwest::StatusCode; use std::collections::HashMap; use thiserror::Error; use url::{ParseError, Url}; From 33981ae86cab53ed9264069abc0672ed6e6143d6 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Wed, 20 Jul 2022 14:35:13 +0100 Subject: [PATCH 066/153] use http:StatusCode --- src/api_client.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 967798286..acd79b64d 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -24,6 +24,7 @@ use ethereum_consensus::primitives::{ Bytes32, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, RandaoReveal, Root, Slot, ValidatorIndex, }; +use http::StatusCode; use itertools::Itertools; use std::collections::HashMap; use thiserror::Error; @@ -401,9 +402,9 @@ impl Client { let request = self.http.get(target); let response = request.send().await?; let result = match response.status() { - http::StatusCode::OK => HealthStatus::Ready, - http::StatusCode::PARTIAL_CONTENT => HealthStatus::Syncing, - http::StatusCode::SERVICE_UNAVAILABLE => HealthStatus::NotInitialized, + StatusCode::OK => HealthStatus::Ready, + StatusCode::PARTIAL_CONTENT => HealthStatus::Syncing, + StatusCode::SERVICE_UNAVAILABLE => HealthStatus::NotInitialized, _ => HealthStatus::Unknown, }; Ok(result) From 4ad0df8dd6612fbe54588f27b1959d638ee2655a Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 20 Jul 2022 16:38:50 +0100 Subject: [PATCH 067/153] implements get_peer_count() --- src/api_client.rs | 5 +++-- src/types.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 624ffd244..a882031b3 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -382,8 +382,9 @@ impl Client { unimplemented!("") } - pub async fn get_peer_count() -> Result { - unimplemented!("") + pub async fn get_peer_count(&self) -> Result { + let result: Value = self.get("eth/v1/node/peer_count").await?; + Ok(result.data) } pub async fn get_node_version(self) -> Result { diff --git a/src/types.rs b/src/types.rs index 567ed10f6..b88752968 100644 --- a/src/types.rs +++ b/src/types.rs @@ -252,7 +252,7 @@ pub struct PeerDescription { pub direction: ConnectionOrientation, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct PeerSummary { #[serde(with = "crate::serde::as_string")] pub disconnected: usize, From 55e9a9fa56cd2fb45d6e50c9a0a25b6e5d07b976 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 20 Jul 2022 13:54:26 -0600 Subject: [PATCH 068/153] Update src/api_client.rs --- src/api_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index a882031b3..769b30056 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -382,7 +382,7 @@ impl Client { unimplemented!("") } - pub async fn get_peer_count(&self) -> Result { + pub async fn get_peer_summary(&self) -> Result { let result: Value = self.get("eth/v1/node/peer_count").await?; Ok(result.data) } From 30be7f6ea270f2454c7ab1d08400d7251ce9b37b Mon Sep 17 00:00:00 2001 From: realbigsean Date: Fri, 29 Jul 2022 10:38:26 -0400 Subject: [PATCH 069/153] feature gate for peer-id --- Cargo.toml | 3 +++ src/api_client.rs | 13 +++++++++---- src/types.rs | 3 +++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7de101044..09eeda23f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,6 @@ itertools = "0.10.3" thiserror = "1.0.30" ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus" } + +[features] +peer-id = ["ethereum-consensus/peer-id"] \ No newline at end of file diff --git a/src/api_client.rs b/src/api_client.rs index b8b7efcc5..3f65ea17f 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -5,16 +5,18 @@ use crate::error::ApiError; use crate::types::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, EventTopic, - FinalityCheckpoints, GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, - PeerDescriptor, PeerSummary, ProposerDuty, PublicKeyOrIndex, RootData, StateId, - SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, - ValidatorSummary, Value, VersionData, + FinalityCheckpoints, GenesisDetails, HealthStatus, PeerSummary, ProposerDuty, PublicKeyOrIndex, + RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, + SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, }; +#[cfg(feature = "peer-id")] +use crate::types::{NetworkIdentity, PeerDescription, PeerDescriptor}; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, }; use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; use ethereum_consensus::builder::SignedValidatorRegistration; +#[cfg(feature = "peer-id")] use ethereum_consensus::networking::Multiaddr; use ethereum_consensus::phase0::mainnet::{ Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, @@ -371,14 +373,17 @@ impl Client { } /* node namespace */ + #[cfg(feature = "peer-id")] pub async fn get_node_identity() -> Result { unimplemented!("") } + #[cfg(feature = "peer-id")] pub async fn get_node_peers(filters: &[PeerDescriptor]) -> Result, Error> { unimplemented!("") } + #[cfg(feature = "peer-id")] pub async fn get_peer(peer_id: Multiaddr) -> Result { unimplemented!("") } diff --git a/src/types.rs b/src/types.rs index a53245c0a..96d9d1f4c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,5 @@ use crate::error::ApiError; +#[cfg(feature = "peer-id")] use ethereum_consensus::networking::{Enr, MetaData, Multiaddr, PeerId}; use ethereum_consensus::phase0::mainnet::{Checkpoint, SignedBeaconBlockHeader, Validator}; use ethereum_consensus::primitives::{ @@ -214,6 +215,7 @@ pub enum EventTopic { ContributionAndProof, } +#[cfg(feature = "peer-id")] #[derive(Serialize, Deserialize)] pub struct NetworkIdentity { pub peer_id: PeerId, @@ -243,6 +245,7 @@ pub struct PeerDescriptor { pub direction: ConnectionOrientation, } +#[cfg(feature = "peer-id")] #[derive(Serialize, Deserialize)] pub struct PeerDescription { pub peer_id: PeerId, From ad9ed9faacf0ec845974ebb5e2afe617ac6b2b5c Mon Sep 17 00:00:00 2001 From: realbigsean Date: Fri, 29 Jul 2022 10:39:24 -0400 Subject: [PATCH 070/153] add newline --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 09eeda23f..e4e96dbab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ thiserror = "1.0.30" ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus" } [features] -peer-id = ["ethereum-consensus/peer-id"] \ No newline at end of file +peer-id = ["ethereum-consensus/peer-id"] From 4ee3284fe2f10eb0f288f44589a2609c142f01b5 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 2 Aug 2022 12:32:00 +1000 Subject: [PATCH 071/153] Use vendored TLS for reqwest --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e4e96dbab..542539fc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0" tokio = { version = "1.0", features = ["full"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } -reqwest = { version = "0.11.10", features = ["json"] } +reqwest = { version = "0.11.10", features = ["json", "native-tls-vendored"] } url = "2.2.2" http = "0.2.7" From 1c9c264db4a7c4055cea00e041b19dcb30172c92 Mon Sep 17 00:00:00 2001 From: FancyRatsDave Date: Sat, 3 Sep 2022 13:24:13 -0500 Subject: [PATCH 072/153] Implement remaining Beacon namespace endpoints --- src/api_client.rs | 133 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 25 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 3f65ea17f..498cf9a87 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -232,35 +232,80 @@ impl Client { id: StateId, epoch: Option, ) -> Result, Error> { - unimplemented!("") + let path = format!("eth/v1/beacon/states/{id}/sync_committees"); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + if let Some(epoch) = epoch { + request = request.query(&[("epoch", epoch)]); + } + let response = request.send().await?; + let result: ApiResult>> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn get_beacon_header_at_head(&self) -> Result { - unimplemented!("") + let result: Value = self.get("/eth/v1/beacon/genesis").await?; + Ok(result.data) } - pub async fn get_beacon_header_for_slot(slot: Slot) -> Result { - unimplemented!("") + pub async fn get_beacon_header_for_slot( + &self, + slot: Slot, + ) -> Result { + let target = self.endpoint.join("eth/v1/beacon/headers")?; + let mut request = self.http.get(target); + request = request.query(&[("slot", slot)]); + let response = request.send().await?; + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn get_beacon_header_for_parent_root( + &self, parent_root: Root, ) -> Result { - unimplemented!("") + let target = self.endpoint.join("eth/v1/beacon/headers")?; + let mut request = self.http.get(target); + request = request.query(&[("parent_root", parent_root)]); + let response = request.send().await?; + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } - pub async fn get_beacon_header(id: BlockId) -> Result { - unimplemented!("") + pub async fn get_beacon_header(&self, id: BlockId) -> Result { + let path = format!("eth/v1/beacon/headers/{id}"); + let target = self.endpoint.join(&path)?; + let request = self.http.get(target); + let response = request.send().await?; + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } - pub async fn post_signed_beacon_block(block: &SignedBeaconBlock) -> Result<(), Error> { - unimplemented!("") + pub async fn post_signed_beacon_block(&self, block: &SignedBeaconBlock) -> Result<(), Error> { + let target = self.endpoint.join("eth/v1/beacon/blocks")?; + self.http.post(target).json(block).send().await?; + Ok(()) } pub async fn post_signed_blinded_beacon_block( + &self, block: &SignedBlindedBeaconBlock, ) -> Result<(), Error> { - unimplemented!("") + let target = self.endpoint.join("eth/v1/beacon/blinded_blocks")?; + self.http.post(target).json(block).send().await?; + Ok(()) } // v2 endpoint @@ -307,38 +352,76 @@ impl Client { } } - pub async fn post_attestations(attestations: &[Attestation]) -> Result<(), Error> { - unimplemented!("") + pub async fn post_attestations(&self, attestations: &[Attestation]) -> Result<(), Error> { + let target = self.endpoint.join("eth/v1/beacon/pool/attestations")?; + self.http.post(target).json(&attestations).send().await?; + Ok(()) } - pub async fn get_attester_slashings_from_pool() -> Result, Error> { - unimplemented!("") + pub async fn get_attester_slashings_from_pool(&self) -> Result, Error> { + let result: Value> = + self.get("eth/v1/beacon/pool/attester_slashings").await?; + Ok(result.data) } - pub async fn post_attester_slashing(attester_slashing: &AttesterSlashing) -> Result<(), Error> { - unimplemented!("") + pub async fn post_attester_slashing( + &self, + attester_slashing: &AttesterSlashing, + ) -> Result<(), Error> { + let target = self + .endpoint + .join("eth/v1/beacon/pool/attester_slashings")?; + self.http + .post(target) + .json(&attester_slashing) + .send() + .await?; + Ok(()) } - pub async fn get_proposer_slashings_from_pool() -> Result, Error> { - unimplemented!("") + pub async fn get_proposer_slashings_from_pool(&self) -> Result, Error> { + let result: Value> = + self.get("eth/v1/beacon/pool/proposer_slashings").await?; + Ok(result.data) } - pub async fn post_proposer_slashing(proposer_slashing: &ProposerSlashing) -> Result<(), Error> { - unimplemented!("") + pub async fn post_proposer_slashing( + &self, + proposer_slashing: &ProposerSlashing, + ) -> Result<(), Error> { + let target = self + .endpoint + .join("eth/v1/beacon/pool/proposer_slashings")?; + self.http + .post(target) + .json(&proposer_slashing) + .send() + .await?; + Ok(()) } pub async fn post_sync_committee_messages( + &self, messages: &[SyncCommitteeMessage], ) -> Result<(), Error> { - unimplemented!("") + let target = self.endpoint.join("eth/v1/beacon/pool/sync_committees")?; + self.http.post(target).json(&messages).send().await?; + Ok(()) } - pub async fn get_voluntary_exits_from_pool() -> Result, Error> { - unimplemented!("") + pub async fn get_voluntary_exits_from_pool(&self) -> Result, Error> { + let result: Value> = + self.get("eth/v1/beacon/pool/voluntary_exits").await?; + Ok(result.data) } - pub async fn post_signed_voluntary_exit(exit: &SignedVoluntaryExit) -> Result<(), Error> { - unimplemented!("") + pub async fn post_signed_voluntary_exit( + &self, + exit: &SignedVoluntaryExit, + ) -> Result<(), Error> { + let target = self.endpoint.join("eth/v1/beacon/pool/voluntary_exits")?; + self.http.post(target).json(&exit).send().await?; + Ok(()) } /* config namespace */ From 20cea63411fc22d5c39fe40d84ee241c1d64e3cd Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Mon, 5 Sep 2022 08:09:39 -0600 Subject: [PATCH 073/153] refactor `post` impl to match API spec semantics --- src/api_client.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 3f65ea17f..25b9d46d0 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -91,19 +91,16 @@ impl Client { Ok(response) } - pub async fn post( + pub async fn post( &self, path: &str, argument: &T, - ) -> Result { - let result: ApiResult = self.http_post(path, argument).await?.json().await?; - match result { - ApiResult::Ok(result) => Ok(result), - ApiResult::Err(err) => Err(err.into()), - } + ) -> Result<(), Error> { + let response = self.http_post(path, argument).await?; + api_error_or_ok(response).await } - pub async fn http_post( + pub async fn http_post( &self, path: &str, argument: &T, @@ -511,10 +508,8 @@ impl Client { &self, registrations: &[BeaconProposerRegistration], ) -> Result<(), Error> { - let response = self - .http_post("/eth/v1/validator/prepare_beacon_proposer", ®istrations) - .await?; - api_error_or_ok(response).await + self.post("/eth/v1/validator/prepare_beacon_proposer", registrations) + .await } // endpoint for builder registrations From 9cd4bc751d8120d95ffe1544ecb9edbe7a5485a2 Mon Sep 17 00:00:00 2001 From: "fancyrats.eth" <90787743+FancyRatsDave@users.noreply.github.com> Date: Tue, 6 Sep 2022 12:54:38 -0500 Subject: [PATCH 074/153] Update src/api_client.rs Co-authored-by: Alex Stokes --- src/api_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 498cf9a87..deb161252 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -247,7 +247,7 @@ impl Client { } pub async fn get_beacon_header_at_head(&self) -> Result { - let result: Value = self.get("/eth/v1/beacon/genesis").await?; + let result: Value = self.get("/eth/v1/beacon/headers").await?; Ok(result.data) } From 35299d581cb81303c16b329cfb22ffea05550b63 Mon Sep 17 00:00:00 2001 From: FancyRatsDave Date: Tue, 6 Sep 2022 13:08:28 -0500 Subject: [PATCH 075/153] Update to use self.post instead of self.http.post; Simplified basic self.get call; --- src/api_client.rs | 43 +++++++++---------------------------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index dd25cfe85..91f35dc53 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -280,19 +280,12 @@ impl Client { pub async fn get_beacon_header(&self, id: BlockId) -> Result { let path = format!("eth/v1/beacon/headers/{id}"); - let target = self.endpoint.join(&path)?; - let request = self.http.get(target); - let response = request.send().await?; - let result: ApiResult> = response.json().await?; - match result { - ApiResult::Ok(result) => Ok(result.data), - ApiResult::Err(err) => Err(err.into()), - } + let result: Value = self.get(&path).await?; + Ok(result.data) } pub async fn post_signed_beacon_block(&self, block: &SignedBeaconBlock) -> Result<(), Error> { - let target = self.endpoint.join("eth/v1/beacon/blocks")?; - self.http.post(target).json(block).send().await?; + self.post("/eth/v1/beacon/blocks", block).await?; Ok(()) } @@ -300,8 +293,7 @@ impl Client { &self, block: &SignedBlindedBeaconBlock, ) -> Result<(), Error> { - let target = self.endpoint.join("eth/v1/beacon/blinded_blocks")?; - self.http.post(target).json(block).send().await?; + self.post("/eth/v1/beacon/blinded_blocks", block).await?; Ok(()) } @@ -350,8 +342,7 @@ impl Client { } pub async fn post_attestations(&self, attestations: &[Attestation]) -> Result<(), Error> { - let target = self.endpoint.join("eth/v1/beacon/pool/attestations")?; - self.http.post(target).json(&attestations).send().await?; + self.post("/eth/v1/beacon/pool/attestations", attestations).await?; Ok(()) } @@ -365,14 +356,7 @@ impl Client { &self, attester_slashing: &AttesterSlashing, ) -> Result<(), Error> { - let target = self - .endpoint - .join("eth/v1/beacon/pool/attester_slashings")?; - self.http - .post(target) - .json(&attester_slashing) - .send() - .await?; + self.post("/eth/v1/beacon/pool/attester_slashings", attester_slashing).await?; Ok(()) } @@ -386,14 +370,7 @@ impl Client { &self, proposer_slashing: &ProposerSlashing, ) -> Result<(), Error> { - let target = self - .endpoint - .join("eth/v1/beacon/pool/proposer_slashings")?; - self.http - .post(target) - .json(&proposer_slashing) - .send() - .await?; + self.post("/eth/v1/beacon/pool/proposer_slashings", proposer_slashing).await?; Ok(()) } @@ -401,8 +378,7 @@ impl Client { &self, messages: &[SyncCommitteeMessage], ) -> Result<(), Error> { - let target = self.endpoint.join("eth/v1/beacon/pool/sync_committees")?; - self.http.post(target).json(&messages).send().await?; + self.post("/eth/v1/beacon/pool/sync_committees", messages).await?; Ok(()) } @@ -416,8 +392,7 @@ impl Client { &self, exit: &SignedVoluntaryExit, ) -> Result<(), Error> { - let target = self.endpoint.join("eth/v1/beacon/pool/voluntary_exits")?; - self.http.post(target).json(&exit).send().await?; + self.post("/eth/v1/beacon/pool/voluntary_exits", exit).await?; Ok(()) } From 0070f761a414adff2930b988c321ee741b7ce2b1 Mon Sep 17 00:00:00 2001 From: FancyRatsDave Date: Tue, 6 Sep 2022 16:36:53 -0500 Subject: [PATCH 076/153] Cargo fmt --- src/api_client.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 91f35dc53..dfb32ae2e 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -342,7 +342,8 @@ impl Client { } pub async fn post_attestations(&self, attestations: &[Attestation]) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/attestations", attestations).await?; + self.post("/eth/v1/beacon/pool/attestations", attestations) + .await?; Ok(()) } @@ -356,7 +357,8 @@ impl Client { &self, attester_slashing: &AttesterSlashing, ) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/attester_slashings", attester_slashing).await?; + self.post("/eth/v1/beacon/pool/attester_slashings", attester_slashing) + .await?; Ok(()) } @@ -370,7 +372,8 @@ impl Client { &self, proposer_slashing: &ProposerSlashing, ) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/proposer_slashings", proposer_slashing).await?; + self.post("/eth/v1/beacon/pool/proposer_slashings", proposer_slashing) + .await?; Ok(()) } @@ -378,7 +381,8 @@ impl Client { &self, messages: &[SyncCommitteeMessage], ) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/sync_committees", messages).await?; + self.post("/eth/v1/beacon/pool/sync_committees", messages) + .await?; Ok(()) } @@ -392,7 +396,8 @@ impl Client { &self, exit: &SignedVoluntaryExit, ) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/voluntary_exits", exit).await?; + self.post("/eth/v1/beacon/pool/voluntary_exits", exit) + .await?; Ok(()) } From 69c3eda692de3e21ff29cd2e8ff20eb40d63b39b Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 7 Sep 2022 08:48:00 -0600 Subject: [PATCH 077/153] pin version of `ethereum-consensus` --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 542539fc3..92443f4b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ itertools = "0.10.3" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "v0.1.1" } [features] peer-id = ["ethereum-consensus/peer-id"] From de34eeb92e4fdee5709d142910abf42cf857609b Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 7 Sep 2022 11:32:19 -0600 Subject: [PATCH 078/153] update dep version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 92443f4b9..f61fea606 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ itertools = "0.10.3" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "v0.1.1" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "e1188b1" } [features] peer-id = ["ethereum-consensus/peer-id"] From 0fb81cf9a0beaf7d295c2f364c259a8977b3d1a6 Mon Sep 17 00:00:00 2001 From: literallymarvellous Date: Thu, 8 Sep 2022 09:53:18 +0100 Subject: [PATCH 079/153] refactor: api_error_or_ok --- src/api_client.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 25b9d46d0..a7a5eefe2 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -47,11 +47,13 @@ pub enum Error { } pub async fn api_error_or_ok(response: reqwest::Response) -> Result<(), Error> { - if response.status() == reqwest::StatusCode::OK { - Ok(()) - } else { - let api_err = response.json::().await?; - Err(Error::Api(api_err)) + match response.status() { + reqwest::StatusCode::OK => Ok(()), + reqwest::StatusCode::ACCEPTED => Ok(()), + _ => { + let api_err = response.json::().await?; + Err(Error::Api(api_err)) + } } } From 7ab7d51e4e03ee555f9a7237d6402609bc012e7c Mon Sep 17 00:00:00 2001 From: FancyRatsDave Date: Thu, 8 Sep 2022 09:08:31 -0500 Subject: [PATCH 080/153] More idiomatic post functions --- src/api_client.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index bb20a6c1b..af94fae2b 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -287,16 +287,14 @@ impl Client { } pub async fn post_signed_beacon_block(&self, block: &SignedBeaconBlock) -> Result<(), Error> { - self.post("/eth/v1/beacon/blocks", block).await?; - Ok(()) + self.post("/eth/v1/beacon/blocks", block).await } pub async fn post_signed_blinded_beacon_block( &self, block: &SignedBlindedBeaconBlock, ) -> Result<(), Error> { - self.post("/eth/v1/beacon/blinded_blocks", block).await?; - Ok(()) + self.post("/eth/v1/beacon/blinded_blocks", block).await } // v2 endpoint @@ -345,8 +343,7 @@ impl Client { pub async fn post_attestations(&self, attestations: &[Attestation]) -> Result<(), Error> { self.post("/eth/v1/beacon/pool/attestations", attestations) - .await?; - Ok(()) + .await } pub async fn get_attester_slashings_from_pool(&self) -> Result, Error> { @@ -360,8 +357,7 @@ impl Client { attester_slashing: &AttesterSlashing, ) -> Result<(), Error> { self.post("/eth/v1/beacon/pool/attester_slashings", attester_slashing) - .await?; - Ok(()) + .await } pub async fn get_proposer_slashings_from_pool(&self) -> Result, Error> { @@ -375,8 +371,7 @@ impl Client { proposer_slashing: &ProposerSlashing, ) -> Result<(), Error> { self.post("/eth/v1/beacon/pool/proposer_slashings", proposer_slashing) - .await?; - Ok(()) + .await } pub async fn post_sync_committee_messages( @@ -384,8 +379,7 @@ impl Client { messages: &[SyncCommitteeMessage], ) -> Result<(), Error> { self.post("/eth/v1/beacon/pool/sync_committees", messages) - .await?; - Ok(()) + .await } pub async fn get_voluntary_exits_from_pool(&self) -> Result, Error> { @@ -398,9 +392,7 @@ impl Client { &self, exit: &SignedVoluntaryExit, ) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/voluntary_exits", exit) - .await?; - Ok(()) + self.post("/eth/v1/beacon/pool/voluntary_exits", exit).await } /* config namespace */ From 71c651c7e96368b28b59777ea3138d3a9e19bd70 Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Fri, 9 Sep 2022 02:51:27 +0200 Subject: [PATCH 081/153] Fix a bug in query join --- src/api_client.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index a7a5eefe2..69b5fd3c5 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -149,11 +149,11 @@ impl Client { let target = self.endpoint.join(&path)?; let mut request = self.http.get(target); if !validator_ids.is_empty() { - let validator_ids = validator_ids.iter().join(", "); + let validator_ids = validator_ids.iter().join(","); request = request.query(&[("id", validator_ids)]); } if !filters.is_empty() { - let filters = filters.iter().join(", "); + let filters = filters.iter().join(","); request = request.query(&[("status", filters)]); } let response = request.send().await?; @@ -185,7 +185,7 @@ impl Client { let mut request = self.http.get(target); if !filters.is_empty() { - let filters = filters.iter().join(", "); + let filters = filters.iter().join(","); request = request.query(&[("id", filters)]); } let response = request.send().await?; From c3dc7421aeaf73b3658331ed1ca7815519486536 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 9 Sep 2022 09:48:11 +0100 Subject: [PATCH 082/153] implements the 3 endpoints in config namespace rm redunant type rm redundant imports --- src/api_client.rs | 22 ++++++++++++---------- src/types.rs | 11 +++++++++-- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 25b9d46d0..09e8ec56d 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -4,7 +4,7 @@ use crate::error::ApiError; use crate::types::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, - BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, EventTopic, + BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, DepositContract, EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, PeerSummary, ProposerDuty, PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, @@ -23,8 +23,7 @@ use ethereum_consensus::phase0::mainnet::{ ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, }; use ethereum_consensus::primitives::{ - Bytes32, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, RandaoReveal, Root, - Slot, ValidatorIndex, + Bytes32, CommitteeIndex, Coordinate, Epoch, RandaoReveal, Root, Slot, ValidatorIndex, }; use http::StatusCode; use itertools::Itertools; @@ -294,7 +293,7 @@ impl Client { request = request.query(&[("slot", slot)]); } if let Some(committee_index) = committee_index { - request = request.query(&[("committe_index", committee_index)]); + request = request.query(&[("committee_index", committee_index)]); } let response = request.send().await?; let result: ApiResult>> = response.json().await?; @@ -339,16 +338,19 @@ impl Client { } /* config namespace */ - pub async fn get_fork_schedule() -> Result, Error> { - unimplemented!("") + pub async fn get_fork_schedule(&self) -> Result, Error> { + let result: Value> = self.get("eth/v1/config/fork_schedule/").await?; + Ok(result.data) } - pub async fn get_spec() -> Result, Error> { - unimplemented!("") + pub async fn get_spec(&self) -> Result, Error> { + let result: Value> = self.get("eth/v1/config/spec/").await?; + Ok(result.data) } - pub async fn get_deposit_contract_address() -> Result<(ChainId, ExecutionAddress), Error> { - unimplemented!("") + pub async fn get_deposit_contract_address(&self) -> Result { + let result: Value = self.get("/eth/v1/config/deposit_contract").await?; + Ok(result.data) } /* debug namespace */ diff --git a/src/types.rs b/src/types.rs index 96d9d1f4c..eebcec286 100644 --- a/src/types.rs +++ b/src/types.rs @@ -3,8 +3,8 @@ use crate::error::ApiError; use ethereum_consensus::networking::{Enr, MetaData, Multiaddr, PeerId}; use ethereum_consensus::phase0::mainnet::{Checkpoint, SignedBeaconBlockHeader, Validator}; use ethereum_consensus::primitives::{ - BlsPublicKey, CommitteeIndex, Epoch, ExecutionAddress, Gwei, Root, Slot, ValidatorIndex, - Version, + BlsPublicKey, ChainId, CommitteeIndex, Epoch, ExecutionAddress, Gwei, Root, Slot, + ValidatorIndex, Version, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::collections::HashMap; @@ -15,6 +15,13 @@ pub struct VersionData { pub version: String, } +#[derive(Serialize, Deserialize)] +pub struct DepositContract { + #[serde(with = "crate::serde::as_string")] + pub chain_id: ChainId, + pub address: ExecutionAddress, +} + #[derive(Serialize, Deserialize)] pub struct GenesisDetails { #[serde(with = "crate::serde::as_string")] From 1377fc681232a238be473cbd04fbef0c20d05381 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Sat, 10 Sep 2022 18:29:50 +0100 Subject: [PATCH 083/153] Apply suggestions from code review Co-authored-by: Alex Stokes --- src/api_client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 09e8ec56d..8221ce696 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -339,12 +339,12 @@ impl Client { /* config namespace */ pub async fn get_fork_schedule(&self) -> Result, Error> { - let result: Value> = self.get("eth/v1/config/fork_schedule/").await?; + let result: Value> = self.get("eth/v1/config/fork_schedule").await?; Ok(result.data) } pub async fn get_spec(&self) -> Result, Error> { - let result: Value> = self.get("eth/v1/config/spec/").await?; + let result: Value> = self.get("eth/v1/config/spec").await?; Ok(result.data) } From 3761d3979bf183242cd821bf8ca42812d37ad555 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 14 Sep 2022 16:11:28 +0200 Subject: [PATCH 084/153] remove `peer-id` feature gate --- Cargo.toml | 5 +---- src/api_client.rs | 5 ----- src/types.rs | 3 --- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f61fea606..2ea3da069 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,4 @@ itertools = "0.10.3" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "e1188b1" } - -[features] -peer-id = ["ethereum-consensus/peer-id"] +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "a8110af76d97bf2bf27fb987a671808fcbdf1834" } diff --git a/src/api_client.rs b/src/api_client.rs index beff5f531..b8b4640ce 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -9,14 +9,12 @@ use crate::types::{ RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, }; -#[cfg(feature = "peer-id")] use crate::types::{NetworkIdentity, PeerDescription, PeerDescriptor}; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, }; use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; use ethereum_consensus::builder::SignedValidatorRegistration; -#[cfg(feature = "peer-id")] use ethereum_consensus::networking::Multiaddr; use ethereum_consensus::phase0::mainnet::{ Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, @@ -429,17 +427,14 @@ impl Client { } /* node namespace */ - #[cfg(feature = "peer-id")] pub async fn get_node_identity() -> Result { unimplemented!("") } - #[cfg(feature = "peer-id")] pub async fn get_node_peers(filters: &[PeerDescriptor]) -> Result, Error> { unimplemented!("") } - #[cfg(feature = "peer-id")] pub async fn get_peer(peer_id: Multiaddr) -> Result { unimplemented!("") } diff --git a/src/types.rs b/src/types.rs index eebcec286..47f5362db 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,4 @@ use crate::error::ApiError; -#[cfg(feature = "peer-id")] use ethereum_consensus::networking::{Enr, MetaData, Multiaddr, PeerId}; use ethereum_consensus::phase0::mainnet::{Checkpoint, SignedBeaconBlockHeader, Validator}; use ethereum_consensus::primitives::{ @@ -222,7 +221,6 @@ pub enum EventTopic { ContributionAndProof, } -#[cfg(feature = "peer-id")] #[derive(Serialize, Deserialize)] pub struct NetworkIdentity { pub peer_id: PeerId, @@ -252,7 +250,6 @@ pub struct PeerDescriptor { pub direction: ConnectionOrientation, } -#[cfg(feature = "peer-id")] #[derive(Serialize, Deserialize)] pub struct PeerDescription { pub peer_id: PeerId, From bcc0767fade94c30eef2436542091530478e3713 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 13 Sep 2022 09:49:48 +0100 Subject: [PATCH 085/153] impl debug namespace rm unused imports --- src/api_client.rs | 30 +++++++++++++++++------------- src/types.rs | 8 ++++++++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index b8b4640ce..81aa2d22f 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -4,24 +4,24 @@ use crate::error::ApiError; use crate::types::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, - BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, DepositContract, EventTopic, - FinalityCheckpoints, GenesisDetails, HealthStatus, PeerSummary, ProposerDuty, PublicKeyOrIndex, - RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, - SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, + BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, Coordinate, DepositContract, + EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, PeerSummary, ProposerDuty, + PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, + SyncCommitteeSummary, SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, }; use crate::types::{NetworkIdentity, PeerDescription, PeerDescriptor}; +use ethereum_consensus::altair::mainnet::{ + Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, + ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, +}; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, }; use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; use ethereum_consensus::builder::SignedValidatorRegistration; use ethereum_consensus::networking::Multiaddr; -use ethereum_consensus::phase0::mainnet::{ - Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, - ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, -}; use ethereum_consensus::primitives::{ - Bytes32, CommitteeIndex, Coordinate, Epoch, RandaoReveal, Root, Slot, ValidatorIndex, + Bytes32, CommitteeIndex, Epoch, RandaoReveal, Root, Slot, ValidatorIndex, }; use http::StatusCode; use itertools::Itertools; @@ -410,13 +410,17 @@ impl Client { /* debug namespace */ // v2 endpoint - pub async fn get_state(id: StateId) -> Result { - unimplemented!("") + pub async fn get_state(&self, id: StateId) -> Result { + let result: Value = self + .get(&format!("eth/v2/debug/beacon/states/{}", id)) + .await?; + Ok(result.data) } // v2 endpoint - pub async fn get_heads() -> Result, Error> { - unimplemented!("") + pub async fn get_heads(&self) -> Result, Error> { + let result: Value> = self.get("/eth/v2/debug/beacon/heads").await?; + Ok(result.data) } /* events namespace */ diff --git a/src/types.rs b/src/types.rs index 47f5362db..cc0ebfc42 100644 --- a/src/types.rs +++ b/src/types.rs @@ -14,6 +14,14 @@ pub struct VersionData { pub version: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct Coordinate { + #[serde(with = "crate::serde::as_string")] + slot: Slot, + root: Root, + execution_optimistic: bool, +} + #[derive(Serialize, Deserialize)] pub struct DepositContract { #[serde(with = "crate::serde::as_string")] From 82aedcf383400bdb6ba406f6b1b784a09aba08d4 Mon Sep 17 00:00:00 2001 From: Joe Date: Thu, 15 Sep 2022 13:58:09 +0100 Subject: [PATCH 086/153] fix toml --- Cargo.toml | 3 +-- src/api_client.rs | 24 +++++++++++++++--------- src/types.rs | 14 +++++++------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2ea3da069..c3c675d51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,5 +19,4 @@ serde_json = "1.0.81" itertools = "0.10.3" thiserror = "1.0.30" - -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "a8110af76d97bf2bf27fb987a671808fcbdf1834" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="680616d"} diff --git a/src/api_client.rs b/src/api_client.rs index 81aa2d22f..fd163b479 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -4,22 +4,24 @@ use crate::error::ApiError; use crate::types::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, - BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, Coordinate, DepositContract, - EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, PeerSummary, ProposerDuty, - PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, + BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, CoordinateWithMetadata, + DepositContract, EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, PeerSummary, + ProposerDuty, PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, }; +#[cfg(feature = "peer-id")] use crate::types::{NetworkIdentity, PeerDescription, PeerDescriptor}; -use ethereum_consensus::altair::mainnet::{ - Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, - ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, -}; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, }; use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; use ethereum_consensus::builder::SignedValidatorRegistration; +#[cfg(feature = "peer-id")] use ethereum_consensus::networking::Multiaddr; +use ethereum_consensus::phase0::mainnet::{ + Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, + ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, +}; use ethereum_consensus::primitives::{ Bytes32, CommitteeIndex, Epoch, RandaoReveal, Root, Slot, ValidatorIndex, }; @@ -418,8 +420,9 @@ impl Client { } // v2 endpoint - pub async fn get_heads(&self) -> Result, Error> { - let result: Value> = self.get("/eth/v2/debug/beacon/heads").await?; + pub async fn get_heads(&self) -> Result, Error> { + let result: Value> = + self.get("/eth/v2/debug/beacon/heads").await?; Ok(result.data) } @@ -431,14 +434,17 @@ impl Client { } /* node namespace */ + #[cfg(feature = "peer-id")] pub async fn get_node_identity() -> Result { unimplemented!("") } + #[cfg(feature = "peer-id")] pub async fn get_node_peers(filters: &[PeerDescriptor]) -> Result, Error> { unimplemented!("") } + #[cfg(feature = "peer-id")] pub async fn get_peer(peer_id: Multiaddr) -> Result { unimplemented!("") } diff --git a/src/types.rs b/src/types.rs index cc0ebfc42..33dc885ff 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,7 +2,7 @@ use crate::error::ApiError; use ethereum_consensus::networking::{Enr, MetaData, Multiaddr, PeerId}; use ethereum_consensus::phase0::mainnet::{Checkpoint, SignedBeaconBlockHeader, Validator}; use ethereum_consensus::primitives::{ - BlsPublicKey, ChainId, CommitteeIndex, Epoch, ExecutionAddress, Gwei, Root, Slot, + BlsPublicKey, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, Root, Slot, ValidatorIndex, Version, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -14,12 +14,12 @@ pub struct VersionData { pub version: String, } -#[derive(Serialize, Deserialize, Debug)] -pub struct Coordinate { - #[serde(with = "crate::serde::as_string")] - slot: Slot, - root: Root, - execution_optimistic: bool, +#[derive(Serialize, Deserialize)] +pub struct CoordinateWithMetadata { + #[serde(flatten)] + pub coordinate: Coordinate, + #[serde(flatten)] + pub meta: HashMap, } #[derive(Serialize, Deserialize)] From 32a0294ca67746850efbcbf2f4a50d6a9a6f41bb Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 15 Sep 2022 15:21:27 +0200 Subject: [PATCH 087/153] Update src/api_client.rs --- src/api_client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index fd163b479..899e40858 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -9,7 +9,6 @@ use crate::types::{ ProposerDuty, PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, }; -#[cfg(feature = "peer-id")] use crate::types::{NetworkIdentity, PeerDescription, PeerDescriptor}; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, From 363037a56556bf84014a1260e1f52b5b95e82c8f Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 15 Sep 2022 15:21:43 +0200 Subject: [PATCH 088/153] Update src/api_client.rs --- src/api_client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 899e40858..419c6b848 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -15,7 +15,6 @@ use ethereum_consensus::altair::mainnet::{ }; use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; use ethereum_consensus::builder::SignedValidatorRegistration; -#[cfg(feature = "peer-id")] use ethereum_consensus::networking::Multiaddr; use ethereum_consensus::phase0::mainnet::{ Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, From 019197a08edadfed123b9ff24fdbfd4c66faa220 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 15 Sep 2022 15:21:53 +0200 Subject: [PATCH 089/153] Update src/api_client.rs --- src/api_client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 419c6b848..8c4ec6587 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -432,7 +432,6 @@ impl Client { } /* node namespace */ - #[cfg(feature = "peer-id")] pub async fn get_node_identity() -> Result { unimplemented!("") } From d21fa9261c4bd43d411b14fb4ccebeb517598a54 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 15 Sep 2022 15:22:00 +0200 Subject: [PATCH 090/153] Update src/api_client.rs --- src/api_client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 8c4ec6587..3f0a14e5b 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -436,7 +436,6 @@ impl Client { unimplemented!("") } - #[cfg(feature = "peer-id")] pub async fn get_node_peers(filters: &[PeerDescriptor]) -> Result, Error> { unimplemented!("") } From 70e80d391cc6851940cbd19f1c19db77548a6ba9 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 15 Sep 2022 15:22:05 +0200 Subject: [PATCH 091/153] Update src/api_client.rs --- src/api_client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 3f0a14e5b..dd18d1dcf 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -440,7 +440,6 @@ impl Client { unimplemented!("") } - #[cfg(feature = "peer-id")] pub async fn get_peer(peer_id: Multiaddr) -> Result { unimplemented!("") } From 63c4e51e5508615b1585e6ebf8872d5daed89289 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 20 Sep 2022 11:53:40 +0100 Subject: [PATCH 092/153] impl get_node_identity & attempt get_node_peers fix clippy fix get_node_peers() fix clippy --- src/api_client.rs | 29 ++++++++++++++++++++++++----- src/types.rs | 32 +++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index dd18d1dcf..8000658ee 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -9,7 +9,7 @@ use crate::types::{ ProposerDuty, PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, }; -use crate::types::{NetworkIdentity, PeerDescription, PeerDescriptor}; +use crate::types::{ConnectionOrientation, NetworkIdentity, PeerDescription, PeerState}; use ethereum_consensus::altair::mainnet::{ SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, }; @@ -432,12 +432,31 @@ impl Client { } /* node namespace */ - pub async fn get_node_identity() -> Result { - unimplemented!("") + pub async fn get_node_identity(&self) -> Result { + let result: Value = self.get("/eth/v1/node/identity").await?; + Ok(result.data) } - pub async fn get_node_peers(filters: &[PeerDescriptor]) -> Result, Error> { - unimplemented!("") + pub async fn get_node_peers( + &self, + peer_states: &[PeerState], + connection_orientations: &[ConnectionOrientation], + ) -> Result, Error> { + let path = "eth/v1/node/peers"; + let target = self.endpoint.join(path)?; + let mut request = self.http.get(target); + if !peer_states.is_empty() { + request = request.query(&[("state", peer_states.iter().join(","))]); + } + if !connection_orientations.is_empty() { + request = request.query(&[("direction", connection_orientations.iter().join(","))]); + } + let response = request.send().await?; + let result: ApiResult>> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn get_peer(peer_id: Multiaddr) -> Result { diff --git a/src/types.rs b/src/types.rs index 33dc885ff..0ae6c6d2d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -238,7 +238,7 @@ pub struct NetworkIdentity { pub metadata: MetaData, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub enum PeerState { Disconnected, Connecting, @@ -246,25 +246,47 @@ pub enum PeerState { Disconnecting, } -#[derive(Serialize, Deserialize)] +impl fmt::Display for PeerState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + Self::Disconnected => "disconnected", + Self::Connecting => "connecting", + Self::Connected => "connected", + Self::Disconnecting => "disconnecting", + }; + write!(f, "{}", printable) + } +} + +#[derive(Serialize, Deserialize, Debug)] pub enum ConnectionOrientation { Inbound, Outbound, } +impl fmt::Display for ConnectionOrientation { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + Self::Inbound => "inbound", + Self::Outbound => "outbound", + }; + write!(f, "{}", printable) + } +} + #[derive(Serialize, Deserialize)] pub struct PeerDescriptor { pub state: PeerState, pub direction: ConnectionOrientation, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct PeerDescription { pub peer_id: PeerId, pub enr: Enr, pub last_seen_p2p_address: Multiaddr, - pub state: PeerState, - pub direction: ConnectionOrientation, + pub state: String, + pub direction: String, } #[derive(Serialize, Deserialize, Debug)] From f31681a6cd9b87fec71a6451331e04906aee64fc Mon Sep 17 00:00:00 2001 From: Joe Date: Sat, 8 Oct 2022 20:47:05 +0100 Subject: [PATCH 093/153] fix types using serde(rename_all ="lowercase") --- src/types.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/types.rs b/src/types.rs index 0ae6c6d2d..d2a981079 100644 --- a/src/types.rs +++ b/src/types.rs @@ -239,6 +239,7 @@ pub struct NetworkIdentity { } #[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "lowercase")] pub enum PeerState { Disconnected, Connecting, @@ -259,6 +260,7 @@ impl fmt::Display for PeerState { } #[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "lowercase")] pub enum ConnectionOrientation { Inbound, Outbound, @@ -285,8 +287,8 @@ pub struct PeerDescription { pub peer_id: PeerId, pub enr: Enr, pub last_seen_p2p_address: Multiaddr, - pub state: String, - pub direction: String, + pub state: PeerState, + pub direction: ConnectionOrientation, } #[derive(Serialize, Deserialize, Debug)] From 5141bb672061095df959c14a724e2888ea527fc6 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 14 Oct 2022 09:49:27 +0100 Subject: [PATCH 094/153] impl get_peer() fix clippy fix fmt --- src/api_client.rs | 11 ++++++++--- src/types.rs | 6 ++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index dd18d1dcf..97b288163 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -15,7 +15,7 @@ use ethereum_consensus::altair::mainnet::{ }; use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; use ethereum_consensus::builder::SignedValidatorRegistration; -use ethereum_consensus::networking::Multiaddr; +use ethereum_consensus::networking::PeerId; use ethereum_consensus::phase0::mainnet::{ Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, @@ -440,8 +440,13 @@ impl Client { unimplemented!("") } - pub async fn get_peer(peer_id: Multiaddr) -> Result { - unimplemented!("") + pub async fn get_peer(&self, peer_id: PeerId) -> Result { + let id = serde_json::to_string(&peer_id) + .unwrap() + .replace(&['\"'][..], ""); + let result: Value = + self.get(&format!("/eth/v1/node/peers/{}", id)).await?; + Ok(result.data) } pub async fn get_peer_summary(&self) -> Result { diff --git a/src/types.rs b/src/types.rs index 33dc885ff..80726f52f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -238,7 +238,8 @@ pub struct NetworkIdentity { pub metadata: MetaData, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "lowercase")] pub enum PeerState { Disconnected, Connecting, @@ -246,7 +247,8 @@ pub enum PeerState { Disconnecting, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "lowercase")] pub enum ConnectionOrientation { Inbound, Outbound, From 67961e6a7c72e38f2fa8569466946a7f4a7e314f Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 17 Oct 2022 09:29:18 +0100 Subject: [PATCH 095/153] impl get_sync_status --- src/api_client.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index dd18d1dcf..0179bf6f1 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -454,8 +454,9 @@ impl Client { Ok(result.data.version) } - pub async fn get_sync_status() -> Result { - unimplemented!("") + pub async fn get_sync_status(&self) -> Result { + let result: Value = self.get("eth/v1/node/syncing").await?; + Ok(result.data) } pub async fn get_health(&self) -> Result { From 26d1bf7cd3ad11fbc78cd6a1fbed7fdad595a10d Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 24 Oct 2022 16:52:54 +0100 Subject: [PATCH 096/153] impl post_attester_duties() --- src/api_client.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index dd18d1dcf..854f964d9 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -474,10 +474,22 @@ impl Client { /* validator namespace */ pub async fn get_attester_duties( + &self, epoch: Epoch, indices: &[ValidatorIndex], ) -> Result<(Root, Vec), Error> { - unimplemented!("") + let endpoint = format!("eth/v1/validator/duties/attester/{epoch}"); + let mut result: Value> = self + .http_post(&endpoint, indices) + .await? + .json() + .await + .unwrap(); + let dependent_root_value = result.meta.remove("dependent_root").ok_or_else(|| { + Error::MissingExpectedData("missing `dependent_root` in response".to_string()) + })?; + let dependent_root: Root = serde_json::from_value(dependent_root_value)?; + Ok((dependent_root, result.data)) } pub async fn get_proposer_duties( From 78e24f6c3b7b2fa2765e42e2eda23fc1827d3ff2 Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 24 Oct 2022 20:44:16 +0100 Subject: [PATCH 097/153] implement get_sync_committee_duties --- src/api_client.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index dd18d1dcf..83f76d0fd 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -494,10 +494,18 @@ impl Client { } pub async fn get_sync_committee_duties( + &self, epoch: Epoch, indices: &[ValidatorIndex], - ) -> Result<(Root, Vec), Error> { - unimplemented!("") + ) -> Result, Error> { + let endpoint = format!("eth/v1/validator/duties/sync/{epoch}"); + let mut result: Value> = self + .http_post(&endpoint, indices) + .await? + .json() + .await + .unwrap(); + Ok(result.data) } // v2 endpoint From d2a094458c1cc76f43d3113c0a7a5c8f85320d98 Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 24 Oct 2022 20:55:57 +0100 Subject: [PATCH 098/153] fix clippy --- src/api_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 83f76d0fd..552d6282c 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -499,7 +499,7 @@ impl Client { indices: &[ValidatorIndex], ) -> Result, Error> { let endpoint = format!("eth/v1/validator/duties/sync/{epoch}"); - let mut result: Value> = self + let result: Value> = self .http_post(&endpoint, indices) .await? .json() From 326e8833f36f181a1c528536a4542a947a8937af Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Mon, 24 Oct 2022 14:40:02 -0600 Subject: [PATCH 099/153] make URL text consistent --- src/api_client.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 8000658ee..cf14c01a7 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -111,7 +111,7 @@ impl Client { /* beacon namespace */ pub async fn get_genesis_details(&self) -> Result { - let details: Value = self.get("/eth/v1/beacon/genesis").await?; + let details: Value = self.get("eth/v1/beacon/genesis").await?; Ok(details.data) } @@ -243,7 +243,7 @@ impl Client { } pub async fn get_beacon_header_at_head(&self) -> Result { - let result: Value = self.get("/eth/v1/beacon/headers").await?; + let result: Value = self.get("eth/v1/beacon/headers").await?; Ok(result.data) } @@ -284,14 +284,14 @@ impl Client { } pub async fn post_signed_beacon_block(&self, block: &SignedBeaconBlock) -> Result<(), Error> { - self.post("/eth/v1/beacon/blocks", block).await + self.post("eth/v1/beacon/blocks", block).await } pub async fn post_signed_blinded_beacon_block( &self, block: &SignedBlindedBeaconBlock, ) -> Result<(), Error> { - self.post("/eth/v1/beacon/blinded_blocks", block).await + self.post("eth/v1/beacon/blinded_blocks", block).await } // v2 endpoint @@ -339,7 +339,7 @@ impl Client { } pub async fn post_attestations(&self, attestations: &[Attestation]) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/attestations", attestations) + self.post("eth/v1/beacon/pool/attestations", attestations) .await } @@ -353,7 +353,7 @@ impl Client { &self, attester_slashing: &AttesterSlashing, ) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/attester_slashings", attester_slashing) + self.post("eth/v1/beacon/pool/attester_slashings", attester_slashing) .await } @@ -367,7 +367,7 @@ impl Client { &self, proposer_slashing: &ProposerSlashing, ) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/proposer_slashings", proposer_slashing) + self.post("eth/v1/beacon/pool/proposer_slashings", proposer_slashing) .await } @@ -375,7 +375,7 @@ impl Client { &self, messages: &[SyncCommitteeMessage], ) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/sync_committees", messages) + self.post("eth/v1/beacon/pool/sync_committees", messages) .await } @@ -389,7 +389,7 @@ impl Client { &self, exit: &SignedVoluntaryExit, ) -> Result<(), Error> { - self.post("/eth/v1/beacon/pool/voluntary_exits", exit).await + self.post("eth/v1/beacon/pool/voluntary_exits", exit).await } /* config namespace */ @@ -404,7 +404,7 @@ impl Client { } pub async fn get_deposit_contract_address(&self) -> Result { - let result: Value = self.get("/eth/v1/config/deposit_contract").await?; + let result: Value = self.get("eth/v1/config/deposit_contract").await?; Ok(result.data) } @@ -420,7 +420,7 @@ impl Client { // v2 endpoint pub async fn get_heads(&self) -> Result, Error> { let result: Value> = - self.get("/eth/v2/debug/beacon/heads").await?; + self.get("eth/v2/debug/beacon/heads").await?; Ok(result.data) } @@ -433,7 +433,7 @@ impl Client { /* node namespace */ pub async fn get_node_identity(&self) -> Result { - let result: Value = self.get("/eth/v1/node/identity").await?; + let result: Value = self.get("eth/v1/node/identity").await?; Ok(result.data) } @@ -586,7 +586,7 @@ impl Client { &self, registrations: &[BeaconProposerRegistration], ) -> Result<(), Error> { - self.post("/eth/v1/validator/prepare_beacon_proposer", registrations) + self.post("eth/v1/validator/prepare_beacon_proposer", registrations) .await } From cbe5bfff880497527153c496e7892d4a1b573449 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 25 Oct 2022 15:42:34 +0100 Subject: [PATCH 100/153] indices as strings --- src/api_client.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 854f964d9..602e4548e 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -479,8 +479,12 @@ impl Client { indices: &[ValidatorIndex], ) -> Result<(Root, Vec), Error> { let endpoint = format!("eth/v1/validator/duties/attester/{epoch}"); + let indices = indices + .iter() + .map(|index| index.to_string()) + .collect::>(); let mut result: Value> = self - .http_post(&endpoint, indices) + .http_post(&endpoint, &indices) .await? .json() .await From 731d9d39fad01d3d1a74cc841b4e342d53c05dcd Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:44:57 +0100 Subject: [PATCH 101/153] Update src/api_client.rs Co-authored-by: Alex Stokes --- src/api_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 602e4548e..40e019076 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -490,7 +490,7 @@ impl Client { .await .unwrap(); let dependent_root_value = result.meta.remove("dependent_root").ok_or_else(|| { - Error::MissingExpectedData("missing `dependent_root` in response".to_string()) + Error::MissingExpectedData("`dependent_root`".to_string()) })?; let dependent_root: Root = serde_json::from_value(dependent_root_value)?; Ok((dependent_root, result.data)) From d48dfa6c82bfe57aff6696d96f49723d2cfd08d6 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:50:57 +0100 Subject: [PATCH 102/153] fix clippy --- src/api_client.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 40e019076..51b8b9fc2 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -489,9 +489,11 @@ impl Client { .json() .await .unwrap(); - let dependent_root_value = result.meta.remove("dependent_root").ok_or_else(|| { - Error::MissingExpectedData("`dependent_root`".to_string()) - })?; + let dependent_root_value = result + .meta + .remove("dependent_root") + .ok_or_else(|| { + Error::MissingExpectedData("`dependent_root`".to_string())})?; let dependent_root: Root = serde_json::from_value(dependent_root_value)?; Ok((dependent_root, result.data)) } From ffeb82b7b1188a249aa3ce499420b861a38aef86 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 25 Oct 2022 15:52:06 +0100 Subject: [PATCH 103/153] indices as strings --- src/api_client.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 552d6282c..daf7e099d 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -499,8 +499,12 @@ impl Client { indices: &[ValidatorIndex], ) -> Result, Error> { let endpoint = format!("eth/v1/validator/duties/sync/{epoch}"); + let indices = indices + .iter() + .map(|index| index.to_string()) + .collect::>(); let result: Value> = self - .http_post(&endpoint, indices) + .http_post(&endpoint, &indices) .await? .json() .await From d0f48b2228e2f083c2bf1d3537125b599855d058 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Tue, 25 Oct 2022 16:26:32 +0100 Subject: [PATCH 104/153] fix fmt --- src/api_client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 51b8b9fc2..04a5011a1 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -492,8 +492,7 @@ impl Client { let dependent_root_value = result .meta .remove("dependent_root") - .ok_or_else(|| { - Error::MissingExpectedData("`dependent_root`".to_string())})?; + .ok_or_else(|| Error::MissingExpectedData("`dependent_root`".to_string()))?; let dependent_root: Root = serde_json::from_value(dependent_root_value)?; Ok((dependent_root, result.data)) } From b8961bc255d2ad7207541c6893a06eb6c3613688 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 15 Nov 2022 20:31:03 +0000 Subject: [PATCH 105/153] rm in-client string wrangling --- src/api_client.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 97b288163..7f3504f63 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -441,9 +441,7 @@ impl Client { } pub async fn get_peer(&self, peer_id: PeerId) -> Result { - let id = serde_json::to_string(&peer_id) - .unwrap() - .replace(&['\"'][..], ""); + let id = serde_json::to_string(&peer_id); let result: Value = self.get(&format!("/eth/v1/node/peers/{}", id)).await?; Ok(result.data) From fd5cc543caa9d6dd47f14c9f6b929885424b6325 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 16 Nov 2022 14:05:55 +0000 Subject: [PATCH 106/153] rm string logic - upstreamed to ethereum_consensus --- src/api_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index 7f3504f63..9c9d08448 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -441,7 +441,7 @@ impl Client { } pub async fn get_peer(&self, peer_id: PeerId) -> Result { - let id = serde_json::to_string(&peer_id); + let id = &peer_id; let result: Value = self.get(&format!("/eth/v1/node/peers/{}", id)).await?; Ok(result.data) From ccaf6a5921f3b3e2eb1bd9244e4f9bab14988f86 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 16 Nov 2022 14:14:18 +0000 Subject: [PATCH 107/153] fix type --- src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index d2a981079..e62b49e57 100644 --- a/src/types.rs +++ b/src/types.rs @@ -282,7 +282,7 @@ pub struct PeerDescriptor { pub direction: ConnectionOrientation, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize)] pub struct PeerDescription { pub peer_id: PeerId, pub enr: Enr, From 33e8bb14728b30c4151a46c7c3c0331699a1a864 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 16 Nov 2022 14:19:58 +0000 Subject: [PATCH 108/153] bump eth_consensus version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c3c675d51..804086b90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ serde_json = "1.0.81" itertools = "0.10.3" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="680616d"} +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="29a7136"} From 053c4f0e6f2e944c51fef6cf7be0a70de10ff3c9 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Thu, 17 Nov 2022 10:58:23 +0000 Subject: [PATCH 109/153] Update src/api_client.rs Co-authored-by: Alex Stokes --- src/api_client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 10c1ff883..6549b5bf1 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -460,9 +460,8 @@ impl Client { } pub async fn get_peer(&self, peer_id: PeerId) -> Result { - let id = &peer_id; let result: Value = - self.get(&format!("/eth/v1/node/peers/{}", id)).await?; + self.get(&format!("/eth/v1/node/peers/{}", peer_id)).await?; Ok(result.data) } From adcf861845298876fa8eacab489b3d6d5a502a55 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 17 Jan 2023 19:13:00 -0700 Subject: [PATCH 110/153] update CI --- .github/workflows/ci.yml | 59 ++++++++++++++++++++++++++++++++++++++ .github/workflows/rust.yml | 30 ------------------- examples/sketch.rs | 7 ++--- justfile | 6 ++-- rust-toolchain.toml | 2 ++ src/api_client.rs | 4 +-- src/types.rs | 20 ++++++------- 7 files changed, 79 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/rust.yml create mode 100644 rust-toolchain.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..60dffba8e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: CI suite + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Read toolchain file + id: rust-toolchain + run: | + RUST_TOOLCHAIN=$(grep 'channel' rust-toolchain.toml | awk '{split($0,a," = "); print a[2]}' | tr -d '"') + echo "RUST_TOOLCHAIN=$RUST_TOOLCHAIN" >> $GITHUB_OUTPUT + shell: bash + + - name: Install toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ steps.rust-toolchain.outputs.RUST_TOOLCHAIN }} + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Build + run: cargo build --all-targets --verbose + + - name: Run tests + run: cargo test --verbose + + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Install toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt, clippy + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Check format + run: cargo +nightly fmt --all --check + + - name: Check clippy + run: cargo +nightly clippy --all-targets --verbose -- -D warnings diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index b8115b1fe..000000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Rust - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Install - run: rustup update stable - - name: Rust Cache - uses: Swatinem/rust-cache@v1.3.0 - - name: Format - run: cargo fmt -- --check - - name: Run clippy - run: cargo clippy --verbose -- -D warnings - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose diff --git a/examples/sketch.rs b/examples/sketch.rs index 2a760597e..dfe7137e9 100644 --- a/examples/sketch.rs +++ b/examples/sketch.rs @@ -1,6 +1,5 @@ use beacon_api_client::{ApiError, ApiResult, ConsensusVersion, Value}; use ethereum_consensus::bellatrix::mainnet::BlindedBeaconBlock; -use serde_json; use std::collections::HashMap; fn main() { @@ -21,11 +20,11 @@ fn main() { let full_success_response = ApiResult::Ok(block_with_version); let str_repr = serde_json::to_string(&full_success_response).unwrap(); - println!("{}", str_repr); + println!("{str_repr}"); let recovered_success: ApiResult> = serde_json::from_str(&str_repr).unwrap(); - println!("{:#?}", recovered_success); + println!("{recovered_success:#?}"); let full_error_response: ApiResult> = ApiResult::Err(ApiError::try_from((404, "some failure")).unwrap()); @@ -33,5 +32,5 @@ fn main() { println!("{str_repr}"); let recovered_error: ApiResult = serde_json::from_str(&str_repr).unwrap(); - println!("{:#?}", recovered_error); + println!("{recovered_error:#?}"); } diff --git a/justfile b/justfile index 1ebb930bd..6f4d33b01 100644 --- a/justfile +++ b/justfile @@ -1,9 +1,9 @@ test: cargo test fmt: - cargo fmt + cargo +nightly fmt --all lint: fmt - cargo clippy + cargo +nightly clippy --all-targets build: - cargo build + cargo build --all-targets run-ci: lint build test diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..99c6e11a1 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.66" diff --git a/src/api_client.rs b/src/api_client.rs index 7615d7d0a..2a68ea96d 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -412,7 +412,7 @@ impl Client { // v2 endpoint pub async fn get_state(&self, id: StateId) -> Result { let result: Value = self - .get(&format!("eth/v2/debug/beacon/states/{}", id)) + .get(&format!("eth/v2/debug/beacon/states/{id}")) .await?; Ok(result.data) } @@ -461,7 +461,7 @@ impl Client { pub async fn get_peer(&self, peer_id: PeerId) -> Result { let result: Value = - self.get(&format!("/eth/v1/node/peers/{}", peer_id)).await?; + self.get(&format!("/eth/v1/node/peers/{peer_id}")).await?; Ok(result.data) } diff --git a/src/types.rs b/src/types.rs index e62b49e57..4756c9047 100644 --- a/src/types.rs +++ b/src/types.rs @@ -55,10 +55,10 @@ impl fmt::Display for StateId { StateId::Justified => "justified", StateId::Head => "head", StateId::Genesis => "genesis", - StateId::Slot(slot) => return write!(f, "{}", slot), - StateId::Root(root) => return write!(f, "{}", root), + StateId::Slot(slot) => return write!(f, "{slot}"), + StateId::Root(root) => return write!(f, "{root}"), }; - write!(f, "{}", printable) + write!(f, "{printable}") } } @@ -82,10 +82,10 @@ impl fmt::Display for BlockId { BlockId::Finalized => "finalized", BlockId::Head => "head", BlockId::Genesis => "genesis", - BlockId::Slot(slot) => return write!(f, "{}", slot), - BlockId::Root(root) => return write!(f, "{}", root), + BlockId::Slot(slot) => return write!(f, "{slot}"), + BlockId::Root(root) => return write!(f, "{root}"), }; - write!(f, "{}", printable) + write!(f, "{printable}") } } @@ -137,7 +137,7 @@ impl fmt::Display for ValidatorStatus { Self::Exited => "exited", Self::Withdrawal => "withdrawal", }; - write!(f, "{}", printable) + write!(f, "{printable}") } } @@ -153,7 +153,7 @@ impl fmt::Display for PublicKeyOrIndex { Self::PublicKey(ref pk) => pk.to_string(), Self::Index(i) => i.to_string(), }; - write!(f, "{}", printable) + write!(f, "{printable}") } } @@ -255,7 +255,7 @@ impl fmt::Display for PeerState { Self::Connected => "connected", Self::Disconnecting => "disconnecting", }; - write!(f, "{}", printable) + write!(f, "{printable}") } } @@ -272,7 +272,7 @@ impl fmt::Display for ConnectionOrientation { Self::Inbound => "inbound", Self::Outbound => "outbound", }; - write!(f, "{}", printable) + write!(f, "{printable}") } } From b760592445621e6852c28f28f3fdd2b891fcbe46 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 17 Jan 2023 20:00:17 -0700 Subject: [PATCH 111/153] check for API error when `POST`ing with response data --- src/api_client.rs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 5904b47f1..ca677b425 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -45,8 +45,22 @@ pub enum Error { pub async fn api_error_or_ok(response: reqwest::Response) -> Result<(), Error> { match response.status() { - reqwest::StatusCode::OK => Ok(()), - reqwest::StatusCode::ACCEPTED => Ok(()), + reqwest::StatusCode::OK | reqwest::StatusCode::ACCEPTED => Ok(()), + _ => { + let api_err = response.json::().await?; + Err(Error::Api(api_err)) + } + } +} + +async fn api_error_or_value( + response: reqwest::Response, +) -> Result { + match response.status() { + reqwest::StatusCode::OK | reqwest::StatusCode::ACCEPTED => { + let value = response.json().await?; + Ok(value) + } _ => { let api_err = response.json::().await?; Err(Error::Api(api_err)) @@ -505,12 +519,8 @@ impl Client { .iter() .map(|index| index.to_string()) .collect::>(); - let mut result: Value> = self - .http_post(&endpoint, &indices) - .await? - .json() - .await - .unwrap(); + let response = self.http_post(&endpoint, &indices).await?; + let mut result: Value> = api_error_or_value(response).await?; let dependent_root_value = result .meta .remove("dependent_root") @@ -542,12 +552,8 @@ impl Client { .iter() .map(|index| index.to_string()) .collect::>(); - let result: Value> = self - .http_post(&endpoint, &indices) - .await? - .json() - .await - .unwrap(); + let response = self.http_post(&endpoint, &indices).await?; + let result: Value> = api_error_or_value(response).await?; Ok(result.data) } From 33efc982b1fb7aa4d83d275e455ef9bcf1436059 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 17 Jan 2023 20:06:45 -0700 Subject: [PATCH 112/153] add custom `rustfmt` --- examples/post.rs | 5 +-- examples/sketch.rs | 5 +-- rustfmt.toml | 9 +++++ src/api_client.rs | 83 +++++++++++++++++++--------------------------- src/error.rs | 8 ++--- src/main.rs | 8 ++--- src/types.rs | 15 +++++---- 7 files changed, 58 insertions(+), 75 deletions(-) create mode 100644 rustfmt.toml diff --git a/examples/post.rs b/examples/post.rs index 5a73df425..fa851e138 100644 --- a/examples/post.rs +++ b/examples/post.rs @@ -6,10 +6,7 @@ use url::Url; async fn main() { let client = Client::new(Url::parse("http://localhost:8080").unwrap()); let data = SignedValidatorRegistration::default(); - let response = client - .http_post("/eth/v1/builder/validators", &data) - .await - .unwrap(); + let response = client.http_post("/eth/v1/builder/validators", &data).await.unwrap(); dbg!(&response); dbg!(&response.status()); dbg!(&response.text().await); diff --git a/examples/sketch.rs b/examples/sketch.rs index dfe7137e9..cf9eab8c8 100644 --- a/examples/sketch.rs +++ b/examples/sketch.rs @@ -3,10 +3,7 @@ use ethereum_consensus::bellatrix::mainnet::BlindedBeaconBlock; use std::collections::HashMap; fn main() { - let block = Value { - meta: HashMap::new(), - data: BlindedBeaconBlock::default(), - }; + let block = Value { meta: HashMap::new(), data: BlindedBeaconBlock::default() }; let block_repr = serde_json::to_string(&block).unwrap(); println!("{block_repr}"); diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..e70aee8cc --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,9 @@ +reorder_imports = true +imports_granularity = "Crate" +use_small_heuristics = "Max" +comment_width = 100 +wrap_comments = true +binop_separator = "Back" +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true diff --git a/src/api_client.rs b/src/api_client.rs index ca677b425..bc0e38b01 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -1,27 +1,30 @@ // TODO: remove once things are all used #![allow(unused_variables)] -use crate::error::ApiError; -use crate::types::{ - ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, - BlockId, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, CoordinateWithMetadata, - DepositContract, EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, PeerSummary, - ProposerDuty, PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, - SyncCommitteeSummary, SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, +use crate::{ + error::ApiError, + types::{ + ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, + BeaconProposerRegistration, BlockId, CommitteeDescriptor, CommitteeFilter, + CommitteeSummary, ConnectionOrientation, CoordinateWithMetadata, DepositContract, + EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, NetworkIdentity, + PeerDescription, PeerState, PeerSummary, ProposerDuty, PublicKeyOrIndex, RootData, StateId, + SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, + ValidatorStatus, ValidatorSummary, Value, VersionData, + }, }; -use crate::types::{ConnectionOrientation, NetworkIdentity, PeerDescription, PeerState}; -use ethereum_consensus::altair::mainnet::{ - SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, -}; -use ethereum_consensus::bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}; -use ethereum_consensus::builder::SignedValidatorRegistration; -use ethereum_consensus::networking::PeerId; -use ethereum_consensus::phase0::mainnet::{ - Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, - ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, -}; -use ethereum_consensus::primitives::{ - Bytes32, CommitteeIndex, Epoch, RandaoReveal, Root, Slot, ValidatorIndex, +use ethereum_consensus::{ + altair::mainnet::{ + SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, + }, + bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}, + builder::SignedValidatorRegistration, + networking::PeerId, + phase0::mainnet::{ + Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, + ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, + }, + primitives::{Bytes32, CommitteeIndex, Epoch, RandaoReveal, Root, Slot, ValidatorIndex}, }; use http::StatusCode; use itertools::Itertools; @@ -76,10 +79,7 @@ pub struct Client { impl Client { pub fn new_with_client>(client: reqwest::Client, endpoint: U) -> Self { - Self { - http: client, - endpoint: endpoint.into(), - } + Self { http: client, endpoint: endpoint.into() } } pub fn new>(endpoint: U) -> Self { @@ -324,9 +324,8 @@ impl Client { &self, id: BlockId, ) -> Result, Error> { - let result: Value> = self - .get(&format!("eth/v1/beacon/blocks/{id}/attestations")) - .await?; + let result: Value> = + self.get(&format!("eth/v1/beacon/blocks/{id}/attestations")).await?; Ok(result.data) } @@ -353,8 +352,7 @@ impl Client { } pub async fn post_attestations(&self, attestations: &[Attestation]) -> Result<(), Error> { - self.post("eth/v1/beacon/pool/attestations", attestations) - .await + self.post("eth/v1/beacon/pool/attestations", attestations).await } pub async fn get_attester_slashings_from_pool(&self) -> Result, Error> { @@ -367,8 +365,7 @@ impl Client { &self, attester_slashing: &AttesterSlashing, ) -> Result<(), Error> { - self.post("eth/v1/beacon/pool/attester_slashings", attester_slashing) - .await + self.post("eth/v1/beacon/pool/attester_slashings", attester_slashing).await } pub async fn get_proposer_slashings_from_pool(&self) -> Result, Error> { @@ -381,16 +378,14 @@ impl Client { &self, proposer_slashing: &ProposerSlashing, ) -> Result<(), Error> { - self.post("eth/v1/beacon/pool/proposer_slashings", proposer_slashing) - .await + self.post("eth/v1/beacon/pool/proposer_slashings", proposer_slashing).await } pub async fn post_sync_committee_messages( &self, messages: &[SyncCommitteeMessage], ) -> Result<(), Error> { - self.post("eth/v1/beacon/pool/sync_committees", messages) - .await + self.post("eth/v1/beacon/pool/sync_committees", messages).await } pub async fn get_voluntary_exits_from_pool(&self) -> Result, Error> { @@ -425,9 +420,8 @@ impl Client { /* debug namespace */ // v2 endpoint pub async fn get_state(&self, id: StateId) -> Result { - let result: Value = self - .get(&format!("eth/v2/debug/beacon/states/{id}")) - .await?; + let result: Value = + self.get(&format!("eth/v2/debug/beacon/states/{id}")).await?; Ok(result.data) } @@ -515,10 +509,7 @@ impl Client { indices: &[ValidatorIndex], ) -> Result<(Root, Vec), Error> { let endpoint = format!("eth/v1/validator/duties/attester/{epoch}"); - let indices = indices - .iter() - .map(|index| index.to_string()) - .collect::>(); + let indices = indices.iter().map(|index| index.to_string()).collect::>(); let response = self.http_post(&endpoint, &indices).await?; let mut result: Value> = api_error_or_value(response).await?; let dependent_root_value = result @@ -548,10 +539,7 @@ impl Client { indices: &[ValidatorIndex], ) -> Result, Error> { let endpoint = format!("eth/v1/validator/duties/sync/{epoch}"); - let indices = indices - .iter() - .map(|index| index.to_string()) - .collect::>(); + let indices = indices.iter().map(|index| index.to_string()).collect::>(); let response = self.http_post(&endpoint, &indices).await?; let result: Value> = api_error_or_value(response).await?; Ok(result.data) @@ -624,8 +612,7 @@ impl Client { &self, registrations: &[BeaconProposerRegistration], ) -> Result<(), Error> { - self.post("eth/v1/validator/prepare_beacon_proposer", registrations) - .await + self.post("eth/v1/validator/prepare_beacon_proposer", registrations).await } // endpoint for builder registrations diff --git a/src/error.rs b/src/error.rs index 6dede418a..afc8ae36c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,6 @@ use http::StatusCode; use serde::{Deserialize, Serialize}; -use std::error::Error; -use std::fmt; +use std::{error::Error, fmt}; #[derive(Serialize, Deserialize, Debug)] pub struct ApiError { @@ -23,9 +22,6 @@ impl<'a> TryFrom<(u16, &'a str)> for ApiError { fn try_from((code, message): (u16, &'a str)) -> Result { let code = StatusCode::from_u16(code)?; - Ok(Self { - code, - message: message.to_string(), - }) + Ok(Self { code, message: message.to_string() }) } } diff --git a/src/main.rs b/src/main.rs index 19e81ed2f..8ae5c7eab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ -use beacon_api_client::Client; -use beacon_api_client::StateId; +use beacon_api_client::{Client, StateId}; use url::Url; #[tokio::main] @@ -8,10 +7,7 @@ async fn main() { let url: Url = Url::parse(s).unwrap(); let client = Client::new(url); - let checkpoints = client - .get_finality_checkpoints(StateId::Finalized) - .await - .unwrap(); + let checkpoints = client.get_finality_checkpoints(StateId::Finalized).await.unwrap(); println!("previous checkpoint: {:?}", checkpoints.previous_justified); println!("current checkpoint: {:?}", checkpoints.current_justified); diff --git a/src/types.rs b/src/types.rs index 4756c9047..a615b35f0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,13 +1,14 @@ use crate::error::ApiError; -use ethereum_consensus::networking::{Enr, MetaData, Multiaddr, PeerId}; -use ethereum_consensus::phase0::mainnet::{Checkpoint, SignedBeaconBlockHeader, Validator}; -use ethereum_consensus::primitives::{ - BlsPublicKey, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, Root, Slot, - ValidatorIndex, Version, +use ethereum_consensus::{ + networking::{Enr, MetaData, Multiaddr, PeerId}, + phase0::mainnet::{Checkpoint, SignedBeaconBlockHeader, Validator}, + primitives::{ + BlsPublicKey, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, Root, + Slot, ValidatorIndex, Version, + }, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::collections::HashMap; -use std::fmt; +use std::{collections::HashMap, fmt}; #[derive(Serialize, Deserialize)] pub struct VersionData { From d2ab5b88f492d2ea7f22b7fb998a5f0f2724f690 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 17 Jan 2023 20:24:04 -0700 Subject: [PATCH 113/153] update README with link to docs --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f307a4285..6618ed220 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # beacon-api-client -A client for the Ethereum beacon node APIs +A client for the Ethereum beacon node APIs: + +https://ethereum.github.io/beacon-APIs From d6eb875fd0b4e56db7d0cb92b11788f0e4e78a4a Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 17 Jan 2023 20:55:15 -0700 Subject: [PATCH 114/153] implement more methods in `validator` namespace --- src/api_client.rs | 103 ++++++++++++++++++++++++++++++++++++---------- src/types.rs | 9 ++++ 2 files changed, 91 insertions(+), 21 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index bc0e38b01..a73bca047 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -1,6 +1,3 @@ -// TODO: remove once things are all used -#![allow(unused_variables)] - use crate::{ error::ApiError, types::{ @@ -434,7 +431,7 @@ impl Client { /* events namespace */ // TODO: figure out return type - pub async fn get_events(topics: &[EventTopic]) -> Result { + pub async fn get_events(_topics: &[EventTopic]) -> Result { // get back "event: TOPIC, data: T" unimplemented!("") } @@ -546,66 +543,129 @@ impl Client { } // v2 endpoint - pub async fn get_block( + pub async fn get_block_proposal( + &self, slot: Slot, randao_reveal: RandaoReveal, - graffiti: Bytes32, + graffiti: Option, ) -> Result { - unimplemented!("") + let path = format!("eth/v2/validator/blocks/{slot}"); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + request = request.query(&[("randao_reveal", randao_reveal)]); + if let Some(graffiti) = graffiti { + request = request.query(&[("graffiti", graffiti)]); + } + let response = request.send().await?; + // TODO handle polymorphism... + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } - pub async fn get_blinded_block( + pub async fn get_blinded_block_proposal( + &self, slot: Slot, randao_reveal: RandaoReveal, - graffiti: Bytes32, + graffiti: Option, ) -> Result { - unimplemented!("") + let path = format!("eth/v2/validator/blinded_blocks/{slot}"); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + request = request.query(&[("randao_reveal", randao_reveal)]); + if let Some(graffiti) = graffiti { + request = request.query(&[("graffiti", graffiti)]); + } + let response = request.send().await?; + // TODO handle polymorphism... + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn get_attestation_data( + &self, slot: Slot, committee_index: CommitteeIndex, ) -> Result { - unimplemented!("") + let target = self.endpoint.join("eth/v1/validator/attestation_data")?; + let mut request = self.http.get(target); + request = request.query(&[("slot", slot)]); + request = request.query(&[("committee_index", committee_index)]); + let response = request.send().await?; + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn get_attestation_aggregate( + &self, attestation_data_root: Root, slot: Slot, ) -> Result { - unimplemented!("") + let target = self.endpoint.join("eth/v1/validator/aggregate_attestation")?; + let mut request = self.http.get(target); + request = request.query(&[("attestation_data_root", attestation_data_root)]); + request = request.query(&[("slot", slot)]); + let response = request.send().await?; + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn post_aggregates_with_proofs( + &self, aggregates_with_proofs: &[SignedAggregateAndProof], ) -> Result<(), Error> { - Ok(()) + self.post("eth/v1/validator/aggregate_and_proofs", aggregates_with_proofs).await } - pub async fn subscribe_subnets_for_attestation( + pub async fn subscribe_subnets_for_attestation_committees( + &self, committee_descriptors: &[CommitteeDescriptor], ) -> Result<(), Error> { - Ok(()) + self.post("eth/v1/validator/beacon_committee_subscriptions", committee_descriptors).await } - pub async fn subscribe_subnets_for_sync_committee( + pub async fn subscribe_subnets_for_sync_committees( + &self, sync_committee_descriptors: &[SyncCommitteeDescriptor], ) -> Result<(), Error> { - Ok(()) + self.post("eth/v1/validator/sync_committee_subscriptions", sync_committee_descriptors).await } pub async fn get_sync_committee_contribution( + &self, slot: Slot, subcommittee_index: usize, beacon_block_root: Root, ) -> Result { - unimplemented!("") + let target = self.endpoint.join("eth/v1/validator/sync_committee_contribution")?; + let mut request = self.http.get(target); + request = request.query(&[("slot", slot)]); + request = request.query(&[("subcommittee_index", subcommittee_index)]); + request = request.query(&[("beacon_block_root", beacon_block_root)]); + let response = request.send().await?; + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } - pub async fn post_contributions_with_proofs( + pub async fn post_sync_committee_contributions_with_proofs( + &self, contributions_with_proofs: &[SignedContributionAndProof], ) -> Result<(), Error> { - Ok(()) + self.post("eth/v1/validator/contribution_and_proofs", contributions_with_proofs).await } pub async fn prepare_proposers( @@ -617,8 +677,9 @@ impl Client { // endpoint for builder registrations pub async fn register_validators_with_builders( + &self, registrations: &[SignedValidatorRegistration], ) -> Result<(), Error> { - Ok(()) + self.post("eth/v1/validator/register_validator", registrations).await } } diff --git a/src/types.rs b/src/types.rs index a615b35f0..0256a053f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -359,17 +359,26 @@ pub struct SyncCommitteeDuty { pub validator_sync_committee_indices: Vec, } +#[derive(Serialize, Deserialize)] pub struct CommitteeDescriptor { + #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, + #[serde(with = "crate::serde::as_string")] pub committee_index: CommitteeIndex, + #[serde(with = "crate::serde::as_string")] pub committees_at_slot: usize, + #[serde(with = "crate::serde::as_string")] pub slot: Slot, pub is_aggregator: bool, } +#[derive(Serialize, Deserialize)] pub struct SyncCommitteeDescriptor { + #[serde(with = "crate::serde::as_string")] pub validator_index: ValidatorIndex, + #[serde(with = "crate::serde::collection_over_string")] pub sync_committee_indices: Vec, + #[serde(with = "crate::serde::as_string")] pub until_epoch: Epoch, } From 5184d83ba6d6408c6ef6a1ec1884efcb89e63ad3 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 17 Jan 2023 21:26:48 -0700 Subject: [PATCH 115/153] update to latest `ethereum-consensus` --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 804086b90..c07f17843 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ serde_json = "1.0.81" itertools = "0.10.3" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="29a7136"} +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="fbe43ee3af9ac486bc0169266e7497c47b9ccd38"} From 13d7e232ed9ef69df8b92a507e7a7f49020a897d Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 31 Jan 2023 12:24:56 +0100 Subject: [PATCH 116/153] add new forks for `ConsensusVersion` --- src/types.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types.rs b/src/types.rs index 0256a053f..bcae0988b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -403,6 +403,8 @@ pub enum ConsensusVersion { Phase0, Altair, Bellatrix, + Capella, + Deneb, } #[derive(Serialize, Deserialize, Debug)] From 53690a711e33614d59d4d44fb09762b4699e2a4e Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 31 Jan 2023 14:12:13 +0100 Subject: [PATCH 117/153] add `VersionedValue` serde utility --- Cargo.toml | 2 +- examples/sketch.rs | 49 ++++++++++++++++++++++++++++++++++++++-------- src/types.rs | 27 +++++++++++++++++-------- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c07f17843..47e180984 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ serde_json = "1.0.81" itertools = "0.10.3" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="fbe43ee3af9ac486bc0169266e7497c47b9ccd38"} +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="5531c285fc4030e265b8499612946d8a704e2c57" } diff --git a/examples/sketch.rs b/examples/sketch.rs index cf9eab8c8..4a0f8b654 100644 --- a/examples/sketch.rs +++ b/examples/sketch.rs @@ -1,29 +1,62 @@ -use beacon_api_client::{ApiError, ApiResult, ConsensusVersion, Value}; -use ethereum_consensus::bellatrix::mainnet::BlindedBeaconBlock; +use beacon_api_client::{ApiError, ApiResult, Value, VersionedValue}; +use ethereum_consensus::{bellatrix::mainnet as bellatrix, capella::mainnet as capella}; use std::collections::HashMap; +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +#[serde(tag = "version", content = "data")] +#[serde(rename_all = "lowercase")] +enum BlindedBeaconBlock { + Bellatrix(bellatrix::BlindedBeaconBlock), + Capella(capella::BlindedBeaconBlock), +} + fn main() { - let block = Value { meta: HashMap::new(), data: BlindedBeaconBlock::default() }; + let block = Value { meta: HashMap::new(), data: bellatrix::BlindedBeaconBlock::default() }; let block_repr = serde_json::to_string(&block).unwrap(); println!("{block_repr}"); - let version = serde_json::to_value(ConsensusVersion::Bellatrix).unwrap(); + let version = serde_json::to_value("bellatrix").unwrap(); let block_with_version = Value { meta: HashMap::from_iter([("version".to_string(), version)]), - data: BlindedBeaconBlock::default(), + data: bellatrix::BlindedBeaconBlock::default(), }; let block_with_version_repr = serde_json::to_string(&block_with_version).unwrap(); println!("{block_with_version_repr}"); - let full_success_response = ApiResult::Ok(block_with_version); + let block = BlindedBeaconBlock::Bellatrix(Default::default()); + let block_with_version_repr = serde_json::to_string(&block).unwrap(); + println!("{block_with_version_repr}"); + let recovered_block: BlindedBeaconBlock = + serde_json::from_str(&block_with_version_repr).unwrap(); + println!("{recovered_block:#?}"); + + let block = BlindedBeaconBlock::Capella(Default::default()); + let block_with_version_repr = serde_json::to_string(&block).unwrap(); + println!("{block_with_version_repr}"); + + let full_success_response = ApiResult::Ok(block.clone()); + let str_repr = serde_json::to_string(&full_success_response).unwrap(); + println!("{str_repr}"); + + let recovered_success: ApiResult> = + serde_json::from_str(&str_repr).unwrap(); + println!("{recovered_success:#?}"); + + let full_success_response = ApiResult::Ok(VersionedValue { + payload: block, + meta: HashMap::from_iter([( + String::from("finalized_root"), + serde_json::Value::String("0xdeadbeefcafe".to_string()), + )]), + }); let str_repr = serde_json::to_string(&full_success_response).unwrap(); println!("{str_repr}"); - let recovered_success: ApiResult> = + let recovered_success: ApiResult> = serde_json::from_str(&str_repr).unwrap(); println!("{recovered_success:#?}"); - let full_error_response: ApiResult> = + let full_error_response: ApiResult> = ApiResult::Err(ApiError::try_from((404, "some failure")).unwrap()); let str_repr = serde_json::to_string(&full_error_response).unwrap(); println!("{str_repr}"); diff --git a/src/types.rs b/src/types.rs index bcae0988b..4e3c0ab28 100644 --- a/src/types.rs +++ b/src/types.rs @@ -397,14 +397,25 @@ pub struct Value { pub meta: HashMap, } -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "lowercase")] -pub enum ConsensusVersion { - Phase0, - Altair, - Bellatrix, - Capella, - Deneb, +/* +`VersionedValue` captures: +```json +{ + "version": "fork-version", + "data": { ... }, + < optional additional metadata >, +} + +And can be combined with Rust `enum`s to handle polymorphic {de,}serialization. +``` + */ +#[derive(serde::Serialize, serde::Deserialize, Debug)] +#[serde(bound = "T: serde::Serialize + serde::de::DeserializeOwned")] +pub struct VersionedValue { + #[serde(flatten)] + pub payload: T, + #[serde(flatten)] + pub meta: HashMap, } #[derive(Serialize, Deserialize, Debug)] From 5d0fb80586fca4c9eaa0789db8a23d2e837c4fae Mon Sep 17 00:00:00 2001 From: realbigsean Date: Fri, 10 Feb 2023 15:05:17 -0500 Subject: [PATCH 118/153] update ethereum consensus dep --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 47e180984..b8abeb050 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ serde_json = "1.0.81" itertools = "0.10.3" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="5531c285fc4030e265b8499612946d8a704e2c57" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="de14cb1399ae694275a65fb94702e859c9e15430" } From 67a68e0635a6de7c93bc930c3fc97aa3f1c80512 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 29 Jun 2023 11:31:40 -0600 Subject: [PATCH 119/153] take self by ref, as intended --- src/api_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_client.rs b/src/api_client.rs index a73bca047..8b1b0d300 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -475,7 +475,7 @@ impl Client { Ok(result.data) } - pub async fn get_node_version(self) -> Result { + pub async fn get_node_version(&self) -> Result { let result: Value = self.get("eth/v1/node/version").await?; Ok(result.data.version) } From 6534e8f8566e5dec383730f18a83982508beaa41 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 30 Jun 2023 15:00:25 +0100 Subject: [PATCH 120/153] initial commit for cli --- Cargo.toml | 6 +++-- src/cli/config.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++ src/cli/mod.rs | 11 +++++++++ src/lib.rs | 10 +++++--- src/main.rs | 22 +++++++++-------- src/types.rs | 30 ++++++++++++++++++++--- 6 files changed, 122 insertions(+), 19 deletions(-) create mode 100644 src/cli/config.rs create mode 100644 src/cli/mod.rs diff --git a/Cargo.toml b/Cargo.toml index b8abeb050..171e97519 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,9 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["clap"] + [dependencies] tokio = { version = "1.0", features = ["full"] } tracing = "0.1" @@ -13,10 +16,9 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } reqwest = { version = "0.11.10", features = ["json", "native-tls-vendored"] } url = "2.2.2" http = "0.2.7" - serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" itertools = "0.10.3" - +clap = {version = "4.1.1", features = ["derive"], optional=true } thiserror = "1.0.30" ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="de14cb1399ae694275a65fb94702e859c9e15430" } diff --git a/src/cli/config.rs b/src/cli/config.rs new file mode 100644 index 000000000..35aa82459 --- /dev/null +++ b/src/cli/config.rs @@ -0,0 +1,62 @@ +use crate::{api_client::Client, types::StateId}; +use clap::{Args, Parser, Subcommand}; +use std::fmt; + +#[derive(Debug, Parser)] +#[clap(version, about = "Beacon API client")] +pub struct CliConfig { + #[clap(short, long)] + pub endpoint: String, + #[clap(subcommand)] + pub command: Namespace, +} + +#[derive(Debug, Clone, Subcommand)] +#[clap(author, version, about)] +pub enum Namespace { + #[clap(subcommand)] + Beacon(BeaconMethod), +} + +impl fmt::Display for Namespace { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + Namespace::Beacon(_) => "beacon", + }; + write!(f, "{printable}") + } +} + +#[derive(Debug, Clone, Subcommand)] +pub enum BeaconMethod { + //Beacon ns + Genesis(GenesisArg), + Root(RootArg), +} + +//ARGS +//BEACON NAMESPACE ARGS +#[derive(Debug, Clone, Args)] +pub struct GenesisArg { + genesis: Option, +} + +impl GenesisArg { + pub async fn execute(&self, client: &Client) { + let out = client.get_genesis_details().await.unwrap(); + println!("{:?}", out); + } +} + +#[derive(Debug, Clone, Args)] +pub struct RootArg { + pub state_id: StateId, +} + +impl RootArg { + pub async fn execute(&self, client: &Client) { + let id = &self.state_id; + let out = client.get_state_root(id.to_owned()).await.unwrap(); + println!("{}", out); + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 000000000..faf821aa9 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,11 @@ +mod config; +use crate::api_client; + +pub use config::{BeaconMethod, CliConfig, Namespace::Beacon}; + +pub async fn run_cli(client: api_client::Client, args: CliConfig) { + match args.command { + Beacon(BeaconMethod::Genesis(genesis)) => genesis.execute(&client).await, + Beacon(BeaconMethod::Root(ref root_arg)) => root_arg.execute(&client).await, + } +} diff --git a/src/lib.rs b/src/lib.rs index 5cb8b86c9..f92a6c53a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,10 @@ -mod api_client; -mod error; -mod serde; -mod types; +pub mod api_client; +pub mod cli; +pub mod error; +pub mod serde; +pub mod types; pub use api_client::*; +pub use cli::*; pub use error::ApiError; pub use types::*; diff --git a/src/main.rs b/src/main.rs index 8ae5c7eab..37104573e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,17 @@ -use beacon_api_client::{Client, StateId}; +pub mod api_client; use url::Url; +pub mod cli; +pub mod error; +pub mod serde; +pub mod types; +use clap::Parser; #[tokio::main] async fn main() { - let s = "http://127.0.0.1:8003/"; - let url: Url = Url::parse(s).unwrap(); - let client = Client::new(url); - - let checkpoints = client.get_finality_checkpoints(StateId::Finalized).await.unwrap(); - - println!("previous checkpoint: {:?}", checkpoints.previous_justified); - println!("current checkpoint: {:?}", checkpoints.current_justified); - println!("finalized checkpoint: {:?}", checkpoints.finalized); + // read in args from CLI + let args = cli::CliConfig::parse(); + // instantiate client and pass to run_cli + let url: Url = Url::parse(&args.endpoint).unwrap(); + let client = api_client::Client::new(url); + cli::run_cli(client, args).await; } diff --git a/src/types.rs b/src/types.rs index 4e3c0ab28..8dc0afb47 100644 --- a/src/types.rs +++ b/src/types.rs @@ -6,9 +6,10 @@ use ethereum_consensus::{ BlsPublicKey, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, Root, Slot, ValidatorIndex, Version, }, + serde::try_bytes_from_hex_str, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::{collections::HashMap, fmt}; +use std::{collections::HashMap, fmt, str::FromStr}; #[derive(Serialize, Deserialize)] pub struct VersionData { @@ -30,7 +31,7 @@ pub struct DepositContract { pub address: ExecutionAddress, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct GenesisDetails { #[serde(with = "crate::serde::as_string")] pub genesis_time: u64, @@ -39,7 +40,7 @@ pub struct GenesisDetails { pub genesis_fork_version: Version, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub enum StateId { Head, Genesis, @@ -63,6 +64,29 @@ impl fmt::Display for StateId { } } +impl FromStr for StateId { + type Err = &'static str; + fn from_str(s: &str) -> Result { + match s { + "finalized" => Ok(StateId::Finalized), + "justified" => Ok(StateId::Justified), + "head" => Ok(StateId::Head), + "genesis" => Ok(StateId::Genesis), + _ => { + if s.parse::().is_ok() { + return Ok(StateId::Slot(s.parse::().unwrap())) + } else if try_bytes_from_hex_str(s).is_ok() { + return Ok(StateId::Root( + try_bytes_from_hex_str(s).unwrap().as_slice().try_into().unwrap(), + )) + } else { + return Err("invalid input to state_id") + } + } + } + } +} + #[derive(Serialize, Deserialize)] pub struct RootData { pub root: Root, From 6868128482ab1427eaebb55a73caaa6b628fc0ee Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 30 Jun 2023 15:11:55 +0100 Subject: [PATCH 121/153] rm uneccessary return statements --- src/types.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types.rs b/src/types.rs index 8dc0afb47..91d541feb 100644 --- a/src/types.rs +++ b/src/types.rs @@ -74,13 +74,13 @@ impl FromStr for StateId { "genesis" => Ok(StateId::Genesis), _ => { if s.parse::().is_ok() { - return Ok(StateId::Slot(s.parse::().unwrap())) + Ok(StateId::Slot(s.parse::().unwrap())) } else if try_bytes_from_hex_str(s).is_ok() { - return Ok(StateId::Root( + Ok(StateId::Root( try_bytes_from_hex_str(s).unwrap().as_slice().try_into().unwrap(), )) } else { - return Err("invalid input to state_id") + Err("invalid input to state_id") } } } From c476d5f9c695029303bedc36bfe95bdc2d565301 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 7 Jul 2023 16:57:56 -0600 Subject: [PATCH 122/153] bugfix: correct serde of sync committee endpoint --- src/api_client.rs | 4 ++-- src/types.rs | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index a73bca047..114f0408e 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -238,7 +238,7 @@ impl Client { &self, id: StateId, epoch: Option, - ) -> Result, Error> { + ) -> Result { let path = format!("eth/v1/beacon/states/{id}/sync_committees"); let target = self.endpoint.join(&path)?; let mut request = self.http.get(target); @@ -246,7 +246,7 @@ impl Client { request = request.query(&[("epoch", epoch)]); } let response = request.send().await?; - let result: ApiResult>> = response.json().await?; + let result: ApiResult> = response.json().await?; match result { ApiResult::Ok(result) => Ok(result.data), ApiResult::Err(err) => Err(err.into()), diff --git a/src/types.rs b/src/types.rs index 4e3c0ab28..60591b043 100644 --- a/src/types.rs +++ b/src/types.rs @@ -195,22 +195,25 @@ pub struct CommitteeFilter { pub slot: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] +pub struct Committee( + #[serde(with = "crate::serde::collection_over_string")] pub Vec, +); + +#[derive(Serialize, Deserialize, Debug)] pub struct CommitteeSummary { #[serde(with = "crate::serde::as_string")] pub index: CommitteeIndex, #[serde(with = "crate::serde::as_string")] pub slot: Slot, - #[serde(with = "crate::serde::collection_over_string")] - pub validators: Vec, + pub validators: Committee, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct SyncCommitteeSummary { #[serde(with = "crate::serde::collection_over_string")] pub validators: Vec, - // TODO fix serde here - pub validator_aggregates: Vec>, + pub validator_aggregates: Vec, } #[derive(Serialize, Deserialize)] From 04652f66fef49b30c6ba63d7aaa3dbceb5e739cb Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 7 Jul 2023 17:35:24 -0600 Subject: [PATCH 123/153] support indexed errors --- src/error.rs | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/error.rs b/src/error.rs index afc8ae36c..3efc6e5e9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,16 +2,44 @@ use http::StatusCode; use serde::{Deserialize, Serialize}; use std::{error::Error, fmt}; +// NOTE: `IndexedError` must come before `ErrorMessage` so +// the `serde(untagged)` machinery does not greedily match it first. #[derive(Serialize, Deserialize, Debug)] -pub struct ApiError { - #[serde(with = "crate::serde::as_u16")] - pub code: StatusCode, - pub message: String, +#[serde(untagged)] +pub enum ApiError { + IndexedError { + #[serde(with = "crate::serde::as_u16")] + code: StatusCode, + message: String, + failures: Vec, + }, + ErrorMessage { + #[serde(with = "crate::serde::as_u16")] + code: StatusCode, + message: String, + }, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct IndexedError { + index: usize, + message: String, } impl fmt::Display for ApiError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message) + match self { + Self::ErrorMessage { message, .. } => { + write!(f, "{message}") + } + Self::IndexedError { message, failures, .. } => { + write!(f, "{message}: ")?; + for failure in failures { + write!(f, "{failure:?}, ")?; + } + Ok(()) + } + } } } @@ -22,6 +50,6 @@ impl<'a> TryFrom<(u16, &'a str)> for ApiError { fn try_from((code, message): (u16, &'a str)) -> Result { let code = StatusCode::from_u16(code)?; - Ok(Self { code, message: message.to_string() }) + Ok(Self::ErrorMessage { code, message: message.to_string() }) } } From fc8a84c7f1e9fc24e49e5f86dd896d025a3d5241 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 7 Jul 2023 17:41:27 -0600 Subject: [PATCH 124/153] remove TODO comments --- src/api_client.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 25ec4e39e..92a1b254b 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -430,9 +430,7 @@ impl Client { } /* events namespace */ - // TODO: figure out return type pub async fn get_events(_topics: &[EventTopic]) -> Result { - // get back "event: TOPIC, data: T" unimplemented!("") } @@ -557,7 +555,6 @@ impl Client { request = request.query(&[("graffiti", graffiti)]); } let response = request.send().await?; - // TODO handle polymorphism... let result: ApiResult> = response.json().await?; match result { ApiResult::Ok(result) => Ok(result.data), @@ -579,7 +576,6 @@ impl Client { request = request.query(&[("graffiti", graffiti)]); } let response = request.send().await?; - // TODO handle polymorphism... let result: ApiResult> = response.json().await?; match result { ApiResult::Ok(result) => Ok(result.data), From e010178470beea3ce2f6e1b6395d1fef2b96c809 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 8 Jul 2023 11:13:37 -0600 Subject: [PATCH 125/153] refactor errors, add support for presets --- examples/post.rs | 2 +- src/api_client.rs | 60 ++++++++++++++++--------------- src/{error.rs => api_error.rs} | 0 src/lib.rs | 65 ++++++++++++++++++++++++++++++++-- src/main.rs | 2 +- src/types.rs | 4 +-- 6 files changed, 99 insertions(+), 34 deletions(-) rename src/{error.rs => api_error.rs} (100%) diff --git a/examples/post.rs b/examples/post.rs index fa851e138..585c55073 100644 --- a/examples/post.rs +++ b/examples/post.rs @@ -1,4 +1,4 @@ -use beacon_api_client::Client; +use beacon_api_client::mainnet::Client; use ethereum_consensus::builder::SignedValidatorRegistration; use url::Url; diff --git a/src/api_client.rs b/src/api_client.rs index 92a1b254b..0d2e6e5fe 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -1,5 +1,4 @@ use crate::{ - error::ApiError, types::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, BlockId, CommitteeDescriptor, CommitteeFilter, @@ -9,39 +8,19 @@ use crate::{ SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, }, + ApiError, Error, }; use ethereum_consensus::{ - altair::mainnet::{ - SignedContributionAndProof, SyncCommitteeContribution, SyncCommitteeMessage, - }, - bellatrix::mainnet::{BlindedBeaconBlock, SignedBlindedBeaconBlock}, + altair::SyncCommitteeMessage, builder::SignedValidatorRegistration, networking::PeerId, - phase0::mainnet::{ - Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, Fork, - ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, - }, + phase0::{AttestationData, Fork, ProposerSlashing, SignedVoluntaryExit}, primitives::{Bytes32, CommitteeIndex, Epoch, RandaoReveal, Root, Slot, ValidatorIndex}, }; use http::StatusCode; use itertools::Itertools; use std::collections::HashMap; -use thiserror::Error; -use url::{ParseError, Url}; - -#[derive(Debug, Error)] -pub enum Error { - #[error("could not parse URL: {0}")] - Url(#[from] ParseError), - #[error("could not send request: {0}")] - Http(#[from] reqwest::Error), - #[error("error from API: {0}")] - Api(#[from] ApiError), - #[error("missing expected data in response: {0}")] - MissingExpectedData(String), - #[error("json error: {0}")] - Json(#[from] serde_json::Error), -} +use url::Url; pub async fn api_error_or_ok(response: reqwest::Response) -> Result<(), Error> { match response.status() { @@ -69,14 +48,39 @@ async fn api_error_or_value( } #[derive(Clone)] -pub struct Client { +pub struct Client { http: reqwest::Client, endpoint: Url, + _phantom: std::marker::PhantomData<(A, B, C, D, E, F, G, H, I, J)>, } -impl Client { +impl< + SignedContributionAndProof: serde::Serialize, + SyncCommitteeContribution: serde::Serialize + serde::de::DeserializeOwned, + BlindedBeaconBlock: serde::Serialize + serde::de::DeserializeOwned, + SignedBlindedBeaconBlock: serde::Serialize + serde::de::DeserializeOwned, + Attestation: serde::Serialize + serde::de::DeserializeOwned, + AttesterSlashing: serde::Serialize + serde::de::DeserializeOwned, + BeaconBlock: serde::Serialize + serde::de::DeserializeOwned, + BeaconState: serde::Serialize + serde::de::DeserializeOwned, + SignedAggregateAndProof: serde::Serialize, + SignedBeaconBlock: serde::Serialize + serde::de::DeserializeOwned, + > + Client< + SignedContributionAndProof, + SyncCommitteeContribution, + BlindedBeaconBlock, + SignedBlindedBeaconBlock, + Attestation, + AttesterSlashing, + BeaconBlock, + BeaconState, + SignedAggregateAndProof, + SignedBeaconBlock, + > +{ pub fn new_with_client>(client: reqwest::Client, endpoint: U) -> Self { - Self { http: client, endpoint: endpoint.into() } + Self { http: client, endpoint: endpoint.into(), _phantom: std::marker::PhantomData } } pub fn new>(endpoint: U) -> Self { diff --git a/src/error.rs b/src/api_error.rs similarity index 100% rename from src/error.rs rename to src/api_error.rs diff --git a/src/lib.rs b/src/lib.rs index 5cb8b86c9..2486147a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,69 @@ mod api_client; -mod error; +mod api_error; mod serde; mod types; pub use api_client::*; -pub use error::ApiError; +pub use api_error::*; +pub use error::*; +pub use presets::*; pub use types::*; + +mod error { + use crate::ApiError; + use thiserror::Error; + use url::ParseError; + + #[derive(Debug, Error)] + pub enum Error { + #[error("could not parse URL: {0}")] + Url(#[from] ParseError), + #[error("could not send request: {0}")] + Http(#[from] reqwest::Error), + #[error("error from API: {0}")] + Api(#[from] ApiError), + #[error("missing expected data in response: {0}")] + MissingExpectedData(String), + #[error("json error: {0}")] + Json(#[from] serde_json::Error), + } +} + +pub mod presets { + pub mod mainnet { + use ethereum_consensus::{ + altair::mainnet as altair, bellatrix::mainnet as bellatrix, phase0::mainnet as phase0, + }; + + pub type Client = crate::Client< + altair::SignedContributionAndProof, + altair::SyncCommitteeContribution, + bellatrix::BlindedBeaconBlock, + bellatrix::SignedBlindedBeaconBlock, + phase0::Attestation, + phase0::AttesterSlashing, + phase0::BeaconBlock, + phase0::BeaconState, + phase0::SignedAggregateAndProof, + phase0::SignedBeaconBlock, + >; + } + pub mod minimal { + use ethereum_consensus::{ + altair::minimal as altair, bellatrix::minimal as bellatrix, phase0::minimal as phase0, + }; + + pub type Client = crate::Client< + altair::SignedContributionAndProof, + altair::SyncCommitteeContribution, + bellatrix::BlindedBeaconBlock, + bellatrix::SignedBlindedBeaconBlock, + phase0::Attestation, + phase0::AttesterSlashing, + phase0::BeaconBlock, + phase0::BeaconState, + phase0::SignedAggregateAndProof, + phase0::SignedBeaconBlock, + >; + } +} diff --git a/src/main.rs b/src/main.rs index 8ae5c7eab..539f404fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use beacon_api_client::{Client, StateId}; +use beacon_api_client::{mainnet::Client, StateId}; use url::Url; #[tokio::main] diff --git a/src/types.rs b/src/types.rs index 60591b043..3ac7210c9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ -use crate::error::ApiError; +use crate::ApiError; use ethereum_consensus::{ networking::{Enr, MetaData, Multiaddr, PeerId}, - phase0::mainnet::{Checkpoint, SignedBeaconBlockHeader, Validator}, + phase0::{Checkpoint, SignedBeaconBlockHeader, Validator}, primitives::{ BlsPublicKey, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, Root, Slot, ValidatorIndex, Version, From 9f160422e737dc6e6479a3db3cf5ed593124051e Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 8 Jul 2023 11:18:32 -0600 Subject: [PATCH 126/153] allow "complex type" wrt clippy --- src/api_client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api_client.rs b/src/api_client.rs index 0d2e6e5fe..d9baac318 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -47,6 +47,7 @@ async fn api_error_or_value( } } +#[allow(clippy::type_complexity)] #[derive(Clone)] pub struct Client { http: reqwest::Client, From 3b4bd8ccda5c60378934fd9086f3fb527b341bfa Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 8 Jul 2023 18:46:07 -0600 Subject: [PATCH 127/153] update rust toolchain --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 99c6e11a1..22048ac5b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.66" +channel = "1.70.0" From 98a5c0c91e8b2a8cd522ee8a0405279232031e6e Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 8 Jul 2023 18:47:46 -0600 Subject: [PATCH 128/153] remove explicit version of `ethereum-consensus` dep --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b8abeb050..184de4c64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ serde_json = "1.0.81" itertools = "0.10.3" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="de14cb1399ae694275a65fb94702e859c9e15430" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus" } From 1f98f7552f92946d0ced22256b702b8116b493fd Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 8 Jul 2023 18:53:31 -0600 Subject: [PATCH 129/153] use correct version of `MetaData` type --- src/types.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index 3ac7210c9..61c2c35ce 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,7 @@ use crate::ApiError; use ethereum_consensus::{ - networking::{Enr, MetaData, Multiaddr, PeerId}, + altair::MetaData, + networking::{Enr, Multiaddr, PeerId}, phase0::{Checkpoint, SignedBeaconBlockHeader, Validator}, primitives::{ BlsPublicKey, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, Root, From 93d7e8c38fe9782c4862909663e7b57c44f805a9 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 9 Jul 2023 13:24:34 -0600 Subject: [PATCH 130/153] pin rev of `ethereum-consensus` we use --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 184de4c64..7520a8a9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ serde_json = "1.0.81" itertools = "0.10.3" thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "e380108" } From 5cad78381f6cfa70e75c11897df5dce72e24ae83 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:00:22 +0100 Subject: [PATCH 131/153] Apply suggestions from code review Co-authored-by: Alex Stokes --- Cargo.toml | 5 +++-- src/cli/config.rs | 20 ++++---------------- src/cli/mod.rs | 8 +++++--- src/lib.rs | 10 +++++----- src/main.rs | 16 +++++----------- src/types.rs | 24 ++++++++++++++---------- 6 files changed, 36 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 171e97519..264501d4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,8 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["clap"] +default = ["cli"] +cli = ["clap"] [dependencies] tokio = { version = "1.0", features = ["full"] } @@ -19,6 +20,6 @@ http = "0.2.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" itertools = "0.10.3" -clap = {version = "4.1.1", features = ["derive"], optional=true } +clap = {version = "4.3.10", features = ["derive"], optional = true } thiserror = "1.0.30" ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev="de14cb1399ae694275a65fb94702e859c9e15430" } diff --git a/src/cli/config.rs b/src/cli/config.rs index 35aa82459..bc7fb3157 100644 --- a/src/cli/config.rs +++ b/src/cli/config.rs @@ -5,37 +5,25 @@ use std::fmt; #[derive(Debug, Parser)] #[clap(version, about = "Beacon API client")] pub struct CliConfig { - #[clap(short, long)] + #[clap(long)] pub endpoint: String, #[clap(subcommand)] - pub command: Namespace, + pub namespace: Namespace, } #[derive(Debug, Clone, Subcommand)] -#[clap(author, version, about)] pub enum Namespace { #[clap(subcommand)] Beacon(BeaconMethod), } -impl fmt::Display for Namespace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let printable = match *self { - Namespace::Beacon(_) => "beacon", - }; - write!(f, "{printable}") - } -} #[derive(Debug, Clone, Subcommand)] pub enum BeaconMethod { - //Beacon ns - Genesis(GenesisArg), - Root(RootArg), + Genesis, + Root(StateIdArg), } -//ARGS -//BEACON NAMESPACE ARGS #[derive(Debug, Clone, Args)] pub struct GenesisArg { genesis: Option, diff --git a/src/cli/mod.rs b/src/cli/mod.rs index faf821aa9..346e0fa5b 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,9 +1,11 @@ mod config; -use crate::api_client; -pub use config::{BeaconMethod, CliConfig, Namespace::Beacon}; +use crate::api_client::Client; -pub async fn run_cli(client: api_client::Client, args: CliConfig) { +use config::{BeaconMethod, Namespace::Beacon}; +pub use config::CliConfig; + +pub async fn run_cli(client: &Client, args: &CliConfig) { match args.command { Beacon(BeaconMethod::Genesis(genesis)) => genesis.execute(&client).await, Beacon(BeaconMethod::Root(ref root_arg)) => root_arg.execute(&client).await, diff --git a/src/lib.rs b/src/lib.rs index f92a6c53a..18f3c100d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ -pub mod api_client; -pub mod cli; -pub mod error; -pub mod serde; -pub mod types; +mod api_client; +mod cli; +mod error; +mod serde; +mod types; pub use api_client::*; pub use cli::*; diff --git a/src/main.rs b/src/main.rs index 37104573e..35b50e8c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,11 @@ -pub mod api_client; use url::Url; -pub mod cli; -pub mod error; -pub mod serde; -pub mod types; use clap::Parser; +use beacon_api_client::{Client, CliConfig, run_cli}; #[tokio::main] async fn main() { - // read in args from CLI - let args = cli::CliConfig::parse(); - // instantiate client and pass to run_cli - let url: Url = Url::parse(&args.endpoint).unwrap(); - let client = api_client::Client::new(url); - cli::run_cli(client, args).await; + let args = CliConfig::parse(); + let url = Url::parse(&args.endpoint).unwrap(); + let client = Client::new(url); + run_cli(&client, &args).await; } diff --git a/src/types.rs b/src/types.rs index 91d541feb..ec7d12e3e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -65,7 +65,8 @@ impl fmt::Display for StateId { } impl FromStr for StateId { - type Err = &'static str; + type Err = String; + fn from_str(s: &str) -> Result { match s { "finalized" => Ok(StateId::Finalized), @@ -73,15 +74,18 @@ impl FromStr for StateId { "head" => Ok(StateId::Head), "genesis" => Ok(StateId::Genesis), _ => { - if s.parse::().is_ok() { - Ok(StateId::Slot(s.parse::().unwrap())) - } else if try_bytes_from_hex_str(s).is_ok() { - Ok(StateId::Root( - try_bytes_from_hex_str(s).unwrap().as_slice().try_into().unwrap(), - )) - } else { - Err("invalid input to state_id") - } + match s.parse::() { + Ok(slot) => Ok(Self::Slot(slot)), + Err(_) => match try_bytes_from_hex_str(s) { + Ok(root_data) => { + let root = Root::try_from(&root_data).map_err(|err| format!("could not parse state identifier by root from the provided argument {s}: {err}")?; + Ok(Self::Root(root)) + } + Err(err) => { + let err = format!("could not parse state identifier by root from the provided argument {s}: {err}"); + Err(err) + } + } } } } From ee8eeb1c569cdd71b6a3ad0af6f5243d825fdcd9 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:06:25 +0100 Subject: [PATCH 132/153] Apply suggestions from code review Co-authored-by: Alex Stokes --- src/cli/config.rs | 10 +--------- src/cli/mod.rs | 5 +++-- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/cli/config.rs b/src/cli/config.rs index bc7fb3157..efc123acd 100644 --- a/src/cli/config.rs +++ b/src/cli/config.rs @@ -37,14 +37,6 @@ impl GenesisArg { } #[derive(Debug, Clone, Args)] -pub struct RootArg { +pub struct StateIdArg { pub state_id: StateId, } - -impl RootArg { - pub async fn execute(&self, client: &Client) { - let id = &self.state_id; - let out = client.get_state_root(id.to_owned()).await.unwrap(); - println!("{}", out); - } -} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 346e0fa5b..617685f01 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,5 +1,6 @@ mod config; + use crate::api_client::Client; use config::{BeaconMethod, Namespace::Beacon}; @@ -7,7 +8,7 @@ pub use config::CliConfig; pub async fn run_cli(client: &Client, args: &CliConfig) { match args.command { - Beacon(BeaconMethod::Genesis(genesis)) => genesis.execute(&client).await, - Beacon(BeaconMethod::Root(ref root_arg)) => root_arg.execute(&client).await, + Beacon(BeaconMethod::Genesis(arg)) => arg.execute(client).await, + Beacon(BeaconMethod::Root(arg)) => arg.execute(client).await, } } From 56c47121936e571eda5e12c63fb085bd1e7b0706 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 11 Jul 2023 09:47:39 +0100 Subject: [PATCH 133/153] fix match on impl FromStr for StateId, fix run_cli --- Cargo.toml | 1 + src/cli/config.rs | 21 ++++++++++----------- src/cli/mod.rs | 12 +++++++----- src/main.rs | 4 ++-- src/types.rs | 41 +++++++++++++++++++++++++++-------------- 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 264501d4e..9d2d08504 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } reqwest = { version = "0.11.10", features = ["json", "native-tls-vendored"] } url = "2.2.2" http = "0.2.7" + serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" itertools = "0.10.3" diff --git a/src/cli/config.rs b/src/cli/config.rs index efc123acd..8b21c0898 100644 --- a/src/cli/config.rs +++ b/src/cli/config.rs @@ -17,24 +17,23 @@ pub enum Namespace { Beacon(BeaconMethod), } - #[derive(Debug, Clone, Subcommand)] pub enum BeaconMethod { Genesis, Root(StateIdArg), } -#[derive(Debug, Clone, Args)] -pub struct GenesisArg { - genesis: Option, -} +// #[derive(Debug, Clone, Args)] +// pub struct GenesisArg { +// genesis: Option, +// } -impl GenesisArg { - pub async fn execute(&self, client: &Client) { - let out = client.get_genesis_details().await.unwrap(); - println!("{:?}", out); - } -} +// impl GenesisArg { +// pub async fn execute(&self, client: &Client) { +// let out = client.get_genesis_details().await.unwrap(); +// println!("{:?}", out); +// } +// } #[derive(Debug, Clone, Args)] pub struct StateIdArg { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 617685f01..8803a2a79 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,14 +1,16 @@ mod config; - use crate::api_client::Client; -use config::{BeaconMethod, Namespace::Beacon}; pub use config::CliConfig; +use config::{BeaconMethod, Namespace::Beacon}; pub async fn run_cli(client: &Client, args: &CliConfig) { - match args.command { - Beacon(BeaconMethod::Genesis(arg)) => arg.execute(client).await, - Beacon(BeaconMethod::Root(arg)) => arg.execute(client).await, + match args.namespace { + Beacon(BeaconMethod::Genesis) => { + println!("{:?}", client.get_genesis_details().await.unwrap()) + } + _ => println!("not yet implemented"), + //Beacon(BeaconMethod::Root(arg)) => arg.execute(client).await, } } diff --git a/src/main.rs b/src/main.rs index 35b50e8c7..2d023b9cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ -use url::Url; +use beacon_api_client::{run_cli, CliConfig, Client}; use clap::Parser; -use beacon_api_client::{Client, CliConfig, run_cli}; +use url::Url; #[tokio::main] async fn main() { diff --git a/src/types.rs b/src/types.rs index ec7d12e3e..489b540cf 100644 --- a/src/types.rs +++ b/src/types.rs @@ -66,30 +66,43 @@ impl fmt::Display for StateId { impl FromStr for StateId { type Err = String; - + fn from_str(s: &str) -> Result { match s { "finalized" => Ok(StateId::Finalized), "justified" => Ok(StateId::Justified), "head" => Ok(StateId::Head), "genesis" => Ok(StateId::Genesis), - _ => { - match s.parse::() { - Ok(slot) => Ok(Self::Slot(slot)), - Err(_) => match try_bytes_from_hex_str(s) { - Ok(root_data) => { - let root = Root::try_from(&root_data).map_err(|err| format!("could not parse state identifier by root from the provided argument {s}: {err}")?; - Ok(Self::Root(root)) - } - Err(err) => { - let err = format!("could not parse state identifier by root from the provided argument {s}: {err}"); - Err(err) - } + _ => match s.parse::() { + Ok(slot) => Ok(Self::Slot(slot)), + Err(_) => match try_bytes_from_hex_str(s) { + Ok(root_data) => { + let root = Root::try_from(&root_data[..]).map_err(|err| format!("could not parse state identifier by root from the provided argument {s}: {err}"))?; + Ok(Self::Root(root)) + } + Err(err) => { + let err = format!("could not parse state identifier by root from the provided argument {s}: {err}"); + Err(err) } - } + }, + }, } } } +// _ => { +// if s.parse::().is_ok() { +// Ok(StateId::Slot(s.parse::().unwrap())) +// } else if try_bytes_from_hex_str(s).is_ok() { +// Ok(StateId::Root( +// try_bytes_from_hex_str(s).unwrap().as_slice().try_into().unwrap(), +// )) +// } else { +// Err("invalid input to state_id".to_string()) +// } +// } +// } +// } +// } #[derive(Serialize, Deserialize)] pub struct RootData { From c96027e89c9e21f87f788c132f9872721b5ed5c3 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 11 Jul 2023 11:10:34 +0100 Subject: [PATCH 134/153] add root arg to cli --- src/cli/config.rs | 12 ------------ src/cli/mod.rs | 9 ++++++--- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/cli/config.rs b/src/cli/config.rs index 8b21c0898..c8309f491 100644 --- a/src/cli/config.rs +++ b/src/cli/config.rs @@ -23,18 +23,6 @@ pub enum BeaconMethod { Root(StateIdArg), } -// #[derive(Debug, Clone, Args)] -// pub struct GenesisArg { -// genesis: Option, -// } - -// impl GenesisArg { -// pub async fn execute(&self, client: &Client) { -// let out = client.get_genesis_details().await.unwrap(); -// println!("{:?}", out); -// } -// } - #[derive(Debug, Clone, Args)] pub struct StateIdArg { pub state_id: StateId, diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 8803a2a79..2e40532f8 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -4,13 +4,16 @@ use crate::api_client::Client; pub use config::CliConfig; use config::{BeaconMethod, Namespace::Beacon}; +use ethereum_consensus::primitives::Root; pub async fn run_cli(client: &Client, args: &CliConfig) { - match args.namespace { + match &args.namespace { Beacon(BeaconMethod::Genesis) => { - println!("{:?}", client.get_genesis_details().await.unwrap()) + println!("{:?}", &client.get_genesis_details().await.unwrap()); + } + Beacon(BeaconMethod::Root(arg)) => { + println!("{}", &client.get_state_root(arg.state_id.clone()).await.unwrap()) } _ => println!("not yet implemented"), - //Beacon(BeaconMethod::Root(arg)) => arg.execute(client).await, } } From 5a5019a1c35f37099b27eff3b8e61c33644def16 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 11 Jul 2023 13:13:59 +0100 Subject: [PATCH 135/153] improve clap configuration --- src/cli/config.rs | 26 ++++++++++++++++++-------- src/cli/mod.rs | 2 -- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/cli/config.rs b/src/cli/config.rs index c8309f491..a12ce2bb3 100644 --- a/src/cli/config.rs +++ b/src/cli/config.rs @@ -1,29 +1,39 @@ -use crate::{api_client::Client, types::StateId}; +use crate::types::StateId; use clap::{Args, Parser, Subcommand}; -use std::fmt; #[derive(Debug, Parser)] -#[clap(version, about = "Beacon API client")] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] pub struct CliConfig { - #[clap(long)] + #[arg(long)] pub endpoint: String, - #[clap(subcommand)] + #[command(subcommand)] pub namespace: Namespace, } -#[derive(Debug, Clone, Subcommand)] +#[derive(Debug, Subcommand)] pub enum Namespace { #[clap(subcommand)] Beacon(BeaconMethod), } -#[derive(Debug, Clone, Subcommand)] +#[derive(Debug, Subcommand)] pub enum BeaconMethod { Genesis, Root(StateIdArg), } -#[derive(Debug, Clone, Args)] +#[derive(Args, Debug)] pub struct StateIdArg { + #[arg( + value_parser = clap::value_parser!(StateId), + long_help = "Identifier for the state under consideration. Possible values are: + head + genesis + finalized + justified + + ", + )] pub state_id: StateId, } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 2e40532f8..4319f060e 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -4,7 +4,6 @@ use crate::api_client::Client; pub use config::CliConfig; use config::{BeaconMethod, Namespace::Beacon}; -use ethereum_consensus::primitives::Root; pub async fn run_cli(client: &Client, args: &CliConfig) { match &args.namespace { @@ -14,6 +13,5 @@ pub async fn run_cli(client: &Client, args: &CliConfig) { Beacon(BeaconMethod::Root(arg)) => { println!("{}", &client.get_state_root(arg.state_id.clone()).await.unwrap()) } - _ => println!("not yet implemented"), } } From b185f1e406b0038d084902ab3d60dd10ab7988b8 Mon Sep 17 00:00:00 2001 From: Joseph Cook <33655003+jmcook1186@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:36:14 +0100 Subject: [PATCH 136/153] Apply suggestions from code review Co-authored-by: Alex Stokes --- Cargo.toml | 2 +- src/types.rs | 16 +--------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab29f270c..b59e19493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,6 @@ http = "0.2.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" itertools = "0.10.3" -clap = {version = "4.3.10", features = ["derive"], optional = true } +clap = {version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "e380108" } diff --git a/src/types.rs b/src/types.rs index c82b51496..6f5f6c7a0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -78,7 +78,7 @@ impl FromStr for StateId { Ok(slot) => Ok(Self::Slot(slot)), Err(_) => match try_bytes_from_hex_str(s) { Ok(root_data) => { - let root = Root::try_from(&root_data[..]).map_err(|err| format!("could not parse state identifier by root from the provided argument {s}: {err}"))?; + let root = Root::try_from(root_data.as_ref()).map_err(|err| format!("could not parse state identifier by root from the provided argument {s}: {err}"))?; Ok(Self::Root(root)) } Err(err) => { @@ -90,20 +90,6 @@ impl FromStr for StateId { } } } -// _ => { -// if s.parse::().is_ok() { -// Ok(StateId::Slot(s.parse::().unwrap())) -// } else if try_bytes_from_hex_str(s).is_ok() { -// Ok(StateId::Root( -// try_bytes_from_hex_str(s).unwrap().as_slice().try_into().unwrap(), -// )) -// } else { -// Err("invalid input to state_id".to_string()) -// } -// } -// } -// } -// } #[derive(Serialize, Deserialize)] pub struct RootData { From bd2b49cdab9d75b52695bfab61970b4425883bdf Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 14 Jul 2023 13:43:19 +0100 Subject: [PATCH 137/153] fix Client import, fmt --- src/cli/mod.rs | 4 +--- src/lib.rs | 4 ++-- src/main.rs | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4319f060e..681371370 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,7 +1,5 @@ mod config; - -use crate::api_client::Client; - +use crate::mainnet::Client; pub use config::CliConfig; use config::{BeaconMethod, Namespace::Beacon}; diff --git a/src/lib.rs b/src/lib.rs index 4b23acc67..1fb5c818b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,12 @@ mod api_client; -mod cli; mod api_error; +mod cli; mod serde; mod types; pub use api_client::*; -pub use cli::*; pub use api_error::*; +pub use cli::*; pub use error::*; pub use presets::*; pub use types::*; diff --git a/src/main.rs b/src/main.rs index be77c1b95..3462009fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use beacon_api_client::{run_cli, CliConfig, mainnet::Client}; +use beacon_api_client::{mainnet::Client, run_cli, CliConfig}; use clap::Parser; use url::Url; From 7799ca36cc6fdaf269c9b5adab78808f27345d35 Mon Sep 17 00:00:00 2001 From: jacobkaufmann Date: Mon, 31 Jul 2023 10:31:04 -0600 Subject: [PATCH 138/153] feat: impl beacon APIs 2.4.1 --- Cargo.toml | 2 +- src/api_client.rs | 82 +++++++++++++++++++++++++++++++++++++++++------ src/types.rs | 18 +++++++++++ 3 files changed, 91 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b59e19493..59abd4256 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = {version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "e380108" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "27ca9b6" } diff --git a/src/api_client.rs b/src/api_client.rs index d9baac318..303025213 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -1,21 +1,24 @@ use crate::{ types::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, - BeaconProposerRegistration, BlockId, CommitteeDescriptor, CommitteeFilter, - CommitteeSummary, ConnectionOrientation, CoordinateWithMetadata, DepositContract, - EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, NetworkIdentity, - PeerDescription, PeerState, PeerSummary, ProposerDuty, PublicKeyOrIndex, RootData, StateId, - SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, - ValidatorStatus, ValidatorSummary, Value, VersionData, + BeaconProposerRegistration, BlockId, BroadcastValidation, CommitteeDescriptor, + CommitteeFilter, CommitteeSummary, ConnectionOrientation, CoordinateWithMetadata, + DepositContract, EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, + NetworkIdentity, PeerDescription, PeerState, PeerSummary, ProposerDuty, PublicKeyOrIndex, + RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, + SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, }, ApiError, Error, }; use ethereum_consensus::{ altair::SyncCommitteeMessage, builder::SignedValidatorRegistration, + deneb::BlobSidecar, networking::PeerId, phase0::{AttestationData, Fork, ProposerSlashing, SignedVoluntaryExit}, - primitives::{Bytes32, CommitteeIndex, Epoch, RandaoReveal, Root, Slot, ValidatorIndex}, + primitives::{ + BlobIndex, Bytes32, CommitteeIndex, Epoch, RandaoReveal, Root, Slot, ValidatorIndex, + }, }; use http::StatusCode; use itertools::Itertools; @@ -258,6 +261,22 @@ impl< } } + pub async fn get_randao(&self, id: StateId, epoch: Option) -> Result { + let path = format!("eth/v1/beacon/states/{id}/randao"); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + if let Some(epoch) = epoch { + request = request.query(&[("epoch", epoch)]); + } + let response = request.send().await?; + + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } + } + pub async fn get_beacon_header_at_head(&self) -> Result { let result: Value = self.get("eth/v1/beacon/headers").await?; Ok(result.data) @@ -299,15 +318,43 @@ impl< Ok(result.data) } + pub async fn post_signed_blinded_beacon_block( + &self, + block: &SignedBlindedBeaconBlock, + ) -> Result<(), Error> { + self.post("eth/v1/beacon/blinded_blocks", block).await + } + + pub async fn post_signed_blinded_beacon_block_v2( + &self, + block: &SignedBlindedBeaconBlock, + broadcast_validation: Option, + ) -> Result<(), Error> { + let target = self.endpoint.join("eth/v2/beacon/blinded_blocks")?; + let mut request = self.http.post(target).json(block); + if let Some(validation) = broadcast_validation { + request = request.query(&[("broadcast_validation", validation)]); + } + let response = request.send().await?; + api_error_or_ok(response).await + } + pub async fn post_signed_beacon_block(&self, block: &SignedBeaconBlock) -> Result<(), Error> { self.post("eth/v1/beacon/blocks", block).await } - pub async fn post_signed_blinded_beacon_block( + pub async fn post_signed_beacon_block_v2( &self, - block: &SignedBlindedBeaconBlock, + block: &SignedBeaconBlock, + broadcast_validation: Option, ) -> Result<(), Error> { - self.post("eth/v1/beacon/blinded_blocks", block).await + let target = self.endpoint.join("eth/v2/beacon/blocks")?; + let mut request = self.http.post(target).json(block); + if let Some(validation) = broadcast_validation { + request = request.query(&[("broadcast_validation", validation)]); + } + let response = request.send().await?; + api_error_or_ok(response).await } // v2 endpoint @@ -331,6 +378,21 @@ impl< Ok(result.data) } + pub async fn get_blob_sidecars( + &self, + id: BlockId, + indices: &[BlobIndex], + ) -> Result>, Error> { + let path = format!("eth/v1/beacon/blob_sidecars/{id}"); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + if !indices.is_empty() { + request = request.query(&[("indices", indices)]); + } + let response = request.send().await?; + api_error_or_value(response).await + } + pub async fn get_attestations_from_pool( &self, slot: Option, diff --git a/src/types.rs b/src/types.rs index 6f5f6c7a0..9cbeab946 100644 --- a/src/types.rs +++ b/src/types.rs @@ -251,6 +251,24 @@ pub struct BeaconHeaderSummary { pub signed_header: SignedBeaconBlockHeader, } +#[derive(Serialize, Deserialize, Debug)] +pub enum BroadcastValidation { + Gossip, + Consensus, + ConsensusAndEquivocation, +} + +impl fmt::Display for BroadcastValidation { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match self { + Self::Gossip => "gossip", + Self::Consensus => "consensus", + Self::ConsensusAndEquivocation => "consensus_and_equivocation", + }; + write!(f, "{printable}") + } +} + pub enum EventTopic { Head, Block, From a2de25c85cc473e2546a7fb84273ffe8654f6286 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Mon, 31 Jul 2023 15:53:44 -0600 Subject: [PATCH 139/153] add `Default` to `GenesisDetails` --- src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index 6f5f6c7a0..c66c54457 100644 --- a/src/types.rs +++ b/src/types.rs @@ -32,7 +32,7 @@ pub struct DepositContract { pub address: ExecutionAddress, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Default)] pub struct GenesisDetails { #[serde(with = "crate::serde::as_string")] pub genesis_time: u64, From 17e5939cad4795d13401197f51da0ba872467c5f Mon Sep 17 00:00:00 2001 From: jacobkaufmann Date: Tue, 1 Aug 2023 12:39:50 -0600 Subject: [PATCH 140/153] add remaining APIs --- Cargo.toml | 2 +- src/api_client.rs | 108 +++++++++++++++++++++++++++++++++++++++++++--- src/types.rs | 30 ++++++++++++- 3 files changed, 131 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 59abd4256..76e6388bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = {version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "27ca9b6" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "56418ea" } diff --git a/src/api_client.rs b/src/api_client.rs index 303025213..cb7bae95a 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -3,16 +3,21 @@ use crate::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, BlockId, BroadcastValidation, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, ConnectionOrientation, CoordinateWithMetadata, - DepositContract, EventTopic, FinalityCheckpoints, GenesisDetails, HealthStatus, - NetworkIdentity, PeerDescription, PeerState, PeerSummary, ProposerDuty, PublicKeyOrIndex, - RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, SyncCommitteeSummary, - SyncStatus, ValidatorStatus, ValidatorSummary, Value, VersionData, + DepositContract, DepositSnapshot, EventTopic, ExpectedWithdrawals, FinalityCheckpoints, + GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, PeerState, PeerSummary, + ProposerDuty, PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, + SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorLiveness, ValidatorStatus, + ValidatorSummary, Value, VersionData, }, ApiError, Error, }; use ethereum_consensus::{ - altair::SyncCommitteeMessage, + altair::{ + LightClientBootstrap, LightClientFinalityUpdate, LightClientOptimisticUpdate, + LightClientUpdate, SyncCommitteeMessage, + }, builder::SignedValidatorRegistration, + capella::SignedBlsToExecutionChange, deneb::BlobSidecar, networking::PeerId, phase0::{AttestationData, Fork, ProposerSlashing, SignedVoluntaryExit}, @@ -393,6 +398,55 @@ impl< api_error_or_value(response).await } + pub async fn get_deposit_snapshot(&self) -> Result { + let result: Value = self.get("eth/v1/beacon/deposit_snapshot").await?; + Ok(result.data) + } + + pub async fn get_blinded_block(&self, id: BlockId) -> Result { + let result: Value = + self.get(&format!("eth/v1/beacon/blinded_blocks/{id}")).await?; + Ok(result.data) + } + + pub async fn get_light_client_bootstrap( + &self, + block: Root, + ) -> Result, Error> { + let result: Value> = + self.get(&format!("eth/v1/beacon/light_client/bootstrap/{block}")).await?; + Ok(result.data) + } + + pub async fn get_light_client_updates( + &self, + start: u64, + count: u64, + ) -> Result>, Error> { + let target = self.endpoint.join(&format!("eth/v1/beacon/light_client/updates"))?; + let mut request = self.http.get(target); + request = request.query(&[("start_period", start), ("count", count)]); + + let response = request.send().await?; + api_error_or_value(response).await + } + + pub async fn get_light_client_finality_update( + &self, + ) -> Result, Error> { + let result: Value> = + self.get(&format!("eth/v1/beacon/light_client/finality_update")).await?; + Ok(result.data) + } + + pub async fn get_light_client_optimistic_update( + &self, + ) -> Result, Error> { + let result: Value> = + self.get(&format!("eth/v1/beacon/light_client/optimistic_update")).await?; + Ok(result.data) + } + pub async fn get_attestations_from_pool( &self, slot: Option, @@ -465,6 +519,38 @@ impl< self.post("eth/v1/beacon/pool/voluntary_exits", exit).await } + pub async fn get_bls_to_execution_changes( + &self, + ) -> Result, Error> { + let result: Value> = + self.get("eth/v1/beacon/pool/bls_to_execution_changes").await?; + Ok(result.data) + } + + pub async fn post_bls_to_execution_changes( + &self, + changes: &[SignedBlsToExecutionChange], + ) -> Result<(), Error> { + self.post("eth/v1/beacon/pool/bls_to_execution_changes", changes).await + } + + /* builder namespace */ + pub async fn get_expected_withdrawals( + &self, + id: StateId, + slot: Option, + ) -> Result { + let path = format!("eth/v1/builder/states/{id}/expected_withdrawals"); + let target = self.endpoint.join(&path)?; + let mut request = self.http.get(target); + if let Some(slot) = slot { + request = request.query(&[("proposal_slot", slot)]); + } + + let response = request.send().await?; + api_error_or_value(response).await + } + /* config namespace */ pub async fn get_fork_schedule(&self) -> Result, Error> { let result: Value> = self.get("eth/v1/config/fork_schedule").await?; @@ -635,7 +721,7 @@ impl< randao_reveal: RandaoReveal, graffiti: Option, ) -> Result { - let path = format!("eth/v2/validator/blinded_blocks/{slot}"); + let path = format!("eth/v1/validator/blinded_blocks/{slot}"); let target = self.endpoint.join(&path)?; let mut request = self.http.get(target); request = request.query(&[("randao_reveal", randao_reveal)]); @@ -745,4 +831,14 @@ impl< ) -> Result<(), Error> { self.post("eth/v1/validator/register_validator", registrations).await } + + pub async fn post_liveness( + &self, + epoch: Epoch, + indices: &[ValidatorIndex], + ) -> Result, Error> { + let response = + self.http_post(&format!("eth/v1/validator/liveness/{epoch}"), indices).await?; + api_error_or_value(response).await + } } diff --git a/src/types.rs b/src/types.rs index 9cbeab946..6d8f7e8ff 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,11 +1,12 @@ use crate::ApiError; use ethereum_consensus::{ altair::MetaData, + capella::Withdrawal, networking::{Enr, Multiaddr, PeerId}, phase0::{Checkpoint, SignedBeaconBlockHeader, Validator}, primitives::{ - BlsPublicKey, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, Root, - Slot, ValidatorIndex, Version, + BlsPublicKey, ChainId, CommitteeIndex, Coordinate, Epoch, ExecutionAddress, Gwei, Hash32, + Root, Slot, ValidatorIndex, Version, }, serde::try_bytes_from_hex_str, }; @@ -32,6 +33,17 @@ pub struct DepositContract { pub address: ExecutionAddress, } +#[derive(Serialize, Deserialize, Debug)] +pub struct DepositSnapshot { + pub finalized: Vec, + pub deposit_root: Hash32, + #[serde(with = "crate::serde::as_string")] + pub deposit_count: u64, + pub execution_block_hash: Hash32, + #[serde(with = "crate::serde::as_string")] + pub execution_block_height: u64, +} + #[derive(Serialize, Deserialize, Debug)] pub struct GenesisDetails { #[serde(with = "crate::serde::as_string")] @@ -438,6 +450,20 @@ pub struct BeaconProposerRegistration { pub fee_recipient: ExecutionAddress, } +#[derive(Debug, Serialize, Deserialize)] +pub struct ExpectedWithdrawals { + pub execution_optimistic: bool, + pub finalized: bool, + pub data: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ValidatorLiveness { + #[serde(with = "crate::serde::as_string")] + index: ValidatorIndex, + is_live: bool, +} + #[derive(Serialize, Deserialize, Debug)] #[serde(bound = "T: Serialize + serde::de::DeserializeOwned")] pub struct Value { From dc8616387e01fd6dedb6ab95b9256d40da50b8cd Mon Sep 17 00:00:00 2001 From: jacobkaufmann Date: Wed, 2 Aug 2023 10:21:38 -0600 Subject: [PATCH 141/153] minor fixes --- src/api_client.rs | 44 ++++++++++++++++++++++++++++++-------------- src/types.rs | 8 -------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index cb7bae95a..2aff77988 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -3,11 +3,11 @@ use crate::{ ApiResult, AttestationDuty, BalanceSummary, BeaconHeaderSummary, BeaconProposerRegistration, BlockId, BroadcastValidation, CommitteeDescriptor, CommitteeFilter, CommitteeSummary, ConnectionOrientation, CoordinateWithMetadata, - DepositContract, DepositSnapshot, EventTopic, ExpectedWithdrawals, FinalityCheckpoints, - GenesisDetails, HealthStatus, NetworkIdentity, PeerDescription, PeerState, PeerSummary, - ProposerDuty, PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, - SyncCommitteeDuty, SyncCommitteeSummary, SyncStatus, ValidatorLiveness, ValidatorStatus, - ValidatorSummary, Value, VersionData, + DepositContract, DepositSnapshot, EventTopic, FinalityCheckpoints, GenesisDetails, + HealthStatus, NetworkIdentity, PeerDescription, PeerState, PeerSummary, ProposerDuty, + PublicKeyOrIndex, RootData, StateId, SyncCommitteeDescriptor, SyncCommitteeDuty, + SyncCommitteeSummary, SyncStatus, ValidatorLiveness, ValidatorStatus, ValidatorSummary, + Value, VersionData, }, ApiError, Error, }; @@ -17,7 +17,7 @@ use ethereum_consensus::{ LightClientUpdate, SyncCommitteeMessage, }, builder::SignedValidatorRegistration, - capella::SignedBlsToExecutionChange, + capella::{SignedBlsToExecutionChange, Withdrawal}, deneb::BlobSidecar, networking::PeerId, phase0::{AttestationData, Fork, ProposerSlashing, SignedVoluntaryExit}, @@ -395,7 +395,11 @@ impl< request = request.query(&[("indices", indices)]); } let response = request.send().await?; - api_error_or_value(response).await + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn get_deposit_snapshot(&self) -> Result { @@ -423,19 +427,23 @@ impl< start: u64, count: u64, ) -> Result>, Error> { - let target = self.endpoint.join(&format!("eth/v1/beacon/light_client/updates"))?; + let target = self.endpoint.join("eth/v1/beacon/light_client/updates")?; let mut request = self.http.get(target); request = request.query(&[("start_period", start), ("count", count)]); let response = request.send().await?; - api_error_or_value(response).await + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } pub async fn get_light_client_finality_update( &self, ) -> Result, Error> { let result: Value> = - self.get(&format!("eth/v1/beacon/light_client/finality_update")).await?; + self.get("eth/v1/beacon/light_client/finality_update").await?; Ok(result.data) } @@ -443,7 +451,7 @@ impl< &self, ) -> Result, Error> { let result: Value> = - self.get(&format!("eth/v1/beacon/light_client/optimistic_update")).await?; + self.get("eth/v1/beacon/light_client/optimistic_update").await?; Ok(result.data) } @@ -539,7 +547,7 @@ impl< &self, id: StateId, slot: Option, - ) -> Result { + ) -> Result, Error> { let path = format!("eth/v1/builder/states/{id}/expected_withdrawals"); let target = self.endpoint.join(&path)?; let mut request = self.http.get(target); @@ -548,7 +556,11 @@ impl< } let response = request.send().await?; - api_error_or_value(response).await + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } /* config namespace */ @@ -839,6 +851,10 @@ impl< ) -> Result, Error> { let response = self.http_post(&format!("eth/v1/validator/liveness/{epoch}"), indices).await?; - api_error_or_value(response).await + let result: ApiResult> = response.json().await?; + match result { + ApiResult::Ok(result) => Ok(result.data), + ApiResult::Err(err) => Err(err.into()), + } } } diff --git a/src/types.rs b/src/types.rs index 51d185614..67ad56092 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,6 @@ use crate::ApiError; use ethereum_consensus::{ altair::MetaData, - capella::Withdrawal, networking::{Enr, Multiaddr, PeerId}, phase0::{Checkpoint, SignedBeaconBlockHeader, Validator}, primitives::{ @@ -450,13 +449,6 @@ pub struct BeaconProposerRegistration { pub fee_recipient: ExecutionAddress, } -#[derive(Debug, Serialize, Deserialize)] -pub struct ExpectedWithdrawals { - pub execution_optimistic: bool, - pub finalized: bool, - pub data: Vec, -} - #[derive(Debug, Serialize, Deserialize)] pub struct ValidatorLiveness { #[serde(with = "crate::serde::as_string")] From bec53906c3b97b53e6e8a4fd677be1706fc6065d Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 5 Aug 2023 16:44:44 -0600 Subject: [PATCH 142/153] formatting --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 76e6388bf..25143865d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,6 @@ http = "0.2.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" itertools = "0.10.3" -clap = {version = "4.3.11", features = ["derive"], optional = true } +clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "56418ea" } From d838d930f80fdfcadfe32147bcb2e805aec074bc Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 5 Aug 2023 16:45:32 -0600 Subject: [PATCH 143/153] update dep --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 25143865d..b5488ee0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "56418ea" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "2bcb975" } From ce833efb240e63f0d265f1053d494009f4b0ec06 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 20 Aug 2023 09:19:20 -0600 Subject: [PATCH 144/153] update `ethereum-consensus` dep --- Cargo.toml | 2 +- src/types.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b5488ee0c..37a910bc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "2bcb975" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "d1c665f707bd0724e38e7b83b1a01a658da0aa21" } diff --git a/src/types.rs b/src/types.rs index 67ad56092..02fc3b7ad 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,6 @@ use crate::ApiError; use ethereum_consensus::{ - altair::MetaData, + altair::networking::MetaData, networking::{Enr, Multiaddr, PeerId}, phase0::{Checkpoint, SignedBeaconBlockHeader, Validator}, primitives::{ From 565d4e429ded4ffde944a96ec190ae00b1aba548 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 20 Aug 2023 09:35:03 -0600 Subject: [PATCH 145/153] update `ethereum-consensus` dep --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 37a910bc7..0239a6b94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "d1c665f707bd0724e38e7b83b1a01a658da0aa21" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "4d4e3fb57584d9c3bba6b4fc488f23db4937ae79" } From 2dc029e4cad1987eb360be5325c48706139ca946 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Sun, 20 Aug 2023 14:25:48 -0400 Subject: [PATCH 146/153] make client and endpoint public --- src/api_client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api_client.rs b/src/api_client.rs index 2aff77988..2b3921c55 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -58,8 +58,8 @@ async fn api_error_or_value( #[allow(clippy::type_complexity)] #[derive(Clone)] pub struct Client { - http: reqwest::Client, - endpoint: Url, + pub http: reqwest::Client, + pub endpoint: Url, _phantom: std::marker::PhantomData<(A, B, C, D, E, F, G, H, I, J)>, } From f09ec649f6dd9cb1d1c8abdb980c3aeba569cb02 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 29 Aug 2023 14:36:55 -0700 Subject: [PATCH 147/153] move some generics to the `Client` type rather than per method --- Cargo.toml | 2 +- src/api_client.rs | 48 +++++++++++++++++++++++++---------------------- src/lib.rs | 16 ++++++++++++++-- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0239a6b94..f9d222dc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "4d4e3fb57584d9c3bba6b4fc488f23db4937ae79" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "dfb50f7" } diff --git a/src/api_client.rs b/src/api_client.rs index 2b3921c55..a05e38518 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -12,13 +12,9 @@ use crate::{ ApiError, Error, }; use ethereum_consensus::{ - altair::{ - LightClientBootstrap, LightClientFinalityUpdate, LightClientOptimisticUpdate, - LightClientUpdate, SyncCommitteeMessage, - }, + altair::SyncCommitteeMessage, builder::SignedValidatorRegistration, capella::{SignedBlsToExecutionChange, Withdrawal}, - deneb::BlobSidecar, networking::PeerId, phase0::{AttestationData, Fork, ProposerSlashing, SignedVoluntaryExit}, primitives::{ @@ -57,10 +53,10 @@ async fn api_error_or_value( #[allow(clippy::type_complexity)] #[derive(Clone)] -pub struct Client { +pub struct Client { pub http: reqwest::Client, pub endpoint: Url, - _phantom: std::marker::PhantomData<(A, B, C, D, E, F, G, H, I, J)>, + _phantom: std::marker::PhantomData<(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O)>, } impl< @@ -74,6 +70,11 @@ impl< BeaconState: serde::Serialize + serde::de::DeserializeOwned, SignedAggregateAndProof: serde::Serialize, SignedBeaconBlock: serde::Serialize + serde::de::DeserializeOwned, + BlobSidecar: serde::Serialize + serde::de::DeserializeOwned, + LightClientBootstrap: serde::Serialize + serde::de::DeserializeOwned, + LightClientUpdate: serde::Serialize + serde::de::DeserializeOwned, + LightClientFinalityUpdate: serde::Serialize + serde::de::DeserializeOwned, + LightClientOptimisticUpdate: serde::Serialize + serde::de::DeserializeOwned, > Client< SignedContributionAndProof, @@ -86,6 +87,11 @@ impl< BeaconState, SignedAggregateAndProof, SignedBeaconBlock, + BlobSidecar, + LightClientBootstrap, + LightClientUpdate, + LightClientFinalityUpdate, + LightClientOptimisticUpdate, > { pub fn new_with_client>(client: reqwest::Client, endpoint: U) -> Self { @@ -383,11 +389,11 @@ impl< Ok(result.data) } - pub async fn get_blob_sidecars( + pub async fn get_blob_sidecars( &self, id: BlockId, indices: &[BlobIndex], - ) -> Result>, Error> { + ) -> Result, Error> { let path = format!("eth/v1/beacon/blob_sidecars/{id}"); let target = self.endpoint.join(&path)?; let mut request = self.http.get(target); @@ -413,20 +419,20 @@ impl< Ok(result.data) } - pub async fn get_light_client_bootstrap( + pub async fn get_light_client_bootstrap( &self, block: Root, - ) -> Result, Error> { - let result: Value> = + ) -> Result { + let result: Value<_> = self.get(&format!("eth/v1/beacon/light_client/bootstrap/{block}")).await?; Ok(result.data) } - pub async fn get_light_client_updates( + pub async fn get_light_client_updates( &self, start: u64, count: u64, - ) -> Result>, Error> { + ) -> Result, Error> { let target = self.endpoint.join("eth/v1/beacon/light_client/updates")?; let mut request = self.http.get(target); request = request.query(&[("start_period", start), ("count", count)]); @@ -439,19 +445,17 @@ impl< } } - pub async fn get_light_client_finality_update( + pub async fn get_light_client_finality_update( &self, - ) -> Result, Error> { - let result: Value> = - self.get("eth/v1/beacon/light_client/finality_update").await?; + ) -> Result { + let result: Value<_> = self.get("eth/v1/beacon/light_client/finality_update").await?; Ok(result.data) } - pub async fn get_light_client_optimistic_update( + pub async fn get_light_client_optimistic_update( &self, - ) -> Result, Error> { - let result: Value> = - self.get("eth/v1/beacon/light_client/optimistic_update").await?; + ) -> Result { + let result: Value<_> = self.get("eth/v1/beacon/light_client/optimistic_update").await?; Ok(result.data) } diff --git a/src/lib.rs b/src/lib.rs index 1fb5c818b..11b7c3bbc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,8 @@ mod error { pub mod presets { pub mod mainnet { use ethereum_consensus::{ - altair::mainnet as altair, bellatrix::mainnet as bellatrix, phase0::mainnet as phase0, + altair::mainnet as altair, bellatrix::mainnet as bellatrix, deneb::mainnet as deneb, + phase0::mainnet as phase0, }; pub type Client = crate::Client< @@ -48,11 +49,17 @@ pub mod presets { phase0::BeaconState, phase0::SignedAggregateAndProof, phase0::SignedBeaconBlock, + deneb::BlobSidecar, + altair::LightClientBootstrap, + altair::LightClientUpdate, + altair::LightClientFinalityUpdate, + altair::LightClientOptimisticUpdate, >; } pub mod minimal { use ethereum_consensus::{ - altair::minimal as altair, bellatrix::minimal as bellatrix, phase0::minimal as phase0, + altair::minimal as altair, bellatrix::minimal as bellatrix, deneb::minimal as deneb, + phase0::minimal as phase0, }; pub type Client = crate::Client< @@ -66,6 +73,11 @@ pub mod presets { phase0::BeaconState, phase0::SignedAggregateAndProof, phase0::SignedBeaconBlock, + deneb::BlobSidecar, + altair::LightClientBootstrap, + altair::LightClientUpdate, + altair::LightClientFinalityUpdate, + altair::LightClientOptimisticUpdate, >; } } From 7f28993615fde52d563dd601a0511c34fe9b7c38 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 29 Aug 2023 15:10:46 -0700 Subject: [PATCH 148/153] update deps --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f9d222dc6..74d312a03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "dfb50f7" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "12508c1f9b0c8f4bf4c5e9b6d441e840c1b37fd9" } From 98d075fc0af06eb794e89260d1306f89b01cabd8 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 6 Sep 2023 17:43:28 -0600 Subject: [PATCH 149/153] update deps --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 74d312a03..491bb47e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "12508c1f9b0c8f4bf4c5e9b6d441e840c1b37fd9" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "8d85daf5b50e522bde351eabf6340ccfd0030db1" } From da97354d0925ac9e23b1f7d5f71125a733229399 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 8 Sep 2023 11:38:48 -0600 Subject: [PATCH 150/153] update deps --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 491bb47e1..4e0731d82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "8d85daf5b50e522bde351eabf6340ccfd0030db1" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "fb57f8cac513beba805f935c3bcfe6a544231c6a" } From 9f901fee4e9718070cd8d870e3ac100d9b6b388e Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 8 Sep 2023 11:53:23 -0600 Subject: [PATCH 151/153] update deps --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4e0731d82..aba07ff4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "fb57f8cac513beba805f935c3bcfe6a544231c6a" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "8d6177ff1324e8c3ce1736b1549e88207146dc35" } From 223e221251f829541d2cfba2fa537bbedc52a517 Mon Sep 17 00:00:00 2001 From: chirag-bgh Date: Thu, 14 Sep 2023 00:26:17 +0530 Subject: [PATCH 152/153] deps: update ethereum-consensus --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index aba07ff4d..0589f3db7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "8d6177ff1324e8c3ce1736b1549e88207146dc35" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "6dee2390c8f4900ce3a521a6e6ac7ed0335cad2a" } From 715b8048ccc931de341b9526bba80d8ca0d22cad Mon Sep 17 00:00:00 2001 From: chirag-bgh Date: Fri, 15 Sep 2023 01:18:40 +0530 Subject: [PATCH 153/153] deps: update ethereum-consensus --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0589f3db7..34fd6d16f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1.0.81" itertools = "0.10.3" clap = { version = "4.3.11", features = ["derive"], optional = true } thiserror = "1.0.30" -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "6dee2390c8f4900ce3a521a6e6ac7ed0335cad2a" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "742607d52ddb2b465f01a0b241cd4c4e2f3b67cc" }