solana-explorer: add map_filter_transactions module

This commit is contained in:
colindickson
2023-11-01 10:18:13 -04:00
parent 030238e07e
commit 2b104ff1d5
12 changed files with 214 additions and 49 deletions

View File

@@ -2,3 +2,4 @@ mod pb;
mod map_block_full;
mod map_block_meta;
mod map_filter_instructions;
mod map_filter_transactions;

View File

@@ -1,5 +1,5 @@
use substreams_solana::pb::sf::solana::r#type::v1::Block;
use crate::pb::sol::block_meta::v1::BlockMeta;
use crate::pb::sol::block::v1::BlockMeta;
#[substreams::handlers::map]
fn map_block_meta(blk: Block) -> Result<BlockMeta, substreams::errors::Error> {

View File

@@ -1,14 +1,13 @@
use anyhow::anyhow;
use serde::Deserialize;
use substreams_solana::pb::sf::solana::r#type::v1::{Block, CompiledInstruction};
use crate::pb::sol::programs::v1::{Instruction, Instructions};
use crate::pb::sol::transactions::v1::{Instruction, Instructions};
#[derive(Deserialize, Debug)]
struct TransactionFilterParams {
struct InstructionFilterParams {
program_id: Option<String>,
}
#[substreams::handlers::map]
fn map_filter_instructions(params: String, blk: Block) -> Result<Instructions, Vec<substreams::errors::Error>> {
let filters = parse_filters_from_params(params)?;
@@ -17,14 +16,11 @@ fn map_filter_instructions(params: String, blk: Block) -> Result<Instructions, V
blk.transactions.iter().for_each(|tx| {
let msg = tx.transaction.clone().unwrap().message.unwrap();
tx.transaction.clone().unwrap().
let acct_keys = msg.account_keys.as_slice();
let insts : Vec<Instruction> = msg.instructions.iter()
.filter(|inst| apply_filter(inst, &filters, acct_keys.to_vec()))
.map(|inst| {
Instruction {
slot_number: blk.slot,
block_hash: blk.blockhash.clone(),
program_id: bs58::encode(acct_keys[inst.program_id_index as usize].to_vec()).into_string(),
accounts: inst.accounts.iter().map(|acct| bs58::encode(acct_keys[*acct as usize].to_vec()).into_string()).collect(),
data: bs58::encode(inst.data.clone()).into_string(),
@@ -36,7 +32,7 @@ fn map_filter_instructions(params: String, blk: Block) -> Result<Instructions, V
Ok(Instructions { instructions })
}
fn parse_filters_from_params(params: String) -> Result<TransactionFilterParams, Vec<substreams::errors::Error>> {
fn parse_filters_from_params(params: String) -> Result<InstructionFilterParams, Vec<substreams::errors::Error>> {
let parsed_result = serde_qs::from_str(&params);
if parsed_result.is_err() {
return Err(Vec::from([anyhow!("Unexpected error while parsing parameters")]));
@@ -48,7 +44,7 @@ fn parse_filters_from_params(params: String) -> Result<TransactionFilterParams,
Ok(filters)
}
fn apply_filter(instruction: &CompiledInstruction, filters: &TransactionFilterParams, account_keys: Vec<Vec<u8>>) -> bool {
fn apply_filter(instruction: &CompiledInstruction, filters: &InstructionFilterParams, account_keys: Vec<Vec<u8>>) -> bool {
if filters.program_id.is_none() {
return true;
}
@@ -60,7 +56,6 @@ fn apply_filter(instruction: &CompiledInstruction, filters: &TransactionFilterPa
}
let program_account_key_val = bs58::encode(program_account_key.unwrap()).into_string();
//check if account key value is equal to the program id
if program_account_key_val != program_id_filter {
return false;
}

View File

@@ -0,0 +1,69 @@
use anyhow::anyhow;
use serde::Deserialize;
use substreams_solana::pb::sf::solana::r#type::v1::{Block, ConfirmedTransaction};
use crate::pb::sol::transactions::v1::{Instruction, Transaction, Transactions};
#[derive(Deserialize, Debug)]
struct TransactionFilterParams {
signature: Option<String>,
}
#[substreams::handlers::map]
fn map_filter_transactions(params: String, blk: Block) -> Result<Transactions, Vec<substreams::errors::Error>> {
let filters = parse_filters_from_params(params)?;
let mut transactions : Vec<Transaction> = Vec::new();
blk.transactions.iter()
.filter(|tx| apply_filter(tx, &filters))
.for_each(|tx| {
let msg = tx.transaction.clone().unwrap().message.unwrap();
let acct_keys = msg.account_keys.as_slice();
let insts : Vec<Instruction> = msg.instructions.iter()
.map(|inst| {
Instruction {
program_id: bs58::encode(acct_keys[inst.program_id_index as usize].to_vec()).into_string(),
accounts: inst.accounts.iter().map(|acct| bs58::encode(acct_keys[*acct as usize].to_vec()).into_string()).collect(),
data: bs58::encode(inst.data.clone()).into_string(),
}
}).collect();
let t = Transaction {
signatures: tx.transaction.clone().unwrap().signatures.iter().map(|sig| bs58::encode(sig).into_string()).collect(),
instructions: insts
};
transactions.push(t);
});
Ok(Transactions { transactions })
}
fn parse_filters_from_params(params: String) -> Result<TransactionFilterParams, Vec<substreams::errors::Error>> {
let parsed_result = serde_qs::from_str(&params);
if parsed_result.is_err() {
return Err(Vec::from([anyhow!("Unexpected error while parsing parameters")]));
}
let filters = parsed_result.unwrap();
//todo: verify_filters(&filters)?;
Ok(filters)
}
fn apply_filter(transaction: &&ConfirmedTransaction, filters: &TransactionFilterParams) -> bool {
if filters.signature.is_none() {
return true;
}
let mut found = false;
transaction.transaction.as_ref().unwrap().signatures.iter().for_each(|sig| {
let xsig = bs58::encode(&sig).into_string();
if xsig == filters.signature.clone().unwrap() {
found = true;
}
});
found
}

View File

@@ -11,18 +11,18 @@ pub mod sf {
}
}
pub mod sol {
pub mod block_meta {
// @@protoc_insertion_point(attribute:sol.block_meta.v1)
pub mod block {
// @@protoc_insertion_point(attribute:sol.block.v1)
pub mod v1 {
include!("sol.block_meta.v1.rs");
// @@protoc_insertion_point(sol.block_meta.v1)
include!("sol.block.v1.rs");
// @@protoc_insertion_point(sol.block.v1)
}
}
pub mod programs {
// @@protoc_insertion_point(attribute:sol.programs.v1)
pub mod transactions {
// @@protoc_insertion_point(attribute:sol.transactions.v1)
pub mod v1 {
include!("sol.programs.v1.rs");
// @@protoc_insertion_point(sol.programs.v1)
include!("sol.transactions.v1.rs");
// @@protoc_insertion_point(sol.transactions.v1)
}
}
}

View File

@@ -14,9 +14,19 @@ pub struct Instruction {
pub accounts: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
#[prost(string, tag="3")]
pub data: ::prost::alloc::string::String,
#[prost(uint64, tag="4")]
pub slot_number: u64,
#[prost(string, tag="5")]
pub block_hash: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Transactions {
#[prost(message, repeated, tag="1")]
pub transactions: ::prost::alloc::vec::Vec<Transaction>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Transaction {
#[prost(string, repeated, tag="1")]
pub signatures: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
#[prost(message, repeated, tag="2")]
pub instructions: ::prost::alloc::vec::Vec<Instruction>,
}
// @@protoc_insertion_point(module)