Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block producer selects da height to never exceed u64::MAX - 1 transactions from L1 #2189

Merged
merged 15 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- [2135](https://github.com/FuelLabs/fuel-core/pull/2135): Added metrics logging for number of blocks served over the p2p req/res protocol.
- [2151](https://github.com/FuelLabs/fuel-core/pull/2151): Added limitations on gas used during dry_run in API.
- [2189](https://github.com/FuelLabs/fuel-core/pull/2151): Select next DA height to never include more than u16::MAX -1 transactions from L1.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to move this into unreleased section because of release=)


### Changed

Expand Down
47 changes: 47 additions & 0 deletions crates/fuel-core/src/service/adapters/producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,53 @@ impl fuel_core_producer::ports::Relayer for MaybeRelayerAdapter {
Ok(0)
}
}

async fn get_transactions_number_for_block(
&self,
height: &DaBlockHeight,
) -> anyhow::Result<u64> {
#[cfg(feature = "relayer")]
{
if let Some(sync) = self.relayer_synced.as_ref() {
get_transactions_number_for_height(**height, sync)
} else {
Ok(0)
}
}
#[cfg(not(feature = "relayer"))]
{
anyhow::ensure!(
**height == 0,
"Cannot have a da height above zero without a relayer"
);
// If the relayer is not enabled, then all blocks are zero.
Ok(0)
}
}
}

#[cfg(feature = "relayer")]
fn get_transactions_number_for_height(
height: u64,
sync: &fuel_core_relayer::SharedState<
crate::database::Database<
crate::database::database_description::relayer::Relayer,
>,
>,
) -> anyhow::Result<u64> {
let da_height = DaBlockHeight(height);
let transactions = sync
.database()
.storage::<fuel_core_relayer::storage::EventsHistory>()
.get(&da_height)?
.unwrap_or_default()
.iter()
.filter(|event| match event {
fuel_core_types::services::relayer::Event::Message(_) => false,
fuel_core_types::services::relayer::Event::Transaction(_) => true,
})
.count();
Ok(transactions as u64)
}

#[cfg(feature = "relayer")]
Expand Down
48 changes: 24 additions & 24 deletions crates/services/executor/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ where
.ok_or(ExecutorError::PreviousBlockIsNotFound)?;
let previous_da_height = prev_block_header.header().da_height;
let Some(next_unprocessed_da_height) = previous_da_height.0.checked_add(1) else {
return Err(ExecutorError::DaHeightExceededItsLimit)
return Err(ExecutorError::DaHeightExceededItsLimit);
};

let mut root_calculator = MerkleRootCalculator::new();
Expand All @@ -851,7 +851,7 @@ where
match event {
Event::Message(message) => {
if message.da_height() != da_height {
return Err(ExecutorError::RelayerGivesIncorrectMessages)
return Err(ExecutorError::RelayerGivesIncorrectMessages);
}
block_storage_tx
.storage_as_mut::<Messages>()
Expand Down Expand Up @@ -1039,7 +1039,7 @@ where

fn check_mint_is_not_found(execution_data: &ExecutionData) -> ExecutorResult<()> {
if execution_data.found_mint {
return Err(ExecutorError::MintIsNotLastTransaction)
return Err(ExecutorError::MintIsNotLastTransaction);
}
Ok(())
}
Expand All @@ -1055,7 +1055,7 @@ where
.storage::<ProcessedTransactions>()
.contains_key(tx_id)?
{
return Err(ExecutorError::TransactionIdCollision(*tx_id))
return Err(ExecutorError::TransactionIdCollision(*tx_id));
}
Ok(())
}
Expand Down Expand Up @@ -1202,14 +1202,14 @@ where

fn check_mint_amount(mint: &Mint, expected_amount: u64) -> ExecutorResult<()> {
if *mint.mint_amount() != expected_amount {
return Err(ExecutorError::CoinbaseAmountMismatch)
return Err(ExecutorError::CoinbaseAmountMismatch);
}
Ok(())
}

fn check_gas_price(mint: &Mint, expected_gas_price: Word) -> ExecutorResult<()> {
if *mint.gas_price() != expected_gas_price {
return Err(ExecutorError::CoinbaseGasPriceMismatch)
return Err(ExecutorError::CoinbaseGasPriceMismatch);
}
Ok(())
}
Expand All @@ -1219,14 +1219,14 @@ where
execution_data: &ExecutionData,
) -> ExecutorResult<()> {
if checked_mint.transaction().tx_pointer().tx_index() != execution_data.tx_count {
return Err(ExecutorError::MintHasUnexpectedIndex)
return Err(ExecutorError::MintHasUnexpectedIndex);
}
Ok(())
}

fn verify_mint_for_empty_contract(mint: &Mint) -> ExecutorResult<()> {
if *mint.mint_amount() != 0 {
return Err(ExecutorError::CoinbaseAmountMismatch)
return Err(ExecutorError::CoinbaseAmountMismatch);
}

let input = input::contract::Contract {
Expand All @@ -1242,7 +1242,7 @@ where
state_root: Bytes32::zeroed(),
};
if mint.input_contract() != &input || mint.output_contract() != &output {
return Err(ExecutorError::MintMismatch)
return Err(ExecutorError::MintMismatch);
}
Ok(())
}
Expand Down Expand Up @@ -1273,7 +1273,7 @@ where
.replace(&coinbase_id, &())?
.is_some()
{
return Err(ExecutorError::TransactionIdCollision(coinbase_id))
return Err(ExecutorError::TransactionIdCollision(coinbase_id));
}
Ok(tx)
}
Expand Down Expand Up @@ -1333,12 +1333,12 @@ where
let Input::Contract(input) = core::mem::take(input) else {
return Err(ExecutorError::Other(
"Input of the `Mint` transaction is not a contract".to_string(),
))
));
};
let Output::Contract(output) = outputs[0] else {
return Err(ExecutorError::Other(
"The output of the `Mint` transaction is not a contract".to_string(),
))
));
};
Ok((input, output))
}
Expand Down Expand Up @@ -1447,7 +1447,7 @@ where
or we added a new predicate inputs.");
return Err(ExecutorError::InvalidTransactionOutcome {
transaction_id: tx_id,
})
});
}
}
}
Expand Down Expand Up @@ -1582,12 +1582,12 @@ where
if !coin.matches_input(input).unwrap_or_default() {
return Err(
TransactionValidityError::CoinMismatch(*utxo_id).into()
)
);
}
} else {
return Err(
TransactionValidityError::CoinDoesNotExist(*utxo_id).into()
)
);
}
}
Input::Contract(contract) => {
Expand All @@ -1598,7 +1598,7 @@ where
return Err(TransactionValidityError::ContractDoesNotExist(
contract.contract_id,
)
.into())
.into());
}
}
Input::MessageCoinSigned(MessageCoinSigned { nonce, .. })
Expand All @@ -1610,18 +1610,18 @@ where
return Err(TransactionValidityError::MessageSpendTooEarly(
*nonce,
)
.into())
.into());
}

