forked from solana-labs/solana-program-library
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CRUD program: create / update / delete (solana-labs#1226)
* Add scaffolding * Add create / update / delete instructions and tests * Update SerializationError -> IOError and program id * Address review feedback * Make initialize work with `create_with_seed` * Cargo fmt * Use offset for writing * Update crud -> record * More feedback * Remove rent * Update program id * Use official Solana crates 1.5.10 * Update Cargo lock and toml * Cargo fmt * Update record program version to 1.5.11 * Bump compute budget
- Loading branch information
Showing
12 changed files
with
1,042 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
[package] | ||
name = "spl-record" | ||
version = "0.1.0" | ||
description = "Solana Program Library Record Program" | ||
authors = ["Solana Maintainers <[email protected]>"] | ||
repository = "https://github.com/solana-labs/solana-program-library" | ||
license = "Apache-2.0" | ||
edition = "2018" | ||
|
||
[features] | ||
no-entrypoint = [] | ||
test-bpf = [] | ||
|
||
[dependencies] | ||
borsh = "0.8.1" | ||
borsh-derive = "0.8.1" | ||
num-derive = "0.3" | ||
num-traits = "0.2" | ||
solana-program = "1.5.11" | ||
thiserror = "1.0" | ||
|
||
[dev-dependencies] | ||
solana-program-test = "1.5.11" | ||
solana-sdk = "1.5.11" | ||
tokio = { version = "0.3", features = ["macros"]} | ||
|
||
[lib] | ||
crate-type = ["cdylib", "lib"] | ||
|
||
[package.metadata.docs.rs] | ||
targets = ["x86_64-unknown-linux-gnu"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[target.bpfel-unknown-unknown.dependencies.std] | ||
features = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ReciQBw6sQKH9TVVJQDnbnJ5W7FP539tPHjZhRF4E9r |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
//! Program entrypoint | ||
#![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))] | ||
|
||
use solana_program::{ | ||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey, | ||
}; | ||
|
||
entrypoint!(process_instruction); | ||
fn process_instruction( | ||
program_id: &Pubkey, | ||
accounts: &[AccountInfo], | ||
instruction_data: &[u8], | ||
) -> ProgramResult { | ||
crate::processor::process_instruction(program_id, accounts, instruction_data) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
//! Error types | ||
use num_derive::FromPrimitive; | ||
use solana_program::{decode_error::DecodeError, program_error::ProgramError}; | ||
use thiserror::Error; | ||
|
||
/// Errors that may be returned by the program. | ||
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] | ||
pub enum RecordError { | ||
/// Incorrect authority provided on update or delete | ||
#[error("Incorrect authority provided on update or delete")] | ||
IncorrectAuthority, | ||
|
||
/// Calculation overflow | ||
#[error("Calculation overflow")] | ||
Overflow, | ||
} | ||
impl From<RecordError> for ProgramError { | ||
fn from(e: RecordError) -> Self { | ||
ProgramError::Custom(e as u32) | ||
} | ||
} | ||
impl<T> DecodeError<T> for RecordError { | ||
fn type_of() -> &'static str { | ||
"Record Error" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
//! Program instructions | ||
use crate::id; | ||
use borsh::{BorshDeserialize, BorshSerialize}; | ||
use solana_program::{ | ||
instruction::{AccountMeta, Instruction}, | ||
pubkey::Pubkey, | ||
}; | ||
|
||
/// Instructions supported by the program | ||
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, PartialEq)] | ||
pub enum RecordInstruction { | ||
/// Create a new record | ||
/// | ||
/// Accounts expected by this instruction: | ||
/// | ||
/// 0. `[writable]` Record account, must be uninitialized | ||
/// 1. `[]` Record authority | ||
Initialize, | ||
|
||
/// Write to the provided record account | ||
/// | ||
/// Accounts expected by this instruction: | ||
/// | ||
/// 0. `[writable]` Record account, must be previously initialized | ||
/// 1. `[signer]` Current record authority | ||
Write { | ||
/// Offset to start writing record, expressed as `u64`. | ||
offset: u64, | ||
/// Data to replace the existing record data | ||
data: Vec<u8>, | ||
}, | ||
|
||
/// Update the authority of the provided record account | ||
/// | ||
/// Accounts expected by this instruction: | ||
/// | ||
/// 0. `[writable]` Record account, must be previously initialized | ||
/// 1. `[signer]` Current record authority | ||
/// 2. `[]` New record authority | ||
SetAuthority, | ||
|
||
/// Close the provided record account, draining lamports to recipient account | ||
/// | ||
/// Accounts expected by this instruction: | ||
/// | ||
/// 0. `[writable]` Record account, must be previously initialized | ||
/// 1. `[signer]` Record authority | ||
/// 2. `[]` Receiver of account lamports | ||
CloseAccount, | ||
} | ||
|
||
/// Create a `RecordInstruction::Initialize` instruction | ||
pub fn initialize(record_account: &Pubkey, authority: &Pubkey) -> Instruction { | ||
Instruction::new_with_borsh( | ||
id(), | ||
&RecordInstruction::Initialize, | ||
vec![ | ||
AccountMeta::new(*record_account, false), | ||
AccountMeta::new_readonly(*authority, false), | ||
], | ||
) | ||
} | ||
|
||
/// Create a `RecordInstruction::Write` instruction | ||
pub fn write(record_account: &Pubkey, signer: &Pubkey, offset: u64, data: Vec<u8>) -> Instruction { | ||
Instruction::new_with_borsh( | ||
id(), | ||
&RecordInstruction::Write { offset, data }, | ||
vec![ | ||
AccountMeta::new(*record_account, false), | ||
AccountMeta::new_readonly(*signer, true), | ||
], | ||
) | ||
} | ||
|
||
/// Create a `RecordInstruction::SetAuthority` instruction | ||
pub fn set_authority( | ||
record_account: &Pubkey, | ||
signer: &Pubkey, | ||
new_authority: &Pubkey, | ||
) -> Instruction { | ||
Instruction::new_with_borsh( | ||
id(), | ||
&RecordInstruction::SetAuthority, | ||
vec![ | ||
AccountMeta::new(*record_account, false), | ||
AccountMeta::new_readonly(*signer, true), | ||
AccountMeta::new_readonly(*new_authority, false), | ||
], | ||
) | ||
} | ||
|
||
/// Create a `RecordInstruction::CloseAccount` instruction | ||
pub fn close_account(record_account: &Pubkey, signer: &Pubkey, receiver: &Pubkey) -> Instruction { | ||
Instruction::new_with_borsh( | ||
id(), | ||
&RecordInstruction::CloseAccount, | ||
vec![ | ||
AccountMeta::new(*record_account, false), | ||
AccountMeta::new_readonly(*signer, true), | ||
AccountMeta::new(*receiver, false), | ||
], | ||
) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::state::tests::TEST_DATA; | ||
use solana_program::program_error::ProgramError; | ||
|
||
#[test] | ||
fn serialize_initialize() { | ||
let instruction = RecordInstruction::Initialize; | ||
let expected = vec![0]; | ||
assert_eq!(instruction.try_to_vec().unwrap(), expected); | ||
assert_eq!( | ||
RecordInstruction::try_from_slice(&expected).unwrap(), | ||
instruction | ||
); | ||
} | ||
|
||
#[test] | ||
fn serialize_write() { | ||
let data = TEST_DATA.try_to_vec().unwrap(); | ||
let offset = 0u64; | ||
let instruction = RecordInstruction::Write { | ||
offset: 0, | ||
data: data.clone(), | ||
}; | ||
let mut expected = vec![1]; | ||
expected.extend_from_slice(&offset.to_le_bytes()); | ||
expected.append(&mut data.try_to_vec().unwrap()); | ||
assert_eq!(instruction.try_to_vec().unwrap(), expected); | ||
assert_eq!( | ||
RecordInstruction::try_from_slice(&expected).unwrap(), | ||
instruction | ||
); | ||
} | ||
|
||
#[test] | ||
fn serialize_set_authority() { | ||
let instruction = RecordInstruction::SetAuthority; | ||
let expected = vec![2]; | ||
assert_eq!(instruction.try_to_vec().unwrap(), expected); | ||
assert_eq!( | ||
RecordInstruction::try_from_slice(&expected).unwrap(), | ||
instruction | ||
); | ||
} | ||
|
||
#[test] | ||
fn serialize_close_account() { | ||
let instruction = RecordInstruction::CloseAccount; | ||
let expected = vec![3]; | ||
assert_eq!(instruction.try_to_vec().unwrap(), expected); | ||
assert_eq!( | ||
RecordInstruction::try_from_slice(&expected).unwrap(), | ||
instruction | ||
); | ||
} | ||
|
||
#[test] | ||
fn deserialize_invalid_instruction() { | ||
let mut expected = vec![12]; | ||
expected.append(&mut TEST_DATA.try_to_vec().unwrap()); | ||
let err: ProgramError = RecordInstruction::try_from_slice(&expected) | ||
.unwrap_err() | ||
.into(); | ||
assert!(matches!(err, ProgramError::BorshIoError(_))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
//! Record program | ||
#![deny(missing_docs)] | ||
|
||
mod entrypoint; | ||
pub mod error; | ||
pub mod instruction; | ||
pub mod processor; | ||
pub mod state; | ||
|
||
// Export current SDK types for downstream users building with a different SDK version | ||
pub use solana_program; | ||
|
||
solana_program::declare_id!("ReciQBw6sQKH9TVVJQDnbnJ5W7FP539tPHjZhRF4E9r"); |
Oops, something went wrong.