Skip to content

Commit

Permalink
fix: deployment estimation deferred to sdk (#6212)
Browse files Browse the repository at this point in the history
  • Loading branch information
kayagokalp authored Jul 3, 2024
1 parent 8769cf9 commit db8ba0b
Show file tree
Hide file tree
Showing 10 changed files with 392 additions and 242 deletions.
21 changes: 20 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,25 @@ jobs:
mv fuel-core-${{ needs.get-fuel-core-version.outputs.fuel_core_version }}-x86_64-unknown-linux-gnu/fuel-core /usr/local/bin/fuel-core
- name: Run tests
run: cargo test --locked --release -p forc-debug
cargo-test-forc-client:
runs-on: ubuntu-latest
needs: get-fuel-core-version
steps:
- uses: actions/checkout@v3
- name: Install toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
targets: "x86_64-unknown-linux-gnu, wasm32-unknown-unknown"
- uses: Swatinem/rust-cache@v2
- name: Install fuel-core for tests
run: |
curl -sSLf https://github.com/FuelLabs/fuel-core/releases/download/v${{ needs.get-fuel-core-version.outputs.fuel_core_version }}/fuel-core-${{ needs.get-fuel-core-version.outputs.fuel_core_version }}-x86_64-unknown-linux-gnu.tar.gz -L -o fuel-core.tar.gz
tar -xvf fuel-core.tar.gz
chmod +x fuel-core-${{ needs.get-fuel-core-version.outputs.fuel_core_version }}-x86_64-unknown-linux-gnu/fuel-core
mv fuel-core-${{ needs.get-fuel-core-version.outputs.fuel_core_version }}-x86_64-unknown-linux-gnu/fuel-core /usr/local/bin/fuel-core
- name: Run tests
run: cargo test --locked --release -p forc-client
cargo-test-sway-lsp:
runs-on: ubuntu-latest
steps:
Expand All @@ -538,7 +557,7 @@ jobs:
toolchain: ${{ env.RUST_VERSION }}
- uses: Swatinem/rust-cache@v2
- name: Run tests
run: cargo test --locked --release --workspace --exclude forc-debug --exclude sway-lsp
run: cargo test --locked --release --workspace --exclude forc-debug --exclude sway-lsp --exclude forc-client
cargo-unused-deps-check:
runs-on: ubuntu-latest
steps:
Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions forc-plugins/forc-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ sway-utils = { version = "0.61.1", path = "../../sway-utils" }
tokio = { version = "1.8", features = ["macros", "rt-multi-thread", "process"] }
tracing = "0.1"

[dev-dependencies]
portpicker = "0.1.1"
tempfile = "3"
toml_edit = "0.21.1"

[[bin]]
name = "forc-deploy"
path = "src/bin/deploy.rs"
Expand Down
5 changes: 5 additions & 0 deletions forc-plugins/forc-client/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ pub const DEVNET_FAUCET_URL: &str = "https://faucet-devnet.fuel.network";
pub const DEVNET_ENDPOINT_URL: &str = "https://devnet.fuel.network";
pub const TESTNET_FAUCET_URL: &str = "https://faucet-testnet.fuel.network";
pub const TESTNET_ENDPOINT_URL: &str = "https://testnet.fuel.network";
/// Default PrivateKey to sign transactions submitted to local node.
pub const DEFAULT_PRIVATE_KEY: &str =
"0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c";
/// The maximum time to wait for a transaction to be included in a block by the node
pub const TX_SUBMIT_TIMEOUT_MS: u64 = 30_000u64;
88 changes: 45 additions & 43 deletions forc-plugins/forc-client/src/op/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
use crate::{
cmd,
constants::TX_SUBMIT_TIMEOUT_MS,
util::{
gas::get_estimated_max_fee,
node_url::get_node_url,
pkg::built_pkgs,
tx::{TransactionBuilderExt, WalletSelectionMode, TX_SUBMIT_TIMEOUT_MS},
tx::{prompt_forc_wallet_password, select_secret_key, WalletSelectionMode},
},
};
use anyhow::{bail, Context, Result};
use forc_pkg::manifest::GenericManifestFile;
use forc_pkg::{self as pkg, PackageManifestFile};
use forc_tracing::println_warning;
use forc_util::default_output_directory;
use forc_wallet::utils::default_wallet_path;
use fuel_core_client::client::types::TransactionStatus;
use fuel_core_client::client::FuelClient;
use fuel_crypto::fuel_types::ChainId;
use fuel_tx::{Output, Salt, TransactionBuilder};
use fuel_tx::Salt;
use fuel_vm::prelude::*;
use fuels_accounts::provider::Provider;
use fuels_accounts::{provider::Provider, wallet::WalletUnlocked, Account};
use fuels_core::types::{transaction::TxPolicies, transaction_builders::CreateTransactionBuilder};
use futures::FutureExt;
use pkg::{manifest::build_profile::ExperimentalFlags, BuildProfile, BuiltPackage};
use serde::{Deserialize, Serialize};
Expand All @@ -30,7 +32,7 @@ use sway_core::language::parsed::TreeType;
use sway_core::BuildTarget;
use tracing::info;

#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub struct DeployedContract {
pub id: fuel_tx::ContractId,
}
Expand Down Expand Up @@ -176,6 +178,13 @@ pub async fn deploy(command: cmd::Deploy) -> Result<Vec<DeployedContract>> {
None
};

