Include token owners in TransactionTokenBalances (#20642)

* Cache owners in TransactionTokenBalances

* Light cleanup

* Use return struct, and remove pub

* Single-use statements

* Why not, just do the whole crate

* Add metrics

* Make datapoint_debug to prevent spam unless troubleshooting
This commit is contained in:
Tyera Eulberg 2021-10-13 21:46:52 -06:00 committed by GitHub
parent 13462d63a2
commit e806fa6904
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 213 additions and 137 deletions

3
Cargo.lock generated
View File

@ -5758,10 +5758,13 @@ dependencies = [
"bincode",
"bs58 0.4.0",
"lazy_static",
"log 0.4.14",
"serde",
"serde_derive",
"serde_json",
"solana-account-decoder",
"solana-measure",
"solana-metrics",
"solana-runtime",
"solana-sdk",
"solana-vote-program",

View File

@ -8361,6 +8361,7 @@ pub mod tests {
amount: "11".to_string(),
ui_amount_string: "1.1".to_string(),
},
owner: Pubkey::new_unique().to_string(),
}]),
post_token_balances: Some(vec![TransactionTokenBalance {
account_index: 0,
@ -8371,6 +8372,7 @@ pub mod tests {
amount: "11".to_string(),
ui_amount_string: "1.1".to_string(),
},
owner: Pubkey::new_unique().to_string(),
}]),
rewards: Some(vec![Reward {
pubkey: "My11111111111111111111111111111111111111111".to_string(),

View File

@ -3394,10 +3394,13 @@ dependencies = [
"bincode",
"bs58 0.4.0",
"lazy_static",
"log",
"serde",
"serde_derive",
"serde_json",
"solana-account-decoder",
"solana-measure",
"solana-metrics",
"solana-runtime",
"solana-sdk",
"solana-vote-program",

View File

@ -66,6 +66,7 @@ message TokenBalance {
uint32 account_index = 1;
string mint = 2;
UiTokenAmount ui_token_amount = 3;
string owner = 4;
}
message UiTokenAmount {

View File

@ -408,6 +408,7 @@ impl From<TransactionTokenBalance> for generated::TokenBalance {
amount: value.ui_token_amount.amount,
ui_amount_string: value.ui_token_amount.ui_amount_string,
}),
owner: value.owner,
}
}
}
@ -435,6 +436,7 @@ impl From<generated::TokenBalance> for TransactionTokenBalance {
)
},
},
owner: value.owner,
}
}
}

View File

@ -111,6 +111,8 @@ pub struct StoredTransactionTokenBalance {
pub account_index: u8,
pub mint: String,
pub ui_token_amount: StoredTokenAmount,
#[serde(deserialize_with = "default_on_eof")]
pub owner: String,
}
impl From<StoredTransactionTokenBalance> for TransactionTokenBalance {
@ -119,11 +121,13 @@ impl From<StoredTransactionTokenBalance> for TransactionTokenBalance {
account_index,
mint,
ui_token_amount,
owner,
} = value;
Self {
account_index,
mint,
ui_token_amount: ui_token_amount.into(),
owner,
}
}
}
@ -134,11 +138,13 @@ impl From<TransactionTokenBalance> for StoredTransactionTokenBalance {
account_index,
mint,
ui_token_amount,
owner,
} = value;
Self {
account_index,
mint,
ui_token_amount: ui_token_amount.into(),
owner,
}
}
}

View File

@ -15,10 +15,13 @@ bincode = "1.3.3"
bs58 = "0.4.0"
Inflector = "0.11.4"
lazy_static = "1.4.0"
log = "0.4.14"
serde = "1.0.130"
serde_derive = "1.0.103"
serde_json = "1.0.68"
solana-account-decoder = { path = "../account-decoder", version = "=1.9.0" }
solana-measure = { path = "../measure", version = "=1.9.0" }
solana-metrics = { path = "../metrics", version = "=1.9.0" }
solana-runtime = { path = "../runtime", version = "=1.9.0" }
solana-sdk = { path = "../sdk", version = "=1.9.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.9.0" }

View File

