#42761 [BC-High] Memseq does not verify client-specified expiration for transactions before including them in DA (Data Availability).
Submitted on Mar 26th 2025 at 01:43:24 UTC by @ZeroTrust for Attackathon | Movement Labs
Report ID: #42761
Report Type: Blockchain/DLT
Report severity: High
Target: https://github.com/immunefi-team/attackathon-movement/tree/main/protocol-units/sequencing/memseq/sequencer
Impacts:
Direct loss of funds
Description
Brief/Intro
Movement uses Memseq as its MemoryPool. Once transactions are added to Memseq, there is no mechanism to remove those with client-specified expiration.
Furthermore, when transactions in Memseq are packed into DA (Data Availability), client-specified expiration is not checked. This could result in the execution of expired transactions, potentially leading to user fund losses.
Vulnerability Details
Memseq::wait_for_next_block() is utilized to fetch transactions from the mempool for inclusion into a block.
/// Waits for the next block to be built, either when the block size is reached or the building time expires.
async fn wait_for_next_block(&self) -> Result<Option<Block>, anyhow::Error> {
let mut transactions = Vec::with_capacity(self.block_size as usize);
let now = Instant::now();
loop {
let current_block_size = transactions.len() as u32;
if current_block_size >= self.block_size {
break;
}
let remaining = self.block_size - current_block_size;
@> let mut transactions_to_add = self.mempool.pop_transactions(remaining as usize).await?;
transactions.append(&mut transactions_to_add);
// sleep to yield to other tasks and wait for more transactions
tokio::task::yield_now().await;
if now.elapsed().as_millis() as u64 > self.building_time_ms {
break;
}
}
if transactions.is_empty() {
Ok(None)
} else {
let new_block =
self.build_next_block(block::BlockMetadata::default(), transactions).await?;
Ok(Some(new_block))
}
}As shown, Memseq::wait_for_next_block() only removes transactions packed into DA from the mempool via mempool.pop_transactions() , without clearing transactions with client-specified expiration from the mempool .
Transactions in the Mempool have two types of expirations: systemTTL and client-specified expiration . As seen in the above function, the system only processes transactions with systemTTL expiration via the task mechanism— without any mechanism to remove transactions with client-specified expiration . More critically, client-specified expiration is not checked when these transactions are later packed into DA (Data Availability) or executed by the executor. As a result, expired transactions may still be executed, potentially leading to financial losses for users .
Impact Details
Transactions with a client-specified expiration will be executed, but some of the user's transactions are time-sensitive, such as Trade Transactions. If these transactions are executed beyond the user-specified time, it may result in financial losses for the user.
References
https://github.com/immunefi-team/attackathon-movement/blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/protocol-units/da/movement/protocol/light-node/src/sequencer.rs#L142 https://github.com/immunefi-team/attackathon-movement/blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/protocol-units/da/movement/protocol/light-node/src/sequencer.rs#L264 https://github.com/immunefi-team/attackathon-movement/blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/protocol-units/da/movement/protocol/light-node/src/sequencer.rs#L119 https://github.com/immunefi-team/attackathon-movement/blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/protocol-units/sequencing/memseq/sequencer/src/lib.rs#L101 https://github.com/immunefi-team/attackathon-movement/blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/protocol-units/sequencing/memseq/sequencer/src/lib.rs#L133
Proof of Concept
Proof of Concept
Let’s assume the following user transaction submission process to examine the issue in this scenario:
A user submits a transaction with a client-specified expiration time of 3 seconds and sets the gas fee to the system’s minimum required value.
The transaction passes all validation checks and is added to the Memseq (memory sequence pool).
Since the transaction has low priority, higher-priority transactions are packaged into DA (Data Availability) first and proceed to the executor for execution.
After several blocks (e.g., 10 seconds later), the transaction from step (1) is finally included in a block. Since there is no timestamp check, even though 10 seconds > 3 seconds, the transaction is still incorrectly included in DA.
The transaction is then executed by the executor.
As a result, transactions with client-specified expiration times may still be executed even after their intended deadline. Some user transactions, such as trade transactions, are time-sensitive. If they are executed beyond the user-specified expiration time, it could lead to financial losses for the user.
Proof of Code
As demonstrated in the test code, timeout transactions can be committed and executed
Test 1:
Add the test code to: networks/movement/movement-client/src/bin/e2e/time_out.rs
networks/movement/movement-client/Cargo.toml
Run tests:
Test 2:
Modify protocol-units/execution/maptos/opt-executor/src/executor/execution.rs Test function test_execute_block_state_db:
test_execute_block_state_db_timeout:
Was this helpful?