diff --git a/src/bank.rs b/src/bank.rs index 66776a0caa..c685c15bff 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -19,6 +19,7 @@ use native_loader; use payment_plan::Payment; use poh_recorder::PohRecorder; use poh_service::NUM_TICKS_PER_SECOND; +use program::ProgramError; use rayon::prelude::*; use rpc::RpcSignatureStatus; use signature::Keypair; @@ -86,10 +87,8 @@ pub enum BankError { /// Contract's instruction token balance does not equal the balance after the instruction UnbalancedInstruction(u8), - /// Contract's transactions resulted in an account with a negative balance - /// The difference from InsufficientFundsForFee is that the transaction was executed by the - /// contract - ResultWithNegativeTokens(u8), + /// The program returned an error + ProgramError(u8, ProgramError), /// Contract id is unknown UnknownContractId(u8), @@ -100,9 +99,6 @@ pub enum BankError { /// Contract spent the tokens of an account that doesn't belong to it ExternalAccountTokenSpend(u8), - /// The program returned an error - ProgramRuntimeError(u8), - /// Recoding into PoH failed RecordFailure, @@ -499,9 +495,7 @@ impl Bank { let status = match res[i] { Ok(_) => RpcSignatureStatus::Confirmed, Err(BankError::AccountInUse) => RpcSignatureStatus::AccountInUse, - Err(BankError::ProgramRuntimeError(_)) => { - RpcSignatureStatus::ProgramRuntimeError - } + Err(BankError::ProgramError(_, _)) => RpcSignatureStatus::ProgramRuntimeError, Err(_) => RpcSignatureStatus::GenericFailure, }; if status != RpcSignatureStatus::SignatureNotFound { @@ -823,28 +817,31 @@ impl Bank { { let err = match err { system_program::Error::ResultWithNegativeTokens => { - BankError::ResultWithNegativeTokens(instruction_index as u8) + ProgramError::ResultWithNegativeTokens } - _ => BankError::ProgramRuntimeError(instruction_index as u8), + _ => ProgramError::RuntimeError, }; - return Err(err); + return Err(BankError::ProgramError(instruction_index as u8, err)); } } else if budget_program::check_id(&program_id) { if budget_program::process_instruction(&tx, instruction_index, program_accounts) .is_err() { - return Err(BankError::ProgramRuntimeError(instruction_index as u8)); + let err = ProgramError::RuntimeError; + return Err(BankError::ProgramError(instruction_index as u8, err)); } } else if storage_program::check_id(&program_id) { if storage_program::process_instruction(&tx, instruction_index, program_accounts) .is_err() { - return Err(BankError::ProgramRuntimeError(instruction_index as u8)); + let err = ProgramError::RuntimeError; + return Err(BankError::ProgramError(instruction_index as u8, err)); } } else if vote_program::check_id(&program_id) { if vote_program::process_instruction(&tx, instruction_index, program_accounts).is_err() { - return Err(BankError::ProgramRuntimeError(instruction_index as u8)); + let err = ProgramError::RuntimeError; + return Err(BankError::ProgramError(instruction_index as u8, err)); } } else { let mut accounts = self.load_executable_accounts(tx.program_ids[instruction_index])?; @@ -864,7 +861,8 @@ impl Bank { &tx.instructions[instruction_index].userdata, self.tick_height(), ) { - return Err(BankError::ProgramRuntimeError(instruction_index as u8)); + let err = ProgramError::RuntimeError; + return Err(BankError::ProgramError(instruction_index as u8, err)); } } @@ -1606,13 +1604,22 @@ mod tests { ); let res = bank.process_transactions(&vec![t1.clone()]); assert_eq!(res.len(), 1); - assert_eq!(res[0], Err(BankError::ResultWithNegativeTokens(1))); + assert_eq!( + res[0], + Err(BankError::ProgramError( + 1, + ProgramError::ResultWithNegativeTokens + )) + ); assert_eq!(bank.get_balance(&mint.pubkey()), 1); assert_eq!(bank.get_balance(&key1), 0); assert_eq!(bank.get_balance(&key2), 0); assert_eq!( bank.get_signature(&t1.last_id, &t1.signatures[0]), - Some(Err(BankError::ResultWithNegativeTokens(1))) + Some(Err(BankError::ProgramError( + 1, + ProgramError::ResultWithNegativeTokens + ))) ); } @@ -1667,7 +1674,10 @@ mod tests { assert!(bank.has_signature(&signature)); assert_matches!( bank.get_signature_status(&signature), - Err(BankError::ResultWithNegativeTokens(0)) + Err(BankError::ProgramError( + 0, + ProgramError::ResultWithNegativeTokens + )) ); // The tokens didn't move, but the from address paid the transaction fee. @@ -1700,7 +1710,10 @@ mod tests { assert_eq!(bank.get_balance(&pubkey), 1_000); assert_matches!( bank.transfer(10_001, &mint.keypair(), pubkey, mint.last_id()), - Err(BankError::ResultWithNegativeTokens(0)) + Err(BankError::ProgramError( + 0, + ProgramError::ResultWithNegativeTokens + )) ); assert_eq!(bank.transaction_count(), 1); diff --git a/src/lib.rs b/src/lib.rs index 60ecef26a6..10fc51ad74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ pub mod payment_plan; pub mod poh; pub mod poh_recorder; pub mod poh_service; +pub mod program; pub mod recvmmsg; pub mod replicate_stage; pub mod replicator; diff --git a/src/program.rs b/src/program.rs new file mode 100644 index 0000000000..11961caaf2 --- /dev/null +++ b/src/program.rs @@ -0,0 +1,11 @@ +/// Reasons a program might have rejected an instruction. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ProgramError { + /// Contract's transactions resulted in an account with a negative balance + /// The difference from InsufficientFundsForFee is that the transaction was executed by the + /// contract + ResultWithNegativeTokens, + + /// The program returned an error + RuntimeError, +} diff --git a/src/rpc.rs b/src/rpc.rs index b861a3d492..3f02b07c17 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -191,7 +191,7 @@ impl RpcSol for RpcSolImpl { match meta.request_processor.get_signature_status(signature) { Ok(_) => RpcSignatureStatus::Confirmed, Err(BankError::AccountInUse) => RpcSignatureStatus::AccountInUse, - Err(BankError::ProgramRuntimeError(_)) => RpcSignatureStatus::ProgramRuntimeError, + Err(BankError::ProgramError(_, _)) => RpcSignatureStatus::ProgramRuntimeError, // Report SignatureReserved as SignatureNotFound as SignatureReserved is // transitory while the bank processes the associated transaction. Err(BankError::SignatureReserved) => RpcSignatureStatus::SignatureNotFound,