Refactor: consolidate memo extraction for each message version (#22422) (#22435)

(cherry picked from commit 35a5dd9c45)

Co-authored-by: Justin Starry <justin@solana.com>
This commit is contained in:
mergify[bot]
2022-01-12 00:23:03 +00:00
committed by GitHub
parent 58dcc451a9
commit f4d1577337
2 changed files with 93 additions and 74 deletions

View File

@ -0,0 +1,33 @@
#![feature(test)]
extern crate test;
use {
solana_sdk::{instruction::CompiledInstruction, message::Message, pubkey::Pubkey},
solana_transaction_status::extract_memos::{spl_memo_id_v1, spl_memo_id_v3, ExtractMemos},
test::Bencher,
};
#[bench]
fn bench_extract_memos(b: &mut Bencher) {
let mut account_keys: Vec<Pubkey> = (0..64).map(|_| Pubkey::new_unique()).collect();
account_keys[62] = spl_memo_id_v1();
account_keys[63] = spl_memo_id_v3();
let memo = "Test memo";
let instructions: Vec<_> = (0..20)
.map(|i| CompiledInstruction {
program_id_index: 62 + (i % 2),
accounts: vec![],
data: memo.as_bytes().to_vec(),
})
.collect();
let message = Message {
account_keys,
instructions,
..Message::default()
};
b.iter(|| message.extract_memos());
}

View File

@ -1,6 +1,7 @@
use { use {
crate::parse_instruction::parse_memo_data, crate::parse_instruction::parse_memo_data,
solana_sdk::{ solana_sdk::{
instruction::CompiledInstruction,
message::{Message, SanitizedMessage}, message::{Message, SanitizedMessage},
pubkey::Pubkey, pubkey::Pubkey,
}, },
@ -9,13 +10,18 @@ use {
// A helper function to convert spl_memo::v1::id() as spl_sdk::pubkey::Pubkey to // A helper function to convert spl_memo::v1::id() as spl_sdk::pubkey::Pubkey to
// solana_sdk::pubkey::Pubkey // solana_sdk::pubkey::Pubkey
pub fn spl_memo_id_v1() -> Pubkey { pub fn spl_memo_id_v1() -> Pubkey {
Pubkey::new_from_array(spl_memo::v1::id().to_bytes()) *MEMO_PROGRAM_ID_V1
} }
// A helper function to convert spl_memo::id() as spl_sdk::pubkey::Pubkey to // A helper function to convert spl_memo::id() as spl_sdk::pubkey::Pubkey to
// solana_sdk::pubkey::Pubkey // solana_sdk::pubkey::Pubkey
pub fn spl_memo_id_v3() -> Pubkey { pub fn spl_memo_id_v3() -> Pubkey {
Pubkey::new_from_array(spl_memo::id().to_bytes()) *MEMO_PROGRAM_ID_V3
}
lazy_static! {
static ref MEMO_PROGRAM_ID_V1: Pubkey = Pubkey::new_from_array(spl_memo::v1::id().to_bytes());
static ref MEMO_PROGRAM_ID_V3: Pubkey = Pubkey::new_from_array(spl_memo::id().to_bytes());
} }
pub fn extract_and_fmt_memos<T: ExtractMemos>(message: &T) -> Option<String> { pub fn extract_and_fmt_memos<T: ExtractMemos>(message: &T) -> Option<String> {
@ -27,12 +33,10 @@ pub fn extract_and_fmt_memos<T: ExtractMemos>(message: &T) -> Option<String> {
} }
} }
fn maybe_push_parsed_memo(memos: &mut Vec<String>, program_id: Pubkey, data: &[u8]) { fn extract_and_fmt_memo_data(data: &[u8]) -> String {
if program_id == spl_memo_id_v1() || program_id == spl_memo_id_v3() {
let memo_len = data.len(); let memo_len = data.len();
let parsed_memo = parse_memo_data(data).unwrap_or_else(|_| "(unparseable)".to_string()); let parsed_memo = parse_memo_data(data).unwrap_or_else(|_| "(unparseable)".to_string());
memos.push(format!("[{}] {}", memo_len, parsed_memo)); format!("[{}] {}", memo_len, parsed_memo)
}
} }
pub trait ExtractMemos { pub trait ExtractMemos {
@ -41,50 +45,56 @@ pub trait ExtractMemos {
impl ExtractMemos for Message { impl ExtractMemos for Message {
fn extract_memos(&self) -> Vec<String> { fn extract_memos(&self) -> Vec<String> {
let mut memos = vec![]; extract_memos_inner(self.account_keys.iter(), &self.instructions)
if self.account_keys.contains(&spl_memo_id_v1())
|| self.account_keys.contains(&spl_memo_id_v3())
{
for instruction in &self.instructions {
let program_id = self.account_keys[instruction.program_id_index as usize];
maybe_push_parsed_memo(&mut memos, program_id, &instruction.data);
}
}
memos
} }
} }
impl ExtractMemos for SanitizedMessage { impl ExtractMemos for SanitizedMessage {
fn extract_memos(&self) -> Vec<String> { fn extract_memos(&self) -> Vec<String> {
let mut memos = vec![]; extract_memos_inner(self.account_keys_iter(), self.instructions())
if self
.account_keys_iter()
.any(|&pubkey| pubkey == spl_memo_id_v1() || pubkey == spl_memo_id_v3())
{
for (program_id, instruction) in self.program_instructions_iter() {
maybe_push_parsed_memo(&mut memos, *program_id, &instruction.data);
} }
} }
memos
enum KeyType<'a> {
MemoProgram,
OtherProgram,
Unknown(&'a Pubkey),
} }
fn extract_memos_inner<'a>(
account_keys: impl Iterator<Item = &'a Pubkey>,
instructions: &[CompiledInstruction],
) -> Vec<String> {
let mut account_keys: Vec<KeyType> = account_keys.map(KeyType::Unknown).collect();
instructions
.iter()
.filter_map(|ix| {
let index = ix.program_id_index as usize;
let key_type = account_keys.get(index)?;
let memo_data = match key_type {
KeyType::MemoProgram => Some(&ix.data),
KeyType::OtherProgram => None,
KeyType::Unknown(program_id) => {
if **program_id == *MEMO_PROGRAM_ID_V1 || **program_id == *MEMO_PROGRAM_ID_V3 {
account_keys[index] = KeyType::MemoProgram;
Some(&ix.data)
} else {
account_keys[index] = KeyType::OtherProgram;
None
}
}
}?;
Some(extract_and_fmt_memo_data(memo_data))
})
.collect()
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use { use super::*;
super::*,
solana_sdk::{
hash::Hash,
instruction::CompiledInstruction,
message::{
v0::{self, LoadedAddresses},
MessageHeader,
},
},
};
#[test] #[test]
fn test_extract_memos() { fn test_extract_memos_inner() {
let fee_payer = Pubkey::new_unique(); let fee_payer = Pubkey::new_unique();
let another_program_id = Pubkey::new_unique(); let another_program_id = Pubkey::new_unique();
let memo0 = "Test memo"; let memo0 = "Test memo";
@ -110,40 +120,16 @@ mod test {
data: memo1.as_bytes().to_vec(), data: memo1.as_bytes().to_vec(),
}, },
]; ];
let message = Message::new_with_compiled_instructions( let account_keys = vec![
1,
0,
3,
vec![
fee_payer, fee_payer,
spl_memo_id_v1(), spl_memo_id_v1(),
another_program_id, another_program_id,
spl_memo_id_v3(), spl_memo_id_v3(),
], ];
Hash::default(),
memo_instructions.clone(), assert_eq!(
extract_memos_inner(account_keys.iter(), &memo_instructions),
expected_memos
); );
assert_eq!(message.extract_memos(), expected_memos);
let sanitized_message = SanitizedMessage::Legacy(message);
assert_eq!(sanitized_message.extract_memos(), expected_memos);
let sanitized_message = SanitizedMessage::V0(v0::LoadedMessage {
message: v0::Message {
header: MessageHeader {
num_required_signatures: 1,
num_readonly_signed_accounts: 0,
num_readonly_unsigned_accounts: 3,
},
account_keys: vec![fee_payer],
instructions: memo_instructions,
..v0::Message::default()
},
loaded_addresses: LoadedAddresses {
writable: vec![],
readonly: vec![spl_memo_id_v1(), another_program_id, spl_memo_id_v3()],
},
});
assert_eq!(sanitized_message.extract_memos(), expected_memos);
} }
} }