sdk: Limited Borsh 0.9 support (Pubkey and helpers) (#32511)
* sdk: Implement Borsh 0.9 traits on Pubkey * Alphabetize cargo.toml * Add backwards-compatible borsh file * Add borsh0_10.rs for more clarity * Deprecate `borsh` utils, use borsh0_10 everywhere * Mark borsh 0.9 helpers as deprecated * Add macros for deriving helper impls * Add borsh 0.9 tests * Refactor tests into macro
This commit is contained in:
parent
1552a07f5b
commit
8c14886891
|
@ -6386,6 +6386,7 @@ dependencies = [
|
|||
"bitflags 2.3.3",
|
||||
"blake3",
|
||||
"borsh 0.10.3",
|
||||
"borsh 0.9.3",
|
||||
"bs58",
|
||||
"bv",
|
||||
"bytemuck",
|
||||
|
|
|
@ -12,7 +12,7 @@ use {
|
|||
ComputeBudget, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_COMPUTE_UNIT_LIMIT,
|
||||
},
|
||||
solana_sdk::{
|
||||
borsh::try_from_slice_unchecked,
|
||||
borsh0_10::try_from_slice_unchecked,
|
||||
compute_budget::{self, ComputeBudgetInstruction},
|
||||
feature_set::{
|
||||
add_set_tx_loaded_accounts_data_size_instruction,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
|
||||
solana_sdk::{
|
||||
borsh::try_from_slice_unchecked,
|
||||
borsh0_10::try_from_slice_unchecked,
|
||||
compute_budget::{self, ComputeBudgetInstruction},
|
||||
entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
|
||||
feature_set::{
|
||||
|
|
|
@ -5232,6 +5232,7 @@ dependencies = [
|
|||
"bitflags 2.3.3",
|
||||
"blake3",
|
||||
"borsh 0.10.3",
|
||||
"borsh 0.9.3",
|
||||
"bs58",
|
||||
"bv",
|
||||
"bytemuck",
|
||||
|
|
|
@ -15,6 +15,7 @@ rust-version = "1.68.0" # solana platform-tools rust version
|
|||
bincode = { workspace = true }
|
||||
blake3 = { workspace = true, features = ["digest", "traits-preview"] }
|
||||
borsh = { workspace = true }
|
||||
borsh0-9 = { package = "borsh", version = "0.9.3" }
|
||||
bs58 = { workspace = true }
|
||||
bv = { workspace = true, features = ["serde"] }
|
||||
bytemuck = { workspace = true, features = ["derive"] }
|
||||
|
|
|
@ -1,69 +1,25 @@
|
|||
#![allow(clippy::integer_arithmetic)]
|
||||
//! Utilities for the [borsh] serialization format.
|
||||
//!
|
||||
//! To avoid backwards-incompatibilities when the Solana SDK changes its dependency
|
||||
//! on borsh, it's recommended to instead use the version-specific file directly,
|
||||
//! ie. `borsh0_10`.
|
||||
//!
|
||||
//! This file remains for developers who use these borsh helpers, but it will
|
||||
//! be removed in a future release
|
||||
//!
|
||||
//! [borsh]: https://borsh.io/
|
||||
use {
|
||||
borsh::{
|
||||
maybestd::io::{Error, Write},
|
||||
schema::{BorshSchema, Declaration, Definition, Fields},
|
||||
BorshDeserialize, BorshSerialize,
|
||||
},
|
||||
std::collections::HashMap,
|
||||
};
|
||||
|
||||
/// Get packed length for the given BorchSchema Declaration
|
||||
fn get_declaration_packed_len(
|
||||
declaration: &str,
|
||||
definitions: &HashMap<Declaration, Definition>,
|
||||
) -> usize {
|
||||
match definitions.get(declaration) {
|
||||
Some(Definition::Array { length, elements }) => {
|
||||
*length as usize * get_declaration_packed_len(elements, definitions)
|
||||
}
|
||||
Some(Definition::Enum { variants }) => {
|
||||
1 + variants
|
||||
.iter()
|
||||
.map(|(_, declaration)| get_declaration_packed_len(declaration, definitions))
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
Some(Definition::Struct { fields }) => match fields {
|
||||
Fields::NamedFields(named_fields) => named_fields
|
||||
.iter()
|
||||
.map(|(_, declaration)| get_declaration_packed_len(declaration, definitions))
|
||||
.sum(),
|
||||
Fields::UnnamedFields(declarations) => declarations
|
||||
.iter()
|
||||
.map(|declaration| get_declaration_packed_len(declaration, definitions))
|
||||
.sum(),
|
||||
Fields::Empty => 0,
|
||||
},
|
||||
Some(Definition::Sequence {
|
||||
elements: _elements,
|
||||
}) => panic!("Missing support for Definition::Sequence"),
|
||||
Some(Definition::Tuple { elements }) => elements
|
||||
.iter()
|
||||
.map(|element| get_declaration_packed_len(element, definitions))
|
||||
.sum(),
|
||||
None => match declaration {
|
||||
"bool" | "u8" | "i8" => 1,
|
||||
"u16" | "i16" => 2,
|
||||
"u32" | "i32" => 4,
|
||||
"u64" | "i64" => 8,
|
||||
"u128" | "i128" => 16,
|
||||
"nil" => 0,
|
||||
_ => panic!("Missing primitive type: {declaration}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
use borsh::{maybestd::io::Error, BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
|
||||
/// Get the worst-case packed length for the given BorshSchema
|
||||
///
|
||||
/// Note: due to the serializer currently used by Borsh, this function cannot
|
||||
/// be used on-chain in the Solana SBF execution environment.
|
||||
#[deprecated(
|
||||
since = "1.17.0",
|
||||
note = "Please use `borsh0_10::get_packed_len` instead"
|
||||
)]
|
||||
pub fn get_packed_len<S: BorshSchema>() -> usize {
|
||||
let schema_container = S::schema_container();
|
||||
get_declaration_packed_len(&schema_container.declaration, &schema_container.definitions)
|
||||
crate::borsh0_10::get_packed_len::<S>()
|
||||
}
|
||||
|
||||
/// Deserializes without checking that the entire slice has been consumed
|
||||
|
@ -75,28 +31,12 @@ pub fn get_packed_len<S: BorshSchema>() -> usize {
|
|||
/// or equal to the expected size will properly deserialize. For example, if the
|
||||
/// user passes a buffer destined for a different type, the error won't get caught
|
||||
/// as easily.
|
||||
#[deprecated(
|
||||
since = "1.17.0",
|
||||
note = "Please use `borsh0_10::try_from_slice_unchecked` instead"
|
||||
)]
|
||||
pub fn try_from_slice_unchecked<T: BorshDeserialize>(data: &[u8]) -> Result<T, Error> {
|
||||
let mut data_mut = data;
|
||||
let result = T::deserialize(&mut data_mut)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Helper struct which to count how much data would be written during serialization
|
||||
#[derive(Default)]
|
||||
struct WriteCounter {
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl Write for WriteCounter {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
|
||||
let amount = data.len();
|
||||
self.count += amount;
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
crate::borsh0_10::try_from_slice_unchecked::<T>(data)
|
||||
}
|
||||
|
||||
/// Get the packed length for the serialized form of this object instance.
|
||||
|
@ -105,199 +45,246 @@ impl Write for WriteCounter {
|
|||
/// sequence, such as a Vec or HashMap. Since it is impossible to know the packed
|
||||
/// length only from the type's schema, this can be used when an instance already
|
||||
/// exists, to figure out how much space to allocate in an account.
|
||||
#[deprecated(
|
||||
since = "1.17.0",
|
||||
note = "Please use `borsh0_10::get_instance_packed_len` instead"
|
||||
)]
|
||||
pub fn get_instance_packed_len<T: BorshSerialize>(instance: &T) -> Result<usize, Error> {
|
||||
let mut counter = WriteCounter::default();
|
||||
instance.serialize(&mut counter)?;
|
||||
Ok(counter.count)
|
||||
crate::borsh0_10::get_instance_packed_len(instance)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use {
|
||||
super::*,
|
||||
borsh::{maybestd::io::ErrorKind, BorshSchema, BorshSerialize},
|
||||
std::{collections::HashMap, mem::size_of},
|
||||
};
|
||||
macro_rules! impl_get_packed_len {
|
||||
($borsh:ident $(,#[$meta:meta])?) => {
|
||||
/// Get the worst-case packed length for the given BorshSchema
|
||||
///
|
||||
/// Note: due to the serializer currently used by Borsh, this function cannot
|
||||
/// be used on-chain in the Solana SBF execution environment.
|
||||
$(#[$meta])?
|
||||
pub fn get_packed_len<S: $borsh::BorshSchema>() -> usize {
|
||||
let $borsh::schema::BorshSchemaContainer { declaration, definitions } =
|
||||
&S::schema_container();
|
||||
get_declaration_packed_len(declaration, definitions)
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)]
|
||||
enum TestEnum {
|
||||
NoValue,
|
||||
Number(u32),
|
||||
Struct {
|
||||
#[allow(dead_code)]
|
||||
number: u64,
|
||||
#[allow(dead_code)]
|
||||
array: [u8; 8],
|
||||
},
|
||||
}
|
||||
|
||||
// for test simplicity
|
||||
impl Default for TestEnum {
|
||||
fn default() -> Self {
|
||||
Self::NoValue
|
||||
/// Get packed length for the given BorshSchema Declaration
|
||||
fn get_declaration_packed_len(
|
||||
declaration: &str,
|
||||
definitions: &std::collections::HashMap<$borsh::schema::Declaration, $borsh::schema::Definition>,
|
||||
) -> usize {
|
||||
match definitions.get(declaration) {
|
||||
Some($borsh::schema::Definition::Array { length, elements }) => {
|
||||
*length as usize * get_declaration_packed_len(elements, definitions)
|
||||
}
|
||||
Some($borsh::schema::Definition::Enum { variants }) => {
|
||||
1 + variants
|
||||
.iter()
|
||||
.map(|(_, declaration)| get_declaration_packed_len(declaration, definitions))
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
Some($borsh::schema::Definition::Struct { fields }) => match fields {
|
||||
$borsh::schema::Fields::NamedFields(named_fields) => named_fields
|
||||
.iter()
|
||||
.map(|(_, declaration)| get_declaration_packed_len(declaration, definitions))
|
||||
.sum(),
|
||||
$borsh::schema::Fields::UnnamedFields(declarations) => declarations
|
||||
.iter()
|
||||
.map(|declaration| get_declaration_packed_len(declaration, definitions))
|
||||
.sum(),
|
||||
$borsh::schema::Fields::Empty => 0,
|
||||
},
|
||||
Some($borsh::schema::Definition::Sequence {
|
||||
elements: _elements,
|
||||
}) => panic!("Missing support for Definition::Sequence"),
|
||||
Some($borsh::schema::Definition::Tuple { elements }) => elements
|
||||
.iter()
|
||||
.map(|element| get_declaration_packed_len(element, definitions))
|
||||
.sum(),
|
||||
None => match declaration {
|
||||
"bool" | "u8" | "i8" => 1,
|
||||
"u16" | "i16" => 2,
|
||||
"u32" | "i32" => 4,
|
||||
"u64" | "i64" => 8,
|
||||
"u128" | "i128" => 16,
|
||||
"nil" => 0,
|
||||
_ => panic!("Missing primitive type: {declaration}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) use impl_get_packed_len;
|
||||
|
||||
#[derive(Default, BorshSerialize, BorshDeserialize, BorshSchema)]
|
||||
struct TestStruct {
|
||||
pub array: [u64; 16],
|
||||
pub number_u128: u128,
|
||||
pub number_u32: u32,
|
||||
pub tuple: (u8, u16),
|
||||
pub enumeration: TestEnum,
|
||||
pub r#bool: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
|
||||
struct Child {
|
||||
pub data: [u8; 64],
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
|
||||
struct Parent {
|
||||
pub data: Vec<Child>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unchecked_deserialization() {
|
||||
let data = vec![
|
||||
Child { data: [0u8; 64] },
|
||||
Child { data: [1u8; 64] },
|
||||
Child { data: [2u8; 64] },
|
||||
];
|
||||
let parent = Parent { data };
|
||||
|
||||
// exact size, both work
|
||||
let mut byte_vec = vec![0u8; 4 + get_packed_len::<Child>() * 3];
|
||||
let mut bytes = byte_vec.as_mut_slice();
|
||||
parent.serialize(&mut bytes).unwrap();
|
||||
let deserialized = Parent::try_from_slice(&byte_vec).unwrap();
|
||||
assert_eq!(deserialized, parent);
|
||||
let deserialized = try_from_slice_unchecked::<Parent>(&byte_vec).unwrap();
|
||||
assert_eq!(deserialized, parent);
|
||||
|
||||
// too big, only unchecked works
|
||||
let mut byte_vec = vec![0u8; 4 + get_packed_len::<Child>() * 10];
|
||||
let mut bytes = byte_vec.as_mut_slice();
|
||||
parent.serialize(&mut bytes).unwrap();
|
||||
let err = Parent::try_from_slice(&byte_vec).unwrap_err();
|
||||
assert_eq!(err.kind(), ErrorKind::InvalidData);
|
||||
let deserialized = try_from_slice_unchecked::<Parent>(&byte_vec).unwrap();
|
||||
assert_eq!(deserialized, parent);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packed_len() {
|
||||
assert_eq!(
|
||||
get_packed_len::<TestEnum>(),
|
||||
size_of::<u8>() + size_of::<u64>() + u8::BITS as usize
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<TestStruct>(),
|
||||
size_of::<u64>() * 16
|
||||
+ size_of::<bool>()
|
||||
+ size_of::<u128>()
|
||||
+ size_of::<u32>()
|
||||
+ size_of::<u8>()
|
||||
+ size_of::<u16>()
|
||||
+ get_packed_len::<TestEnum>()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instance_packed_len_matches_packed_len() {
|
||||
let enumeration = TestEnum::Struct {
|
||||
number: u64::MAX,
|
||||
array: [255; 8],
|
||||
};
|
||||
assert_eq!(
|
||||
get_packed_len::<TestEnum>(),
|
||||
get_instance_packed_len(&enumeration).unwrap(),
|
||||
);
|
||||
let test_struct = TestStruct {
|
||||
enumeration,
|
||||
..TestStruct::default()
|
||||
};
|
||||
assert_eq!(
|
||||
get_packed_len::<TestStruct>(),
|
||||
get_instance_packed_len(&test_struct).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u8>(),
|
||||
get_instance_packed_len(&0u8).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u16>(),
|
||||
get_instance_packed_len(&0u16).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u32>(),
|
||||
get_instance_packed_len(&0u32).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u64>(),
|
||||
get_instance_packed_len(&0u64).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u128>(),
|
||||
get_instance_packed_len(&0u128).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<[u8; 10]>(),
|
||||
get_instance_packed_len(&[0u8; 10]).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<(i8, i16, i32, i64, i128)>(),
|
||||
get_instance_packed_len(&(i8::MAX, i16::MAX, i32::MAX, i64::MAX, i128::MAX)).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instance_packed_len_with_vec() {
|
||||
let data = vec![
|
||||
Child { data: [0u8; 64] },
|
||||
Child { data: [1u8; 64] },
|
||||
Child { data: [2u8; 64] },
|
||||
Child { data: [3u8; 64] },
|
||||
Child { data: [4u8; 64] },
|
||||
Child { data: [5u8; 64] },
|
||||
];
|
||||
let parent = Parent { data };
|
||||
assert_eq!(
|
||||
get_instance_packed_len(&parent).unwrap(),
|
||||
4 + parent.data.len() * get_packed_len::<Child>()
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
|
||||
struct StructWithHashMap {
|
||||
data: HashMap<String, TestEnum>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instance_packed_len_with_varying_sizes_in_hashmap() {
|
||||
let mut data = HashMap::new();
|
||||
let string1 = "the first string, it's actually really really long".to_string();
|
||||
let enum1 = TestEnum::NoValue;
|
||||
let string2 = "second string, shorter".to_string();
|
||||
let enum2 = TestEnum::Number(u32::MAX);
|
||||
let string3 = "third".to_string();
|
||||
let enum3 = TestEnum::Struct {
|
||||
number: 0,
|
||||
array: [0; 8],
|
||||
};
|
||||
data.insert(string1.clone(), enum1.clone());
|
||||
data.insert(string2.clone(), enum2.clone());
|
||||
data.insert(string3.clone(), enum3.clone());
|
||||
let instance = StructWithHashMap { data };
|
||||
assert_eq!(
|
||||
get_instance_packed_len(&instance).unwrap(),
|
||||
4 + get_instance_packed_len(&string1).unwrap()
|
||||
+ get_instance_packed_len(&enum1).unwrap()
|
||||
+ get_instance_packed_len(&string2).unwrap()
|
||||
+ get_instance_packed_len(&enum2).unwrap()
|
||||
+ get_instance_packed_len(&string3).unwrap()
|
||||
+ get_instance_packed_len(&enum3).unwrap()
|
||||
);
|
||||
macro_rules! impl_try_from_slice_unchecked {
|
||||
($borsh:ident $(,#[$meta:meta])?) => {
|
||||
/// Deserializes without checking that the entire slice has been consumed
|
||||
///
|
||||
/// Normally, `try_from_slice` checks the length of the final slice to ensure
|
||||
/// that the deserialization uses up all of the bytes in the slice.
|
||||
///
|
||||
/// Note that there is a potential issue with this function. Any buffer greater than
|
||||
/// or equal to the expected size will properly deserialize. For example, if the
|
||||
/// user passes a buffer destined for a different type, the error won't get caught
|
||||
/// as easily.
|
||||
$(#[$meta])?
|
||||
pub fn try_from_slice_unchecked<T: $borsh::BorshDeserialize>(data: &[u8]) -> Result<T, $borsh::maybestd::io::Error> {
|
||||
let mut data_mut = data;
|
||||
let result = T::deserialize(&mut data_mut)?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) use impl_try_from_slice_unchecked;
|
||||
|
||||
macro_rules! impl_get_instance_packed_len {
|
||||
($borsh:ident $(,#[$meta:meta])?) => {
|
||||
/// Helper struct which to count how much data would be written during serialization
|
||||
#[derive(Default)]
|
||||
struct WriteCounter {
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl $borsh::maybestd::io::Write for WriteCounter {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, $borsh::maybestd::io::Error> {
|
||||
let amount = data.len();
|
||||
self.count += amount;
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), $borsh::maybestd::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the packed length for the serialized form of this object instance.
|
||||
///
|
||||
/// Useful when working with instances of types that contain a variable-length
|
||||
/// sequence, such as a Vec or HashMap. Since it is impossible to know the packed
|
||||
/// length only from the type's schema, this can be used when an instance already
|
||||
/// exists, to figure out how much space to allocate in an account.
|
||||
$(#[$meta])?
|
||||
pub fn get_instance_packed_len<T: $borsh::BorshSerialize>(instance: &T) -> Result<usize, $borsh::maybestd::io::Error> {
|
||||
let mut counter = WriteCounter::default();
|
||||
instance.serialize(&mut counter)?;
|
||||
Ok(counter.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) use impl_get_instance_packed_len;
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! impl_tests {
|
||||
($borsh:ident) => {
|
||||
use {
|
||||
super::*,
|
||||
std::{collections::HashMap, mem::size_of},
|
||||
$borsh::{maybestd::io::ErrorKind, BorshDeserialize, BorshSerialize},
|
||||
};
|
||||
|
||||
type Child = [u8; 64];
|
||||
type Parent = Vec<Child>;
|
||||
|
||||
#[test]
|
||||
fn unchecked_deserialization() {
|
||||
let parent = vec![[0u8; 64], [1u8; 64], [2u8; 64]];
|
||||
|
||||
// exact size, both work
|
||||
let mut byte_vec = vec![0u8; 4 + get_packed_len::<Child>() * 3];
|
||||
let mut bytes = byte_vec.as_mut_slice();
|
||||
parent.serialize(&mut bytes).unwrap();
|
||||
let deserialized = Parent::try_from_slice(&byte_vec).unwrap();
|
||||
assert_eq!(deserialized, parent);
|
||||
let deserialized = try_from_slice_unchecked::<Parent>(&byte_vec).unwrap();
|
||||
assert_eq!(deserialized, parent);
|
||||
|
||||
// too big, only unchecked works
|
||||
let mut byte_vec = vec![0u8; 4 + get_packed_len::<Child>() * 10];
|
||||
let mut bytes = byte_vec.as_mut_slice();
|
||||
parent.serialize(&mut bytes).unwrap();
|
||||
let err = Parent::try_from_slice(&byte_vec).unwrap_err();
|
||||
assert_eq!(err.kind(), ErrorKind::InvalidData);
|
||||
let deserialized = try_from_slice_unchecked::<Parent>(&byte_vec).unwrap();
|
||||
assert_eq!(deserialized, parent);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packed_len() {
|
||||
assert_eq!(get_packed_len::<u64>(), size_of::<u64>());
|
||||
assert_eq!(get_packed_len::<Child>(), size_of::<u8>() * 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instance_packed_len_matches_packed_len() {
|
||||
let child = [0u8; 64];
|
||||
assert_eq!(
|
||||
get_packed_len::<Child>(),
|
||||
get_instance_packed_len(&child).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u8>(),
|
||||
get_instance_packed_len(&0u8).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u16>(),
|
||||
get_instance_packed_len(&0u16).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u32>(),
|
||||
get_instance_packed_len(&0u32).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u64>(),
|
||||
get_instance_packed_len(&0u64).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<u128>(),
|
||||
get_instance_packed_len(&0u128).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<[u8; 10]>(),
|
||||
get_instance_packed_len(&[0u8; 10]).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
get_packed_len::<(i8, i16, i32, i64, i128)>(),
|
||||
get_instance_packed_len(&(i8::MAX, i16::MAX, i32::MAX, i64::MAX, i128::MAX))
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instance_packed_len_with_vec() {
|
||||
let parent = vec![
|
||||
[0u8; 64], [1u8; 64], [2u8; 64], [3u8; 64], [4u8; 64], [5u8; 64],
|
||||
];
|
||||
assert_eq!(
|
||||
get_instance_packed_len(&parent).unwrap(),
|
||||
4 + parent.len() * get_packed_len::<Child>()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instance_packed_len_with_varying_sizes_in_hashmap() {
|
||||
let mut data = HashMap::new();
|
||||
let key1 = "the first string, it's actually really really long".to_string();
|
||||
let value1 = "".to_string();
|
||||
let key2 = "second string, shorter".to_string();
|
||||
let value2 = "a real value".to_string();
|
||||
let key3 = "third".to_string();
|
||||
let value3 = "an even longer value".to_string();
|
||||
data.insert(key1.clone(), value1.clone());
|
||||
data.insert(key2.clone(), value2.clone());
|
||||
data.insert(key3.clone(), value3.clone());
|
||||
assert_eq!(
|
||||
get_instance_packed_len(&data).unwrap(),
|
||||
4 + get_instance_packed_len(&key1).unwrap()
|
||||
+ get_instance_packed_len(&value1).unwrap()
|
||||
+ get_instance_packed_len(&key2).unwrap()
|
||||
+ get_instance_packed_len(&value2).unwrap()
|
||||
+ get_instance_packed_len(&key3).unwrap()
|
||||
+ get_instance_packed_len(&value3).unwrap()
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
#[cfg(test)]
|
||||
pub(crate) use impl_tests;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#![allow(clippy::integer_arithmetic)]
|
||||
//! Utilities for the [borsh] serialization format, version 0.10.
|
||||
//!
|
||||
//! [borsh]: https://borsh.io/
|
||||
use crate::borsh::{
|
||||
impl_get_instance_packed_len, impl_get_packed_len, impl_try_from_slice_unchecked,
|
||||
};
|
||||
|
||||
impl_get_packed_len!(borsh);
|
||||
impl_try_from_slice_unchecked!(borsh);
|
||||
impl_get_instance_packed_len!(borsh);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::borsh::impl_tests;
|
||||
impl_tests!(borsh);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#![allow(clippy::integer_arithmetic)]
|
||||
//! Utilities for the [borsh] serialization format, version 0.9.
|
||||
//!
|
||||
//! This file is provided for backwards compatibility with types that still use
|
||||
//! borsh 0.9, even though this crate canonically uses borsh 0.10.
|
||||
//!
|
||||
//! [borsh]: https://borsh.io/
|
||||
use crate::borsh::{
|
||||
impl_get_instance_packed_len, impl_get_packed_len, impl_try_from_slice_unchecked,
|
||||
};
|
||||
|
||||
impl_get_packed_len!(
|
||||
borsh0_9,
|
||||
#[deprecated(
|
||||
since = "1.17.0",
|
||||
note = "Please upgrade to Borsh 0.10 and use `borsh0_10::get_packed_len` instead"
|
||||
)]
|
||||
);
|
||||
impl_try_from_slice_unchecked!(
|
||||
borsh0_9,
|
||||
#[deprecated(
|
||||
since = "1.17.0",
|
||||
note = "Please upgrade to Borsh 0.10 and use `borsh0_10::try_from_slice_unchecked` instead"
|
||||
)]
|
||||
);
|
||||
impl_get_instance_packed_len!(
|
||||
borsh0_9,
|
||||
#[deprecated(
|
||||
since = "1.17.0",
|
||||
note = "Please upgrade to Borsh 0.10 and use `borsh0_10::get_instance_packed_len` instead"
|
||||
)]
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
mod tests {
|
||||
use crate::borsh::impl_tests;
|
||||
impl_tests!(borsh0_9);
|
||||
}
|
|
@ -477,6 +477,8 @@ pub(crate) mod atomic_u64;
|
|||
pub mod big_mod_exp;
|
||||
pub mod blake3;
|
||||
pub mod borsh;
|
||||
pub mod borsh0_10;
|
||||
pub mod borsh0_9;
|
||||
pub mod bpf_loader;
|
||||
pub mod bpf_loader_deprecated;
|
||||
pub mod bpf_loader_upgradeable;
|
||||
|
|
|
@ -668,6 +668,48 @@ impl fmt::Display for Pubkey {
|
|||
}
|
||||
}
|
||||
|
||||
impl borsh0_9::de::BorshDeserialize for Pubkey {
|
||||
fn deserialize(buf: &mut &[u8]) -> ::core::result::Result<Self, borsh0_9::maybestd::io::Error> {
|
||||
Ok(Self(borsh0_9::BorshDeserialize::deserialize(buf)?))
|
||||
}
|
||||
}
|
||||
impl borsh0_9::BorshSchema for Pubkey
|
||||
where
|
||||
[u8; 32]: borsh0_9::BorshSchema,
|
||||
{
|
||||
fn declaration() -> borsh0_9::schema::Declaration {
|
||||
"Pubkey".to_string()
|
||||
}
|
||||
fn add_definitions_recursively(
|
||||
definitions: &mut borsh0_9::maybestd::collections::HashMap<
|
||||
borsh0_9::schema::Declaration,
|
||||
borsh0_9::schema::Definition,
|
||||
>,
|
||||
) {
|
||||
let fields = borsh0_9::schema::Fields::UnnamedFields(<[_]>::into_vec(
|
||||
borsh0_9::maybestd::boxed::Box::new([
|
||||
<[u8; 32] as borsh0_9::BorshSchema>::declaration(),
|
||||
]),
|
||||
));
|
||||
let definition = borsh0_9::schema::Definition::Struct { fields };
|
||||
<Self as borsh0_9::BorshSchema>::add_definition(
|
||||
<Self as borsh0_9::BorshSchema>::declaration(),
|
||||
definition,
|
||||
definitions,
|
||||
);
|
||||
<[u8; 32] as borsh0_9::BorshSchema>::add_definitions_recursively(definitions);
|
||||
}
|
||||
}
|
||||
impl borsh0_9::ser::BorshSerialize for Pubkey {
|
||||
fn serialize<W: borsh0_9::maybestd::io::Write>(
|
||||
&self,
|
||||
writer: &mut W,
|
||||
) -> ::core::result::Result<(), borsh0_9::maybestd::io::Error> {
|
||||
borsh0_9::BorshSerialize::serialize(&self.0, writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use {super::*, std::str::from_utf8};
|
||||
|
|
|
@ -580,7 +580,7 @@ impl Stake {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
super::*, crate::borsh::try_from_slice_unchecked, assert_matches::assert_matches,
|
||||
super::*, crate::borsh0_10::try_from_slice_unchecked, assert_matches::assert_matches,
|
||||
bincode::serialize,
|
||||
};
|
||||
|
||||
|
|
|
@ -43,16 +43,16 @@ pub use signer::signers;
|
|||
#[cfg(not(target_os = "solana"))]
|
||||
pub use solana_program::program_stubs;
|
||||
pub use solana_program::{
|
||||
account_info, address_lookup_table_account, alt_bn128, big_mod_exp, blake3, borsh, bpf_loader,
|
||||
bpf_loader_deprecated, bpf_loader_upgradeable, clock, config, custom_heap_default,
|
||||
custom_panic_default, debug_account_data, declare_deprecated_sysvar_id, declare_sysvar_id,
|
||||
decode_error, ed25519_program, epoch_rewards, epoch_schedule, fee_calculator, impl_sysvar_get,
|
||||
incinerator, instruction, keccak, lamports, loader_instruction, loader_upgradeable_instruction,
|
||||
loader_v4, loader_v4_instruction, message, msg, native_token, nonce, program, program_error,
|
||||
program_memory, program_option, program_pack, rent, sanitize, sdk_ids, secp256k1_program,
|
||||
secp256k1_recover, serde_varint, serialize_utils, short_vec, slot_hashes, slot_history,
|
||||
stable_layout, stake, stake_history, syscalls, system_instruction, system_program, sysvar,
|
||||
unchecked_div_by_const, vote, wasm_bindgen,
|
||||
account_info, address_lookup_table_account, alt_bn128, big_mod_exp, blake3, borsh, borsh0_10,
|
||||
borsh0_9, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, clock, config,
|
||||
custom_heap_default, custom_panic_default, debug_account_data, declare_deprecated_sysvar_id,
|
||||
declare_sysvar_id, decode_error, ed25519_program, epoch_rewards, epoch_schedule,
|
||||
fee_calculator, impl_sysvar_get, incinerator, instruction, keccak, lamports,
|
||||
loader_instruction, loader_upgradeable_instruction, loader_v4, loader_v4_instruction, message,
|
||||
msg, native_token, nonce, program, program_error, program_memory, program_option, program_pack,
|
||||
rent, sanitize, sdk_ids, secp256k1_program, secp256k1_recover, serde_varint, serialize_utils,
|
||||
short_vec, slot_hashes, slot_history, stable_layout, stake, stake_history, syscalls,
|
||||
system_instruction, system_program, sysvar, unchecked_div_by_const, vote, wasm_bindgen,
|
||||
};
|
||||
|
||||
pub mod account;
|
||||
|
|
Loading…
Reference in New Issue