Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
819 changes: 819 additions & 0 deletions solana-program-library/solana-compute-budget/Cargo.lock

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions solana-program-library/solana-compute-budget/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "substreams-solana-programs-compute-budget"
version = "1.0.0"
edition = "2021"

[lib]
name = "substreams"
crate-type = ["cdylib"]

[dependencies]
substreams = "^0.5.0"
substreams-solana = { git = "https://github.com/streamingfast/substreams-solana", branch = "master" }
substreams-solana-program-instructions = "0.1"
bytes = "1.1.0"
prost = "0.11"
num-bigint = "0.4"
bs58 = "0.5.0"
base64 = "0.21.5"
borsh = { version = "0.10.3"}
chrono = { version = "0.4", features = [ "std" ], default-features = false }

[profile.release]
lto = true
opt-level = 's'
strip = "debuginfo"
20 changes: 20 additions & 0 deletions solana-program-library/solana-compute-budget/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
ENDPOINT ?= mainnet.sol.streamingfast.io:443

.PHONY: build
build:
LDFLAGS="-Wl,-no_compact_unwind" cargo build --target wasm32-unknown-unknown --release

.PHONY: stream
stream: build
substreams run -e $(ENDPOINT) substreams.yaml map_block -s 252842623 -t +1

.PHONY: protogen
protogen:
substreams protogen ./substreams.yaml --exclude-paths="sf/substreams,google"

.PHONY: package
package:
substreams pack ./substreams.yaml


# 2024-04-10T20:36:00.696Z ERRO (cli) application shutdown unexpectedly, quitting: stream invalid: rpc error: code = InvalidArgument desc = rpc error: code = InvalidArgument desc = step new irr: handler step new: execute modules: applying executor results "map_block": execute: maps wasm call: block 252842623: module "map_block": general wasm execution panicked: wasm execution failed deterministically: panic in the wasm: "called `Result::unwrap()` on an `Err` value: Custom { kind: InvalidInput, error: \"Unexpected length of input\" }" at src/instruction.rs:61:89
22 changes: 22 additions & 0 deletions solana-program-library/solana-compute-budget/proto/main.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
syntax = "proto2";

package sf.solana.programs.compute.budget.v1;

message ComputeBudgetMeta {
required string block_date = 1;
required int64 block_time = 2;
required string tx_id = 3;
required string dapp = 4;
required uint64 block_slot = 5;
required uint32 instruction_index = 7;
required bool is_inner_instruction = 8;
required uint32 inner_instruction_index = 9;
required string instruction_type = 10;
optional uint32 bytes = 11;
optional uint32 units = 12;
optional uint64 micro_lamports = 13;
}

