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
10 changes: 10 additions & 0 deletions src/tx_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub enum PoolError {
InvalidNonce(u64, u64),
#[error("Storage Error: {0}")]
StorageError(String),
#[error("Gas Limit Exceeded: max {0}, got {1}")]
GasLimitExceeded(u64, u64),
}

/// A simple Transaction Pool (Mempool).
Expand All @@ -41,6 +43,14 @@ impl TxPool {

/// Add a transaction to the pool.
pub fn add_transaction(&self, tx: Transaction) -> Result<(), PoolError> {
// 0. Check Gas Limit (Fusaka)
if tx.gas_limit > crate::types::MAX_TX_GAS_LIMIT {
return Err(PoolError::GasLimitExceeded(
crate::types::MAX_TX_GAS_LIMIT,
tx.gas_limit,
));
}

// 1. Validate Signature
let sighash = tx.sighash();
if !verify(&tx.public_key, &sighash.0, &tx.signature) {
Expand Down
1 change: 1 addition & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
pub type View = u64;

pub const DEFAULT_BLOCK_GAS_LIMIT: u64 = 30_000_000;
pub const MAX_TX_GAS_LIMIT: u64 = 16_777_216; // 2^24 (Fusaka EIP-7825)
pub const INITIAL_BASE_FEE: u64 = 10_000_000; // 0.01 Gwei

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
Expand Down
5 changes: 5 additions & 0 deletions src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ impl Executor {
self.process_liveness_slashing(block, &mut db);

for tx in &block.payload {
if tx.gas_limit > crate::types::MAX_TX_GAS_LIMIT {
return Err(ExecutionError::Transaction(
"Tx exceeds fixed tx gas limit (Fusaka)".into(),
));
}
if tx.gas_limit > self.block_gas_limit {
return Err(ExecutionError::Transaction(
"Tx exceeds block gas limit".into(),
Expand Down
113 changes: 113 additions & 0 deletions tests/gas_limit_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use ockham::crypto::{Hash, generate_keypair, sign};
use ockham::state::StateManager;
use ockham::storage::{AccountInfo, MemStorage, Storage};
use ockham::tx_pool::{PoolError, TxPool};
use ockham::types::{
Address, Block, Bytes, MAX_TX_GAS_LIMIT, QuorumCertificate, Transaction, U256,
};
use ockham::vm::{ExecutionError, Executor};
use std::sync::{Arc, Mutex};

#[test]
fn test_transaction_gas_limit() {
// Generate keys
let (pk, sk) = generate_keypair();

let storage = Arc::new(MemStorage::new());
{
// Fund the sender
let addr = ockham::types::keccak256(pk.0.to_bytes());
let address = Address::from_slice(&addr[12..]);
let account = AccountInfo {
nonce: 0,
balance: U256::from(10_000_000_000_000_000_000u64), // 10 ETH
code_hash: Hash(ockham::types::keccak256(&[]).0), // Empty Code Hash
code: None,
};
storage.save_account(&address, &account).unwrap();
}

let state = Arc::new(Mutex::new(StateManager::new(storage, None)));
// Block limit 30M, Tx limit 16.7M
let executor = Executor::new(state, 30_000_000);

// 1. Valid Tx (Below Limit)
let mut tx_ok = Transaction {
chain_id: 1,
nonce: 0,
max_priority_fee_per_gas: U256::from(1_000_000_000u64), // 1 Gwei
max_fee_per_gas: U256::from(10_000_000_000u64), // 10 Gwei
gas_limit: MAX_TX_GAS_LIMIT, // Borderline OK
to: None,
value: U256::ZERO,
data: Bytes::new(),
access_list: vec![],
public_key: pk.clone(),
signature: ockham::crypto::Signature::default(),
};
tx_ok.signature = sign(&sk, &tx_ok.sighash().0);

let mut block = Block::new_dummy(pk.clone(), 1, Hash::default(), QuorumCertificate::default());
block.base_fee_per_gas = U256::from(10_000_000); // 0.01 Gwei
block.payload = vec![tx_ok];

if let Err(e) = executor.execute_block(&mut block) {
panic!("Valid Transaction Failed: {:?}", e);
}

// 2. Invalid Tx (Above Limit)
let mut tx_bad = Transaction {
chain_id: 1,
nonce: 1,
max_priority_fee_per_gas: U256::ZERO,
max_fee_per_gas: U256::ZERO,
gas_limit: MAX_TX_GAS_LIMIT + 1, // Exceeds
to: None,
value: U256::ZERO,
data: Bytes::new(),
access_list: vec![],
public_key: pk.clone(),
signature: ockham::crypto::Signature::default(),
};
tx_bad.signature = sign(&sk, &tx_bad.sighash().0);

let mut block_bad =
Block::new_dummy(pk.clone(), 2, Hash::default(), QuorumCertificate::default());
block_bad.payload = vec![tx_bad];

let res = executor.execute_block(&mut block_bad);
match res {
Err(ExecutionError::Transaction(msg)) => {
assert!(msg.contains("Fusaka"));
}
_ => panic!(
"Expected Transaction Error with Fusaka message, got {:?}",
res
),
}
}

#[test]
fn test_pool_gas_limit() {
let storage = Arc::new(MemStorage::new());
let pool = TxPool::new(storage);
let (pk, sk) = generate_keypair();

let mut tx = Transaction {
chain_id: 1,
nonce: 0,
max_priority_fee_per_gas: U256::ZERO,
max_fee_per_gas: U256::ZERO,
gas_limit: MAX_TX_GAS_LIMIT + 1, // Exceeds
to: None,
value: U256::ZERO,
data: Bytes::new(),
access_list: vec![],
public_key: pk.clone(),
signature: ockham::crypto::Signature::default(),
};
tx.signature = sign(&sk, &tx.sighash().0);

let res = pool.add_transaction(tx);
assert!(matches!(res, Err(PoolError::GasLimitExceeded(..))));
}
5 changes: 2 additions & 3 deletions tests/slashing_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use ockham::consensus::{ConsensusAction, SimplexState};
use ockham::crypto::{Hash, PrivateKey, PublicKey};
use ockham::storage::Storage;
use ockham::types::{Block, QuorumCertificate, U256, Vote, VoteType};
use revm::Database;
use std::sync::Arc;
use std::sync::Mutex;

Expand Down Expand Up @@ -60,7 +59,7 @@ fn test_slashing_flow() {

// Initialize Stakes for Offender
{
let mut db = state_manager.lock().unwrap();
let db = state_manager.lock().unwrap();
let mut state = db.get_consensus_state().unwrap().unwrap();
state.stakes.insert(offender_addr, U256::from(5000u64));
db.save_consensus_state(&state).unwrap();
Expand Down Expand Up @@ -177,7 +176,7 @@ fn test_slashing_flow() {
executor.execute_block(&mut block_to_exec).unwrap();

// Check Stake
let mut db = validator.executor.state.lock().unwrap();
let db = validator.executor.state.lock().unwrap();
let state = db.get_consensus_state().unwrap().unwrap();
let stake = state.stakes.get(&offender_addr).unwrap();

Expand Down