sdk: Add Borsh support for types and utilities (#15290)
* sdk: Add Borsh to Pubkey * Add serialization error for easier borsh integration * Add Borsh usage to banks-client and sdk * Rename SerializationError -> IOError * Add new errors to proto * Update Cargo lock * Update Cargo.lock based on CI * Clippy * Update ABI on bank * Address review feedback * Update sanity program instruction count test
This commit is contained in:
55
sdk/program/src/borsh.rs
Normal file
55
sdk/program/src/borsh.rs
Normal file
@ -0,0 +1,55 @@
|
||||
//! Borsh utils
|
||||
use borsh::schema::{BorshSchema, Declaration, Definition, Fields};
|
||||
use 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 {
|
||||
"u8" | "i8" => 1,
|
||||
"u16" | "i16" => 2,
|
||||
"u32" | "i32" => 2,
|
||||
"u64" | "i64" => 8,
|
||||
"u128" | "i128" => 16,
|
||||
"nil" => 0,
|
||||
_ => panic!("Missing primitive type: {}", declaration),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the worst-case packed length for the given BorshSchema
|
||||
pub fn get_packed_len<S: BorshSchema>() -> usize {
|
||||
let schema_container = S::schema_container();
|
||||
get_declaration_packed_len(&schema_container.declaration, &schema_container.definitions)
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
//! The `hash` module provides functions for creating SHA-256 hashes.
|
||||
|
||||
use crate::sanitize::Sanitize;
|
||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{convert::TryFrom, fmt, mem, str::FromStr};
|
||||
use thiserror::Error;
|
||||
@ -9,7 +10,20 @@ pub const HASH_BYTES: usize = 32;
|
||||
/// Maximum string length of a base58 encoded hash
|
||||
const MAX_BASE58_LEN: usize = 44;
|
||||
#[derive(
|
||||
Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash, AbiExample,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
BorshSerialize,
|
||||
BorshDeserialize,
|
||||
BorshSchema,
|
||||
Clone,
|
||||
Copy,
|
||||
Default,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Hash,
|
||||
AbiExample,
|
||||
)]
|
||||
#[repr(transparent)]
|
||||
pub struct Hash(pub [u8; HASH_BYTES]);
|
||||
|
@ -3,6 +3,7 @@
|
||||
use crate::sanitize::Sanitize;
|
||||
use crate::{pubkey::Pubkey, short_vec};
|
||||
use bincode::serialize;
|
||||
use borsh::BorshSerialize;
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
|
||||
@ -186,6 +187,12 @@ pub enum InstructionError {
|
||||
|
||||
#[error("Incorrect authority provided")]
|
||||
IncorrectAuthority,
|
||||
|
||||
#[error("Failed to serialize or deserialize account data: {0}")]
|
||||
IOError(String),
|
||||
|
||||
#[error("An account does not have enough lamports to be rent-exempt")]
|
||||
AccountNotRentExempt,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
@ -207,6 +214,19 @@ impl Instruction {
|
||||
accounts,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_borsh<T: BorshSerialize>(
|
||||
program_id: Pubkey,
|
||||
data: &T,
|
||||
accounts: Vec<AccountMeta>,
|
||||
) -> Self {
|
||||
let data = data.try_to_vec().unwrap();
|
||||
Self {
|
||||
program_id,
|
||||
data,
|
||||
accounts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checked_add(a: u64, b: u64) -> Result<u64, InstructionError> {
|
||||
|
@ -6,6 +6,7 @@
|
||||
extern crate self as solana_program;
|
||||
|
||||
pub mod account_info;
|
||||
pub mod borsh;
|
||||
pub mod bpf_loader;
|
||||
pub mod bpf_loader_deprecated;
|
||||
pub mod bpf_loader_upgradeable;
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::{decode_error::DecodeError, instruction::InstructionError, msg, pubkey::PubkeyError};
|
||||
use borsh::maybestd::io::Error as IOError;
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use std::convert::TryFrom;
|
||||
use thiserror::Error;
|
||||
@ -37,6 +38,10 @@ pub enum ProgramError {
|
||||
MaxSeedLengthExceeded,
|
||||
#[error("Provided seeds do not result in a valid address")]
|
||||
InvalidSeeds,
|
||||
#[error("IO Error: {0}")]
|
||||
IOError(String),
|
||||
#[error("An account does not have enough lamports to be rent-exempt")]
|
||||
AccountNotRentExempt,
|
||||
}
|
||||
|
||||
pub trait PrintProgramError {
|
||||
@ -71,6 +76,8 @@ impl PrintProgramError for ProgramError {
|
||||
Self::AccountBorrowFailed => msg!("Error: AccountBorrowFailed"),
|
||||
Self::MaxSeedLengthExceeded => msg!("Error: MaxSeedLengthExceeded"),
|
||||
Self::InvalidSeeds => msg!("Error: InvalidSeeds"),
|
||||
Self::IOError(_) => msg!("Error: IOError"),
|
||||
Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -97,6 +104,8 @@ pub const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11);
|
||||
pub const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12);
|
||||
pub const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13);
|
||||
pub const INVALID_SEEDS: u64 = to_builtin!(14);
|
||||
pub const IO_ERROR: u64 = to_builtin!(15);
|
||||
pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16);
|
||||
|
||||
impl From<ProgramError> for u64 {
|
||||
fn from(error: ProgramError) -> Self {
|
||||
@ -114,6 +123,8 @@ impl From<ProgramError> for u64 {
|
||||
ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
|
||||
ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED,
|
||||
ProgramError::InvalidSeeds => INVALID_SEEDS,
|
||||
ProgramError::IOError(_) => IO_ERROR,
|
||||
ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT,
|
||||
|
||||
ProgramError::Custom(error) => {
|
||||
if error == 0 {
|
||||
@ -166,6 +177,8 @@ impl TryFrom<InstructionError> for ProgramError {
|
||||
Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys),
|
||||
Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed),
|
||||
Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded),
|
||||
Self::Error::IOError(err) => Ok(Self::IOError(err)),
|
||||
Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
|
||||
_ => Err(error),
|
||||
}
|
||||
}
|
||||
@ -212,3 +225,9 @@ impl From<PubkeyError> for ProgramError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IOError> for ProgramError {
|
||||
fn from(error: IOError) -> Self {
|
||||
ProgramError::IOError(format!("{}", error))
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::{decode_error::DecodeError, hash::hashv};
|
||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use std::{convert::TryFrom, fmt, mem, str::FromStr};
|
||||
use thiserror::Error;
|
||||
@ -37,7 +38,20 @@ impl From<u64> for PubkeyError {
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash, AbiExample,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
BorshSerialize,
|
||||
BorshDeserialize,
|
||||
BorshSchema,
|
||||
Clone,
|
||||
Copy,
|
||||
Default,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Hash,
|
||||
AbiExample,
|
||||
)]
|
||||
pub struct Pubkey([u8; 32]);
|
||||
|
||||
|
Reference in New Issue
Block a user