Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: recursive

- name: Check formatting
run: make check-format
Expand All @@ -31,6 +33,8 @@ jobs:

- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: recursive

- name: Build client
run: make build
Expand All @@ -49,6 +53,8 @@ jobs:

- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: recursive

- name: Run tests
run: make test
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lean_client/dedicated_executor"]
path = lean_client/dedicated_executor
url = https://github.com/grandinetech/dedicated_executor
92 changes: 88 additions & 4 deletions lean_client/Cargo.lock

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

9 changes: 8 additions & 1 deletion lean_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ eth_ssz = { package = "ethereum_ssz", version = "0.10.0" }
ethereum-types = "0.14"
futures = "0.3"
features = { git = "https://github.com/grandinetech/grandine", rev = "64afdee3c6be79fceffb66933dcb69a943f3f1ae" }
git-version = "0.3"
hex = "0.4.3"
http-body-util = "0.1"
http_api_utils = { git = "https://github.com/grandinetech/grandine", rev = "64afdee3c6be79fceffb66933dcb69a943f3f1ae" }
Expand All @@ -264,13 +265,16 @@ libp2p = { version = "0.56.0", default-features = false, features = [
] }
libp2p-identity = { version = "0.2", features = ["secp256k1"] }
libp2p-mplex = "0.39"
dedicated_executor = { path = "dedicated_executor" }
num-bigint = "0.4"
num-traits = "0.2"
num_cpus = "1"
once_cell = "1.21"
parking_lot = "0.12"
paste = "1.0.15"
pretty_assertions = "1.4"
prometheus = "0.14"
prometheus = { version = "0.14", features = ["process"] }
prometheus_metrics = { git = "https://github.com/grandinetech/grandine", rev = "64afdee3c6be79fceffb66933dcb69a943f3f1ae" }
rand = "0.10"
rand_chacha = "0.10"
rayon = "1"
Expand Down Expand Up @@ -309,14 +313,17 @@ bls = { workspace = true }
chain = { workspace = true }
clap = { workspace = true }
containers = { workspace = true }
dedicated_executor = { workspace = true }
ethereum-types = { workspace = true }
features = { workspace = true }
fork_choice = { workspace = true }
git-version = { workspace = true }
hex = { workspace = true }
http_api = { workspace = true }
libp2p-identity = { workspace = true }
metrics = { workspace = true }
networking = { workspace = true }
num_cpus = { workspace = true }
parking_lot = { workspace = true }
ssz = { workspace = true }
tokio = { workspace = true }
Expand Down
40 changes: 40 additions & 0 deletions lean_client/containers/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,46 @@ impl AggregatedSignatureProof {
) -> Result<()> {
self.proof_data.verify(public_keys, message, slot)
}

/// Greedy set-cover over a slice of proofs, returning indices of the proofs
/// to admit in priority order. Repeatedly picks the proof covering the
/// most uncovered validators; stops when no remaining proof adds coverage.
/// Mirrors leanSpec `AggregatedSignatureProof.select_greedily`.
pub fn select_greedily(proofs: &[AggregatedSignatureProof]) -> Vec<usize> {
let mut selected: Vec<usize> = Vec::new();
let mut covered: HashSet<u64> = HashSet::new();
let mut remaining: HashSet<usize> = (0..proofs.len()).collect();

while !remaining.is_empty() {
let best = remaining
.iter()
.copied()
.map(|idx| {
let new_count = proofs[idx]
.get_participant_indices()
.into_iter()
.filter(|vid| !covered.contains(vid))
.count();
(idx, new_count)
})
.max_by_key(|(_, n)| *n);

let Some((best_idx, best_count)) = best else {
break;
};
if best_count == 0 {
break;
}

for vid in proofs[best_idx].get_participant_indices() {
covered.insert(vid);
}
selected.push(best_idx);
remaining.remove(&best_idx);
}

selected
}
}

/// Bitlist representing validator participation in an attestation.
Expand Down
Loading
Loading