message Output {
repeated ComputeBudgetMeta data = 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const PROGRAM_ADDRESS: &str = "ComputeBudget111111111111111111111111111111";
85 changes: 85 additions & 0 deletions solana-program-library/solana-compute-budget/src/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use borsh::{BorshDeserialize, BorshSerialize};
use substreams::log;

#[derive(BorshDeserialize, BorshSerialize, Debug, Clone, Default)]
pub struct RequestHeapFrameLayout {
pub bytes: u32,
}

#[derive(BorshDeserialize, BorshSerialize, Debug, Clone, Default)]
pub struct SetComputeUnitLimitLayout {
pub units: u32,
}

#[derive(BorshDeserialize, BorshSerialize, Debug, Clone, Default)]
pub struct SetComputeUnitPriceLayout {
pub micro_lamports: u64,
}

#[derive(BorshDeserialize, BorshSerialize, Debug, Clone, Default)]
pub struct SetLoadedAccountsDataSizeLimitLayout {
pub bytes: u32,
}

#[derive(Debug)]
pub struct Instruction {
pub name: String,
pub requestHeapFrameArg: RequestHeapFrameLayout,
pub setComputeUnitLimitArg: SetComputeUnitLimitLayout,
pub setComputeUnitPriceArg: SetComputeUnitPriceLayout,
pub setLoadedAccountsDataSizeLimitArg: SetLoadedAccountsDataSizeLimitLayout,
}

pub fn parse_instruction(bytes_stream: Vec<u8>) -> Instruction {
let mut instruction_name = String::default();

let mut requestHeapFrameArg: RequestHeapFrameLayout = RequestHeapFrameLayout::default();
let mut setComputeUnitLimitArg: SetComputeUnitLimitLayout =
SetComputeUnitLimitLayout::default();
let mut setComputeUnitPriceArg: SetComputeUnitPriceLayout =
SetComputeUnitPriceLayout::default();
let mut setLoadedAccountsDataSizeLimitArg: SetLoadedAccountsDataSizeLimitLayout =
SetLoadedAccountsDataSizeLimitLayout::default();

let (disc_bytes, rest) = bytes_stream.split_at(1);
let discriminator: u8 = u8::try_from_slice(disc_bytes).unwrap();
let rest_bytes = &mut rest.clone();

match discriminator {
0 => {
instruction_name = String::from("Unused");
}
1 => {
instruction_name = String::from("RequestHeapFrame");
requestHeapFrameArg = RequestHeapFrameLayout::deserialize(rest_bytes).unwrap();
}
2 => {
instruction_name = String::from("SetComputeUnitLimit");
setComputeUnitLimitArg = SetComputeUnitLimitLayout::deserialize(rest_bytes).unwrap();
}
3 => {
instruction_name = String::from("SetComputeUnitPrice");
log::info!("{:?}", rest_bytes);
if rest_bytes.len() > 7 {
setComputeUnitPriceArg = SetComputeUnitPriceLayout::deserialize(rest_bytes).unwrap();
}

}
4 => {
instruction_name = String::from("SetLoadedAccountsDataSizeLimit");
setLoadedAccountsDataSizeLimitArg =
SetLoadedAccountsDataSizeLimitLayout::deserialize(rest_bytes).unwrap();
}
_ => {}
}

let result: Instruction = Instruction {
name: instruction_name,
requestHeapFrameArg: requestHeapFrameArg,
setComputeUnitLimitArg: setComputeUnitLimitArg,
setComputeUnitPriceArg: setComputeUnitPriceArg,
setLoadedAccountsDataSizeLimitArg: setLoadedAccountsDataSizeLimitArg,
};

return result;
}
128 changes: 128 additions & 0 deletions solana-program-library/solana-compute-budget/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(non_snake_case)]

mod constants;
mod instruction;
mod pb;
mod utils;

use instruction::{parse_instruction, Instruction};
use pb::sf::solana::programs::compute::budget::v1::{ComputeBudgetMeta, Output};
use substreams::log;
use substreams_solana::pb::sf::solana::r#type::v1::Block;
use utils::convert_to_date;

#[substreams::handlers::map]
fn map_block(block: Block) -> Result<Output, substreams::errors::Error> {
let slot = block.slot;
let parent_slot = block.parent_slot;
let timestamp = block.block_time.as_ref().unwrap().timestamp;

let mut data: Vec<ComputeBudgetMeta> = vec![];

for trx in block.transactions_owned() {
let accounts = trx.resolved_accounts_as_strings();
if let Some(transaction) = trx.transaction {
let meta = trx.meta.unwrap();
let msg = transaction.message.unwrap();

for (idx, inst) in msg.instructions.into_iter().enumerate() {
let program = &accounts[inst.program_id_index as usize];

let tx_id: String = bs58::encode(&transaction.signatures[0]).into_string();
if program == constants::PROGRAM_ADDRESS {
let meta: ComputeBudgetMeta = get_meta(
timestamp,
tx_id.clone(),
parent_slot,
inst.program_id_index,
false,
0,
inst.data.clone(),
);
data.push(meta);
}

meta.inner_instructions
.iter()
.filter(|inner_instruction| inner_instruction.index == idx as u32)
.for_each(|inner_instruction| {
inner_instruction
.instructions
.iter()
.for_each(|inner_inst| {
let program = &accounts[inner_inst.program_id_index as usize];
if program == constants::PROGRAM_ADDRESS {
let meta: ComputeBudgetMeta = get_meta(
timestamp,
tx_id.clone(),
parent_slot,
inst.program_id_index,
true,
inner_inst.program_id_index,
inner_inst.data.clone(),
);
data.push(meta);
}
})
});
}
}
}

// TODO: remove
log::info!("{:#?}", slot);
log::info!("{:#?}", data.len());

Ok(Output { data })
}

