From f4ae450f3488cba949744d5fea9f7d214c63108a Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Mon, 30 Aug 2021 21:14:18 -0600 Subject: [PATCH] Populate memo in bigtable transaction structs (#19512) * Populate memo in bigtable transaction structs * Preface memos with len --- storage-bigtable/src/lib.rs | 12 +++--- transaction-status/src/extract_memos.rs | 43 +++++++++++++++++++++ transaction-status/src/lib.rs | 2 + transaction-status/src/parse_instruction.rs | 18 ++++++--- 4 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 transaction-status/src/extract_memos.rs diff --git a/storage-bigtable/src/lib.rs b/storage-bigtable/src/lib.rs index d4e4c763d5..f9e3ef0c49 100644 --- a/storage-bigtable/src/lib.rs +++ b/storage-bigtable/src/lib.rs @@ -12,9 +12,10 @@ use { }, solana_storage_proto::convert::{generated, tx_by_addr}, solana_transaction_status::{ - ConfirmedBlock, ConfirmedTransaction, ConfirmedTransactionStatusWithSignature, Reward, - TransactionByAddrInfo, TransactionConfirmationStatus, TransactionStatus, - TransactionStatusMeta, TransactionWithStatusMeta, + extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransaction, + ConfirmedTransactionStatusWithSignature, Reward, TransactionByAddrInfo, + TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta, + TransactionWithStatusMeta, }, std::{collections::HashMap, convert::TryInto}, thiserror::Error, @@ -559,6 +560,7 @@ impl LedgerStorage { let err = meta.as_ref().and_then(|meta| meta.status.clone().err()); let index = index as u32; let signature = transaction.signatures[0]; + let memo = extract_and_fmt_memos(&transaction.message); for address in &transaction.message.account_keys { if !is_sysvar_id(address) { @@ -569,7 +571,7 @@ impl LedgerStorage { signature, err: err.clone(), index, - memo: None, // TODO + memo: memo.clone(), block_time: confirmed_block.block_time, }); } @@ -581,7 +583,7 @@ impl LedgerStorage { slot, index, err, - memo: None, // TODO + memo, }, )); } diff --git a/transaction-status/src/extract_memos.rs b/transaction-status/src/extract_memos.rs new file mode 100644 index 0000000000..2e7eb540b6 --- /dev/null +++ b/transaction-status/src/extract_memos.rs @@ -0,0 +1,43 @@ +use { + crate::parse_instruction::parse_memo_data, + solana_sdk::{message::Message, pubkey::Pubkey}, +}; + +// A helper function to convert spl_memo::v1::id() as spl_sdk::pubkey::Pubkey to +// solana_sdk::pubkey::Pubkey +pub fn spl_memo_id_v1() -> Pubkey { + Pubkey::new_from_array(spl_memo::v1::id().to_bytes()) +} + +// A helper function to convert spl_memo::id() as spl_sdk::pubkey::Pubkey to +// solana_sdk::pubkey::Pubkey +pub fn spl_memo_id_v3() -> Pubkey { + Pubkey::new_from_array(spl_memo::id().to_bytes()) +} + +pub fn extract_and_fmt_memos(message: &Message) -> Option { + let memos = extract_memos(message); + if memos.is_empty() { + None + } else { + Some(memos.join("; ")) + } +} + +fn extract_memos(message: &Message) -> Vec { + let mut memos = vec![]; + if message.account_keys.contains(&spl_memo_id_v1()) + || message.account_keys.contains(&spl_memo_id_v3()) + { + for instruction in &message.instructions { + let program_id = message.account_keys[instruction.program_id_index as usize]; + if program_id == spl_memo_id_v1() || program_id == spl_memo_id_v3() { + let memo_len = instruction.data.len(); + let parsed_memo = parse_memo_data(&instruction.data) + .unwrap_or_else(|_| "(unparseable)".to_string()); + memos.push(format!("[{}] {}", memo_len, parsed_memo)); + } + } + } + memos +} diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index 6a145dbc08..adfcd2dc46 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -4,6 +4,7 @@ extern crate lazy_static; #[macro_use] extern crate serde_derive; +pub mod extract_memos; pub mod parse_accounts; pub mod parse_associated_token; pub mod parse_bpf_loader; @@ -14,6 +15,7 @@ pub mod parse_token; pub mod parse_vote; pub mod token_balances; +pub use crate::extract_memos::extract_and_fmt_memos; use crate::{ parse_accounts::{parse_accounts, ParsedAccount}, parse_instruction::{parse, ParsedInstruction}, diff --git a/transaction-status/src/parse_instruction.rs b/transaction-status/src/parse_instruction.rs index 94ea9215db..67826f3703 100644 --- a/transaction-status/src/parse_instruction.rs +++ b/transaction-status/src/parse_instruction.rs @@ -1,4 +1,5 @@ use crate::{ + extract_memos::{spl_memo_id_v1, spl_memo_id_v3}, parse_associated_token::{parse_associated_token, spl_associated_token_id_v1_0}, parse_bpf_loader::{parse_bpf_loader, parse_bpf_upgradeable_loader}, parse_stake::parse_stake, @@ -10,15 +11,18 @@ use inflector::Inflector; use serde_json::Value; use solana_account_decoder::parse_token::spl_token_id_v2_0; use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey, stake, system_program}; -use std::{collections::HashMap, str::from_utf8}; +use std::{ + collections::HashMap, + str::{from_utf8, Utf8Error}, +}; use thiserror::Error; lazy_static! { static ref ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey = spl_associated_token_id_v1_0(); static ref BPF_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader::id(); static ref BPF_UPGRADEABLE_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader_upgradeable::id(); - static ref MEMO_V1_PROGRAM_ID: Pubkey = Pubkey::new_from_array(spl_memo::v1::id().to_bytes()); - static ref MEMO_V3_PROGRAM_ID: Pubkey = Pubkey::new_from_array(spl_memo::id().to_bytes()); + static ref MEMO_V1_PROGRAM_ID: Pubkey = spl_memo_id_v1(); + static ref MEMO_V3_PROGRAM_ID: Pubkey = spl_memo_id_v3(); static ref STAKE_PROGRAM_ID: Pubkey = stake::program::id(); static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id(); static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v2_0(); @@ -121,11 +125,15 @@ pub fn parse( } fn parse_memo(instruction: &CompiledInstruction) -> Result { - from_utf8(&instruction.data) - .map(|s| Value::String(s.to_string())) + parse_memo_data(&instruction.data) + .map(Value::String) .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplMemo)) } +pub fn parse_memo_data(data: &[u8]) -> Result { + from_utf8(data).map(|s| s.to_string()) +} + pub(crate) fn check_num_accounts( accounts: &[u8], num: usize,