@ -15,25 +15,26 @@ 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},
pub use {crate::extract_memos::extract_and_fmt_memos, solana_runtime::bank::RewardType};
use {
crate::{
parse_accounts::{parse_accounts, ParsedAccount},
parse_instruction::{parse, ParsedInstruction},
},
solana_account_decoder::parse_token::UiTokenAmount,
solana_sdk::{
clock::{Slot, UnixTimestamp},
commitment_config::CommitmentConfig,
deserialize_utils::default_on_eof,
instruction::CompiledInstruction,
message::{Message, MessageHeader},
pubkey::Pubkey,
sanitize::Sanitize,
signature::Signature,
transaction::{Result, Transaction, TransactionError},
},
std::fmt,
};
use solana_account_decoder::parse_token::UiTokenAmount;
pub use solana_runtime::bank::RewardType;
use solana_sdk::{
clock::{Slot, UnixTimestamp},
commitment_config::CommitmentConfig,
deserialize_utils::default_on_eof,
instruction::CompiledInstruction,
message::{Message, MessageHeader},
pubkey::Pubkey,
sanitize::Sanitize,
signature::Signature,
transaction::{Result, Transaction, TransactionError},
};
use std::fmt;
/// A duplicate representation of an Instruction for pretty JSON serialization
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", untagged)]
@ -126,6 +127,7 @@ pub struct TransactionTokenBalance {
pub account_index: u8,
pub mint: String,
pub ui_token_amount: UiTokenAmount,
pub owner: String,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -134,6 +136,8 @@ pub struct UiTransactionTokenBalance {
pub account_index: u8,
pub mint: String,
pub ui_token_amount: UiTokenAmount,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub owner: Option<String>,
}
impl From<TransactionTokenBalance> for UiTransactionTokenBalance {
@ -142,6 +146,11 @@ impl From<TransactionTokenBalance> for UiTransactionTokenBalance {
account_index: token_balance.account_index,
mint: token_balance.mint,
ui_token_amount: token_balance.ui_token_amount,
owner: if !token_balance.owner.is_empty() {
Some(token_balance.owner)
} else {
None
},
}
}
}

View File

