Skip to content

Commit

Permalink
fix(da_compression): invalid decompression of utxo id and CoinConfig fix
Browse files Browse the repository at this point in the history
Co-Authored-by: green <xgreenx9999@gmail.com>

test: mint tx monkeypatch

fix: clippy

fix: undo backward compat breaking change

fix: p2p test helpers overlapping output_index

fix

f
  • Loading branch information
rymnc committed Jan 17, 2025
1 parent dfc7fd7 commit bc0849a
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 63 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- [2551](https://github.com/FuelLabs/fuel-core/pull/2551): Enhanced the DA compressed block header to include block id.

### Fixed
- [2593](https://github.com/FuelLabs/fuel-core/pull/2593): Fixed utxo id decompression

## [Version 0.41.0]

### Added
Expand Down
88 changes: 50 additions & 38 deletions crates/chain-config/src/config/coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use fuel_core_types::{
BlockHeight,
Bytes32,
},
fuel_vm::SecretKey,
};
use serde::{
Deserialize,
Expand Down Expand Up @@ -75,41 +74,6 @@ impl From<CoinConfig> for TableEntry<Coins> {
}
}

/// Generates a new coin config with a unique utxo id for testing
#[derive(Default, Debug)]
pub struct CoinConfigGenerator {
count: usize,
}

impl CoinConfigGenerator {
pub fn new() -> Self {
Self { count: 0 }
}

pub fn generate(&mut self) -> CoinConfig {
let mut bytes = [0u8; 32];
bytes[..std::mem::size_of::<usize>()].copy_from_slice(&self.count.to_be_bytes());

let config = CoinConfig {
tx_id: Bytes32::from(bytes),
..Default::default()
};
self.count = self.count.checked_add(1).expect("Max coin count reached");

config
}

pub fn generate_with(&mut self, secret: SecretKey, amount: u64) -> CoinConfig {
let owner = Address::from(*secret.public_key().hash());

CoinConfig {
amount,
owner,
..self.generate()
}
}
}

impl CoinConfig {
pub fn utxo_id(&self) -> UtxoId {
UtxoId::new(self.tx_id, self.output_index)
Expand Down Expand Up @@ -157,6 +121,54 @@ impl GenesisCommitment for Coin {
}
}

#[cfg(feature = "test-helpers")]
pub mod coin_config_helpers {
use crate::CoinConfig;
use fuel_core_types::{
fuel_types::Address,
fuel_vm::SecretKey,
};

type CoinCount = u16;

/// Generates a new coin config with a unique utxo id for testing
#[derive(Default, Debug)]
pub struct CoinConfigGenerator {
count: CoinCount,
}

impl CoinConfigGenerator {
pub fn new() -> Self {
Self { count: 0 }
}

pub fn generate(&mut self) -> CoinConfig {
let mut bytes = [0u8; 32];
bytes[..size_of::<CoinCount>()].copy_from_slice(&self.count.to_be_bytes());

let config = CoinConfig {
tx_id: bytes.into(),
output_index: self.count,
..Default::default()
};

self.count = self.count.checked_add(1).expect("Max coin count reached");

config
}

pub fn generate_with(&mut self, secret: SecretKey, amount: u64) -> CoinConfig {
let owner = Address::from(*secret.public_key().hash());

CoinConfig {
amount,
owner,
..self.generate()
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -167,7 +179,7 @@ mod tests {

#[test]
fn test_generate_unique_utxo_id() {
let mut generator = CoinConfigGenerator::new();
let mut generator = coin_config_helpers::CoinConfigGenerator::new();
let config1 = generator.generate();
let config2 = generator.generate();

Expand All @@ -180,7 +192,7 @@ mod tests {
let secret = SecretKey::random(&mut rng);
let amount = 1000;

let mut generator = CoinConfigGenerator::new();
let mut generator = coin_config_helpers::CoinConfigGenerator::new();
let config = generator.generate_with(secret, amount);

assert_eq!(config.owner, Address::from(*secret.public_key().hash()));
Expand Down
2 changes: 1 addition & 1 deletion crates/chain-config/src/config/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use serde::{
use crate::SnapshotMetadata;

#[cfg(feature = "test-helpers")]
use crate::CoinConfigGenerator;
use crate::coin_config_helpers::CoinConfigGenerator;
#[cfg(feature = "test-helpers")]
use bech32::{
ToBase32,
Expand Down
21 changes: 20 additions & 1 deletion crates/compression/src/decompress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use fuel_core_types::{
RegistryKey,
},
fuel_tx::{
field::TxPointer,
input::{
coin::{
Coin,
Expand All @@ -35,6 +36,7 @@ use fuel_core_types::{
Mint,
ScriptCode,
Transaction,
TxPointer as FuelTxPointer,
UtxoId,
},
fuel_types::{
Expand Down Expand Up @@ -68,12 +70,27 @@ where
db,
};

let transactions = <Vec<Transaction> as DecompressibleBy<_>>::decompress_with(
let mut transactions = <Vec<Transaction> as DecompressibleBy<_>>::decompress_with(
block.transactions(),
&ctx,
)
.await?;

let transaction_count = transactions.len();

// patch mint transaction
for tx in transactions.iter_mut() {
if let Transaction::Mint(mint) = tx {
let tx_pointer = mint.tx_pointer_mut();
// this will break if we have multiple mints
*tx_pointer = FuelTxPointer::new(
block.consensus_header().height,
#[allow(clippy::arithmetic_side_effects)]
u16::try_from(transaction_count - 1)?,
);
}
}

Ok(PartialFuelBlock {
header: block.partial_block_header(),
transactions,
Expand Down Expand Up @@ -211,6 +228,8 @@ where
ctx: &DecompressCtx<D>,
) -> anyhow::Result<Self> {
Ok(Transaction::mint(
// we should probably include the mint TxPointer in the compression if we decide to support
// multiple assets for mints, i.e more than 1 mint tx per block
Default::default(), // TODO: what should this we do with this?
c.input_contract.decompress(ctx).await?,
c.output_contract.decompress(ctx).await?,
Expand Down
19 changes: 8 additions & 11 deletions crates/fuel-core/src/graphql_api/da_compression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,10 +273,7 @@ impl_temporal_registry!(ContractId);
impl_temporal_registry!(ScriptCode);
impl_temporal_registry!(PredicateCode);

impl<'a, Tx> UtxoIdToPointer for CompressDbTx<'a, Tx>
where
Tx: OffChainDatabaseTransaction,
{
impl<'a, Tx> UtxoIdToPointer for CompressDbTx<'a, Tx> {
fn lookup(
&self,
utxo_id: fuel_core_types::fuel_tx::UtxoId,
Expand All @@ -301,7 +298,6 @@ where

impl<'a, Tx, Onchain> HistoryLookup for DecompressDbTx<'a, Tx, Onchain>
where
Tx: OffChainDatabaseTransaction,
Onchain: StorageInspect<Coins, Error = fuel_core_storage::Error>
+ StorageInspect<Messages, Error = fuel_core_storage::Error>
+ StorageInspect<FuelBlocks, Error = fuel_core_storage::Error>,
Expand All @@ -314,13 +310,14 @@ where
// This is a genesis coin, which is handled differently.
// See CoinConfigGenerator::generate which generates the genesis coins.
let mut bytes = [0u8; 32];
let tx_index = c.tx_pointer.tx_index();
bytes[..std::mem::size_of_val(&tx_index)]
.copy_from_slice(&tx_index.to_be_bytes());
return Ok(fuel_core_types::fuel_tx::UtxoId::new(
bytes[..size_of::<u16>()].copy_from_slice(&c.output_index.to_be_bytes());

let utxo_id = fuel_core_types::fuel_tx::UtxoId::new(
fuel_core_types::fuel_tx::TxId::from(bytes),
0,
));
c.output_index,
);

return Ok(utxo_id);
}

let block_info = self
Expand Down
4 changes: 2 additions & 2 deletions crates/fuel-core/src/p2p_test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
use crate::{
chain_config::{
coin_config_helpers::CoinConfigGenerator,
CoinConfig,
CoinConfigGenerator,
},
combined_database::CombinedDatabase,
database::{
Expand Down Expand Up @@ -246,7 +246,7 @@ pub async fn make_nodes(
let initial_coin = CoinConfig {
// set idx to prevent overlapping utxo_ids when
// merging with existing coins from config
output_index: 2,
output_index: 10,
..coin_generator.generate_with(secret, 10000)
};
let tx = TransactionBuilder::script(
Expand Down
4 changes: 2 additions & 2 deletions tests/tests/balances.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use fuel_core::{
chain_config::{
coin_config_helpers::CoinConfigGenerator,
CoinConfig,
CoinConfigGenerator,
MessageConfig,
StateConfig,
},
Expand Down Expand Up @@ -334,9 +334,9 @@ async fn first_5_balances() {
mod pagination {
use fuel_core::{
chain_config::{
coin_config_helpers::CoinConfigGenerator,
ChainConfig,
CoinConfig,
CoinConfigGenerator,
MessageConfig,
StateConfig,
},
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/coin.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use fuel_core::{
chain_config::{
coin_config_helpers::CoinConfigGenerator,
CoinConfig,
CoinConfigGenerator,
StateConfig,
},
database::Database,
Expand Down
4 changes: 2 additions & 2 deletions tests/tests/coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use rand::{
mod coin {
use super::*;
use fuel_core::chain_config::{
coin_config_helpers::CoinConfigGenerator,
ChainConfig,
CoinConfigGenerator,
};
use fuel_core_client::client::types::CoinType;
use fuel_core_types::fuel_crypto::SecretKey;
Expand Down Expand Up @@ -524,7 +524,7 @@ mod message_coin {

// It is combination of coins and deposit coins test cases.
mod all_coins {
use fuel_core::chain_config::CoinConfigGenerator;
use fuel_core::chain_config::coin_config_helpers::CoinConfigGenerator;
use fuel_core_client::client::types::CoinType;
use fuel_core_types::blockchain::primitives::DaBlockHeight;

Expand Down
38 changes: 33 additions & 5 deletions tests/tests/da_compression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use fuel_core_compression::{
VersionedCompressedBlock,
};
use fuel_core_storage::transactional::{
AtomicView,
HistoricalView,
IntoTransaction,
};
Expand All @@ -39,6 +40,7 @@ use fuel_core_types::{
Input,
TransactionBuilder,
TxPointer,
UniqueIdentifier,
},
secrecy::Secret,
signer::SignMode,
Expand All @@ -61,26 +63,33 @@ async fn can_fetch_da_compressed_block_from_graphql() {
temporal_registry_retention: Duration::from_secs(3600),
};
config.da_compression = DaCompressionConfig::Enabled(compression_config);
let chain_id = config
.snapshot_reader
.chain_config()
.consensus_parameters
.chain_id();
let srv = FuelService::new_node(config).await.unwrap();
let client = FuelClient::from(srv.bound_address);

let wallet_secret =
SecretKey::from_str(TESTNET_WALLET_SECRETS[1]).expect("Expected valid secret");
let wallet_address = Address::from(*wallet_secret.public_key().hash());

let coin = client
let coins = client
.coins(
&wallet_address,
None,
PaginationRequest {
cursor: None,
results: 1,
results: 10,
direction: fuel_core_client::client::pagination::PageDirection::Forward,
},
)
.await
.expect("Unable to get coins")
.results
.results;

let coin = coins
.into_iter()
.next()
.expect("Expected at least one coin");
Expand Down Expand Up @@ -117,16 +126,35 @@ async fn can_fetch_da_compressed_block_from_graphql() {

// Reuse the existing offchain db to decompress the block
let db = &srv.shared.database;

let on_chain_before_execution = db.on_chain().view_at(&0u32.into()).unwrap();
let mut tx_inner = db.off_chain().clone().into_transaction();
let db_tx = DecompressDbTx {
db_tx: DbTx {
db_tx: &mut tx_inner,
},
onchain_db: db.on_chain().view_at(&0u32.into()).unwrap(),
onchain_db: on_chain_before_execution,
};
let decompressed = decompress(compression_config, db_tx, block).await.unwrap();

assert!(decompressed.transactions.len() == 2);
let block_from_on_chain_db = db
.on_chain()
.latest_view()
.unwrap()
.get_full_block(&block_height)
.unwrap()
.unwrap();

let db_transactions = block_from_on_chain_db.transactions();
let decompressed_transactions = decompressed.transactions;

assert_eq!(decompressed_transactions.len(), 2);
for (db_tx, decompressed_tx) in
db_transactions.iter().zip(decompressed_transactions.iter())
{
// ensure tx ids match
assert_eq!(db_tx.id(&chain_id), decompressed_tx.id(&chain_id));
}
}

#[tokio::test(flavor = "multi_thread")]
Expand Down

0 comments on commit bc0849a

Please sign in to comment.