Skip to content

Commit

Permalink
Regenesis of SpentMessages and ProcessedTransactions (#1833)
Browse files Browse the repository at this point in the history
Re-implementation of the #1829
Work towards #1790

The change migrates the `ProcessedTransactions` table as part of the
on-chain snapshot. Since it is used to validate blocks, the root of all
processed transactions must be part of the `Genesis` structure along
with messages, coins, and contracts.

The changes also moved `SpentMessages` from the on-chain database to the
off-chain database since now it is only used for the GraphQL API.

### Before requesting review
- [x] I have reviewed the code myself

---------

Co-authored-by: Hannes Karppila <hannes.karppila@gmail.com>
  • Loading branch information
xgreenx and Dentosal authored Apr 18, 2024
1 parent cbcf170 commit 3b862bf
Show file tree
Hide file tree
Showing 47 changed files with 490 additions and 221 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Description of the upcoming release here.

### Changed

- [#1833](https://github.com/FuelLabs/fuel-core/pull/1833): Regenesis of `SpentMessages` and `ProcessedTransactions`.
- [#1830](https://github.com/FuelLabs/fuel-core/pull/1830): Use versioning enum for WASM executor input and output.
- [#1816](https://github.com/FuelLabs/fuel-core/pull/1816): Updated the upgradable executor to fetch the state transition bytecode from the database when the version doesn't match a native one. This change enables the WASM executor in the "production" build and requires a `wasm32-unknown-unknown` target.
- [#1812](https://github.com/FuelLabs/fuel-core/pull/1812): Follow-up PR to simplify the logic around parallel snapshot creation.
Expand Down
Binary file modified bin/fuel-core/chainspec/dev-testnet/state_transition_bytecode.wasm
Binary file not shown.
Binary file modified bin/fuel-core/chainspec/testnet/state_transition_bytecode.wasm
Binary file not shown.
3 changes: 2 additions & 1 deletion ci_checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ cargo check -p fuel-core-storage --target wasm32-unknown-unknown --no-default-fe
cargo check -p fuel-core-client --target wasm32-unknown-unknown --no-default-features &&
cargo check -p fuel-core-chain-config --target wasm32-unknown-unknown --no-default-features &&
cargo check -p fuel-core-executor --target wasm32-unknown-unknown --no-default-features &&
OVERRIDE_CHAIN_CONFIGS=true cargo test --workspace &&
OVERRIDE_CHAIN_CONFIGS=true cargo test --test integration_tests deployment &&
cargo test --workspace &&
FUEL_ALWAYS_USE_WASM=true cargo test --all-features --workspace &&
cargo test -p fuel-core --no-default-features &&
cargo test -p fuel-core-client --no-default-features &&
Expand Down
11 changes: 11 additions & 0 deletions crates/chain-config/src/config/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use fuel_core_storage::{
ContractsState,
FuelBlocks,
Messages,
ProcessedTransactions,
SealedBlockConsensus,
Transactions,
},
Expand Down Expand Up @@ -433,6 +434,16 @@ impl AddTable<SealedBlockConsensus> for StateConfigBuilder {
fn add(&mut self, _entries: Vec<TableEntry<SealedBlockConsensus>>) {}
}

impl AddTable<ProcessedTransactions> for StateConfigBuilder {
fn add(&mut self, _: Vec<TableEntry<ProcessedTransactions>>) {}
}

impl AsTable<ProcessedTransactions> for StateConfig {
fn as_table(&self) -> Vec<TableEntry<ProcessedTransactions>> {
Vec::new() // Do not include these for now
}
}

impl StateConfig {
pub fn sorted(mut self) -> Self {
self.coins = self
Expand Down
1 change: 1 addition & 0 deletions crates/chain-config/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ impl GenesisCommitment for Genesis {
.chain(self.coins_root)
.chain(self.contracts_root)
.chain(self.messages_root)
.chain(self.transactions_root)
.finalize();

Ok(genesis_hash)
Expand Down
4 changes: 4 additions & 0 deletions crates/client/assets/schema.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ type Genesis {
The Binary Merkle Tree root of all genesis messages.
"""
messagesRoot: Bytes32!
"""
The Binary Merkle Tree root of all processed transaction ids.
"""
transactionsRoot: Bytes32!
}

type Header {
Expand Down
1 change: 1 addition & 0 deletions crates/client/src/client/schema/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ pub struct Genesis {
pub coins_root: Bytes32,
pub contracts_root: Bytes32,
pub messages_root: Bytes32,
pub transactions_root: Bytes32,
}

#[derive(cynic::QueryFragment, Clone, Debug)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ query($height: U32) {
coinsRoot
contractsRoot
messagesRoot
transactionsRoot
}
... on PoAConsensus {
signature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ query($id: BlockId) {
coinsRoot
contractsRoot
messagesRoot
transactionsRoot
}
... on PoAConsensus {
signature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ query($after: String, $before: String, $first: Int, $last: Int) {
coinsRoot
contractsRoot
messagesRoot
transactionsRoot
}
... on PoAConsensus {
signature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ query {
coinsRoot
contractsRoot
messagesRoot
transactionsRoot
}
... on PoAConsensus {
signature
Expand Down
2 changes: 2 additions & 0 deletions crates/client/src/client/types/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub struct Genesis {
pub coins_root: MerkleRoot,
pub contracts_root: MerkleRoot,
pub messages_root: MerkleRoot,
pub transactions_root: MerkleRoot,
}

#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -109,6 +110,7 @@ impl From<schema::block::Genesis> for Genesis {
coins_root: value.coins_root.into(),
contracts_root: value.contracts_root.into(),
messages_root: value.messages_root.into(),
transactions_root: value.transactions_root.into(),
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions crates/fuel-core/src/database/genesis_progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use fuel_core_storage::{
Coins,
ContractsLatestUtxo,
Messages,
ProcessedTransactions,
},
Error as StorageError,
Mappable,
Expand Down Expand Up @@ -138,4 +139,16 @@ impl Database {

Ok(root_calculator.root())
}

pub fn processed_transactions_root(&self) -> Result<MerkleRoot> {
let txs = self.iter_all::<ProcessedTransactions>(None);

let mut root_calculator = MerkleRootCalculator::new();
for tx in txs {
let (tx_id, _) = tx?;
root_calculator.push(tx_id.as_slice());
}

Ok(root_calculator.root())
}
}
27 changes: 6 additions & 21 deletions crates/fuel-core/src/database/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
fuel_core_graphql_api::storage::messages::{
OwnedMessageIds,
OwnedMessageKey,
SpentMessages,
},
};
use fuel_core_chain_config::TableEntry;
Expand All @@ -14,11 +15,7 @@ use fuel_core_storage::{
IterDirection,
IteratorOverTable,
},
tables::{
Messages,
SpentMessages,
},
Error as StorageError,
tables::Messages,
Result as StorageResult,
};
use fuel_core_types::{
Expand Down Expand Up @@ -46,6 +43,10 @@ impl Database<OffChain> {
)
.map(|res| res.map(|(key, _)| *key.nonce()))
}

pub fn message_is_spent(&self, id: &Nonce) -> StorageResult<bool> {
fuel_core_storage::StorageAsRef::storage::<SpentMessages>(&self).contains_key(id)
}
}

impl Database {
Expand All @@ -62,25 +63,9 @@ impl Database {
&self,
) -> impl Iterator<Item = StorageResult<TableEntry<Messages>>> + '_ {
self.iter_all_by_start::<Messages>(None, None)
.filter_map(|msg| {
// Return only unspent messages
if let Ok(msg) = msg {
match self.message_is_spent(msg.1.id()) {
Ok(false) => Some(Ok(msg)),
Ok(true) => None,
Err(e) => Some(Err(e)),
}
} else {
Some(msg.map_err(StorageError::from))
}
})
.map_ok(|(key, value)| TableEntry { key, value })
}

pub fn message_is_spent(&self, id: &Nonce) -> StorageResult<bool> {
fuel_core_storage::StorageAsRef::storage::<SpentMessages>(&self).contains_key(id)
}

pub fn message_exists(&self, id: &Nonce) -> StorageResult<bool> {
fuel_core_storage::StorageAsRef::storage::<Messages>(&self).contains_key(id)
}
Expand Down
29 changes: 12 additions & 17 deletions crates/fuel-core/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2373,8 +2373,8 @@ mod tests {

let mut exec = make_executor(&messages);
let view = exec.storage_view_provider.latest_view();
assert!(!view.message_is_spent(message_coin.nonce()).unwrap());
assert!(!view.message_is_spent(message_data.nonce()).unwrap());
assert!(view.message_exists(message_coin.nonce()).unwrap());
assert!(view.message_exists(message_data.nonce()).unwrap());

let ExecutionResult {
skipped_transactions,
Expand All @@ -2386,8 +2386,8 @@ mod tests {

// Successful execution consumes `message_coin` and `message_data`.
let view = exec.storage_view_provider.latest_view();
assert!(view.message_is_spent(message_coin.nonce()).unwrap());
assert!(view.message_is_spent(message_data.nonce()).unwrap());
assert!(!view.message_exists(message_coin.nonce()).unwrap());
assert!(!view.message_exists(message_data.nonce()).unwrap());
assert_eq!(
*view.coin(&UtxoId::new(tx_id, 0)).unwrap().amount(),
amount + amount
Expand Down Expand Up @@ -2422,8 +2422,8 @@ mod tests {

let mut exec = make_executor(&messages);
let view = exec.storage_view_provider.latest_view();
assert!(!view.message_is_spent(message_coin.nonce()).unwrap());
assert!(!view.message_is_spent(message_data.nonce()).unwrap());
assert!(view.message_exists(message_coin.nonce()).unwrap());
assert!(view.message_exists(message_data.nonce()).unwrap());

let ExecutionResult {
skipped_transactions,
Expand All @@ -2435,8 +2435,8 @@ mod tests {

// We should spend only `message_coin`. The `message_data` should be unspent.
let view = exec.storage_view_provider.latest_view();
assert!(view.message_is_spent(message_coin.nonce()).unwrap());
assert!(!view.message_is_spent(message_data.nonce()).unwrap());
assert!(!view.message_exists(message_coin.nonce()).unwrap());
assert!(view.message_exists(message_data.nonce()).unwrap());
assert_eq!(*view.coin(&UtxoId::new(tx_id, 0)).unwrap().amount(), amount);
}

Expand Down Expand Up @@ -2580,10 +2580,11 @@ mod tests {
// One of two transactions is skipped.
assert_eq!(skipped_transactions.len(), 1);
let err = &skipped_transactions[0].1;
dbg!(err);
assert!(matches!(
err,
&ExecutorError::TransactionValidity(
TransactionValidityError::MessageAlreadySpent(_)
TransactionValidityError::MessageDoesNotExist(_)
)
));

Expand All @@ -2602,7 +2603,7 @@ mod tests {
assert!(matches!(
res,
Err(ExecutorError::TransactionValidity(
TransactionValidityError::MessageAlreadySpent(_)
TransactionValidityError::MessageDoesNotExist(_)
))
));
}
Expand Down Expand Up @@ -2830,10 +2831,7 @@ mod tests {
use fuel_core_relayer::storage::EventsHistory;
use fuel_core_storage::{
iter::IteratorOverTable,
tables::{
FuelBlocks,
SpentMessages,
},
tables::FuelBlocks,
StorageAsMut,
};
use fuel_core_types::{
Expand Down Expand Up @@ -3604,7 +3602,6 @@ mod tests {

// Given
assert_eq!(on_chain_db.iter_all::<Messages>(None).count(), 0);
assert_eq!(on_chain_db.iter_all::<SpentMessages>(None).count(), 0);
let tx = TransactionBuilder::script(vec![], vec![])
.script_gas_limit(10)
.add_unsigned_message_input(
Expand All @@ -3629,8 +3626,6 @@ mod tests {
let view = ChangesIterator::<OnChain>::new(&changes);
assert!(result.skipped_transactions.is_empty());
assert_eq!(view.iter_all::<Messages>(None).count() as u64, 0);
// Message added during this block immediately became spent.
assert_eq!(view.iter_all::<SpentMessages>(None).count(), 1);
assert_eq!(result.events.len(), 2);
assert!(matches!(
result.events[0],
Expand Down
8 changes: 4 additions & 4 deletions crates/fuel-core/src/graphql_api/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,6 @@ impl DatabaseMessages for ReadView {
self.on_chain.all_messages(start_message_id, direction)
}

fn message_is_spent(&self, nonce: &Nonce) -> StorageResult<bool> {
self.on_chain.message_is_spent(nonce)
}

fn message_exists(&self, nonce: &Nonce) -> StorageResult<bool> {
self.on_chain.message_exists(nonce)
}
Expand Down Expand Up @@ -313,4 +309,8 @@ impl OffChainDatabase for ReadView {
) -> StorageResult<Option<RelayedTransactionStatus>> {
self.off_chain.relayed_tx_status(id)
}

fn message_is_spent(&self, nonce: &Nonce) -> StorageResult<bool> {
self.off_chain.message_is_spent(nonce)
}
}
10 changes: 7 additions & 3 deletions crates/fuel-core/src/graphql_api/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ pub trait OffChainDatabase: Send + Sync {
&self,
id: Bytes32,
) -> StorageResult<Option<RelayedTransactionStatus>>;

fn message_is_spent(&self, nonce: &Nonce) -> StorageResult<bool>;
}

/// The on chain database port expected by GraphQL API service.
Expand Down Expand Up @@ -147,8 +149,6 @@ pub trait DatabaseMessages: StorageInspect<Messages, Error = StorageError> {
direction: IterDirection,
) -> BoxedIter<'_, StorageResult<Message>>;

fn message_is_spent(&self, nonce: &Nonce) -> StorageResult<bool>;

fn message_exists(&self, nonce: &Nonce) -> StorageResult<bool>;
}

Expand Down Expand Up @@ -242,7 +242,10 @@ pub mod worker {
fuel_core_graphql_api::storage::{
coins::OwnedCoins,
contracts::ContractsInfo,
messages::OwnedMessageIds,
messages::{
OwnedMessageIds,
SpentMessages,
},
},
graphql_api::storage::{
old::{
Expand Down Expand Up @@ -288,6 +291,7 @@ pub mod worker {
+ StorageMutate<OldFuelBlocks, Error = StorageError>
+ StorageMutate<OldFuelBlockConsensus, Error = StorageError>
+ StorageMutate<OldTransactions, Error = StorageError>
+ StorageMutate<SpentMessages, Error = StorageError>
+ StorageMutate<RelayedTransactionStatuses, Error = StorageError>
{
fn record_tx_id_owner(
Expand Down
4 changes: 4 additions & 0 deletions crates/fuel-core/src/graphql_api/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ pub enum Column {
OldTransactions = 11,
/// Relayed Tx ID to Layer 1 Relayed Transaction status
RelayedTransactionStatus = 12,
/// Messages that have been spent.
/// Existence of a key in this column means that the message has been spent.
/// See [`SpentMessages`](messages::SpentMessages)
SpentMessages = 13,
}

impl Column {
Expand Down
Loading

0 comments on commit 3b862bf

Please sign in to comment.