@ -22,8 +22,7 @@ pub fn parse_accounts(message: &Message) -> Vec<ParsedAccount> {
#[cfg(test)]
mod test {
use super::*;
use solana_sdk::message::MessageHeader;
use {super::*, solana_sdk::message::MessageHeader};
#[test]
fn test_parse_accounts() {

View File

@ -1,8 +1,10 @@
use crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
use {
crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
},
serde_json::json,
solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey},
};
use serde_json::json;
use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
// A helper function to convert spl_associated_token_account_v1_0::id() as spl_sdk::pubkey::Pubkey
// to solana_sdk::pubkey::Pubkey
@ -47,12 +49,14 @@ fn check_num_associated_token_accounts(
#[cfg(test)]
mod test {
use super::*;
use spl_associated_token_account_v1_0::{
create_associated_token_account,
solana_program::{
instruction::CompiledInstruction as SplAssociatedTokenCompiledInstruction,
message::Message, pubkey::Pubkey as SplAssociatedTokenPubkey,
use {
super::*,
spl_associated_token_account_v1_0::{
create_associated_token_account,
solana_program::{
instruction::CompiledInstruction as SplAssociatedTokenCompiledInstruction,
message::Message, pubkey::Pubkey as SplAssociatedTokenPubkey,
},
},
};

View File

@ -1,11 +1,13 @@
use crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
};
use bincode::deserialize;
use serde_json::json;
use solana_sdk::{
instruction::CompiledInstruction, loader_instruction::LoaderInstruction,
loader_upgradeable_instruction::UpgradeableLoaderInstruction, pubkey::Pubkey,
use {
crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
},
bincode::deserialize,
serde_json::json,
solana_sdk::{
instruction::CompiledInstruction, loader_instruction::LoaderInstruction,
loader_upgradeable_instruction::UpgradeableLoaderInstruction, pubkey::Pubkey,
},
};
pub fn parse_bpf_loader(
@ -154,9 +156,11 @@ fn check_num_bpf_upgradeable_loader_accounts(
#[cfg(test)]
mod test {
use super::*;
use serde_json::Value;
use solana_sdk::{message::Message, pubkey};
use {
super::*,
serde_json::Value,
solana_sdk::{message::Message, pubkey},
};
#[test]
fn test_parse_bpf_loader_instructions() {

View File

@ -1,21 +1,23 @@
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,
parse_system::parse_system,
parse_token::parse_token,
parse_vote::parse_vote,
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,
parse_system::parse_system,
parse_token::parse_token,
parse_vote::parse_vote,
},
inflector::Inflector,
serde_json::Value,
solana_account_decoder::parse_token::spl_token_id_v2_0,
solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey, stake, system_program},
std::{
collections::HashMap,
str::{from_utf8, Utf8Error},
},
thiserror::Error,
};
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, Utf8Error},
};
use thiserror::Error;
lazy_static! {
static ref ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey = spl_associated_token_id_v1_0();
@ -150,8 +152,7 @@ pub(crate) fn check_num_accounts(
#[cfg(test)]
mod test {
use super::*;
use serde_json::json;
use {super::*, serde_json::json};
#[test]
fn test_parse() {

View File

@ -1,10 +1,12 @@
use crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
};
use bincode::deserialize;
use serde_json::{json, Map};
use solana_sdk::{
instruction::CompiledInstruction, pubkey::Pubkey, stake::instruction::StakeInstruction,
use {
crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
},
bincode::deserialize,
serde_json::{json, Map},
solana_sdk::{
instruction::CompiledInstruction, pubkey::Pubkey, stake::instruction::StakeInstruction,
},
};
pub fn parse_stake(
@ -275,13 +277,15 @@ fn check_num_stake_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInst
#[cfg(test)]
mod test {
use super::*;
use solana_sdk::{
message::Message,
pubkey::Pubkey,
stake::{
instruction::{self, LockupArgs},
state::{Authorized, Lockup, StakeAuthorize},
use {
super::*,
solana_sdk::{
message::Message,
pubkey::Pubkey,
stake::{
instruction::{self, LockupArgs},
state::{Authorized, Lockup, StakeAuthorize},
},
},
};

View File

@ -1,10 +1,12 @@
use crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
};
use bincode::deserialize;
use serde_json::json;
use solana_sdk::{
instruction::CompiledInstruction, pubkey::Pubkey, system_instruction::SystemInstruction,
use {
crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
},
bincode::deserialize,
serde_json::json,
solana_sdk::{
instruction::CompiledInstruction, pubkey::Pubkey, system_instruction::SystemInstruction,
},
};
pub fn parse_system(
@ -197,8 +199,10 @@ fn check_num_system_accounts(accounts: &[u8], num: usize) -> Result<(), ParseIns
#[cfg(test)]
mod test {
use super::*;
use solana_sdk::{message::Message, pubkey::Pubkey, system_instruction};
use {
super::*,
solana_sdk::{message::Message, pubkey::Pubkey, system_instruction},
};
#[test]
#[allow(clippy::same_item_push)]

View File

@ -1,15 +1,19 @@
use crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
};
use serde_json::{json, Map, Value};
use solana_account_decoder::parse_token::{pubkey_from_spl_token_v2_0, token_amount_to_ui_amount};
use solana_sdk::{
instruction::{AccountMeta, CompiledInstruction, Instruction},
pubkey::Pubkey,
};
use spl_token_v2_0::{
instruction::{AuthorityType, TokenInstruction},
solana_program::{instruction::Instruction as SplTokenInstruction, program_option::COption},
use {
crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
},
serde_json::{json, Map, Value},
solana_account_decoder::parse_token::{pubkey_from_spl_token_v2_0, token_amount_to_ui_amount},
solana_sdk::{
instruction::{AccountMeta, CompiledInstruction, Instruction},
pubkey::Pubkey,
},
spl_token_v2_0::{
instruction::{AuthorityType, TokenInstruction},
solana_program::{
instruction::Instruction as SplTokenInstruction, program_option::COption,
},
},
};
pub fn parse_token(
@ -452,16 +456,18 @@ pub fn spl_token_v2_0_instruction(instruction: SplTokenInstruction) -> Instructi
#[cfg(test)]
mod test {
use super::*;
use solana_sdk::instruction::CompiledInstruction;
use spl_token_v2_0::{
instruction::*,
solana_program::{
instruction::CompiledInstruction as SplTokenCompiledInstruction, message::Message,
pubkey::Pubkey as SplTokenPubkey,
use {
super::*,
solana_sdk::instruction::CompiledInstruction,
spl_token_v2_0::{
instruction::*,
solana_program::{
instruction::CompiledInstruction as SplTokenCompiledInstruction, message::Message,
pubkey::Pubkey as SplTokenPubkey,
},
},
std::str::FromStr,
};
use std::str::FromStr;
fn convert_pubkey(pubkey: Pubkey) -> SplTokenPubkey {
SplTokenPubkey::from_str(&pubkey.to_string()).unwrap()

View File

@ -1,10 +1,12 @@
use crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
use {
crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
},
bincode::deserialize,
serde_json::json,
solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey},
solana_vote_program::vote_instruction::VoteInstruction,
};
use bincode::deserialize;
use serde_json::json;
use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
use solana_vote_program::vote_instruction::VoteInstruction;
pub fn parse_vote(
instruction: &CompiledInstruction,
@ -143,11 +145,13 @@ fn check_num_vote_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInstr
#[cfg(test)]
mod test {
use super::*;
use solana_sdk::{hash::Hash, message::Message, pubkey::Pubkey};
use solana_vote_program::{
vote_instruction,
vote_state::{Vote, VoteAuthorize, VoteInit},
use {
super::*,
solana_sdk::{hash::Hash, message::Message, pubkey::Pubkey},
solana_vote_program::{
vote_instruction,
vote_state::{Vote, VoteAuthorize, VoteInit},
},
};
#[test]

View File

@ -1,14 +1,19 @@
use crate::TransactionTokenBalance;
use solana_account_decoder::parse_token::{
spl_token_id_v2_0, spl_token_v2_0_native_mint, token_amount_to_ui_amount, UiTokenAmount,
use {
crate::TransactionTokenBalance,
solana_account_decoder::parse_token::{
pubkey_from_spl_token_v2_0, spl_token_id_v2_0, spl_token_v2_0_native_mint,
token_amount_to_ui_amount, UiTokenAmount,
},
solana_measure::measure::Measure,
solana_metrics::datapoint_debug,
solana_runtime::{bank::Bank, transaction_batch::TransactionBatch},
solana_sdk::{account::ReadableAccount, pubkey::Pubkey},
spl_token_v2_0::{
solana_program::program_pack::Pack,
state::{Account as TokenAccount, Mint},
},
std::collections::HashMap,
};
use solana_runtime::{bank::Bank, transaction_batch::TransactionBatch};
use solana_sdk::{account::ReadableAccount, pubkey::Pubkey};
use spl_token_v2_0::{
solana_program::program_pack::Pack,
state::{Account as TokenAccount, Mint},
};
use std::{collections::HashMap, str::FromStr};
pub type TransactionTokenBalances = Vec<Vec<TransactionTokenBalance>>;
@ -54,6 +59,7 @@ pub fn collect_token_balances(
mint_decimals: &mut HashMap<Pubkey, u8>,
) -> TransactionTokenBalances {
let mut balances: TransactionTokenBalances = vec![];
let mut collect_time = Measure::start("collect_token_balances");
for transaction in batch.sanitized_transactions() {
let has_token_program = transaction
@ -68,41 +74,56 @@ pub fn collect_token_balances(
continue;
}
if let Some((mint, ui_token_amount)) =
collect_token_balance_from_account(bank, account_id, mint_decimals)
if let Some(TokenBalanceData {
mint,
ui_token_amount,
owner,
}) = collect_token_balance_from_account(bank, account_id, mint_decimals)
{
transaction_balances.push(TransactionTokenBalance {
account_index: index as u8,
mint,
ui_token_amount,
owner,
});
}
}
}
balances.push(transaction_balances);
}
collect_time.stop();
datapoint_debug!(
"collect_token_balances",
("collect_time_us", collect_time.as_us(), i64),
);
balances
}
pub fn collect_token_balance_from_account(
struct TokenBalanceData {
mint: String,
owner: String,
ui_token_amount: UiTokenAmount,
}
fn collect_token_balance_from_account(
bank: &Bank,
account_id: &Pubkey,
mint_decimals: &mut HashMap<Pubkey, u8>,
) -> Option<(String, UiTokenAmount)> {
) -> Option<TokenBalanceData> {
let account = bank.get_account(account_id)?;
let token_account = TokenAccount::unpack(account.data()).ok()?;
let mint_string = &token_account.mint.to_string();
let mint = &Pubkey::from_str(mint_string).unwrap_or_default();
let mint = pubkey_from_spl_token_v2_0(&token_account.mint);
let decimals = mint_decimals.get(mint).cloned().or_else(|| {
let decimals = get_mint_decimals(bank, mint)?;
mint_decimals.insert(*mint, decimals);
let decimals = mint_decimals.get(&mint).cloned().or_else(|| {
let decimals = get_mint_decimals(bank, &mint)?;
mint_decimals.insert(mint, decimals);
Some(decimals)
})?;
Some((
mint_string.to_string(),
token_amount_to_ui_amount(token_account.amount, decimals),
))
Some(TokenBalanceData {
mint: token_account.mint.to_string(),
owner: token_account.owner.to_string(),
ui_token_amount: token_amount_to_ui_amount(token_account.amount, decimals),
})
}