Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 95810d8

Browse files
authored
Enable frozen_abi on banking trace file (#33501)
* Enable frozen_abi on banking trace file * Fix ci with really correct bugfix... * Remove tracker_callers * Fix typo... * Fix AbiExample for Arc/Rc's Weaks * Added comment for AbiExample impl of SystemTime * Simplify and document EvenAsOpaque with new usage * Minor clean-ups * Simplify SystemTime::example() with UNIX_EPOCH... * Add comment for AbiExample subtleties
1 parent 630feed commit 95810d8

File tree

14 files changed

+205
-42
lines changed

14 files changed

+205
-42
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/src/banking_trace.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,17 @@ pub struct BankingTracer {
6262
active_tracer: Option<ActiveTracer>,
6363
}
6464

65-
#[derive(Serialize, Deserialize, Debug)]
65+
#[frozen_abi(digest = "Eq6YrAFtTbtPrCEvh6Et1mZZDCARUg1gcK2qiZdqyjUz")]
66+
#[derive(Serialize, Deserialize, Debug, AbiExample)]
6667
pub struct TimedTracedEvent(pub std::time::SystemTime, pub TracedEvent);
6768

68-
#[derive(Serialize, Deserialize, Debug)]
69+
#[derive(Serialize, Deserialize, Debug, AbiExample, AbiEnumVisitor)]
6970
pub enum TracedEvent {
7071
PacketBatch(ChannelLabel, BankingPacketBatch),
7172
BlockAndBankHash(Slot, Hash, Hash),
7273
}
7374

74-
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
75+
#[derive(Serialize, Deserialize, Debug, Clone, Copy, AbiExample, AbiEnumVisitor)]
7576
pub enum ChannelLabel {
7677
NonVote,
7778
TpuVote,

core/src/sigverify.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use {
1616
solana_sdk::{packet::Packet, saturating_add_assign},
1717
};
1818

19-
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
19+
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, AbiExample)]
2020
pub struct SigverifyTracerPacketStats {
2121
pub total_removed_before_sigverify_stage: usize,
2222
pub total_tracer_packets_received_in_sigverify_stage: usize,

frozen-abi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ subtle = { workspace = true }
3131

3232
[target.'cfg(not(target_os = "solana"))'.dev-dependencies]
3333
solana-logger = { workspace = true }
34+
bitflags = { workspace = true }
3435

3536
[build-dependencies]
3637
rustc_version = { workspace = true }

frozen-abi/src/abi_digester.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub struct AbiDigester {
1717
data_types: std::rc::Rc<std::cell::RefCell<Vec<String>>>,
1818
depth: usize,
1919
for_enum: bool,
20-
opaque_scope: Option<String>,
20+
opaque_type_matcher: Option<String>,
2121
}
2222

2323
pub type DigestResult = Result<AbiDigester, DigestError>;
@@ -70,7 +70,7 @@ impl AbiDigester {
7070
data_types: std::rc::Rc::new(std::cell::RefCell::new(vec![])),
7171
for_enum: false,
7272
depth: 0,
73-
opaque_scope: None,
73+
opaque_type_matcher: None,
7474
}
7575
}
7676

@@ -81,16 +81,16 @@ impl AbiDigester {
8181
data_types: self.data_types.clone(),
8282
depth: self.depth,
8383
for_enum: false,
84-
opaque_scope: self.opaque_scope.clone(),
84+
opaque_type_matcher: self.opaque_type_matcher.clone(),
8585
}
8686
}
8787

88-
pub fn create_new_opaque(&self, top_scope: &str) -> Self {
88+
pub fn create_new_opaque(&self, type_matcher: &str) -> Self {
8989
Self {
9090
data_types: self.data_types.clone(),
9191
depth: self.depth,
9292
for_enum: false,
93-
opaque_scope: Some(top_scope.to_owned()),
93+
opaque_type_matcher: Some(type_matcher.to_owned()),
9494
}
9595
}
9696

@@ -103,7 +103,7 @@ impl AbiDigester {
103103
data_types: self.data_types.clone(),
104104
depth,
105105
for_enum: false,
106-
opaque_scope: self.opaque_scope.clone(),
106+
opaque_type_matcher: self.opaque_type_matcher.clone(),
107107
})
108108
}
109109

@@ -116,15 +116,15 @@ impl AbiDigester {
116116
data_types: self.data_types.clone(),
117117
depth,
118118
for_enum: true,
119-
opaque_scope: self.opaque_scope.clone(),
119+
opaque_type_matcher: self.opaque_type_matcher.clone(),
120120
})
121121
}
122122