let wallet_mode = if command.default_signer || command.signing_key.is_some() {
WalletSelectionMode::Manual
} else {
let password = prompt_forc_wallet_password(&default_wallet_path())?;
WalletSelectionMode::ForcWallet(password)
};

for pkg in built_pkgs {
if pkg
.descriptor
Expand All @@ -197,8 +206,14 @@ pub async fn deploy(command: cmd::Deploy) -> Result<Vec<DeployedContract>> {
bail!("Both `--salt` and `--default-salt` were specified: must choose one")
}
};
let contract_id =
deploy_pkg(&command, &pkg.descriptor.manifest_file, &pkg, salt).await?;
let contract_id = deploy_pkg(
&command,
&pkg.descriptor.manifest_file,
&pkg,
salt,
&wallet_mode,
)
.await?;
contract_ids.push(contract_id);
}
}
Expand All @@ -211,6 +226,7 @@ pub async fn deploy_pkg(
manifest: &PackageManifestFile,
compiled: &BuiltPackage,
salt: Salt,
wallet_mode: &WalletSelectionMode,
) -> Result<DeployedContract> {
let node_url = get_node_url(&command.node, &manifest.network)?;
let client = FuelClient::new(node_url.clone())?;
Expand All @@ -232,44 +248,30 @@ pub async fn deploy_pkg(
let state_root = Contract::initial_state_root(storage_slots.iter());
let contract_id = contract.id(&salt, &root, &state_root);

let wallet_mode = if command.manual_signing {
WalletSelectionMode::Manual
} else {
WalletSelectionMode::ForcWallet
};

let provider = Provider::connect(node_url.clone()).await?;
let tx_policies = TxPolicies::default();

let mut tb = CreateTransactionBuilder::prepare_contract_deployment(
bytecode.clone(),
contract_id,
state_root,
salt,
storage_slots.clone(),
tx_policies,
);
let signing_key = select_secret_key(
wallet_mode,
command.default_signer || command.unsigned,
command.signing_key,
&provider,
)
.await?
.ok_or_else(|| anyhow::anyhow!("failed to select a signer for the transaction"))?;
let wallet = WalletUnlocked::new_from_private_key(signing_key, Some(provider.clone()));

// We need a tx for estimation without the signature.
let mut tb =
TransactionBuilder::create(bytecode.as_slice().into(), salt, storage_slots.clone());
tb.maturity(command.maturity.maturity.into())
.add_output(Output::contract_created(contract_id, state_root));
let tx_for_estimation = tb.finalize_without_signature_inner();

// If user specified max_fee use that but if not, we will estimate with %10 safety margin.
let max_fee = if let Some(max_fee) = command.gas.max_fee {
max_fee
} else {
let estimation_margin = 10;
get_estimated_max_fee(
tx_for_estimation.clone(),
&provider,
&client,
estimation_margin,
)
.await?
};

let tx = tb
.max_fee_limit(max_fee)
.finalize_signed(
provider.clone(),
command.default_signer || command.unsigned,
command.signing_key,
wallet_mode,
)
.await?;
wallet.add_witnesses(&mut tb)?;
wallet.adjust_for_fee(&mut tb, 0).await?;
let tx = tb.build(provider).await?;
let tx = Transaction::from(tx);

let chain_id = client.chain_info().await?.consensus_parameters.chain_id();
Expand Down
2 changes: 1 addition & 1 deletion forc-plugins/forc-client/src/op/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ mod deploy;
mod run;
mod submit;

pub use deploy::deploy;
pub use deploy::{deploy, DeployedContract};
pub use run::run;
pub use submit::submit;
24 changes: 17 additions & 7 deletions forc-plugins/forc-client/src/op/run/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
mod encode;
use crate::{
cmd,
constants::TX_SUBMIT_TIMEOUT_MS,
util::{
gas::get_script_gas_used,
node_url::get_node_url,
pkg::built_pkgs,
tx::{TransactionBuilderExt, WalletSelectionMode, TX_SUBMIT_TIMEOUT_MS},
tx::{prompt_forc_wallet_password, TransactionBuilderExt, WalletSelectionMode},
},
};
use anyhow::{anyhow, bail, Context, Result};
use forc_pkg::{self as pkg, fuel_core_not_running, PackageManifestFile};
use forc_tracing::println_warning;
use forc_util::tx_utils::format_log_receipts;
use forc_wallet::utils::default_wallet_path;
use fuel_core_client::client::FuelClient;
use fuel_tx::{ContractId, Transaction, TransactionBuilder};
use fuels_accounts::provider::Provider;
Expand Down Expand Up @@ -49,14 +51,26 @@ pub async fn run(command: cmd::Run) -> Result<Vec<RanScript>> {
};
let build_opts = build_opts_from_cmd(&command);
let built_pkgs_with_manifest = built_pkgs(&curr_dir, &build_opts)?;
let wallet_mode = if command.default_signer || command.signing_key.is_some() {
WalletSelectionMode::Manual
} else {
let password = prompt_forc_wallet_password(&default_wallet_path())?;
WalletSelectionMode::ForcWallet(password)
};
for built in built_pkgs_with_manifest {
if built
.descriptor
.manifest_file
.check_program_type(&[TreeType::Script])
.is_ok()
{
let pkg_receipts = run_pkg(&command, &built.descriptor.manifest_file, &built).await?;
let pkg_receipts = run_pkg(
&command,
&built.descriptor.manifest_file,
&built,
&wallet_mode,
)
.await?;
receipts.push(pkg_receipts);
}
}
Expand All @@ -68,6 +82,7 @@ pub async fn run_pkg(
command: &cmd::Run,
manifest: &PackageManifestFile,
compiled: &BuiltPackage,
wallet_mode: &WalletSelectionMode,
) -> Result<RanScript> {
let node_url = get_node_url(&command.node, &manifest.network)?;

Expand Down Expand Up @@ -101,11 +116,6 @@ pub async fn run_pkg(
.map_err(|e| anyhow!("Failed to parse contract id: {}", e))
})
.collect::<Result<Vec<ContractId>>>()?;
let wallet_mode = if command.manual_signing {
WalletSelectionMode::Manual
} else {
WalletSelectionMode::ForcWallet
};

let mut tb = TransactionBuilder::script(compiled.bytecode.bytes.clone(), script_data);
tb.maturity(command.maturity.maturity.into())
Expand Down
63 changes: 3 additions & 60 deletions forc-plugins/forc-client/src/util/gas.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
use anyhow::Result;

use fuel_core_client::client::FuelClient;
use fuel_core_types::services::executor::TransactionExecutionResult;
use fuel_tx::{
field::{Inputs, MaxFeeLimit, Witnesses},
Buildable, Chargeable, Create, Input, Script, Transaction, TxPointer,
field::{Inputs, Witnesses},
Buildable, Chargeable, Input, Script, TxPointer,
};
use fuels_accounts::provider::Provider;
use fuels_core::{
constants::DEFAULT_GAS_ESTIMATION_BLOCK_HORIZON, types::transaction::ScriptTransaction,
};
use fuels_core::types::transaction::ScriptTransaction;

fn no_spendable_input<'a, I: IntoIterator<Item = &'a Input>>(inputs: I) -> bool {
!inputs.into_iter().any(|i| {
Expand Down Expand Up @@ -55,56 +51,3 @@ pub(crate) async fn get_script_gas_used(mut tx: Script, provider: &Provider) ->
.await?;
Ok(estimated_tx_cost.gas_used)
}

/// Returns an estimation for the max fee of `Create` transactions.
/// Accepts a `tolerance` which is used to add some safety margin to the estimation.
/// Resulting estimation is calculated as `(dry_run_estimation * tolerance)/100 + dry_run_estimation)`.
pub(crate) async fn get_estimated_max_fee(
tx: Create,
provider: &Provider,
client: &FuelClient,
tolerance: u64,
) -> Result<u64> {
let mut tx = tx.clone();
// Add dummy input to get past validation for dry run.
let no_spendable_input = no_spendable_input(tx.inputs());
let base_asset_id = provider.base_asset_id();
if no_spendable_input {
tx.inputs_mut().push(Input::coin_signed(
Default::default(),
Default::default(),
1_000_000_000,
*base_asset_id,
TxPointer::default(),
0,
));

// Add an empty `Witness` for the `coin_signed` we just added
// and increase the witness limit
tx.witnesses_mut().push(Default::default());
}
let consensus_params = provider.consensus_parameters();
let gas_price = provider
.estimate_gas_price(DEFAULT_GAS_ESTIMATION_BLOCK_HORIZON)
.await?
.gas_price;
let max_fee = tx.max_fee(
consensus_params.gas_costs(),
consensus_params.fee_params(),
gas_price,
);
tx.set_max_fee_limit(max_fee as u64);
let tx = Transaction::from(tx);

let tx_status = client
.dry_run(&[tx])
.await
.map(|mut status_vec| status_vec.remove(0))?;
let total_fee = match tx_status.result {
TransactionExecutionResult::Success { total_fee, .. } => total_fee,
TransactionExecutionResult::Failed { total_fee, .. } => total_fee,
};

let total_fee_with_tolerance = ((total_fee * tolerance) / 100) + total_fee;
Ok(total_fee_with_tolerance)
}
Loading

0 comments on commit db8ba0b

Please sign in to comment.