mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
add error handling
This commit is contained in:
parent
04f6474799
commit
55e241dc97
@ -17,7 +17,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0"
|
||||
anyhow = "1.0.98"
|
||||
num_cpus = "1.13.1"
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
openssl-probe = { version = "0.1.2" }
|
||||
@ -30,7 +30,7 @@ lazy_static = "1.5.0"
|
||||
env_logger = "0.10"
|
||||
log = "0.4"
|
||||
lru = "0.7.8"
|
||||
thiserror = "1.0"
|
||||
thiserror = "2.0.12"
|
||||
rs_merkle = "1.4"
|
||||
sha2 = "0.10.8"
|
||||
hex = "0.4.3"
|
||||
|
||||
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "2.0.12"
|
||||
risc0-zkvm = "2.3.1"
|
||||
nssa-core = { path = "core"}
|
||||
program-methods = { path = "program_methods" }
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
use crate::account::{Account, AccountWithMetadata};
|
||||
|
||||
pub type ProgramId = [u32; 8];
|
||||
@ -19,26 +18,26 @@ pub fn validate_constraints(
|
||||
pre_states: &[AccountWithMetadata],
|
||||
post_states: &[Account],
|
||||
executing_program_id: ProgramId,
|
||||
) -> Result<(), ()> {
|
||||
) -> bool {
|
||||
// 1. Lengths must match
|
||||
if pre_states.len() != post_states.len() {
|
||||
return Err(());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (pre, post) in pre_states.iter().zip(post_states) {
|
||||
// 2. Nonce must remain unchanged
|
||||
if pre.account.nonce != post.nonce {
|
||||
return Err(());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Ownership change only allowed from default accounts
|
||||
if pre.account.program_owner != post.program_owner && pre.account != Account::default() {
|
||||
return Err(());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Decreasing balance only allowed if owned by executing program
|
||||
if post.balance < pre.account.balance && pre.account.program_owner != executing_program_id {
|
||||
return Err(());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. Data changes only allowed if owned by executing program
|
||||
@ -46,7 +45,7 @@ pub fn validate_constraints(
|
||||
&& (executing_program_id != pre.account.program_owner
|
||||
|| executing_program_id != post.program_owner)
|
||||
{
|
||||
return Err(());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,8 +53,8 @@ pub fn validate_constraints(
|
||||
let total_balance_pre_states: u128 = pre_states.iter().map(|pre| pre.account.balance).sum();
|
||||
let total_balance_post_states: u128 = post_states.iter().map(|post| post.balance).sum();
|
||||
if total_balance_pre_states != total_balance_post_states {
|
||||
return Err(());
|
||||
return false;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
true
|
||||
}
|
||||
|
||||
16
nssa/src/error.rs
Normal file
16
nssa/src/error.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NssaError {
|
||||
#[error("Invalid input: {0}")]
|
||||
InvalidInput(String),
|
||||
|
||||
#[error("Operation failed")]
|
||||
OperationFailed,
|
||||
|
||||
#[error("Risc0 error: {0}")]
|
||||
ProgramExecutionFailed(String),
|
||||
|
||||
#[error("Program violated execution rules")]
|
||||
InvalidProgramBehavior,
|
||||
}
|
||||
@ -6,6 +6,7 @@ use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID};
|
||||
use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor};
|
||||
|
||||
mod address;
|
||||
pub mod error;
|
||||
pub mod public_transaction;
|
||||
mod signature;
|
||||
mod state;
|
||||
@ -18,6 +19,8 @@ pub use signature::PublicKey;
|
||||
pub use signature::Signature;
|
||||
pub use state::V01State;
|
||||
|
||||
use crate::error::NssaError;
|
||||
|
||||
pub const AUTHENTICATED_TRANSFER_PROGRAM: Program = Program {
|
||||
id: AUTHENTICATED_TRANSFER_ID,
|
||||
elf: AUTHENTICATED_TRANSFER_ELF,
|
||||
@ -28,10 +31,14 @@ fn write_inputs(
|
||||
pre_states: &[AccountWithMetadata],
|
||||
instruction_data: u128,
|
||||
env_builder: &mut ExecutorEnvBuilder,
|
||||
) -> Result<(), ()> {
|
||||
) -> Result<(), NssaError> {
|
||||
let pre_states = pre_states.to_vec();
|
||||
env_builder.write(&pre_states).map_err(|_| ())?;
|
||||
env_builder.write(&instruction_data).map_err(|_| ())?;
|
||||
env_builder
|
||||
.write(&pre_states)
|
||||
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
|
||||
env_builder
|
||||
.write(&instruction_data)
|
||||
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -39,7 +46,7 @@ fn execute_public(
|
||||
pre_states: &[AccountWithMetadata],
|
||||
instruction_data: u128,
|
||||
program: &Program,
|
||||
) -> Result<Vec<Account>, ()> {
|
||||
) -> Result<Vec<Account>, NssaError> {
|
||||
// Write inputs to the program
|
||||
let mut env_builder = ExecutorEnv::builder();
|
||||
write_inputs(pre_states, instruction_data, &mut env_builder)?;
|
||||
@ -47,10 +54,15 @@ fn execute_public(
|
||||
|
||||
// Execute the program (without proving)
|
||||
let executor = default_executor();
|
||||
let session_info = executor.execute(env, program.elf).map_err(|_| ())?;
|
||||
let session_info = executor
|
||||
.execute(env, program.elf)
|
||||
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
|
||||
|
||||
// Get outputs
|
||||
let mut post_states: Vec<Account> = session_info.journal.decode().map_err(|_| ())?;
|
||||
let mut post_states: Vec<Account> = session_info
|
||||
.journal
|
||||
.decode()
|
||||
.map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?;
|
||||
|
||||
// Claim any output account with default program owner field
|
||||
for account in post_states.iter_mut() {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
AUTHENTICATED_TRANSFER_PROGRAM, address::Address, execute_public,
|
||||
AUTHENTICATED_TRANSFER_PROGRAM, address::Address, error::NssaError, execute_public,
|
||||
public_transaction::PublicTransaction,
|
||||
};
|
||||
use nssa_core::{
|
||||
@ -40,10 +40,11 @@ impl V01State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transition_from_public_transaction(&mut self, tx: &PublicTransaction) -> Result<(), ()> {
|
||||
let state_diff = self
|
||||
.execute_and_verify_public_transaction(tx)
|
||||
.map_err(|_| ())?;
|
||||
pub fn transition_from_public_transaction(
|
||||
&mut self,
|
||||
tx: &PublicTransaction,
|
||||
) -> Result<(), NssaError> {
|
||||
let state_diff = self.execute_and_verify_public_transaction(tx)?;
|
||||
|
||||
for (address, post) in state_diff.into_iter() {
|
||||
let current_account = self.get_account_by_address_mut(address);
|
||||
@ -71,17 +72,21 @@ impl V01State {
|
||||
fn execute_and_verify_public_transaction(
|
||||
&mut self,
|
||||
tx: &PublicTransaction,
|
||||
) -> Result<HashMap<Address, Account>, ()> {
|
||||
) -> Result<HashMap<Address, Account>, NssaError> {
|
||||
let message = tx.message();
|
||||
let witness_set = tx.witness_set();
|
||||
|
||||
// All addresses must be different
|
||||
if message.addresses.iter().collect::<HashSet<_>>().len() != message.addresses.len() {
|
||||
return Err(());
|
||||
return Err(NssaError::InvalidInput(
|
||||
"Duplicate addresses found in message".into(),
|
||||
));
|
||||
}
|
||||
|
||||
if message.nonces.len() != witness_set.signatures_and_public_keys.len() {
|
||||
return Err(());
|
||||
return Err(NssaError::InvalidInput(
|
||||
"Mismatch between number of nonces and signatures/public keys".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut authorized_addresses = Vec::new();
|
||||
@ -92,14 +97,16 @@ impl V01State {
|
||||
{
|
||||
// Check the signature is valid
|
||||
if !signature.is_valid_for(message, public_key) {
|
||||
return Err(());
|
||||
return Err(NssaError::InvalidInput(
|
||||
"Invalid signature for given message and public key".into(),
|
||||
));
|
||||
}
|
||||
|
||||
// Check the nonce corresponds to the current nonce on the public state.
|
||||
let address = Address::from_public_key(public_key);
|
||||
let current_nonce = self.get_account_by_address(&address).nonce;
|
||||
if current_nonce != *nonce {
|
||||
return Err(());
|
||||
return Err(NssaError::InvalidInput("Nonce mismatch".into()));
|
||||
}
|
||||
|
||||
authorized_addresses.push(address);
|
||||
@ -118,19 +125,16 @@ impl V01State {
|
||||
// Check the `program_id` corresponds to a built-in program
|
||||
// Only allowed program so far is the authenticated transfer program
|
||||
let Some(program) = self.builtin_programs.get(&message.program_id) else {
|
||||
return Err(());
|
||||
return Err(NssaError::InvalidInput("Unknown program".into()));
|
||||
};
|
||||
|
||||
// // Execute program
|
||||
let post_states =
|
||||
execute_public(&pre_states, message.instruction_data, program).map_err(|_| ())?;
|
||||
let post_states = execute_public(&pre_states, message.instruction_data, program)?;
|
||||
|
||||
// Verify execution corresponds to a well-behaved program.
|
||||
// See the # Programs section for the definition of the `validate_constraints` method.
|
||||
validate_constraints(&pre_states, &post_states, message.program_id).map_err(|_| ())?;
|
||||
|
||||
if post_states.len() != message.addresses.len() {
|
||||
return Err(());
|
||||
if !validate_constraints(&pre_states, &post_states, message.program_id) {
|
||||
return Err(NssaError::InvalidProgramBehavior);
|
||||
}
|
||||
|
||||
Ok(message.addresses.iter().cloned().zip(post_states).collect())
|
||||
|
||||
@ -94,7 +94,7 @@ impl SequencerCore {
|
||||
fn execute_check_transaction_on_state(
|
||||
&mut self,
|
||||
tx: nssa::PublicTransaction,
|
||||
) -> Result<nssa::PublicTransaction, ()> {
|
||||
) -> Result<nssa::PublicTransaction, nssa::error::NssaError> {
|
||||
self.store.state.transition_from_public_transaction(&tx)?;
|
||||
|
||||
Ok(tx)
|
||||
@ -405,7 +405,7 @@ mod tests {
|
||||
// Signature is not from sender. Execution fails
|
||||
let result = sequencer.execute_check_transaction_on_state(tx);
|
||||
|
||||
assert!(matches!(result, Err(())));
|
||||
assert!(matches!(result, Err(nssa::error::NssaError::ProgramExecutionFailed(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -438,8 +438,7 @@ mod tests {
|
||||
let result = sequencer.execute_check_transaction_on_state(result.unwrap());
|
||||
let is_failed_at_balance_mismatch = matches!(
|
||||
result.err().unwrap(),
|
||||
// TransactionMalformationErrorKind::BalanceMismatch { tx: _ }
|
||||
()
|
||||
nssa::error::NssaError::ProgramExecutionFailed(_)
|
||||
);
|
||||
|
||||
assert!(is_failed_at_balance_mismatch);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user