Conversation
Remove ant-evm, libp2p, and multihash dependencies entirely. All payment types now come from evmlib which absorbed the needed ant-evm types with libp2p stripped out. Key changes: - EncodedPeerId is now [u8; 32] (native saorsa PeerId bytes) - hex_node_id_to_encoded_peer_id simplified to hex decode + wrap - validate_peer_bindings compares raw 32-byte PeerIds directly instead of decoding through libp2p multihash - All test helpers rewritten to use EncodedPeerId::new(rand::random()) instead of libp2p Keypair/PeerId construction This removes ~530 transitive dependencies from the lock file.
There was a problem hiding this comment.
Pull request overview
This PR migrates the node’s EVM/payment integration off ant-evm/libp2p/multihash and onto evmlib, aligning peer identity handling with saorsa’s native 32-byte PeerId format and significantly reducing transitive dependencies.
Changes:
- Replaced
ant-evmpayment types/usages across core and E2E tests withevmlibequivalents. - Simplified peer-id encoding/validation to operate on raw 32-byte peer IDs (no multihash/libp2p wrapping).
- Updated Anvil/Testnet helpers to propagate
Resulterrors instead of assuming infallible calls.
Reviewed changes
Copilot reviewed 18 out of 20 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/e2e/testnet.rs | Swaps RewardsAddress import to evmlib. |
| tests/e2e/security_attacks.rs | Uses evmlib::ProofOfPayment; propagates Testnet::new() errors. |
| tests/e2e/payment_flow.rs | Propagates default_wallet_private_key() / Testnet::new() errors. |
| tests/e2e/merkle_payment.rs | Moves merkle payment types + RewardsAddress to evmlib; propagates Testnet::new() errors. |
| tests/e2e/integration_tests.rs | Adapts to default_wallet_key() returning Result. |
| tests/e2e/data_types/chunk.rs | Updates payment proof/types to evmlib; improves testnet startup error context. |
| tests/e2e/anvil.rs | Makes wallet key retrieval fallible; maps evmlib errors into AnvilError. |
| src/storage/handler.rs | Switches test imports from ant_evm to evmlib types. |
| src/payment/wallet.rs | Replaces ant_evm::RewardsAddress with evmlib::RewardsAddress. |
| src/payment/verifier.rs | Replaces ant_evm types; validates peer bindings against raw 32-byte IDs; improves error formatting. |
| src/payment/single_node.rs | Moves common/payment types to evmlib modules; improves error propagation in tests. |
| src/payment/quote.rs | Updates quote/candidate/metrics types to evmlib. |
| src/payment/proof.rs | Updates proof + merkle proof types to evmlib; adjusts tests to avoid libp2p. |
| src/payment/pricing.rs | Uses evmlib Amount + QuotingMetrics modules. |
| src/payment/metrics.rs | Updates quoting metrics import to evmlib. |
| src/devnet.rs | Swaps RewardsAddress import to evmlib. |
| src/client/mod.rs | Simplifies hex peer-id conversion to direct 32-byte decode + EncodedPeerId::new. |
| src/bin/ant-devnet/main.rs | Propagates Anvil testnet startup + wallet key retrieval errors. |
| Cargo.toml | Removes ant-evm, libp2p, multihash; switches evmlib dependency specification. |
| Cargo.lock | Reflects dependency graph reduction and the updated evmlib resolution. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| )) | ||
| let bytes: [u8; 32] = raw_bytes.try_into().map_err(|v: Vec<u8>| { | ||
| let len = v.len(); | ||
| Error::Payment(format!("Peer ID must be 32 bytes, got {len}")) |
There was a problem hiding this comment.
The length error path drops the offending input value, making it harder to diagnose which peer ID was malformed. Consider including hex_id (and/or expected hex length of 64 chars) in the "Peer ID must be 32 bytes" error message for easier debugging.
| Error::Payment(format!("Peer ID must be 32 bytes, got {len}")) | |
| Error::Payment(format!( | |
| "Peer ID must be 32 bytes (64 hex chars), got {len} bytes from '{hex_id}'" | |
| )) |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 20 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pub fn default_wallet_key(&self) -> Result<String> { | ||
| self.testnet | ||
| .default_wallet_private_key() | ||
| .map_err(|e| AnvilError::Startup(format!("Failed to get wallet key: {e}"))) | ||
| } |
There was a problem hiding this comment.
default_wallet_key() maps failures to AnvilError::Startup, which results in error messages like "Failed to start Anvil: Failed to get wallet key..." even though startup already succeeded. Consider using a more appropriate error variant (or renaming Startup to a more general variant) so callers get accurate context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add unit and e2e tests covering the remaining Section 18 scenarios: Unit tests (32 new): - Quorum: #4 fail→abandoned, #16 timeout→inconclusive, #27 single-round dual-evidence, #28 dynamic threshold undersized, #33 batched per-key, #34 partial response unresolved, #42 quorum-derived paid-list auth - Admission: #5 unauthorized peer, #7 out-of-range rejected - Config: #18 invalid config rejected, #26 dynamic paid threshold - Scheduling: #8 dedup safety, #8 replica/paid collapse - Neighbor sync: #35 round-robin cooldown skip, #36 cycle completion, #38 snapshot stability mid-join, #39 unreachable removal + slot fill, #40 cooldown peer removed, #41 cycle termination guarantee, consecutive rounds, cycle preserves sync times - Pruning: #50 hysteresis prevents premature delete, #51 timestamp reset on heal, #52 paid/record timestamps independent, #23 entry removal - Audit: #19/#53 partial failure mixed responsibility, #54 all pass, #55 empty failure discard, #56 repair opportunity filter, response count validation, digest uses full record bytes - Types: #13 bootstrap drain, repair opportunity edge cases, terminal state variants - Bootstrap claims: #46 first-seen recorded, #49 cleared on normal E2e tests (4 new): - #2 fresh offer with empty PoP rejected - #5/#37 neighbor sync request returns response - #11 audit challenge multi-key (present + absent) - Fetch not-found for non-existent key Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the remaining untested scenarios from REPLICATION_DESIGN.md Section 18, bringing coverage from 47/56 to 56/56: - #20: paid-list local hit bypasses presence quorum (quorum.rs) - #22: paid-list rejection below threshold (quorum.rs) - #29: audit start gate during bootstrap (audit.rs) - #30: audit peer selection from sampled keys (audit.rs) - #31: audit periodic cadence with jitter bounds (config.rs) - #32: dynamic challenge size equals PeerKeySet (audit.rs) - #47: bootstrap claim grace period in audit path (audit.rs) - #48: bootstrap claim abuse after grace period (paid_list.rs) - #53: audit partial per-key failure with mixed responsibility (audit.rs) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add unit and e2e tests covering the remaining Section 18 scenarios: Unit tests (32 new): - Quorum: #4 fail→abandoned, #16 timeout→inconclusive, #27 single-round dual-evidence, #28 dynamic threshold undersized, #33 batched per-key, #34 partial response unresolved, #42 quorum-derived paid-list auth - Admission: #5 unauthorized peer, #7 out-of-range rejected - Config: #18 invalid config rejected, #26 dynamic paid threshold - Scheduling: #8 dedup safety, #8 replica/paid collapse - Neighbor sync: #35 round-robin cooldown skip, #36 cycle completion, #38 snapshot stability mid-join, #39 unreachable removal + slot fill, #40 cooldown peer removed, #41 cycle termination guarantee, consecutive rounds, cycle preserves sync times - Pruning: #50 hysteresis prevents premature delete, #51 timestamp reset on heal, #52 paid/record timestamps independent, #23 entry removal - Audit: #19/#53 partial failure mixed responsibility, #54 all pass, #55 empty failure discard, #56 repair opportunity filter, response count validation, digest uses full record bytes - Types: #13 bootstrap drain, repair opportunity edge cases, terminal state variants - Bootstrap claims: #46 first-seen recorded, #49 cleared on normal E2e tests (4 new): - #2 fresh offer with empty PoP rejected - #5/#37 neighbor sync request returns response - #11 audit challenge multi-key (present + absent) - Fetch not-found for non-existent key Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the remaining untested scenarios from REPLICATION_DESIGN.md Section 18, bringing coverage from 47/56 to 56/56: - #20: paid-list local hit bypasses presence quorum (quorum.rs) - #22: paid-list rejection below threshold (quorum.rs) - #29: audit start gate during bootstrap (audit.rs) - #30: audit peer selection from sampled keys (audit.rs) - #31: audit periodic cadence with jitter bounds (config.rs) - #32: dynamic challenge size equals PeerKeySet (audit.rs) - #47: bootstrap claim grace period in audit path (audit.rs) - #48: bootstrap claim abuse after grace period (paid_list.rs) - #53: audit partial per-key failure with mixed responsibility (audit.rs) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
ant-evm,libp2p, andmultihashdependencies entirelyEncodedPeerIdis now[u8; 32](native saorsa PeerId, no libp2p multihash encoding)hex_node_id_to_encoded_peer_id()simplified to direct hex decodevalidate_peer_bindings()compares raw 32-byte PeerIds directlyContext
Depends on WithAutonomi/evmlib#3 — evmlib must absorb the ant-evm types first.
The saorsa network uses native 32-byte PeerIds (BLAKE3 hash of ML-DSA-65 public keys), not libp2p PeerIds. The old code wrapped these in a libp2p identity-multihash (34 bytes), passed through
libp2p::PeerId, then unwrapped by stripping the 2-byte header. This PR removes that overhead entirely.Test plan
cargo checkpassescargo fmt + clippyclean (strict deny on unwrap/expect/panic)cargo test --lib— 300 tests pass