-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial signed-memo program (#1135)
* Initial s-memo * Populate readme * Add signed-memo to spl docs * Log less, fail faster * Replace and bump memo * Update memo id * Add memo prefix and len * Add test that demonstrates compute bounds * Add logging and compute to memo docs
- Loading branch information
1 parent
1c4753e
commit 190e664
Showing
10 changed files
with
439 additions
and
53 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 |
---|---|---|
@@ -1,7 +1,9 @@ | ||
# Memo Program | ||
|
||
A simple program that validates a string of UTF-8 encoded characters. It can be | ||
used to record a string on-chain, stored in the instruction data of a successful | ||
transaction. | ||
A simple program that validates a string of UTF-8 encoded characters and logs it | ||
in the transaction log. The program also verifies that any accounts provided are | ||
signers of the transaction, and if so, logs their addresses. It can be used to | ||
record a string on-chain, stored in the instruction data of a successful | ||
transaction, and optionally verify the originator. | ||
|
||
Full documentation is available at https://spl.solana.com/memo |
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 |
---|---|---|
@@ -1 +1 @@ | ||
Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo | ||
MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr |
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,15 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -ex | ||
cd "$(dirname "$0")" | ||
cargo fmt -- --check | ||
cargo clippy | ||
cargo build | ||
cargo build-bpf | ||
|
||
if [[ $1 = -v ]]; then | ||
export RUST_LOG=solana=debug | ||
fi | ||
|
||
cargo test | ||
cargo test-bpf |
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 |
---|---|---|
@@ -1,43 +1,16 @@ | ||
//! Program entrypoint | ||
|
||
#![cfg(not(feature = "no-entrypoint"))] | ||
|
||
use solana_program::{ | ||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, program_error::ProgramError, | ||
pubkey::Pubkey, | ||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey, | ||
}; | ||
use std::str::from_utf8; | ||
|
||
entrypoint!(process_instruction); | ||
fn process_instruction( | ||
_program_id: &Pubkey, | ||
_accounts: &[AccountInfo], | ||
program_id: &Pubkey, | ||
accounts: &[AccountInfo], | ||
instruction_data: &[u8], | ||
) -> ProgramResult { | ||
from_utf8(instruction_data).map_err(|_| ProgramError::InvalidInstructionData)?; | ||
Ok(()) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use solana_program::{program_error::ProgramError, pubkey::Pubkey}; | ||
|
||
#[test] | ||
fn test_utf8_memo() { | ||
let program_id = Pubkey::new(&[0; 32]); | ||
|
||
let string = b"letters and such"; | ||
assert_eq!(Ok(()), process_instruction(&program_id, &[], string)); | ||
|
||
let emoji = "🐆".as_bytes(); | ||
let bytes = [0xF0, 0x9F, 0x90, 0x86]; | ||
assert_eq!(emoji, bytes); | ||
assert_eq!(Ok(()), process_instruction(&program_id, &[], &emoji)); | ||
|
||
let mut bad_utf8 = bytes; | ||
bad_utf8[3] = 0xFF; // Invalid UTF-8 byte | ||
assert_eq!( | ||
Err(ProgramError::InvalidInstructionData), | ||
process_instruction(&program_id, &[], &bad_utf8) | ||
); | ||
} | ||
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 |
---|---|---|
@@ -1,11 +1,34 @@ | ||
#![deny(missing_docs)] | ||
|
||
//! A simple program that accepts a string of encoded characters and verifies that it parses. Currently handles UTF-8. | ||
//! A program that accepts a string of encoded characters and verifies that it parses, | ||
//! while verifying and logging signers. Currently handles UTF-8 characters. | ||
|
||
#[cfg(not(feature = "no-entrypoint"))] | ||
mod entrypoint; | ||
pub mod processor; | ||
|
||
// Export current sdk types for downstream users building with a different sdk version | ||
pub use solana_program; | ||
use solana_program::{ | ||
instruction::{AccountMeta, Instruction}, | ||
pubkey::Pubkey, | ||
}; | ||
|
||
solana_program::declare_id!("Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"); | ||
solana_program::declare_id!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"); | ||
|
||
/// Build a memo instruction, possibly signed | ||
/// | ||
/// Accounts expected by this instruction: | ||
/// | ||
/// 0. ..0+N. `[signer]` Expected signers; if zero provided, instruction will be processed as a | ||
/// normal, unsigned spl-memo | ||
/// | ||
pub fn build_memo(memo: &[u8], signer_pubkeys: &[&Pubkey]) -> Instruction { | ||
Instruction { | ||
program_id: id(), | ||
accounts: signer_pubkeys | ||
.iter() | ||
.map(|&pubkey| AccountMeta::new_readonly(*pubkey, true)) | ||
.collect(), | ||
data: memo.to_vec(), | ||
} | ||
} |
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,109 @@ | ||
//! Program state processor | ||
|
||
use solana_program::{ | ||
account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, | ||
pubkey::Pubkey, | ||
}; | ||
use std::str::from_utf8; | ||
|
||
/// Instruction processor | ||
pub fn process_instruction( | ||
_program_id: &Pubkey, | ||
accounts: &[AccountInfo], | ||
input: &[u8], | ||
) -> ProgramResult { | ||
let account_info_iter = &mut accounts.iter(); | ||
let mut missing_required_signature = false; | ||
for account_info in account_info_iter { | ||
if let Some(address) = account_info.signer_key() { | ||
msg!("Signed by {:?}", address); | ||
} else { | ||
missing_required_signature = true; | ||
} | ||
} | ||
if missing_required_signature { | ||
return Err(ProgramError::MissingRequiredSignature); | ||
} | ||
|
||
let memo = from_utf8(input).map_err(|err| { | ||
msg!("Invalid UTF-8, from byte {}", err.valid_up_to()); | ||
ProgramError::InvalidInstructionData | ||
})?; | ||
msg!("Memo (len {}): {:?}", memo.len(), memo); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use solana_program::{ | ||
account_info::IntoAccountInfo, program_error::ProgramError, pubkey::Pubkey, | ||
}; | ||
use solana_sdk::account::Account; | ||
|
||
#[test] | ||
fn test_utf8_memo() { | ||
let program_id = Pubkey::new(&[0; 32]); | ||
|
||
let string = b"letters and such"; | ||
assert_eq!(Ok(()), process_instruction(&program_id, &[], string)); | ||
|
||
let emoji = "🐆".as_bytes(); | ||
let bytes = [0xF0, 0x9F, 0x90, 0x86]; | ||
assert_eq!(emoji, bytes); | ||
assert_eq!(Ok(()), process_instruction(&program_id, &[], &emoji)); | ||
|
||
let mut bad_utf8 = bytes; | ||
bad_utf8[3] = 0xFF; // Invalid UTF-8 byte | ||
assert_eq!( | ||
Err(ProgramError::InvalidInstructionData), | ||
process_instruction(&program_id, &[], &bad_utf8) | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_signers() { | ||
let program_id = Pubkey::new(&[0; 32]); | ||
let memo = "🐆".as_bytes(); | ||
|
||
let pubkey0 = Pubkey::new_unique(); | ||
let pubkey1 = Pubkey::new_unique(); | ||
let pubkey2 = Pubkey::new_unique(); | ||
let mut account0 = Account::default(); | ||
let mut account1 = Account::default(); | ||
let mut account2 = Account::default(); | ||
|
||
let signed_account_infos = vec![ | ||
(&pubkey0, true, &mut account0).into_account_info(), | ||
(&pubkey1, true, &mut account1).into_account_info(), | ||
(&pubkey2, true, &mut account2).into_account_info(), | ||
]; | ||
assert_eq!( | ||
Ok(()), | ||
process_instruction(&program_id, &signed_account_infos, memo) | ||
); | ||
|
||
assert_eq!(Ok(()), process_instruction(&program_id, &[], memo)); | ||
|
||
let unsigned_account_infos = vec![ | ||
(&pubkey0, false, &mut account0).into_account_info(), | ||
(&pubkey1, false, &mut account1).into_account_info(), | ||
(&pubkey2, false, &mut account2).into_account_info(), | ||
]; | ||
assert_eq!( | ||
Err(ProgramError::MissingRequiredSignature), | ||
process_instruction(&program_id, &unsigned_account_infos, memo) | ||
); | ||
|
||
let partially_signed_account_infos = vec![ | ||
(&pubkey0, true, &mut account0).into_account_info(), | ||
(&pubkey1, false, &mut account1).into_account_info(), | ||
(&pubkey2, true, &mut account2).into_account_info(), | ||
]; | ||
assert_eq!( | ||
Err(ProgramError::MissingRequiredSignature), | ||
process_instruction(&program_id, &partially_signed_account_infos, memo) | ||
); | ||
} | ||
} |
Oops, something went wrong.