فهرست منبع

contract: Use native constants instead of wrapped ones for ZERO.

parazyd 3 سال پیش
والد
کامیت
95a2da019b

+ 2 - 2
src/contract/consensus/src/client/proposal_v1.rs

@@ -26,7 +26,7 @@ use darkfi::{
 };
 };
 use darkfi_money_contract::{
 use darkfi_money_contract::{
     client::{ConsensusNote, ConsensusOwnCoin},
     client::{ConsensusNote, ConsensusOwnCoin},
-    model::{Coin, ConsensusInput, ConsensusOutput, PALLAS_ZERO},
+    model::{Coin, ConsensusInput, ConsensusOutput},
 };
 };
 use darkfi_sdk::{
 use darkfi_sdk::{
     crypto::{
     crypto::{
@@ -271,7 +271,7 @@ pub fn create_proposal_proof(
         new_pub_x,
         new_pub_x,
         new_pub_y,
         new_pub_y,
         new_value_pallas,
         new_value_pallas,
-        PALLAS_ZERO,
+        pallas::Base::ZERO,
         new_serial,
         new_serial,
         output.coin_blind,
         output.coin_blind,
     ]));
     ]));

+ 14 - 14
src/contract/consensus/src/entrypoint/genesis_stake_v1.rs

@@ -17,10 +17,8 @@
  */
  */
 
 
 use darkfi_money_contract::{
 use darkfi_money_contract::{
-    error::MoneyError,
-    model::{ConsensusStakeUpdateV1, PALLAS_ZERO},
-    CONSENSUS_CONTRACT_COINS_TREE, CONSENSUS_CONTRACT_UNSTAKED_COINS_TREE,
-    CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1,
+    error::MoneyError, model::ConsensusStakeUpdateV1, CONSENSUS_CONTRACT_COINS_TREE,
+    CONSENSUS_CONTRACT_UNSTAKED_COINS_TREE, CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1,
 };
 };
 use darkfi_sdk::{
 use darkfi_sdk::{
     crypto::{pasta_prelude::*, pedersen_commitment_u64, ContractId, DARK_TOKEN_ID},
     crypto::{pasta_prelude::*, pedersen_commitment_u64, ContractId, DARK_TOKEN_ID},
@@ -36,6 +34,10 @@ use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
 use crate::{model::ConsensusGenesisStakeParamsV1, ConsensusFunction};
 use crate::{model::ConsensusGenesisStakeParamsV1, ConsensusFunction};
 
 
 /// `get_metadata` function for `Consensus::GenesisStakeV1`
 /// `get_metadata` function for `Consensus::GenesisStakeV1`
+///
+/// Here we gather the signature pubkey from the clear input in order
+/// to verify the transaction, and we extract the necessary public inputs
+/// that go into the `ConsensusMint_V1` proof verification.
 pub(crate) fn consensus_genesis_stake_get_metadata_v1(
 pub(crate) fn consensus_genesis_stake_get_metadata_v1(
     _cid: ContractId,
     _cid: ContractId,
     call_idx: u32,
     call_idx: u32,
@@ -50,15 +52,14 @@ pub(crate) fn consensus_genesis_stake_get_metadata_v1(
     let signature_pubkeys = vec![params.input.signature_public];
     let signature_pubkeys = vec![params.input.signature_public];
 
 
     // Genesis stake only happens on epoch 0
     // Genesis stake only happens on epoch 0
-    let epoch = PALLAS_ZERO;
+    let epoch = pallas::Base::ZERO;
 
 
     // Grab the pedersen commitment from the anonymous output
     // Grab the pedersen commitment from the anonymous output
-    let output = &params.output;
-    let value_coords = output.value_commit.to_affine().coordinates().unwrap();
+    let value_coords = params.output.value_commit.to_affine().coordinates().unwrap();
 
 
     zk_public_inputs.push((
     zk_public_inputs.push((
         CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1.to_string(),
         CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1.to_string(),
-        vec![epoch, output.coin.inner(), *value_coords.x(), *value_coords.y()],
+        vec![epoch, params.output.coin.inner(), *value_coords.x(), *value_coords.y()],
     ));
     ));
 
 
     // Serialize everything gathered and return it
     // Serialize everything gathered and return it
@@ -78,7 +79,7 @@ pub(crate) fn consensus_genesis_stake_process_instruction_v1(
     let self_ = &calls[call_idx as usize];
     let self_ = &calls[call_idx as usize];
     let params: ConsensusGenesisStakeParamsV1 = deserialize(&self_.data[1..])?;
     let params: ConsensusGenesisStakeParamsV1 = deserialize(&self_.data[1..])?;
 
 
-    // Verify this contract call is verified against on genesis slot(0).
+    // Verify this contract call is verified on the genesis slot (0).
     let verifying_slot = get_verifying_slot();
     let verifying_slot = get_verifying_slot();
     if verifying_slot != 0 {
     if verifying_slot != 0 {
         msg!("[GenesisStakeV1] Error: Call is executed for slot {}, not genesis", verifying_slot);
         msg!("[GenesisStakeV1] Error: Call is executed for slot {}, not genesis", verifying_slot);
@@ -99,19 +100,18 @@ pub(crate) fn consensus_genesis_stake_process_instruction_v1(
     // Check that the coin from the output hasn't existed before.
     // Check that the coin from the output hasn't existed before.
     let coin = serialize(&params.output.coin);
     let coin = serialize(&params.output.coin);
     if db_contains_key(coins_db, &coin)? {
     if db_contains_key(coins_db, &coin)? {
-        msg!("[GenesisStakeV1] Error: Duplicate coin in output");
+        msg!("[GenesisStakeV1] Error: Output coin was already seen in the set of staked coins");
         return Err(MoneyError::DuplicateCoin.into())
         return Err(MoneyError::DuplicateCoin.into())
     }
     }
 
 
     // Check that the coin from the output hasn't existed before in unstake set.
     // Check that the coin from the output hasn't existed before in unstake set.
     if db_contains_key(unstaked_coins_db, &coin)? {
     if db_contains_key(unstaked_coins_db, &coin)? {
-        msg!("[GenesisStakeV1] Error: Duplicate coin in output");
+        msg!("[GenesisStakeV1] Error: Output coin was already seen in the set of unstaked coins");
         return Err(MoneyError::DuplicateCoin.into())
         return Err(MoneyError::DuplicateCoin.into())
     }
     }
 
 
-    // Verify that the value commitment match. In here we just
-    // confirm that the clear input and the anon output have the same
-    // commitment.
+    // Verify that the value commitments match. In here we just confirm
+    // that the clear input and the anon output have the same commitment.
     if pedersen_commitment_u64(params.input.value, params.input.value_blind) !=
     if pedersen_commitment_u64(params.input.value, params.input.value_blind) !=
         params.output.value_commit
         params.output.value_commit
     {
     {

+ 2 - 2
src/contract/consensus/src/entrypoint/stake_v1.rs

@@ -18,7 +18,7 @@
 
 
 use darkfi_money_contract::{
 use darkfi_money_contract::{
     error::MoneyError,
     error::MoneyError,
-    model::{ConsensusStakeParamsV1, ConsensusStakeUpdateV1, MoneyStakeParamsV1, PALLAS_ZERO},
+    model::{ConsensusStakeParamsV1, ConsensusStakeUpdateV1, MoneyStakeParamsV1},
     CONSENSUS_CONTRACT_COINS_TREE, CONSENSUS_CONTRACT_COIN_MERKLE_TREE,
     CONSENSUS_CONTRACT_COINS_TREE, CONSENSUS_CONTRACT_COIN_MERKLE_TREE,
     CONSENSUS_CONTRACT_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_INFO_TREE,
     CONSENSUS_CONTRACT_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_INFO_TREE,
     CONSENSUS_CONTRACT_UNSTAKED_COINS_TREE, CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1,
     CONSENSUS_CONTRACT_UNSTAKED_COINS_TREE, CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1,
@@ -142,7 +142,7 @@ pub(crate) fn consensus_stake_process_instruction_v1(
     }
     }
 
 
     // If spend hook is set, check its correctness
     // If spend hook is set, check its correctness
-    if previous_input.spend_hook != PALLAS_ZERO &&
+    if previous_input.spend_hook != pallas::Base::ZERO &&
         previous_input.spend_hook != CONSENSUS_CONTRACT_ID.inner()
         previous_input.spend_hook != CONSENSUS_CONTRACT_ID.inner()
     {
     {
         msg!("[ConsensusStakeV1] Error: Invoking contract call does not match spend hook in input");
         msg!("[ConsensusStakeV1] Error: Invoking contract call does not match spend hook in input");

+ 11 - 5
src/contract/consensus/src/model.rs

@@ -25,12 +25,14 @@ use darkfi_serial::{SerialDecodable, SerialEncodable};
 
 
 /// Parameters for `Consensus::GenesisStake`
 /// Parameters for `Consensus::GenesisStake`
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+// ANCHOR: ConsensusGenesisStakeParams
 pub struct ConsensusGenesisStakeParamsV1 {
 pub struct ConsensusGenesisStakeParamsV1 {
     /// Clear input
     /// Clear input
     pub input: ClearInput,
     pub input: ClearInput,
     /// Anonymous output
     /// Anonymous output
     pub output: ConsensusOutput,
     pub output: ConsensusOutput,
 }
 }
+// ANCHOR_END: ConsensusGenesisStakeParams
 
 
 /// Parameters for `Consensus::Proposal`
 /// Parameters for `Consensus::Proposal`
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
@@ -73,16 +75,19 @@ pub struct ConsensusUnstakeRequestParamsV1 {
     pub output: Output,
     pub output: Output,
 }
 }
 
 
-/// Consensus parameters configuration.
-/// Note: Always verify `pallas::Base` are correct, in case of changes,
-/// using pallas_constants tool.
+// ==================================
+// Consensus parameters configuration
+// Note: Always verify `pallas::Base` are correct, in case of changes,
+// using pallas_constants tool.
+// ==================================
+
 /// Number of slots in one epoch
 /// Number of slots in one epoch
 pub const EPOCH_LENGTH: u64 = 10;
 pub const EPOCH_LENGTH: u64 = 10;
 /// Slot time in seconds
 /// Slot time in seconds
 pub const SLOT_TIME: u64 = 90;
 pub const SLOT_TIME: u64 = 90;
 /// Grace period days target
 /// Grace period days target
 pub const GRACE_PERIOD_DAYS: u64 = 2;
 pub const GRACE_PERIOD_DAYS: u64 = 2;
-/// Configured reward
+/// Configured block reward
 pub const REWARD: u64 = 1;
 pub const REWARD: u64 = 1;
 /// Reward `pallas::Base`, calculated by: pallas::Base::from(REWARD)
 /// Reward `pallas::Base`, calculated by: pallas::Base::from(REWARD)
 pub const REWARD_PALLAS: pallas::Base = pallas::Base::from_raw([1, 0, 0, 0]);
 pub const REWARD_PALLAS: pallas::Base = pallas::Base::from_raw([1, 0, 0, 0]);
@@ -118,7 +123,8 @@ pub struct SlotCheckpoint {
 
 
 /// Auxiliary function to calculate the grace(locked) period, denominated
 /// Auxiliary function to calculate the grace(locked) period, denominated
 /// in epochs.
 /// in epochs.
-pub fn calculate_grace_period() -> u64 {
+#[inline]
+pub const fn calculate_grace_period() -> u64 {
     // 86400 seconds in a day
     // 86400 seconds in a day
     (86400 * GRACE_PERIOD_DAYS) / (SLOT_TIME * EPOCH_LENGTH)
     (86400 * GRACE_PERIOD_DAYS) / (SLOT_TIME * EPOCH_LENGTH)
 }
 }

+ 4 - 4
src/contract/money/src/client/mod.rs

@@ -32,7 +32,7 @@ use darkfi_sdk::{
 };
 };
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 
 
-use crate::model::{Coin, PALLAS_ZERO, SCALAR_ZERO};
+use crate::model::Coin;
 
 
 /// `Money::TransferV1` API
 /// `Money::TransferV1` API
 pub mod transfer_v1;
 pub mod transfer_v1;
@@ -159,11 +159,11 @@ impl From<ConsensusNote> for MoneyNote {
             serial: consensus_note.serial,
             serial: consensus_note.serial,
             value: consensus_note.value,
             value: consensus_note.value,
             token_id: *DARK_TOKEN_ID,
             token_id: *DARK_TOKEN_ID,
-            spend_hook: PALLAS_ZERO,
-            user_data: PALLAS_ZERO,
+            spend_hook: pallas::Base::ZERO,
+            user_data: pallas::Base::ZERO,
             coin_blind: consensus_note.coin_blind,
             coin_blind: consensus_note.coin_blind,
             value_blind: consensus_note.value_blind,
             value_blind: consensus_note.value_blind,
-            token_blind: SCALAR_ZERO,
+            token_blind: pallas::Scalar::ZERO,
             memo: vec![],
             memo: vec![],
         }
         }
     }
     }

+ 4 - 4
src/contract/money/src/client/unstake_v1.rs

@@ -36,7 +36,7 @@ use rand::rngs::OsRng;
 
 
 use crate::{
 use crate::{
     client::{ConsensusOwnCoin, MoneyNote},
     client::{ConsensusOwnCoin, MoneyNote},
-    model::{Coin, ConsensusInput, MoneyUnstakeParamsV1, Output, PALLAS_ZERO, SCALAR_ZERO},
+    model::{Coin, ConsensusInput, MoneyUnstakeParamsV1, Output},
 };
 };
 
 
 pub struct MoneyUnstakeCallDebris {
 pub struct MoneyUnstakeCallDebris {
@@ -105,9 +105,9 @@ impl MoneyUnstakeCallBuilder {
         debug!("Finished building output");
         debug!("Finished building output");
 
 
         let serial = pallas::Base::random(&mut OsRng);
         let serial = pallas::Base::random(&mut OsRng);
-        let spend_hook = PALLAS_ZERO;
-        let user_data_enc = PALLAS_ZERO;
-        let token_blind = SCALAR_ZERO;
+        let spend_hook = pallas::Base::ZERO;
+        let user_data_enc = pallas::Base::ZERO;
+        let token_blind = pallas::Scalar::ZERO;
         let coin_blind = pallas::Base::random(&mut OsRng);
         let coin_blind = pallas::Base::random(&mut OsRng);
 
 
         info!("Creating unstake mint proof for output");
         info!("Creating unstake mint proof for output");

+ 2 - 2
src/contract/money/src/entrypoint/stake_v1.rs

@@ -31,7 +31,7 @@ use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
 
 
 use crate::{
 use crate::{
     error::MoneyError,
     error::MoneyError,
-    model::{ConsensusStakeParamsV1, MoneyStakeParamsV1, MoneyStakeUpdateV1, PALLAS_ZERO},
+    model::{ConsensusStakeParamsV1, MoneyStakeParamsV1, MoneyStakeUpdateV1},
     MoneyFunction, MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_NULLIFIERS_TREE,
     MoneyFunction, MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_NULLIFIERS_TREE,
     MONEY_CONTRACT_ZKAS_BURN_NS_V1,
     MONEY_CONTRACT_ZKAS_BURN_NS_V1,
 };
 };
@@ -137,7 +137,7 @@ pub(crate) fn money_stake_process_instruction_v1(
     }
     }
 
 
     // If spend hook is set, check its correctness
     // If spend hook is set, check its correctness
-    if input.spend_hook != PALLAS_ZERO && next.contract_id.inner() != input.spend_hook {
+    if input.spend_hook != pallas::Base::ZERO && next.contract_id.inner() != input.spend_hook {
         msg!("[MoneyStakeV1] Error: Invoking contract call does not match spend hook in input");
         msg!("[MoneyStakeV1] Error: Invoking contract call does not match spend hook in input");
         return Err(MoneyError::SpendHookMismatch.into())
         return Err(MoneyError::SpendHookMismatch.into())
     }
     }

+ 2 - 2
src/contract/money/src/entrypoint/swap_v1.rs

@@ -27,7 +27,7 @@ use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
 use super::transfer_v1::{money_transfer_get_metadata_v1, money_transfer_process_update_v1};
 use super::transfer_v1::{money_transfer_get_metadata_v1, money_transfer_process_update_v1};
 use crate::{
 use crate::{
     error::MoneyError,
     error::MoneyError,
-    model::{MoneyTransferParamsV1, MoneyTransferUpdateV1, PALLAS_ZERO},
+    model::{MoneyTransferParamsV1, MoneyTransferUpdateV1},
     MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_ROOTS_TREE,
     MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_ROOTS_TREE,
     MONEY_CONTRACT_NULLIFIERS_TREE,
     MONEY_CONTRACT_NULLIFIERS_TREE,
 };
 };
@@ -110,7 +110,7 @@ pub(crate) fn money_otcswap_process_instruction_v1(
         // For now, make sure that the inputs' spend hooks are zero.
         // For now, make sure that the inputs' spend hooks are zero.
         // This should however be allowed to some extent, e.g. if we
         // This should however be allowed to some extent, e.g. if we
         // want a DAO to be able to do an atomic swap.
         // want a DAO to be able to do an atomic swap.
-        if input.spend_hook != PALLAS_ZERO {
+        if input.spend_hook != pallas::Base::ZERO {
             msg!("[OtcSwapV1] Error: Unable to swap coins with spend_hook != 0 (input {})", i);
             msg!("[OtcSwapV1] Error: Unable to swap coins with spend_hook != 0 (input {})", i);
             return Err(MoneyError::SpendHookNonZero.into())
             return Err(MoneyError::SpendHookNonZero.into())
         }
         }

+ 2 - 2
src/contract/money/src/entrypoint/transfer_v1.rs

@@ -31,7 +31,7 @@ use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
 
 
 use crate::{
 use crate::{
     error::MoneyError,
     error::MoneyError,
-    model::{MoneyTransferParamsV1, MoneyTransferUpdateV1, PALLAS_ZERO},
+    model::{MoneyTransferParamsV1, MoneyTransferUpdateV1},
     MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
     MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
     MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_FAUCET_PUBKEYS, MONEY_CONTRACT_INFO_TREE,
     MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_FAUCET_PUBKEYS, MONEY_CONTRACT_INFO_TREE,
     MONEY_CONTRACT_NULLIFIERS_TREE, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
     MONEY_CONTRACT_NULLIFIERS_TREE, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
@@ -193,7 +193,7 @@ pub(crate) fn money_transfer_process_instruction_v1(
         }
         }
 
 
         // If spend hook is set, check its correctness
         // If spend hook is set, check its correctness
-        if input.spend_hook != PALLAS_ZERO {
+        if input.spend_hook != pallas::Base::ZERO {
             let next_call_idx = call_idx + 1;
             let next_call_idx = call_idx + 1;
             if next_call_idx >= calls.len() as u32 {
             if next_call_idx >= calls.len() as u32 {
                 msg!("[TransferV1] Error: next_call_idx out of bounds (input {})", i);
                 msg!("[TransferV1] Error: next_call_idx out of bounds (input {})", i);

+ 2 - 2
src/contract/money/src/entrypoint/unstake_v1.rs

@@ -31,7 +31,7 @@ use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
 
 
 use crate::{
 use crate::{
     error::MoneyError,
     error::MoneyError,
-    model::{ConsensusUnstakeParamsV1, MoneyUnstakeParamsV1, MoneyUnstakeUpdateV1, PALLAS_ZERO},
+    model::{ConsensusUnstakeParamsV1, MoneyUnstakeParamsV1, MoneyUnstakeUpdateV1},
     MoneyFunction, CONSENSUS_CONTRACT_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_NULLIFIERS_TREE,
     MoneyFunction, CONSENSUS_CONTRACT_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_NULLIFIERS_TREE,
     MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE, MONEY_CONTRACT_COIN_ROOTS_TREE,
     MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE, MONEY_CONTRACT_COIN_ROOTS_TREE,
     MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
     MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
@@ -157,7 +157,7 @@ pub(crate) fn money_unstake_process_instruction_v1(
     }
     }
 
 
     // If next spend hook is set, check its correctness
     // If next spend hook is set, check its correctness
-    if params.spend_hook != PALLAS_ZERO {
+    if params.spend_hook != pallas::Base::ZERO {
         let next_call_idx = call_idx + 1;
         let next_call_idx = call_idx + 1;
         if next_call_idx >= calls.len() as u32 {
         if next_call_idx >= calls.len() as u32 {
             msg!("[MoneyUnstakeV1] Error: next_call_idx out of bounds");
             msg!("[MoneyUnstakeV1] Error: next_call_idx out of bounds");

+ 2 - 5
src/contract/money/src/model.rs

@@ -248,10 +248,12 @@ pub struct ConsensusStakeParamsV1 {
 
 
 /// State update for `Consensus::Stake`
 /// State update for `Consensus::Stake`
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+// ANCHOR: ConsensusStakeUpdate
 pub struct ConsensusStakeUpdateV1 {
 pub struct ConsensusStakeUpdateV1 {
     /// The newly minted coin
     /// The newly minted coin
     pub coin: Coin,
     pub coin: Coin,
 }
 }
+// ANCHOR_END: ConsensusStakeUpdate
 
 
 /// Parameters for `Consensus::Unstake`
 /// Parameters for `Consensus::Unstake`
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
@@ -266,8 +268,3 @@ pub struct ConsensusUnstakeUpdateV1 {
     /// Revealed nullifier
     /// Revealed nullifier
     pub nullifier: Nullifier,
     pub nullifier: Nullifier,
 }
 }
-
-// `pallas::Base` used as prefix/suffix in poseidon hash
-pub const PALLAS_ZERO: pallas::Base = pallas::Base::zero();
-// `pallas::Scalar` used as prefix/suffix in poseidon hash
-pub const SCALAR_ZERO: pallas::Scalar = pallas::Scalar::zero();