Skip to content

Commit b8c358a

Browse files
mergify[bot]cpubot
authored andcommitted
v3.1: Raise Entry's VersionedTransaction preallocation limit (backport of anza-xyz#10179) (anza-xyz#10182)
* Raise Entry's VersionedTransaction preallocation limit (anza-xyz#10179) * Raise Entry's VersionedTransaction preallocation limit * Add unit tests * Use MAX_DATA_SHREDS_SIZE prealloc gate in ledger deserialize (cherry picked from commit b1e2367) * Update to support wincode 0.1.x --------- Co-authored-by: Zach Brown <[email protected]>
1 parent 809b64f commit b8c358a

File tree

3 files changed

+103
-9
lines changed

3 files changed

+103
-9
lines changed

entry/src/entry.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ use {
3535
thread::{self, JoinHandle},
3636
time::Instant,
3737
},
38-
wincode::{containers::Pod, SchemaRead, SchemaWrite},
38+
wincode::{
39+
containers::{Elem, Pod, Vec as WincodeVec},
40+
len::BincodeLen,
41+
SchemaRead, SchemaWrite,
42+
},
3943
};
4044

4145
pub type EntrySender = Sender<Vec<Entry>>;
@@ -91,6 +95,10 @@ pub struct Api<'a> {
9195
Symbol<'a, unsafe extern "C" fn(hashes: *mut u8, num_hashes: *const u64)>,
9296
}
9397

98+
const MAX_DATA_SHREDS_PER_SLOT: usize = 32_768;
99+
pub const MAX_DATA_SHREDS_SIZE: usize = MAX_DATA_SHREDS_PER_SLOT * solana_packet::PACKET_DATA_SIZE;
100+
pub type MaxDataShredsLen = BincodeLen<MAX_DATA_SHREDS_SIZE>;
101+
94102
/// Each Entry contains three pieces of data. The `num_hashes` field is the number
95103
/// of hashes performed since the previous entry. The `hash` field is the result
96104
/// of hashing `hash` from the previous entry `num_hashes` times. The `transactions`
@@ -130,7 +138,7 @@ pub struct Entry {
130138
/// An unordered list of transactions that were observed before the Entry ID was
131139
/// generated. They may have been observed before a previous Entry ID but were
132140
/// pushed back into this list to ensure deterministic interpretation of the ledger.
133-
#[wincode(with = "Vec<crate::wincode::VersionedTransaction>")]
141+
#[wincode(with = "WincodeVec<Elem<crate::wincode::VersionedTransaction>, MaxDataShredsLen>")]
134142
pub transactions: Vec<VersionedTransaction>,
135143
}
136144