123123
pub fn digest_data<T: ?Sized + Serialize>(&mut self, value: &T) -> DigestResult {
124124
let type_name = normalize_type_name(type_name::<T>());
125125
if type_name.ends_with("__SerializeWith")
126-
|| (self.opaque_scope.is_some()
127-
&& type_name.starts_with(self.opaque_scope.as_ref().unwrap()))
126+
|| (self.opaque_type_matcher.is_some()
127+
&& type_name.contains(self.opaque_type_matcher.as_ref().unwrap()))
128128
{
129129
// we can't use the AbiEnumVisitor trait for these cases.
130130
value.serialize(self.create_new())
@@ -661,6 +661,34 @@ mod tests {
661661
#[frozen_abi(digest = "9PMdHRb49BpkywrmPoJyZWMsEmf5E1xgmsFGkGmea5RW")]
662662
type TestBitVec = bv::BitVec<u64>;
663663

664+
mod bitflags_abi {
665+
use crate::abi_example::{AbiExample, EvenAsOpaque, IgnoreAsHelper};
666+
667+
bitflags::bitflags! {
668+
#[frozen_abi(digest = "HhKNkaeAd7AohTb8S8sPKjAWwzxWY2DPz5FvkWmx5bSH")]
669+
#[derive(Serialize, Deserialize)]
670+
struct TestFlags: u8 {
671+
const TestBit = 0b0000_0001;
672+
}
673+
}
674+
675+
impl AbiExample for TestFlags {
676+
fn example() -> Self {
677+
Self::empty()
678+
}
679+
}
680+
681+
impl IgnoreAsHelper for TestFlags {}
682+
// This (EvenAsOpaque) marker trait is needed for bitflags-generated types because we can't
683+
// impl AbiExample for its private type:
684+
// thread '...TestFlags_frozen_abi...' panicked at ...:
685+
// derive or implement AbiExample/AbiEnumVisitor for
686+
// solana_frozen_abi::abi_digester::tests::_::InternalBitFlags
687+
impl EvenAsOpaque for TestFlags {
688+
const TYPE_NAME_MATCHER: &'static str = "::_::InternalBitFlags";
689+
}
690+
}
691+
664692
mod skip_should_be_same {
665693
#[frozen_abi(digest = "4LbuvQLX78XPbm4hqqZcHFHpseDJcw4qZL9EUZXSi2Ss")]
666694
#[derive(Serialize, AbiExample)]
@@ -691,4 +719,12 @@ mod tests {
691719
Variant2(u8, u16, #[serde(skip)] u32),
692720
}
693721
}
722+
723+
#[frozen_abi(digest = "B1PcwZdUfGnxaRid9e6ZwkST3NZ2KUEYobA1DkxWrYLP")]
724+
#[derive(Serialize, AbiExample)]
725+
struct TestArcWeak(std::sync::Weak<u64>);
726+
727+
#[frozen_abi(digest = "4R8uCLR1BVU1aFgkSaNyKcFD1FeM6rGdsjbJBFpnqx4v")]
728+
#[derive(Serialize, AbiExample)]
729+
struct TestRcWeak(std::rc::Weak<u64>);
694730
}

frozen-abi/src/abi_example.rs

Lines changed: 84 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@ use {
66
std::any::type_name,
77
};
88

9+
// The most important trait for the abi digesting. This trait is used to create any complexities of
10+
// object graph to generate the abi digest. The frozen abi test harness calls T::example() to
11+
// instantiate the tested root type and traverses its fields recursively, abusing the
12+
// serde::serialize().
13+
//
14+
// This trait applicability is similar to the Default trait. That means all referenced types must
15+
// implement this trait. AbiExample is implemented for almost all common types in this file.
16+
//
17+
// When implementing AbiExample manually, you need to return a _minimally-populated_ value
18+
// from it to actually generate a meaningful digest. This impl semantics is unlike Default, which
19+
// usually returns something empty. See actual impls for inspiration.
20+
//
21+
// The requirement of AbiExample impls even applies to those types of `#[serde(skip)]`-ed fields.
22+
// That's because the abi digesting needs a properly initialized object to enter into the
23+
// serde::serialize() to begin with, even knowning they aren't used for serialization and thus abi
24+
// digest. Luckily, `#[serde(skip)]`-ed fields' AbiExample impls can just delegate to T::default(),
25+
// exploiting the nature of this artificial impl requirement as an exception from the usual
26+
// AbiExample semantics.
927
pub trait AbiExample: Sized {
1028
fn example() -> Self;
1129
}
@@ -137,25 +155,12 @@ tuple_example_impls! {
137155
}
138156
}
139157

140-
// Source: https://github.com/rust-lang/rust/blob/ba18875557aabffe386a2534a1aa6118efb6ab88/src/libcore/array/mod.rs#L417
141-
macro_rules! array_example_impls {
142-
{$n:expr, $t:ident $($ts:ident)*} => {
143-
impl<T> AbiExample for [T; $n] where T: AbiExample {
144-
fn example() -> Self {
145-
[$t::example(), $($ts::example()),*]
146-
}
147-
}
148-
array_example_impls!{($n - 1), $($ts)*}
149-
};
150-
{$n:expr,} => {
151-
impl<T> AbiExample for [T; $n] {
152-
fn example() -> Self { [] }
153-
}
154-
};
158+
impl<const N: usize, T: AbiExample> AbiExample for [T; N] {
159+
fn example() -> Self {
160+
std::array::from_fn(|_| T::example())
161+
}
155162
}
156163

157-
array_example_impls! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T}
158-
159164
// Source: https://github.com/rust-lang/rust/blob/ba18875557aabffe386a2534a1aa6118efb6ab88/src/libcore/default.rs#L137
160165
macro_rules! example_impls {
161166
($t:ty, $v:expr) => {
@@ -232,7 +237,14 @@ impl<T: BlockType> AbiExample for BitVec<T> {
232237
}
233238

234239
impl<T: BlockType> IgnoreAsHelper for BitVec<T> {}
235-
impl<T: BlockType> EvenAsOpaque for BitVec<T> {}
240+
// This (EvenAsOpaque) marker trait is needed for BitVec because we can't impl AbiExample for its
241+
// private type:
242+
// thread '...TestBitVec_frozen_abi...' panicked at ...:
243+
// derive or implement AbiExample/AbiEnumVisitor for
244+
// bv::bit_vec::inner::Inner<u64>
245+
impl<T: BlockType> EvenAsOpaque for BitVec<T> {
246+
const TYPE_NAME_MATCHER: &'static str = "bv::bit_vec::inner::";
247+
}
236248

237249
pub(crate) fn normalize_type_name(type_name: &str) -> String {
238250
type_name.chars().filter(|c| *c != '&').collect()
@@ -329,13 +341,38 @@ impl<T: AbiExample> AbiExample for std::sync::Arc<T> {
329341
}
330342
}
331343

344+
// When T is weakly owned by the likes of `std::{sync, rc}::Weak`s, we need to uphold the ownership
345+
// of T in some way at least during abi digesting... However, there's no easy way. Stashing them
346+
// into static is confronted with Send/Sync issue. Stashing them into thread_local is confronted
347+
// with not enough (T + 'static) lifetime bound.. So, just leak the examples. This should be
348+
// tolerated, considering ::example() should ever be called inside tests, not in production code...
349+
fn leak_and_inhibit_drop<'a, T>(t: T) -> &'a mut T {
350+
Box::leak(Box::new(t))
351+
}
352+
353+
impl<T: AbiExample> AbiExample for std::sync::Weak<T> {
354+
fn example() -> Self {
355+
info!("AbiExample for (Arc's Weak<T>): {}", type_name::<Self>());
356+
// leaking is needed otherwise Arc::upgrade() will always return None...
357+
std::sync::Arc::downgrade(leak_and_inhibit_drop(std::sync::Arc::new(T::example())))
358+
}
359+
}
360+
332361
impl<T: AbiExample> AbiExample for std::rc::Rc<T> {
333362
fn example() -> Self {
334363
info!("AbiExample for (Rc<T>): {}", type_name::<Self>());
335364
std::rc::Rc::new(T::example())
336365
}
337366
}
338367

368+
impl<T: AbiExample> AbiExample for std::rc::Weak<T> {
369+
fn example() -> Self {
370+
info!("AbiExample for (Rc's Weak<T>): {}", type_name::<Self>());
371+
// leaking is needed otherwise Rc::upgrade() will always return None...
372+
std::rc::Rc::downgrade(leak_and_inhibit_drop(std::rc::Rc::new(T::example())))
373+
}
374+
}
375+
339376
impl<T: AbiExample> AbiExample for std::sync::Mutex<T> {
340377
fn example() -> Self {
341378
info!("AbiExample for (Mutex<T>): {}", type_name::<Self>());
@@ -457,6 +494,13 @@ impl AbiExample for std::path::PathBuf {
457494
}
458495
}
459496

497+
#[cfg(not(target_os = "solana"))]
498+
impl AbiExample for std::time::SystemTime {
499+
fn example() -> Self {
500+
std::time::SystemTime::UNIX_EPOCH
501+
}
502+
}
503+
460504
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
461505
impl AbiExample for SocketAddr {
462506
fn example() -> Self {
@@ -470,13 +514,22 @@ impl AbiExample for IpAddr {
470514
}
471515
}
472516

473-
// This is a control flow indirection needed for digesting all variants of an enum
517+
// This is a control flow indirection needed for digesting all variants of an enum.
518+
//
519+
// All of types (including non-enums) will be processed by this trait, albeit the
520+
// name of this trait.
521+
// User-defined enums usually just need to impl this with namesake derive macro (AbiEnumVisitor).
522+
//
523+
// Note that sometimes this indirection doesn't work for various reasons. For that end, there are
524+
// hacks with marker traits (IgnoreAsHelper/EvenAsOpaque).
474525
pub trait AbiEnumVisitor: Serialize {
475526
fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult;
476527
}
477528

478529
pub trait IgnoreAsHelper {}
479-
pub trait EvenAsOpaque {}
530+
pub trait EvenAsOpaque {
531+
const TYPE_NAME_MATCHER: &'static str;
532+
}
480533

481534
impl<T: Serialize + ?Sized> AbiEnumVisitor for T {
482535
default fn visit_for_abi(&self, _digester: &mut AbiDigester) -> DigestResult {
@@ -489,7 +542,9 @@ impl<T: Serialize + ?Sized> AbiEnumVisitor for T {
489542

490543
impl<T: Serialize + ?Sized + AbiExample> AbiEnumVisitor for T {
491544
default fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult {
492-
info!("AbiEnumVisitor for (default): {}", type_name::<T>());
545+
info!("AbiEnumVisitor for T: {}", type_name::<T>());
546+
// not calling self.serialize(...) is intentional here as the most generic impl
547+
// consider IgnoreAsHelper and EvenAsOpaque if you're stuck on this....
493548
T::example()
494549
.serialize(digester.create_new())
495550
.map_err(DigestError::wrap_by_type::<T>)
@@ -501,7 +556,7 @@ impl<T: Serialize + ?Sized + AbiExample> AbiEnumVisitor for T {
501556
// relevant test: TestVecEnum
502557
impl<T: Serialize + ?Sized + AbiEnumVisitor> AbiEnumVisitor for &T {
503558
default fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult {
504-
info!("AbiEnumVisitor for (&default): {}", type_name::<T>());
559+
info!("AbiEnumVisitor for &T: {}", type_name::<T>());
505560
// Don't call self.visit_for_abi(...) to avoid the infinite recursion!
506561
T::visit_for_abi(self, digester)
507562
}
@@ -521,9 +576,13 @@ impl<T: Serialize + IgnoreAsHelper> AbiEnumVisitor for &T {
521576
// inability of implementing AbiExample for private structs from other crates
522577
impl<T: Serialize + IgnoreAsHelper + EvenAsOpaque> AbiEnumVisitor for &T {
523578
default fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult {
524-
info!("AbiEnumVisitor for (IgnoreAsOpaque): {}", type_name::<T>());
525-
let top_scope = type_name::<T>().split("::").next().unwrap();
526-
self.serialize(digester.create_new_opaque(top_scope))
579+
let type_name = type_name::<T>();
580+
let matcher = T::TYPE_NAME_MATCHER;
581+
info!(
582+
"AbiEnumVisitor for (EvenAsOpaque): {}: matcher: {}",
583+
type_name, matcher
584+
);
585+
self.serialize(digester.create_new_opaque(matcher))
527586
.map_err(DigestError::wrap_by_type::<T>)
528587
}
529588
}

perf/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ log = { workspace = true }
2121
rand = { workspace = true }
2222
rayon = { workspace = true }
2323
serde = { workspace = true }
24+
solana-frozen-abi = { workspace = true }
25+
solana-frozen-abi-macro = { workspace = true }
2426
solana-metrics = { workspace = true }
2527
solana-rayon-threadlimit = { workspace = true }
2628
solana-sdk = { workspace = true }
@@ -40,6 +42,9 @@ rand_chacha = { workspace = true }
4042
solana-logger = { workspace = true }
4143
test-case = { workspace = true }
4244

45+
[build-dependencies]
46+
rustc_version = { workspace = true }
47+
4348
[[bench]]
4449
name = "sigverify"
4550

0 commit comments

Comments
 (0)