Skip to content

Add the affected program address to each InstructionError #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions transaction-error/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ solana-frozen-abi-macro = { workspace = true, optional = true }
solana-instruction = { workspace = true, default-features = false, features = [
"std",
] }
solana-pubkey = { workspace = true }
solana-sanitize = { workspace = true }

[features]
Expand Down
17 changes: 13 additions & 4 deletions transaction-error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "frozen-abi")]
use solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample};
use {core::fmt, solana_instruction::error::InstructionError, solana_sanitize::SanitizeError};
use {
core::fmt, solana_instruction::error::InstructionError, solana_pubkey::Pubkey,
solana_sanitize::SanitizeError,
};

pub type TransactionResult<T> = Result<T, TransactionError>;

Expand Down Expand Up @@ -43,8 +46,11 @@ pub enum TransactionError {
BlockhashNotFound,

/// An error occurred while processing an instruction. The first element of the tuple
/// indicates the instruction index in which the error occurred.
InstructionError(u8, InstructionError),
/// indicates the instruction index in which the error occurred. For avoidance of doubt, the
/// third element indicates the address of the program that raised the error, if applicable; the
/// error could after all have been raised during a cross-program invocation (ie. in an inner
/// instruction).
InstructionError(u8, InstructionError, Option<Pubkey>),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We won't have a program address in cases where the error is InstructionError::UnsupportedProgramId, so this is made an Option.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate what cases UnsupportedProgramId is for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, for sure. It's thrown, for instance, here:

https://github.com/anza-xyz/agave/blob/1bcf743f445270407bccf55681f74e06ef8b9a48/program-runtime/src/invoke_context.rs#L250-L252

ie. if you tried to issue an instruction without providing the address of a program.

The larger point here is that there exist InstructionErrors that are more like ‘there was a structural problem with this instruction’ rather than ‘a program died and here's why.’ The latter has a program address associated with it while the former may not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding Option<Pubkey> here means TransactionError is now 64 bytes instead of 32 bytes. I'm not sure how often we move/clone these on the agave side, but this could have some small perf implications. I'm aware of at least a few places we clone these values.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ this is not a comment on why we shouldn't do this - just a note that we should be aware of this size change, and be more aware of where we're cloning these in our processing pipeline so we can avoid performance hits

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels bad. I should probably endeavour to store just the index of the program account in the accounts array, and then make the downstream thing that really wants to know what the program was look it up.

I'll rifle through the Agave code and see if that's tractable (ie. the instruction will need to be available wherever something needs know what program address was involved).


/// Loader call chain is too deep
CallChainTooDeep,
Expand Down Expand Up @@ -163,7 +169,10 @@ impl fmt::Display for TransactionError {
=> f.write_str("This transaction has already been processed"),
Self::BlockhashNotFound
=> f.write_str("Blockhash not found"),
Self::InstructionError(idx, err) => write!(f, "Error processing Instruction {idx}: {err}"),
Self::InstructionError(idx, err, None)
=> write!(f, "Error processing Instruction {idx}: {err}"),
Self::InstructionError(idx, err, Some(program_address))
=> write!(f, "Error processing Instruction {idx}: {err} (from program {program_address})"),
Self::CallChainTooDeep
=> f.write_str("Loader call chain is too deep"),
Self::MissingSignatureForFee
Expand Down
1 change: 1 addition & 0 deletions transaction/src/sanitized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ impl SanitizedTransaction {
TransactionError::InstructionError(
index as u8,
solana_instruction::error::InstructionError::Custom(err as u32),
Some(*program_id),
)
})?;
}
Expand Down
Loading