entry/src/wincode.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ impl SchemaRead<'_> for VersionedMsg {
235235
#[cfg(test)]
236236
mod tests {
237237
use {
238-
crate::entry::Entry,
238+
crate::entry::{Entry, MAX_DATA_SHREDS_SIZE},
239239
proptest::prelude::*,
240240
solana_address::{Address, ADDRESS_BYTES},
241241
solana_hash::{Hash, HASH_BYTES},
@@ -441,4 +441,82 @@ mod tests {
441441
prop_assert_eq!(entries, deserialized);
442442
}
443443
}
444+
445+
#[test]
446+
fn entry_deserialize_rejects_excessive_prealloc() {
447+
let message = LegacyMessage {
448+
header: MessageHeader {
449+
num_required_signatures: 0,
450+
num_readonly_signed_accounts: 0,
451+
num_readonly_unsigned_accounts: 0,
452+
},
453+
account_keys: vec![],
454+
recent_blockhash: Hash::new_from_array([0u8; HASH_BYTES]),
455+
instructions: vec![],
456+
};
457+
let transaction = VersionedTransaction {
458+
signatures: vec![],
459+
message: VersionedMessage::Legacy(message),
460+
};
461+
let entry = Entry {
462+
num_hashes: 0,
463+
hash: Hash::new_from_array([0u8; HASH_BYTES]),
464+
transactions: vec![transaction],
465+
};
466+
467+
let mut data = wincode::serialize(&entry).unwrap();
468+
let over_limit: usize = MAX_DATA_SHREDS_SIZE / size_of::<VersionedTransaction>() + 1;
469+
let len_offset = 8 + HASH_BYTES;
470+
// Fudge the length of the vec to be over the limit to trigger the preallocation
471+
// size limit error.
472+
data[len_offset..len_offset + 8].copy_from_slice(&over_limit.to_le_bytes());
473+
474+
let needed_bytes = over_limit * size_of::<VersionedTransaction>();
475+
let err = Entry::deserialize(&data).unwrap_err();
476+
assert!(matches!(
477+
err,
478+
wincode::error::ReadError::PreallocationSizeLimit {
479+
limit: MAX_DATA_SHREDS_SIZE,
480+
needed,
481+
} if needed == needed_bytes,
482+
));
483+
}
484+
485+
#[test]
486+
fn entry_deserialize_accepts_prealloc_at_limit() {
487+
let message = LegacyMessage {
488+
header: MessageHeader {
489+
num_required_signatures: 0,
490+
num_readonly_signed_accounts: 0,
491+
num_readonly_unsigned_accounts: 0,
492+
},
493+
account_keys: vec![],
494+
recent_blockhash: Hash::new_from_array([0u8; HASH_BYTES]),
495+
instructions: vec![],
496+
};
497+
let transaction = VersionedTransaction {
498+
signatures: vec![],
499+
message: VersionedMessage::Legacy(message),
500+
};
501+
let entry = Entry {
502+
num_hashes: 0,
503+
hash: Hash::new_from_array([0u8; HASH_BYTES]),
504+
transactions: vec![transaction],
505+
};
506+
507+
let mut data = wincode::serialize(&entry).unwrap();
508+
let at_limit: usize = MAX_DATA_SHREDS_SIZE / size_of::<VersionedTransaction>();
509+
let len_offset = 8 + HASH_BYTES;
510+
// Fudge the length of the vec to be at the limit.
511+
data[len_offset..len_offset + 8].copy_from_slice(&at_limit.to_le_bytes());
512+
513+
let err = Entry::deserialize(&data).unwrap_err();
514+
assert!(!matches!(
515+
err,
516+
wincode::error::ReadError::PreallocationSizeLimit {
517+
limit: _,
518+
needed: _,
519+
}
520+
));
521+
}
444522
}

ledger/src/blockstore.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use {
3737
solana_account::ReadableAccount,
3838
solana_address_lookup_table_interface::state::AddressLookupTable,
3939
solana_clock::{Slot, UnixTimestamp, DEFAULT_TICKS_PER_SECOND},
40-
solana_entry::entry::{create_ticks, Entry},
40+
solana_entry::entry::{create_ticks, Entry, MaxDataShredsLen},
4141
solana_genesis_config::{GenesisConfig, DEFAULT_GENESIS_ARCHIVE, DEFAULT_GENESIS_FILE},
4242
solana_hash::Hash,
4343
solana_keypair::Keypair,
@@ -82,6 +82,10 @@ use {
8282
tar,
8383
tempfile::{Builder, TempDir},
8484
thiserror::Error,
85+
wincode::{
86+
containers::{Elem, Vec as WincodeVec},
87+
Deserialize as _,
88+
},
8589
};
8690

8791
pub mod blockstore_purge;
@@ -3703,11 +3707,15 @@ impl Blockstore {
37033707
)))
37043708
})
37053709
.and_then(|payload| {
3706-
wincode::deserialize::<Vec<Entry>>(&payload).map_err(|e| {
3707-
BlockstoreError::InvalidShredData(Box::new(bincode::ErrorKind::Custom(
3708-
format!("could not reconstruct entries: {e:?}"),
3709-
)))
3710-
})
3710+
<WincodeVec<Elem<Entry>, MaxDataShredsLen>>::deserialize(&payload).map_err(
3711+
|e| {
3712+
BlockstoreError::InvalidShredData(Box::new(
3713+
bincode::ErrorKind::Custom(format!(
3714+
"could not reconstruct entries: {e:?}"
3715+
)),
3716+
))
3717+
},
3718+
)
37113719
})
37123720
})
37133721
.flatten_ok()

0 commit comments

Comments
 (0)