Limit amount of withdraw operations in state keeper
@@ -24,6 +24,13 @@ use models::{
use plasma::state::{OpSuccess, PlasmaState};
use storage::ConnectionPool;
+/// Since withdraw is an expensive operation, we have to limit amount of
+/// withdrawals in one block to not exceed the gas limit in prover.
+/// 10 is a safe value which won't cause any problems.
+/// If this threshold is reached, block will be immediately sealed and
+/// the remaining withdrawals will go to the next block.
+pub const MAX_WITHDRAWALS_PER_BLOCK: u32 = 10;
+
pub enum ExecutedOpId {
Transaction(TxHash),
PriorityOp(u64),
@@ -51,6 +58,7 @@ struct PendingBlock {
pending_op_block_index: u32,
unprocessed_priority_op_before: u64,
pending_block_iteration: usize,
+ withdrawals_amount: u32,
}
impl PendingBlock {
@@ -63,6 +71,7 @@ impl PendingBlock {
pending_op_block_index: 0,
unprocessed_priority_op_before,
pending_block_iteration: 0,
+ withdrawals_amount: 0,
}
}
}
@@ -395,6 +404,9 @@ impl PlasmaStateKeeper {
executed_ops.push(exec_op);
}
Err(tx) => {
+ // We could not execute the tx due to either of block size limit
+ // or the withdraw operations limit, so we seal this block and
+ // the last transaction will go to the next block instead.
self.seal_pending_block().await;
self.notify_executed_ops(&mut executed_ops).await;
@@ -458,10 +470,24 @@ impl PlasmaStateKeeper {
fn apply_tx(&mut self, tx: FranklinTx) -> Result<ExecutedOperations, FranklinTx> {
let chunks_needed = self.state.chunks_for_tx(&tx);
+
+ // If we can't add the tx to the block due to the size limit, we return this tx,
+ // seal the block and execute it again.
if self.pending_block.chunks_left < chunks_needed {
return Err(tx);
}
+ if matches!(tx, FranklinTx::Withdraw(_)) {
+ // Increase amount of the withdraw operations in this block.
+ self.pending_block.withdrawals_amount += 1;
+ }
+
+ // Check if we've reached the withdraw operations amount limit.
+ // If so, this block will be sealed and this tx will go to the next block.
+ if self.pending_block.withdrawals_amount > MAX_WITHDRAWALS_PER_BLOCK {
+ return Err(tx);
+ }
+
let tx_updates = self.state.execute_tx(tx.clone());
let exec_result = match tx_updates {
