Skip to content

Commit

Permalink
Add vote rejecting sigverify mode
Browse files Browse the repository at this point in the history
  • Loading branch information
sakridge committed Sep 19, 2021
1 parent b7361d8 commit 4ad2e19
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 23 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion core/src/banking_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1616,7 +1616,7 @@ mod tests {
);
trace!("sending bank");
drop(verified_sender);
drop(verified_gossip_sender);
drop(verified_gossip_vote_sender);
drop(tpu_vote_sender);
exit.store(true, Ordering::Relaxed);
poh_service.join().unwrap();
Expand Down
5 changes: 4 additions & 1 deletion core/src/cluster_info_vote_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,10 @@ impl ClusterInfoVoteListener {
labels: Vec<CrdsValueLabel>,
) -> (Vec<Transaction>, Vec<(CrdsValueLabel, Slot, Packets)>) {
let mut msgs = packet::to_packets_chunked(&votes, 1);
sigverify::ed25519_verify_cpu(&mut msgs);

// Votes should already be filtered by this point.
let reject_non_vote = false;
sigverify::ed25519_verify_cpu(&mut msgs, reject_non_vote);

let (vote_txs, packets) = izip!(labels.into_iter(), votes.into_iter(), msgs,)
.filter_map(|(label, vote, packet)| {
Expand Down
18 changes: 17 additions & 1 deletion core/src/sigverify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ pub use solana_perf::sigverify::{
pub struct TransactionSigVerifier {
recycler: Recycler<TxOffset>,
recycler_out: Recycler<PinnedVec<u8>>,
reject_non_vote: bool,
}

impl TransactionSigVerifier {
pub fn new_reject_non_vote() -> Self {
TransactionSigVerifier {
reject_non_vote: true,
..TransactionSigVerifier::default()
}
}
}

impl Default for TransactionSigVerifier {
Expand All @@ -25,13 +35,19 @@ impl Default for TransactionSigVerifier {
Self {
recycler: Recycler::warmed(50, 4096, None, ""),
recycler_out: Recycler::warmed(50, 4096, None, ""),
reject_non_vote: false,
}
}
}

impl SigVerifier for TransactionSigVerifier {
fn verify_batch(&self, mut batch: Vec<Packets>) -> Vec<Packets> {
sigverify::ed25519_verify(&mut batch, &self.recycler, &self.recycler_out);
sigverify::ed25519_verify(
&mut batch,
&self.recycler,
&self.recycler_out,
self.reject_non_vote,
);
batch
}
}
2 changes: 1 addition & 1 deletion core/src/tpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl Tpu {
let (vote_verified_sender, vote_verified_receiver) = unbounded();

let vote_sigverify_stage = {
let verifier = TransactionSigVerifier::default();
let verifier = TransactionSigVerifier::new_reject_non_vote();
SigVerifyStage::new(vote_packet_receiver, vote_verified_sender, verifier)
};

Expand Down
1 change: 1 addition & 0 deletions perf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ log = "0.4.11"
solana-sdk = { path = "../sdk", version = "=1.6.27" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.6.27" }
solana-budget-program = { path = "../programs/budget", version = "=1.6.27" }
solana-vote-program = { path = "../programs/vote", version = "=1.6.27" }
solana-logger = { path = "../logger", version = "=1.6.27" }
solana-measure = { path = "../measure", version = "=1.6.27" }
solana-metrics = { path = "../metrics", version = "=1.6.27" }
Expand Down
2 changes: 1 addition & 1 deletion perf/benches/sigverify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn bench_sigverify(bencher: &mut Bencher) {
let recycler_out = Recycler::new_without_limit("");
// verify packets
bencher.iter(|| {
let _ans = sigverify::ed25519_verify(&mut batches, &recycler, &recycler_out);
let _ans = sigverify::ed25519_verify(&mut batches, &recycler, &recycler_out, false);
})
}

Expand Down
111 changes: 93 additions & 18 deletions perf/src/sigverify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::recycler::Recycler;
use rayon::ThreadPool;
use solana_metrics::inc_new_counter_debug;
use solana_rayon_threadlimit::get_thread_count;
use solana_sdk::hash::Hash;
use solana_sdk::message::MESSAGE_HEADER_LENGTH;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::short_vec::decode_shortu16_len;
Expand Down Expand Up @@ -45,15 +46,23 @@ struct PacketOffsets {
pub sig_start: u32,
pub msg_start: u32,
pub pubkey_start: u32,
pub pubkey_len: u32,
}

impl PacketOffsets {
pub fn new(sig_len: u32, sig_start: u32, msg_start: u32, pubkey_start: u32) -> Self {
pub fn new(
sig_len: u32,
sig_start: u32,
msg_start: u32,
pubkey_start: u32,
pubkey_len: u32,
) -> Self {
Self {
sig_len,
sig_start,
msg_start,
pubkey_start,
pubkey_len,
}
}
}
Expand Down Expand Up @@ -92,7 +101,7 @@ pub fn init() {
}
}

fn verify_packet(packet: &mut Packet) {
fn verify_packet(packet: &mut Packet, reject_non_vote: bool) {
let packet_offsets = get_packet_offsets(packet, 0);
let mut sig_start = packet_offsets.sig_start as usize;
let mut pubkey_start = packet_offsets.pubkey_start as usize;
Expand All @@ -113,6 +122,66 @@ fn verify_packet(packet: &mut Packet) {
return;
}

if reject_non_vote {
if packet_offsets.sig_len != 1 {
packet.meta.discard = true;
return;
}

let mut reject_pubkey_start = pubkey_start;
let mut vote_index = None;
for i in 0..packet_offsets.pubkey_len {
let pubkey_end = reject_pubkey_start.saturating_add(size_of::<Pubkey>());
if &packet.data[pubkey_start..pubkey_end] == solana_vote_program::id().as_ref() {
vote_index = Some(i);
break;
}
reject_pubkey_start = pubkey_end;
}

if vote_index.is_none() {
packet.meta.discard = true;
return;
}

let pubkeys_end = pubkey_start
.saturating_add(size_of::<Pubkey>().saturating_mul(packet_offsets.pubkey_len as usize));
let hash_end = pubkeys_end.saturating_add(size_of::<Hash>());
let num_instructions_offset = hash_end;

if hash_end.saturating_add(1) >= packet.meta.size {
packet.meta.discard = true;
return;
}

let (instructions_len, instructions_size) =
match decode_shortu16_len(&packet.data[num_instructions_offset..]) {
Ok((len, size)) => (len, size),
Err(_) => {
packet.meta.discard = true;
return;
}
};

let instruction0_start = hash_end.saturating_add(instructions_size);
if instruction0_start > packet.meta.size {
packet.meta.discard = true;
return;
}

let vote_index = vote_index.unwrap();
// First instruction should be the vote key
if packet.data[instruction0_start] as u32 != vote_index {
packet.meta.discard = true;
return;
}

if instructions_len != 1 {
packet.meta.discard = true;
return;
}
}

let msg_end = packet.meta.size;
for _ in 0..packet_offsets.sig_len {
let pubkey_end = pubkey_start.saturating_add(size_of::<Pubkey>());
Expand Down Expand Up @@ -234,6 +303,7 @@ fn do_get_packet_offsets(
u32::try_from(sig_start)?,
u32::try_from(msg_start)?,
u32::try_from(pubkey_start)?,
u32::try_from(pubkey_len)?,
))
}

Expand All @@ -243,7 +313,7 @@ fn get_packet_offsets(packet: &Packet, current_offset: usize) -> PacketOffsets {
offsets
} else {
// force sigverify to fail by returning zeros
PacketOffsets::new(0, 0, 0, 0)
PacketOffsets::new(0, 0, 0, 0, 0)
}
}

Expand Down Expand Up @@ -296,14 +366,16 @@ pub fn generate_offsets(batches: &[Packets], recycler: &Recycler<TxOffset>) -> T
)
}

pub fn ed25519_verify_cpu(batches: &mut [Packets]) {
pub fn ed25519_verify_cpu(batches: &mut [Packets], reject_non_vote: bool) {
use rayon::prelude::*;
let count = batch_size(batches);
debug!("CPU ECDSA for {}", batch_size(batches));
PAR_THREAD_POOL.install(|| {
batches
.into_par_iter()
.for_each(|p| p.packets.par_iter_mut().for_each(|p| verify_packet(p)))
batches.into_par_iter().for_each(|p| {
p.packets
.par_iter_mut()
.for_each(|p| verify_packet(p, reject_non_vote))
})
});
inc_new_counter_debug!("ed25519_verify_cpu", count);
}
Expand Down Expand Up @@ -383,10 +455,11 @@ pub fn ed25519_verify(
batches: &mut [Packets],
recycler: &Recycler<TxOffset>,
recycler_out: &Recycler<PinnedVec<u8>>,
reject_non_vote: bool,
) {
let api = perf_libs::api();
if api.is_none() {
return ed25519_verify_cpu(batches);
return ed25519_verify_cpu(batches, reject_non_vote);
}
let api = api.unwrap();

Expand All @@ -399,7 +472,7 @@ pub fn ed25519_verify(
// may be busy doing other things while being a real validator
// TODO: dynamically adjust this crossover
if count < 64 {
return ed25519_verify_cpu(batches);
return ed25519_verify_cpu(batches, reject_non_vote);
}

let (signature_offsets, pubkey_offsets, msg_start_offsets, msg_sizes, sig_lens) =
Expand Down Expand Up @@ -613,7 +686,7 @@ mod tests {
let res = sigverify::do_get_packet_offsets(&packet, 0);
assert_eq!(res, Err(PacketError::InvalidPubkeyLen));

verify_packet(&mut packet);
verify_packet(&mut packet, false);
assert!(packet.meta.discard);

packet.meta.discard = false;
Expand Down Expand Up @@ -649,7 +722,7 @@ mod tests {
let res = sigverify::do_get_packet_offsets(&packet, 0);
assert_eq!(res, Err(PacketError::InvalidPubkeyLen));

verify_packet(&mut packet);
verify_packet(&mut packet, false);
assert!(packet.meta.discard);

packet.meta.discard = false;
Expand Down Expand Up @@ -749,30 +822,32 @@ mod tests {
packet_offsets.sig_start - current_offset,
packet_offsets.msg_start - packet_offsets.sig_start,
packet_offsets.pubkey_start - packet_offsets.msg_start,
0, // TODO: Fix this
)
}

#[test]
fn test_get_packet_offsets() {
// TODO: not 0 for pubkey_len
assert_eq!(
get_packet_offsets_from_tx(test_tx(), 0),
PacketOffsets::new(1, 1, 64, 4)
PacketOffsets::new(1, 1, 64, 4, 0)
);
assert_eq!(
get_packet_offsets_from_tx(test_tx(), 100),
PacketOffsets::new(1, 1, 64, 4)
PacketOffsets::new(1, 1, 64, 4, 0)
);

// Ensure we're not indexing packet by the `current_offset` parameter.
assert_eq!(
get_packet_offsets_from_tx(test_tx(), 1_000_000),
PacketOffsets::new(1, 1, 64, 4)
PacketOffsets::new(1, 1, 64, 4, 0)
);

// Ensure we're returning sig_len, not sig_size.
assert_eq!(
get_packet_offsets_from_tx(test_multisig_tx(), 0),
PacketOffsets::new(2, 1, 128, 4)
PacketOffsets::new(2, 1, 128, 4, 0)
);
}

Expand Down Expand Up @@ -823,7 +898,7 @@ mod tests {
fn ed25519_verify(batches: &mut [Packets]) {
let recycler = Recycler::new_without_limit("");
let recycler_out = Recycler::new_without_limit("");
sigverify::ed25519_verify(batches, &recycler, &recycler_out);
sigverify::ed25519_verify(batches, &recycler, &recycler_out, false);
}

#[test]
Expand Down Expand Up @@ -921,8 +996,8 @@ mod tests {
// verify from GPU verification pipeline (when GPU verification is enabled) are
// equivalent to the CPU verification pipeline.
let mut batches_cpu = batches.clone();
sigverify::ed25519_verify(&mut batches, &recycler, &recycler_out);
ed25519_verify_cpu(&mut batches_cpu);
sigverify::ed25519_verify(&mut batches, &recycler, &recycler_out, false);
ed25519_verify_cpu(&mut batches_cpu, false);

// check result
batches
Expand Down

0 comments on commit 4ad2e19

Please sign in to comment.