fn get_meta(
timestamp: i64,
tx_id: String,
slot: u64,
ix_index: u32,
is_inner_instruction: bool,
inner_ix_index: u32,
instruction_data: Vec<u8>,
) -> ComputeBudgetMeta {
let mut meta: ComputeBudgetMeta = ComputeBudgetMeta::default();
let instruction: Instruction = parse_instruction(instruction_data);

meta.block_date = convert_to_date(timestamp);
meta.block_time = timestamp;
meta.tx_id = tx_id;
meta.dapp = constants::PROGRAM_ADDRESS.to_string();
meta.block_slot = slot;
meta.instruction_index = ix_index;
meta.is_inner_instruction = is_inner_instruction.clone();
meta.inner_instruction_index = inner_ix_index.clone();

match instruction.name.as_str() {
"Unused" => {
meta.instruction_type = "Unused".to_string();
}
"RequestHeapFrame" => {
meta.instruction_type = "RequestHeapFrame".to_string();
meta.bytes = Some(instruction.requestHeapFrameArg.bytes);
}
"SetComputeUnitLimit" => {
meta.instruction_type = "SetComputeUnitLimit".to_string();
meta.units = Some(instruction.setComputeUnitLimitArg.units);
}
"SetComputeUnitPrice" => {
meta.instruction_type = "SetComputeUnitPrice".to_string();
meta.micro_lamports = Some(instruction.setComputeUnitPriceArg.micro_lamports);
}
"SetLoadedAccountsDataSizeLimit" => {
meta.instruction_type = "SetLoadedAccountsDataSizeLimit".to_string();
meta.bytes = Some(instruction.setLoadedAccountsDataSizeLimitArg.bytes);
}
_ => {
meta.instruction_type = String::from("Unknown Instruction");
}
}

return meta;
}
16 changes: 16 additions & 0 deletions solana-program-library/solana-compute-budget/src/pb/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @generated
pub mod sf {
pub mod solana {
pub mod programs {
pub mod compute {
pub mod budget {
// @@protoc_insertion_point(attribute:sf.solana.programs.compute.budget.v1)
pub mod v1 {
include!("sf.solana.programs.compute.budget.v1.rs");
// @@protoc_insertion_point(sf.solana.programs.compute.budget.v1)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// @generated
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ComputeBudgetMeta {
#[prost(string, required, tag="1")]
pub block_date: ::prost::alloc::string::String,
#[prost(int64, required, tag="2")]
pub block_time: i64,
#[prost(string, required, tag="3")]
pub tx_id: ::prost::alloc::string::String,
#[prost(string, required, tag="4")]
pub dapp: ::prost::alloc::string::String,
#[prost(uint64, required, tag="5")]
pub block_slot: u64,
#[prost(uint32, required, tag="7")]
pub instruction_index: u32,
#[prost(bool, required, tag="8")]
pub is_inner_instruction: bool,
#[prost(uint32, required, tag="9")]
pub inner_instruction_index: u32,
#[prost(string, required, tag="10")]
pub instruction_type: ::prost::alloc::string::String,
#[prost(uint32, optional, tag="11")]
pub bytes: ::core::option::Option<u32>,
#[prost(uint32, optional, tag="12")]
pub units: ::core::option::Option<u32>,
#[prost(uint64, optional, tag="13")]
pub micro_lamports: ::core::option::Option<u64>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Output {
#[prost(message, repeated, tag="1")]
pub data: ::prost::alloc::vec::Vec<ComputeBudgetMeta>,
}
// @@protoc_insertion_point(module)
9 changes: 9 additions & 0 deletions solana-program-library/solana-compute-budget/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
extern crate chrono;
use chrono::prelude::*;

pub fn convert_to_date(ts: i64) -> String {
let nt = NaiveDateTime::from_timestamp_opt(ts, 0);
let dt: DateTime<Utc> = DateTime::from_utc(nt.unwrap(), Utc);
let res = dt.format("%Y-%m-%d");
return res.to_string();
}
24 changes: 24 additions & 0 deletions solana-program-library/solana-compute-budget/substreams.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
specVersion: v0.1.0
package:
name: 'tl_solana_programs_compute_budget'
version: v1.0.1

protobuf:
files:
- main.proto
importPaths:
- ./proto


binaries:
default:
type: wasm/rust-v1
file: target/wasm32-unknown-unknown/release/substreams.wasm

modules:
- name: map_block
kind: map
inputs:
- source: sf.solana.type.v1.Block
output:
type: proto:sf.solana.programs.compute.budget.v1.Output