Skip to content
Merged
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [2.0.4] - 2026-07-22

### Fixed

- **On-chain snapshot WARNs**: stop logging full program ELF bytes; keep account type, slot, and authority only.

## [2.0.3] - 2026-07-21

Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "verified_programs_api"
version = "2.0.3"
version = "2.0.4"
edition = "2021"

[dependencies]
Expand Down
55 changes: 52 additions & 3 deletions src/onchain/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use crate::{
};
use sha2::{Digest, Sha256};
use solana_account_decoder::parse_bpf_loader::{
parse_bpf_upgradeable_loader, BpfUpgradeableLoaderAccountType, UiProgram, UiProgramData,
parse_bpf_upgradeable_loader, BpfUpgradeableLoaderAccountType, UiBuffer, UiProgram,
UiProgramData,
};
use solana_client::{
nonblocking::rpc_client::RpcClient, rpc_client::GetConfirmedSignaturesForAddress2Config,
Expand Down Expand Up @@ -174,6 +175,24 @@ async fn snapshot_chunk(
Ok(())
}

/// Account type for errors, without the executable bytes
fn describe_bpf_loader_account(account: &BpfUpgradeableLoaderAccountType) -> String {
match account {
BpfUpgradeableLoaderAccountType::Uninitialized => "Uninitialized".to_string(),
BpfUpgradeableLoaderAccountType::Buffer(UiBuffer { authority, .. }) => {
format!("Buffer(authority: {authority:?})")
}
BpfUpgradeableLoaderAccountType::Program(UiProgram { program_data }) => {
format!("Program(program_data: {program_data})")
}
BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
slot, authority, ..
}) => {
format!("ProgramData(slot: {slot}, authority: {authority:?})")
}
}
}

/// `Ok(Some(_))` -- has authority. `Ok(None)` -- frozen (no authority).
/// `Err(_)` -- parse failure, caller should not interpret either way.
fn parse_program_data_authority(data: &[u8]) -> Result<Option<String>> {
Expand All @@ -182,7 +201,8 @@ fn parse_program_data_authority(data: &[u8]) -> Result<Option<String>> {
Ok(authority)
}
other => Err(ApiError::Custom(format!(
"expected ProgramData account, got: {other:?}"
"expected ProgramData account, got: {}",
describe_bpf_loader_account(&other)
))),
}
}
Expand Down Expand Up @@ -257,7 +277,8 @@ fn extract_program_data_pda(data: &[u8]) -> Result<Pubkey> {
Pubkey::from_str(&program_data).map_err(Into::into)
}
other => Err(ApiError::Custom(format!(
"expected Program account, got: {other:?}"
"expected Program account, got: {}",
describe_bpf_loader_account(&other)
))),
}
}
Expand Down Expand Up @@ -370,6 +391,34 @@ mod tests {
);
}

#[test]
fn describe_bpf_loader_account_omits_executable_bytes() {
use solana_account_decoder::{UiAccountData, UiAccountEncoding};

let huge = "abcdefghijklmnopqrstuvwxyz".to_string() + &"A".repeat(10_000);
let program_data = BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
slot: 42,
authority: Some("Auth111111111111111111111111111111111111111".into()),
data: UiAccountData::Binary(huge.clone(), UiAccountEncoding::Base64),
});
let described = describe_bpf_loader_account(&program_data);
assert!(described.contains("ProgramData"));
assert!(described.contains("42"));
assert!(
!described.contains(&huge),
"must not dump executable base64 into logs"
);
assert!(!described.contains("Binary"));

let buffer = BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
authority: None,
data: UiAccountData::Binary(huge.clone(), UiAccountEncoding::Base64),
});
let described = describe_bpf_loader_account(&buffer);
assert!(described.starts_with("Buffer("));
assert!(!described.contains(&huge));
}

#[tokio::test]
#[ignore = "hits mainnet RPC"]
async fn test_get_on_chain_hash() {
Expand Down
Loading