if !message.matches_input(input).unwrap_or_default() {
return Err(
TransactionValidityError::MessageMismatch(*nonce).into()
)
);
}
} else {
return Err(
TransactionValidityError::MessageDoesNotExist(*nonce).into()
)
);
}
}
}
Expand Down Expand Up @@ -1679,7 +1679,7 @@ where
if reverted =>
{
// Don't spend the retryable messages if transaction is reverted
continue
continue;
}
Input::MessageCoinSigned(MessageCoinSigned { nonce, .. })
| Input::MessageCoinPredicate(MessageCoinPredicate { nonce, .. })
Expand Down Expand Up @@ -1716,7 +1716,7 @@ where
for r in receipts {
if let Receipt::ScriptResult { gas_used, .. } = r {
used_gas = *gas_used;
break
break;
}
}

Expand Down Expand Up @@ -1818,7 +1818,7 @@ where
} else {
return Err(ExecutorError::InvalidTransactionOutcome {
transaction_id: tx_id,
})
});
};

let contract = ContractRef::new(db, *contract_id);
Expand Down Expand Up @@ -1938,7 +1938,7 @@ where
} else {
return Err(ExecutorError::TransactionValidity(
TransactionValidityError::InvalidContractInputIndex(utxo_id),
))
));
}
}
Output::Change {
Expand Down Expand Up @@ -2004,7 +2004,7 @@ where
.into();

if db.storage::<Coins>().replace(&utxo_id, &coin)?.is_some() {
return Err(ExecutorError::OutputAlreadyExists)
return Err(ExecutorError::OutputAlreadyExists);
}
execution_data
.events
Expand Down
19 changes: 17 additions & 2 deletions crates/services/producer/src/block_producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,10 @@ where
.consensus_parameters_provider
.consensus_params_at_version(&block_header.consensus_parameters_version)?
.block_gas_limit();
// We have a hard limit of u16::MAX transactions per block, including the final mint transactions.
// Therefore we choose the `new_da_height` to never include more than u16::MAX - 1 transactions in a block.
let new_da_height = self
.select_new_da_height(gas_limit, previous_da_height)
.select_new_da_height(gas_limit, previous_da_height, u16::MAX - 1)
xgreenx marked this conversation as resolved.
Show resolved Hide resolved
.await?;

block_header.application.da_height = new_da_height;
Expand All @@ -375,9 +377,12 @@ where
&self,
gas_limit: u64,
previous_da_height: DaBlockHeight,
transactions_limit: u16,
rafal-ch marked this conversation as resolved.
Show resolved Hide resolved
) -> anyhow::Result<DaBlockHeight> {
let mut new_best = previous_da_height;
let mut total_cost: u64 = 0;
let transactions_limit: u64 = transactions_limit as u64;
let mut total_transactions: u64 = 0;
let highest = self
.relayer
.wait_for_at_least_height(&previous_da_height)
Expand All @@ -404,7 +409,17 @@ where
if total_cost > gas_limit {
break;
} else {
new_best = DaBlockHeight(height);
let transactions_number = self
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_cost_for_block and get_transactions_number_for_block do the same lookup into the database. I think it will be better to have one function that returns both values for performance reasons.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I have changed it in 5b48295 and changed all the adapters implementations accordingly

.relayer
.get_transactions_number_for_block(&DaBlockHeight(height))
.await?;
total_transactions =
total_transactions.saturating_add(transactions_number);
if total_transactions > transactions_limit {
break;
} else {
new_best = DaBlockHeight(height);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we can simplify it to be

if gas_check {
    break;
}

if tx_number_check {
    break;
}

new_best = DaBlockHeight(height);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, simplified it a bit more in 6b5008d

}
if new_best == previous_da_height {
Expand Down
Loading
Loading