diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c5baea60da..b18f9f14d58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ Description of the upcoming release here. ### Added - [#1355](https://github.com/FuelLabs/fuel-core/pull/1355): Added new metrics related to block importing, such as tps, sync delays etc +- [#1339](https://github.com/FuelLabs/fuel-core/pull/1339): Adds `baseAssetId` to `FeeParameters` in the GraphQL API. +- [#1331](https://github.com/FuelLabs/fuel-core/pull/1331): Add peer reputation reporting to block import code - [#1324](https://github.com/FuelLabs/fuel-core/pull/1324): Added pyroscope profiling to fuel-core, intended to be used by a secondary docker image that has debug symbols enabled. - [#1309](https://github.com/FuelLabs/fuel-core/pull/1309): Add documentation for running debug builds with CLion and Visual Studio Code. - [#1308](https://github.com/FuelLabs/fuel-core/pull/1308): Add support for loading .env files when compiling with the `env` feature. This allows users to conveniently supply CLI arguments in a secure and IDE-agnostic way. @@ -18,7 +20,6 @@ Description of the upcoming release here. - [#1286](https://github.com/FuelLabs/fuel-core/pull/1286): Include readable names for test cases where missing. - [#1274](https://github.com/FuelLabs/fuel-core/pull/1274): Added tests to benchmark block synchronization. - [#1263](https://github.com/FuelLabs/fuel-core/pull/1263): Add gas benchmarks for `ED19` and `ECR1` instructions. -- [#1331](https://github.com/FuelLabs/fuel-core/pull/1331): Add peer reputation reporting to block import code ### Changed @@ -33,6 +34,7 @@ Description of the upcoming release here. ### Breaking - [#1363](https://github.com/FuelLabs/fuel-core/pull/1363): Change message_proof api to take `nonce` instead of `message_id` +- [#1339](https://github.com/FuelLabs/fuel-core/pull/1339): Added a new required field called `base_asset_id` to the `FeeParameters` definition in `ConsensusParameters`, as well as default values for `base_asset_id` in the `beta` and `dev` chainspecs. - [#1355](https://github.com/FuelLabs/fuel-core/pull/1355): Removed the `metrics` feature flag from the fuel-core crate, and metrics are now included by default. - [#1322](https://github.com/FuelLabs/fuel-core/pull/1322): The `debug` flag is added to the CLI. The flag should be used for local development only. Enabling debug mode: diff --git a/crates/client/assets/schema.sdl b/crates/client/assets/schema.sdl index 68145c64bf7..bd6bc1ea0fb 100644 --- a/crates/client/assets/schema.sdl +++ b/crates/client/assets/schema.sdl @@ -180,6 +180,7 @@ type ConsensusParameters { scriptParams: ScriptParameters! contractParams: ContractParameters! feeParams: FeeParameters! + baseAssetId: AssetId! chainId: U64! gasCosts: GasCosts! } diff --git a/crates/client/src/client/schema/chain.rs b/crates/client/src/client/schema/chain.rs index 28980d3f633..7c9a47f926e 100644 --- a/crates/client/src/client/schema/chain.rs +++ b/crates/client/src/client/schema/chain.rs @@ -14,6 +14,7 @@ pub struct ConsensusParameters { pub script_params: ScriptParameters, pub contract_params: ContractParameters, pub fee_params: FeeParameters, + pub base_asset_id: AssetId, pub chain_id: U64, pub gas_costs: GasCosts, } @@ -263,9 +264,9 @@ impl From for fuel_core_types::fuel_tx::ConsensusParameters script_params: params.script_params.into(), contract_params: params.contract_params.into(), fee_params: params.fee_params.into(), + base_asset_id: params.base_asset_id.into(), chain_id: params.chain_id.0.into(), gas_costs: params.gas_costs.into(), - base_asset_id: AssetId::default().into(), } } } diff --git a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap index cb9d72df04f..a92377d1b0c 100644 --- a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap +++ b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap @@ -62,6 +62,7 @@ query { gasPriceFactor gasPerByte } + baseAssetId chainId gasCosts { add diff --git a/crates/fuel-core/src/coins_query.rs b/crates/fuel-core/src/coins_query.rs index b0f1794dc56..070ced7f2ac 100644 --- a/crates/fuel-core/src/coins_query.rs +++ b/crates/fuel-core/src/coins_query.rs @@ -54,6 +54,7 @@ pub struct SpendQuery { owner: Address, query_per_asset: Vec, exclude: Exclude, + base_asset_id: AssetId, } impl SpendQuery { @@ -63,6 +64,7 @@ impl SpendQuery { owner: Address, query_per_asset: &[AssetSpendTarget], exclude_vec: Option>, + base_asset_id: AssetId, ) -> Result { let mut duplicate_checker = HashSet::new(); @@ -83,6 +85,7 @@ impl SpendQuery { owner, query_per_asset: query_per_asset.into(), exclude, + base_asset_id, }) } @@ -95,7 +98,15 @@ impl SpendQuery { pub fn asset_queries<'a>(&'a self, db: &'a Database) -> Vec> { self.query_per_asset .iter() - .map(|asset| AssetQuery::new(&self.owner, asset, Some(&self.exclude), db)) + .map(|asset| { + AssetQuery::new( + &self.owner, + asset, + &self.base_asset_id, + Some(&self.exclude), + db, + ) + }) .collect() } @@ -242,34 +253,44 @@ mod tests { fuel_tx::*, }; use itertools::Itertools; + use rand::{ + rngs::StdRng, + Rng, + SeedableRng, + }; use std::cmp::Reverse; - fn setup_coins() -> (Address, [AssetId; 2], TestDatabase) { + fn setup_coins() -> (Address, [AssetId; 2], AssetId, TestDatabase) { + let mut rng = StdRng::seed_from_u64(0xf00df00d); let owner = Address::default(); - let asset_ids = [AssetId::new([1u8; 32]), AssetId::new([2u8; 32])]; + let asset_ids = [rng.gen(), rng.gen()]; + let base_asset_id = rng.gen(); let mut db = TestDatabase::new(); (0..5usize).for_each(|i| { db.make_coin(owner, (i + 1) as Word, asset_ids[0]); db.make_coin(owner, (i + 1) as Word, asset_ids[1]); }); - (owner, asset_ids, db) + (owner, asset_ids, base_asset_id, db) } fn setup_messages() -> (Address, AssetId, TestDatabase) { + let mut rng = StdRng::seed_from_u64(0xf00df00d); let owner = Address::default(); - let asset_id = AssetId::BASE; + let base_asset_id = rng.gen(); let mut db = TestDatabase::new(); (0..5usize).for_each(|i| { db.make_message(owner, (i + 1) as Word); }); - (owner, asset_id, db) + (owner, base_asset_id, db) } - fn setup_coins_and_messages() -> (Address, [AssetId; 2], TestDatabase) { + fn setup_coins_and_messages() -> (Address, [AssetId; 2], AssetId, TestDatabase) { + let mut rng = StdRng::seed_from_u64(0xf00df00d); let owner = Address::default(); - let asset_ids = [AssetId::BASE, AssetId::new([1u8; 32])]; + let base_asset_id = rng.gen(); + let asset_ids = [base_asset_id, rng.gen()]; let mut db = TestDatabase::new(); // 2 coins and 3 messages (0..2usize).for_each(|i| { @@ -283,7 +304,7 @@ mod tests { db.make_coin(owner, (i + 1) as Word, asset_ids[1]); }); - (owner, asset_ids, db) + (owner, asset_ids, base_asset_id, db) } mod largest_first { @@ -292,23 +313,32 @@ mod tests { fn query( spend_query: &[AssetSpendTarget], owner: &Address, + base_asset_id: &AssetId, db: &ServiceDatabase, ) -> Result>, CoinsQueryError> { let result: Vec<_> = spend_query .iter() .map(|asset| { - largest_first(&AssetQuery::new(owner, asset, None, db)).map(|coins| { - coins - .iter() - .map(|coin| (*coin.asset_id(), coin.amount())) - .collect() - }) + largest_first(&AssetQuery::new(owner, asset, base_asset_id, None, db)) + .map(|coins| { + coins + .iter() + .map(|coin| { + (*coin.asset_id(base_asset_id), coin.amount()) + }) + .collect() + }) }) .try_collect()?; Ok(result) } - fn single_asset_assert(owner: Address, asset_ids: &[AssetId], db: TestDatabase) { + fn single_asset_assert( + owner: Address, + asset_ids: &[AssetId], + base_asset_id: &AssetId, + db: TestDatabase, + ) { let asset_id = asset_ids[0]; // Query some targets, including higher than the owner's balance @@ -316,6 +346,7 @@ mod tests { let coins = query( &[AssetSpendTarget::new(asset_id, target, u64::MAX)], &owner, + base_asset_id, &db.service_database(), ); @@ -374,29 +405,37 @@ mod tests { let coins = query( &[AssetSpendTarget::new(asset_id, 6, 1)], &owner, + base_asset_id, &db.service_database(), ); assert_matches!(coins, Err(CoinsQueryError::MaxCoinsReached)); } #[test] - fn single_asset() { + fn single_asset_coins() { // Setup for coins - let (owner, asset_ids, db) = setup_coins(); - single_asset_assert(owner, &asset_ids, db); + let (owner, asset_ids, base_asset_id, db) = setup_coins(); + single_asset_assert(owner, &asset_ids, &base_asset_id, db); + } + #[test] + fn single_asset_messages() { // Setup for messages - let (owner, asset_ids, db) = setup_messages(); - single_asset_assert(owner, &[asset_ids], db); + let (owner, base_asset_id, db) = setup_messages(); + single_asset_assert(owner, &[base_asset_id], &base_asset_id, db); + } + #[test] + fn single_asset_coins_and_messages() { // Setup for coins and messages - let (owner, asset_ids, db) = setup_coins_and_messages(); - single_asset_assert(owner, &asset_ids, db); + let (owner, asset_ids, base_asset_id, db) = setup_coins_and_messages(); + single_asset_assert(owner, &asset_ids, &base_asset_id, db); } fn multiple_assets_helper( owner: Address, asset_ids: &[AssetId], + base_asset_id: &AssetId, db: TestDatabase, ) { let coins = query( @@ -405,24 +444,28 @@ mod tests { AssetSpendTarget::new(asset_ids[1], 6, u64::MAX), ], &owner, + base_asset_id, &db.service_database(), ); - assert_matches!(coins, Ok(coins) - if coins == vec![ + let expected = vec![ vec![(asset_ids[0], 5)], - vec![(asset_ids[1], 5), (asset_ids[1], 4)] - ]); + vec![(asset_ids[1], 5), (asset_ids[1], 4)], + ]; + assert_matches!(coins, Ok(coins) if coins == expected); } #[test] - fn multiple_assets() { + fn multiple_assets_coins() { // Setup coins - let (owner, asset_ids, db) = setup_coins(); - multiple_assets_helper(owner, &asset_ids, db); + let (owner, asset_ids, base_asset_id, db) = setup_coins(); + multiple_assets_helper(owner, &asset_ids, &base_asset_id, db); + } + #[test] + fn multiple_assets_coins_and_messages() { // Setup coins and messages - let (owner, asset_ids, db) = setup_coins_and_messages(); - multiple_assets_helper(owner, &asset_ids, db); + let (owner, asset_ids, base_asset_id, db) = setup_coins_and_messages(); + multiple_assets_helper(owner, &asset_ids, &base_asset_id, db); } } @@ -433,10 +476,13 @@ mod tests { query_per_asset: Vec, owner: Address, asset_ids: &[AssetId], + base_asset_id: AssetId, db: &ServiceDatabase, ) -> Result, CoinsQueryError> { - let coins = - random_improve(db, &SpendQuery::new(owner, &query_per_asset, None)?); + let coins = random_improve( + db, + &SpendQuery::new(owner, &query_per_asset, None, base_asset_id)?, + ); // Transform result for convenience coins.map(|coins| { @@ -445,7 +491,7 @@ mod tests { .flat_map(|coins| { coins .into_iter() - .map(|coin| (*coin.asset_id(), coin.amount())) + .map(|coin| (*coin.asset_id(&base_asset_id), coin.amount())) .sorted_by_key(|(asset_id, amount)| { ( asset_ids.iter().position(|c| c == asset_id).unwrap(), @@ -457,7 +503,12 @@ mod tests { }) } - fn single_asset_assert(owner: Address, asset_ids: &[AssetId], db: TestDatabase) { + fn single_asset_assert( + owner: Address, + asset_ids: &[AssetId], + base_asset_id: AssetId, + db: TestDatabase, + ) { let asset_id = asset_ids[0]; // Query some amounts, including higher than the owner's balance @@ -466,6 +517,7 @@ mod tests { vec![AssetSpendTarget::new(asset_id, amount, u64::MAX)], owner, asset_ids, + base_asset_id, &db.service_database(), ); @@ -517,29 +569,37 @@ mod tests { )], owner, asset_ids, + base_asset_id, &db.service_database(), ); assert_matches!(coins, Err(CoinsQueryError::MaxCoinsReached)); } #[test] - fn single_asset() { + fn single_asset_coins() { // Setup for coins - let (owner, asset_ids, db) = setup_coins(); - single_asset_assert(owner, &asset_ids, db); + let (owner, asset_ids, base_asset_id, db) = setup_coins(); + single_asset_assert(owner, &asset_ids, base_asset_id, db); + } + #[test] + fn single_asset_messages() { // Setup for messages - let (owner, asset_ids, db) = setup_messages(); - single_asset_assert(owner, &[asset_ids], db); + let (owner, base_asset_id, db) = setup_messages(); + single_asset_assert(owner, &[base_asset_id], base_asset_id, db); + } + #[test] + fn single_asset_coins_and_messages() { // Setup for coins and messages - let (owner, asset_ids, db) = setup_coins_and_messages(); - single_asset_assert(owner, &asset_ids, db); + let (owner, asset_ids, base_asset_id, db) = setup_coins_and_messages(); + single_asset_assert(owner, &asset_ids, base_asset_id, db); } fn multiple_assets_assert( owner: Address, asset_ids: &[AssetId], + base_asset_id: AssetId, db: TestDatabase, ) { // Query multiple asset IDs @@ -558,6 +618,7 @@ mod tests { ], owner, asset_ids, + base_asset_id, &db.service_database(), ); assert_matches!(coins, Ok(ref coins) if coins.len() <= 6); @@ -581,14 +642,17 @@ mod tests { } #[test] - fn multiple_assets() { + fn multiple_assets_coins() { // Setup coins - let (owner, asset_ids, db) = setup_coins(); - multiple_assets_assert(owner, &asset_ids, db); + let (owner, asset_ids, base_asset_id, db) = setup_coins(); + multiple_assets_assert(owner, &asset_ids, base_asset_id, db); + } + #[test] + fn multiple_assets_coins_and_messages() { // Setup coins and messages - let (owner, asset_ids, db) = setup_coins_and_messages(); - multiple_assets_assert(owner, &asset_ids, db); + let (owner, asset_ids, base_asset_id, db) = setup_coins_and_messages(); + multiple_assets_assert(owner, &asset_ids, base_asset_id, db); } } @@ -599,6 +663,7 @@ mod tests { fn exclusion_assert( owner: Address, asset_ids: &[AssetId], + base_asset_id: AssetId, db: TestDatabase, excluded_ids: Vec, ) { @@ -607,10 +672,13 @@ mod tests { let query = |query_per_asset: Vec, excluded_ids: Vec| -> Result, CoinsQueryError> { - let coins = random_improve( - &db.service_database(), - &SpendQuery::new(owner, &query_per_asset, Some(excluded_ids))?, - ); + let spend_query = SpendQuery::new( + owner, + &query_per_asset, + Some(excluded_ids), + base_asset_id, + )?; + let coins = random_improve(&db.service_database(), &spend_query); // Transform result for convenience coins.map(|coins| { @@ -618,7 +686,9 @@ mod tests { .into_iter() .flat_map(|coin| { coin.into_iter() - .map(|coin| (*coin.asset_id(), coin.amount())) + .map(|coin| { + (*coin.asset_id(&base_asset_id), coin.amount()) + }) .sorted_by_key(|(asset_id, amount)| { ( asset_ids @@ -647,7 +717,6 @@ mod tests { .map(|(id, amount)| { // Check the asset ID before we drop it assert_eq!(id, asset_id); - amount }) .collect::>() @@ -682,9 +751,9 @@ mod tests { } #[test] - fn exclusion() { + fn exclusion_coins() { // Setup coins - let (owner, asset_ids, db) = setup_coins(); + let (owner, asset_ids, base_asset_id, db) = setup_coins(); // Exclude largest coin IDs let excluded_ids = db @@ -694,10 +763,13 @@ mod tests { .map(|coin| CoinId::Utxo(coin.utxo_id)) .collect_vec(); - exclusion_assert(owner, &asset_ids, db, excluded_ids); + exclusion_assert(owner, &asset_ids, base_asset_id, db, excluded_ids); + } + #[test] + fn exclusion_messages() { // Setup messages - let (owner, asset_ids, db) = setup_messages(); + let (owner, base_asset_id, db) = setup_messages(); // Exclude largest messages IDs let excluded_ids = db @@ -707,10 +779,13 @@ mod tests { .map(|message| CoinId::Message(*message.id())) .collect_vec(); - exclusion_assert(owner, &[asset_ids], db, excluded_ids); + exclusion_assert(owner, &[base_asset_id], base_asset_id, db, excluded_ids); + } + #[test] + fn exclusion_coins_and_messages() { // Setup coins and messages - let (owner, asset_ids, db) = setup_coins_and_messages(); + let (owner, asset_ids, base_asset_id, db) = setup_coins_and_messages(); // Exclude largest messages IDs, because coins only 1 and 2 let excluded_ids = db @@ -720,7 +795,7 @@ mod tests { .map(|message| CoinId::Message(*message.id())) .collect_vec(); - exclusion_assert(owner, &asset_ids, db, excluded_ids); + exclusion_assert(owner, &asset_ids, base_asset_id, db, excluded_ids); } } @@ -731,17 +806,74 @@ mod tests { max_coins: usize, } - #[test_case::test_case( - TestCase { + pub enum CoinType { + Coin, + Message, + } + + fn test_case_run( + case: TestCase, + coin_type: CoinType, + base_asset_id: AssetId, + ) -> Result { + let TestCase { + db_amount, + target_amount, + max_coins, + } = case; + let owner = Address::default(); + let asset_ids = [base_asset_id]; + let mut db = TestDatabase::new(); + for amount in db_amount { + match coin_type { + CoinType::Coin => { + let _ = db.make_coin(owner, amount, asset_ids[0]); + } + CoinType::Message => { + let _ = db.make_message(owner, amount); + } + }; + } + + let coins = random_improve( + &db.service_database(), + &SpendQuery::new( + owner, + &[AssetSpendTarget { + id: asset_ids[0], + target: target_amount, + max: max_coins, + }], + None, + base_asset_id, + )?, + )?; + + assert_eq!(coins.len(), 1); + Ok(coins[0].len()) + } + + #[test] + fn insufficient_coins_returns_error() { + let test_case = TestCase { db_amount: vec![0], target_amount: u64::MAX, max_coins: usize::MAX, - } - => Err(CoinsQueryError::InsufficientCoins { - asset_id: AssetId::BASE, collected_amount: 0 - }) - ; "Insufficient coins in the DB(0) to reach target(u64::MAX)" - )] + }; + let mut rng = StdRng::seed_from_u64(0xF00DF00D); + let base_asset_id = rng.gen(); + let coin_result = test_case_run(test_case.clone(), CoinType::Coin, base_asset_id); + let message_result = test_case_run(test_case, CoinType::Message, base_asset_id); + assert_eq!(coin_result, message_result); + assert_matches!( + coin_result, + Err(CoinsQueryError::InsufficientCoins { + asset_id: _base_asset_id, + collected_amount: 0 + }) + ) + } + #[test_case::test_case( TestCase { db_amount: vec![u64::MAX, u64::MAX], @@ -770,55 +902,11 @@ mod tests { ; "Enough coins in the DB to reach target(u64::MAX) but limit is zero" )] fn corner_cases(case: TestCase) -> Result { - pub enum CoinType { - Coin, - Message, - } - - fn test_case_run( - case: TestCase, - coin_type: CoinType, - ) -> Result { - let TestCase { - db_amount, - target_amount, - max_coins, - } = case; - let owner = Address::default(); - let asset_ids = [AssetId::BASE]; - let mut db = TestDatabase::new(); - for amount in db_amount { - match coin_type { - CoinType::Coin => { - let _ = db.make_coin(owner, amount, asset_ids[0]); - } - CoinType::Message => { - let _ = db.make_message(owner, amount); - } - }; - } - - let coins = random_improve( - &db.service_database(), - &SpendQuery::new( - owner, - &[AssetSpendTarget { - id: asset_ids[0], - target: target_amount, - max: max_coins, - }], - None, - )?, - )?; - - assert_eq!(coins.len(), 1); - Ok(coins[0].len()) - } - - let coin_result = test_case_run(case.clone(), CoinType::Coin); - let message_result = test_case_run(case, CoinType::Message); + let mut rng = StdRng::seed_from_u64(0xF00DF00D); + let base_asset_id = rng.gen(); + let coin_result = test_case_run(case.clone(), CoinType::Coin, base_asset_id); + let message_result = test_case_run(case, CoinType::Message, base_asset_id); assert_eq!(coin_result, message_result); - coin_result } diff --git a/crates/fuel-core/src/executor.rs b/crates/fuel-core/src/executor.rs index 9a5f850ee82..1bc99a3740c 100644 --- a/crates/fuel-core/src/executor.rs +++ b/crates/fuel-core/src/executor.rs @@ -537,7 +537,7 @@ where vec![Output::coin( self.config.coinbase_recipient, 0, // We will set it later - AssetId::BASE, + *self.config.consensus_parameters.base_asset_id(), )], ) } @@ -628,7 +628,7 @@ where coinbase_tx.outputs_mut().push(Output::coin( self.config.coinbase_recipient, execution_data.coinbase, - AssetId::BASE, + *self.config.consensus_parameters.base_asset_id(), )); block.transactions[0] = coinbase_tx.clone().into(); } @@ -764,7 +764,7 @@ where asset_id, amount, .. }) = checked_mint.transaction().outputs().first() { - if asset_id != &AssetId::BASE { + if asset_id != self.config.consensus_parameters.base_asset_id() { return Err(ExecutorError::CoinbaseOutputIsInvalid) } @@ -4065,6 +4065,8 @@ mod tests { fn get_block_height_returns_current_executing_block() { let mut rng = StdRng::seed_from_u64(1234); + let base_asset_id = rng.gen(); + // return current block height let script = vec![op::bhei(0x10), op::ret(0x10)]; let tx = TransactionBuilder::script(script.into_iter().collect(), vec![]) @@ -4073,7 +4075,7 @@ mod tests { rng.gen(), rng.gen(), 1000, - AssetId::zeroed(), + base_asset_id, Default::default(), Default::default(), ) @@ -4104,7 +4106,7 @@ mod tests { &CompressedCoin { owner: *coin_input.input_owner().unwrap(), amount: coin_input.amount().unwrap(), - asset_id: *coin_input.asset_id(&AssetId::BASE).unwrap(), + asset_id: *coin_input.asset_id(&base_asset_id).unwrap(), maturity: coin_input.maturity().unwrap(), tx_pointer: TxPointer::new(Default::default(), block_tx_idx), }, @@ -4141,6 +4143,8 @@ mod tests { fn get_time_returns_current_executing_block_time() { let mut rng = StdRng::seed_from_u64(1234); + let base_asset_id = rng.gen(); + // return current block height let script = vec![op::bhei(0x10), op::time(0x11, 0x10), op::ret(0x11)]; let tx = TransactionBuilder::script(script.into_iter().collect(), vec![]) @@ -4149,7 +4153,7 @@ mod tests { rng.gen(), rng.gen(), 1000, - AssetId::zeroed(), + base_asset_id, Default::default(), Default::default(), ) @@ -4181,7 +4185,7 @@ mod tests { &CompressedCoin { owner: *coin_input.input_owner().unwrap(), amount: coin_input.amount().unwrap(), - asset_id: *coin_input.asset_id(&AssetId::BASE).unwrap(), + asset_id: *coin_input.asset_id(&base_asset_id).unwrap(), maturity: coin_input.maturity().unwrap(), tx_pointer: TxPointer::default(), }, diff --git a/crates/fuel-core/src/query/balance.rs b/crates/fuel-core/src/query/balance.rs index ba73cc577d9..df6eb13b7fc 100644 --- a/crates/fuel-core/src/query/balance.rs +++ b/crates/fuel-core/src/query/balance.rs @@ -28,13 +28,18 @@ use std::{ pub mod asset_query; pub trait BalanceQueryData: Send + Sync { - fn balance(&self, owner: Address, asset_id: AssetId) - -> StorageResult; + fn balance( + &self, + owner: Address, + asset_id: AssetId, + base_asset_id: AssetId, + ) -> StorageResult; fn balances( &self, owner: Address, direction: IterDirection, + base_asset_id: AssetId, ) -> BoxedIter>; } @@ -43,10 +48,12 @@ impl BalanceQueryData for Database { &self, owner: Address, asset_id: AssetId, + base_asset_id: AssetId, ) -> StorageResult { let amount = AssetQuery::new( &owner, &AssetSpendTarget::new(asset_id, u64::MAX, u64::MAX), + &base_asset_id, None, self, ) @@ -72,15 +79,17 @@ impl BalanceQueryData for Database { &self, owner: Address, direction: IterDirection, + base_asset_id: AssetId, ) -> BoxedIter> { let mut amounts_per_asset = HashMap::new(); let mut errors = vec![]; - for coin in AssetsQuery::new(&owner, None, None, self).coins() { + for coin in AssetsQuery::new(&owner, None, None, self, &base_asset_id).coins() { match coin { Ok(coin) => { - *amounts_per_asset.entry(*coin.asset_id()).or_default() += - coin.amount(); + *amounts_per_asset + .entry(*coin.asset_id(&base_asset_id)) + .or_default() += coin.amount(); } Err(err) => { errors.push(err); diff --git a/crates/fuel-core/src/query/balance/asset_query.rs b/crates/fuel-core/src/query/balance/asset_query.rs index 125c704af4d..73ed9b70694 100644 --- a/crates/fuel-core/src/query/balance/asset_query.rs +++ b/crates/fuel-core/src/query/balance/asset_query.rs @@ -63,6 +63,7 @@ pub struct AssetsQuery<'a> { pub assets: Option>, pub exclude: Option<&'a Exclude>, pub database: &'a Database, + pub base_asset_id: &'a AssetId, } impl<'a> AssetsQuery<'a> { @@ -71,23 +72,19 @@ impl<'a> AssetsQuery<'a> { assets: Option>, exclude: Option<&'a Exclude>, database: &'a Database, + base_asset_id: &'a AssetId, ) -> Self { Self { owner, assets, exclude, database, + base_asset_id, } } - /// Returns the iterator over all valid(spendable, allowed by `exclude`) coins of the `owner`. - /// - /// # Note: The coins of different type are not grouped by the `asset_id`. - // TODO: Optimize this by creating an index - // https://github.com/FuelLabs/fuel-core/issues/588 - pub fn coins(&self) -> impl Iterator> + '_ { - let coins_iter = self - .database + fn coins_iter(&self) -> impl Iterator> + '_ { + self.database .owned_coins_ids(self.owner, None, IterDirection::Forward) .map(|id| id.map(CoinId::from)) .filter_ok(|id| { @@ -111,17 +108,15 @@ impl<'a> AssetsQuery<'a> { }) .filter_ok(|coin| { if let CoinType::Coin(coin) = coin { - self.assets - .as_ref() - .map(|assets| assets.contains(&coin.asset_id)) - .unwrap_or(true) + self.has_asset(&coin.asset_id) } else { true } - }); + }) + } - let messages_iter = self - .database + fn messages_iter(&self) -> impl Iterator> + '_ { + self.database .owned_message_ids(self.owner, None, IterDirection::Forward) .map(|id| id.map(CoinId::from)) .filter_ok(|id| { @@ -151,14 +146,28 @@ impl<'a> AssetsQuery<'a> { .expect("The checked above that message data is empty."), ) }) - }); - - coins_iter.chain(messages_iter.take_while(|_| { - self.assets - .as_ref() - .map(|assets| assets.contains(&AssetId::BASE)) - .unwrap_or(true) - })) + }) + } + + fn has_asset(&self, asset_id: &AssetId) -> bool { + self.assets + .as_ref() + .map(|assets| assets.contains(asset_id)) + .unwrap_or(true) + } + + /// Returns the iterator over all valid(spendable, allowed by `exclude`) coins of the `owner`. + /// + /// # Note: The coins of different type are not grouped by the `asset_id`. + // TODO: Optimize this by creating an index + // https://github.com/FuelLabs/fuel-core/issues/588 + pub fn coins(&self) -> impl Iterator> + '_ { + let has_base_asset = self.has_asset(self.base_asset_id); + let messages_iter = has_base_asset + .then(|| self.messages_iter()) + .into_iter() + .flatten(); + self.coins_iter().chain(messages_iter) } } @@ -174,6 +183,7 @@ impl<'a> AssetQuery<'a> { pub fn new( owner: &'a Address, asset: &'a AssetSpendTarget, + base_asset_id: &'a AssetId, exclude: Option<&'a Exclude>, database: &'a Database, ) -> Self { @@ -184,7 +194,13 @@ impl<'a> AssetQuery<'a> { asset, exclude, database, - query: AssetsQuery::new(owner, Some(allowed), exclude, database), + query: AssetsQuery::new( + owner, + Some(allowed), + exclude, + database, + base_asset_id, + ), } } diff --git a/crates/fuel-core/src/schema/balance.rs b/crates/fuel-core/src/schema/balance.rs index f95ec7b4f56..9188696a897 100644 --- a/crates/fuel-core/src/schema/balance.rs +++ b/crates/fuel-core/src/schema/balance.rs @@ -1,5 +1,8 @@ use crate::{ - fuel_core_graphql_api::service::Database, + fuel_core_graphql_api::{ + service::Database, + Config, + }, query::BalanceQueryData, schema::scalars::{ Address, @@ -54,7 +57,11 @@ impl BalanceQuery { #[graphql(desc = "asset_id of the coin")] asset_id: AssetId, ) -> async_graphql::Result { let data: &Database = ctx.data_unchecked(); - let balance = data.balance(owner.0, asset_id.0)?.into(); + let base_asset_id = *ctx + .data_unchecked::() + .consensus_parameters + .base_asset_id(); + let balance = data.balance(owner.0, asset_id.0, base_asset_id)?.into(); Ok(balance) } @@ -78,9 +85,15 @@ impl BalanceQuery { let query: &Database = ctx.data_unchecked(); crate::schema::query_pagination(after, before, first, last, |_, direction| { let owner = filter.owner.into(); - Ok(query.balances(owner, direction).map(|result| { - result.map(|balance| (balance.asset_id.into(), balance.into())) - })) + let base_asset_id = *ctx + .data_unchecked::() + .consensus_parameters + .base_asset_id(); + Ok(query + .balances(owner, direction, base_asset_id) + .map(|result| { + result.map(|balance| (balance.asset_id.into(), balance.into())) + })) }) .await } diff --git a/crates/fuel-core/src/schema/chain.rs b/crates/fuel-core/src/schema/chain.rs index e61e7f8ddeb..ce5c9b15a9a 100644 --- a/crates/fuel-core/src/schema/chain.rs +++ b/crates/fuel-core/src/schema/chain.rs @@ -10,6 +10,7 @@ use crate::{ schema::{ block::Block, scalars::{ + AssetId, U32, U64, }, @@ -94,6 +95,12 @@ impl ConsensusParameters { )) } + async fn base_asset_id(&self, ctx: &Context<'_>) -> async_graphql::Result { + let config = ctx.data_unchecked::(); + + Ok(AssetId(*config.consensus_parameters.base_asset_id())) + } + async fn chain_id(&self) -> U64 { (*self.0.chain_id).into() } diff --git a/crates/fuel-core/src/schema/coins.rs b/crates/fuel-core/src/schema/coins.rs index 02c56ae92bf..322fcdfe919 100644 --- a/crates/fuel-core/src/schema/coins.rs +++ b/crates/fuel-core/src/schema/coins.rs @@ -96,8 +96,10 @@ impl MessageCoin { self.0.amount.into() } - async fn asset_id(&self) -> AssetId { - fuel_core_types::fuel_types::AssetId::BASE.into() + async fn asset_id(&self, ctx: &Context<'_>) -> AssetId { + let config = ctx.data_unchecked::(); + let base_asset_id = *config.consensus_parameters.base_asset_id(); + base_asset_id.into() } async fn da_height(&self) -> U64 { @@ -242,7 +244,9 @@ impl CoinQuery { utxos.chain(messages).collect() }); - let spend_query = SpendQuery::new(owner, &query_per_asset, excluded_ids)?; + let base_asset_id = config.consensus_parameters.base_asset_id(); + let spend_query = + SpendQuery::new(owner, &query_per_asset, excluded_ids, *base_asset_id)?; let db = ctx.data_unchecked::(); diff --git a/crates/fuel-core/src/schema/tx/types.rs b/crates/fuel-core/src/schema/tx/types.rs index 22ca2cbf0fe..136a4d0894b 100644 --- a/crates/fuel-core/src/schema/tx/types.rs +++ b/crates/fuel-core/src/schema/tx/types.rs @@ -9,6 +9,7 @@ use crate::{ Database, TxPool, }, + Config, IntoApiResult, }, query::{ @@ -60,7 +61,6 @@ use fuel_core_types::{ Chargeable, Executable, }, - fuel_types, fuel_types::canonical::SerializedSize, fuel_vm::ProgramState as VmProgramState, services::{ @@ -277,17 +277,19 @@ impl Transaction { TransactionId(self.1) } - async fn input_asset_ids(&self) -> Option> { + async fn input_asset_ids(&self, ctx: &Context<'_>) -> Option> { + let config = ctx.data_unchecked::(); + let base_asset_id = config.consensus_parameters.base_asset_id(); match &self.0 { fuel_tx::Transaction::Script(script) => Some( script - .input_asset_ids(&fuel_types::AssetId::BASE) + .input_asset_ids(base_asset_id) .map(|c| AssetId(*c)) .collect(), ), fuel_tx::Transaction::Create(create) => Some( create - .input_asset_ids(&fuel_types::AssetId::BASE) + .input_asset_ids(base_asset_id) .map(|c| AssetId(*c)) .collect(), ), diff --git a/crates/types/src/entities/coins.rs b/crates/types/src/entities/coins.rs index 4020bd1a5b9..a1db13fc8a0 100644 --- a/crates/types/src/entities/coins.rs +++ b/crates/types/src/entities/coins.rs @@ -73,10 +73,10 @@ impl CoinType { } /// Returns the asset held by the coin. - pub fn asset_id(&self) -> &AssetId { + pub fn asset_id<'a>(&'a self, base_asset_id: &'a AssetId) -> &'a AssetId { match self { CoinType::Coin(coin) => &coin.asset_id, - CoinType::MessageCoin(_) => &AssetId::BASE, + CoinType::MessageCoin(_) => base_asset_id, } } }