Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow forc deploy to submit transaction without waiting for commit confirmation #6294

Merged
merged 35 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
72d7c3a
feat(submit-only): add command to submit tx without wait execution
luisburigo Jul 22, 2024
38f76df
feat(submit-only): add command to submit tx without wait execution
luisburigo Jul 23, 2024
ebf0b27
feat(submit-only): add logs on submit the tx
luisburigo Jul 24, 2024
37a8254
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Jul 24, 2024
87a0e27
feat(submit-only): correct typos in command and comments
luisburigo Jul 25, 2024
a6eaf1f
feat(submit-only): move deployment logs inside the method
luisburigo Jul 25, 2024
84a5480
Merge remote-tracking branch 'origin/lb/feat/deploy-submit-only' into…
luisburigo Jul 25, 2024
f1d1a48
feat(submit-only): remove package name from method and retrieve it fr…
luisburigo Jul 25, 2024
4e26ebd
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Jul 25, 2024
adcc3df
feat(submit-only): move deployment logs and fix merge
luisburigo Jul 26, 2024
224b80c
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Jul 26, 2024
27a5ac3
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Jul 26, 2024
ebc78a8
feat(submit-only): add description in method
luisburigo Jul 30, 2024
5eb47e6
Merge remote-tracking branch 'origin/lb/feat/deploy-submit-only' into…
luisburigo Jul 30, 2024
b3f42fc
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Jul 30, 2024
2b512b6
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Jul 31, 2024
59d36c5
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Aug 1, 2024
8e0f871
Merge branch 'master' into lb/feat/deploy-submit-only
sdankel Aug 1, 2024
57becdd
feat(submit-only): add doc
luisburigo Aug 5, 2024
7d19a7d
Merge remote-tracking branch 'origin/lb/feat/deploy-submit-only' into…
luisburigo Aug 5, 2024
a0f51e6
Merge branch 'master' into lb/feat/deploy-submit-only
sdankel Aug 5, 2024
31bdac9
feat(submit-only): change doc to add use case of submit-only
luisburigo Aug 6, 2024
6cb4482
Merge remote-tracking branch 'origin/lb/feat/deploy-submit-only' into…
luisburigo Aug 6, 2024
a289294
feat(submit-only): change doc to add section of delayed transactions
luisburigo Aug 6, 2024
b320488
feat(submit-only): add blank lines around headings for linter compliance
luisburigo Aug 6, 2024
1251245
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Aug 7, 2024
a6f668d
Merge remote-tracking branch 'origin/master' into lb/feat/deploy-subm…
luisburigo Aug 7, 2024
7ee9bf1
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Aug 7, 2024
559c616
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Aug 7, 2024
0065c39
Merge branch 'master' into lb/feat/deploy-submit-only
K1-R1 Aug 8, 2024
6e70c8a
feat(submit-only): add multisig to spell check
luisburigo Aug 8, 2024
4d36d0d
Merge remote-tracking branch 'origin/lb/feat/deploy-submit-only' into…
luisburigo Aug 8, 2024
7f29ca1
Merge branch 'master' into lb/feat/deploy-submit-only
K1-R1 Aug 9, 2024
fcf2029
feat(submit-only): change expected contract id in submit only test
luisburigo Aug 9, 2024
85a3f61
Merge branch 'master' into lb/feat/deploy-submit-only
luisburigo Aug 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/book/spell-check-custom-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,5 @@ booleans
underflows
Codec
bool
str
str
multisig
10 changes: 10 additions & 0 deletions docs/book/src/forc/plugins/forc_client/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ forc-deploy --target beta-5

Since deploying and running projects on the testnet cost gas, you will need coins to pay for them. You can get some using the [testnet faucet](https://faucet-testnet.fuel.network/).

## Delayed transactions

For delayed transactions, you can use the `--submit-only` flag. This flag allows you to submit the transaction without waiting for its finalization.
luisburigo marked this conversation as resolved.
Show resolved Hide resolved

One use case for this is multisig transactions, where a deployment transaction may stay in a pending state while waiting for all signatures.

```sh
forc-deploy --submit-only
```

## Deployment Artifacts

forc-deploy saves the details of each deployment in the `out/deployments` folder within the project's root directory. Below is an example of a deployment artifact:
Expand Down
3 changes: 3 additions & 0 deletions forc-plugins/forc-client/src/cmd/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ pub struct Command {
/// Deprecated in favor of `--default-signer`.
#[clap(long)]
pub unsigned: bool,
/// Submit the deployment transaction(s) without waiting for execution to complete.
#[clap(long)]
pub submit_only: bool,
luisburigo marked this conversation as resolved.
Show resolved Hide resolved
/// Set the key to be used for signing.
pub signing_key: Option<SecretKey>,
/// Sign the deployment transaction manually.
Expand Down
186 changes: 120 additions & 66 deletions forc-plugins/forc-client/src/op/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use forc_pkg::{self as pkg, PackageManifestFile};
use forc_tracing::{println_action_green, 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::types::{ChainInfo, TransactionStatus};
use fuel_core_client::client::FuelClient;
use fuel_crypto::fuel_types::ChainId;
use fuel_tx::{Salt, Transaction};
Expand Down Expand Up @@ -52,7 +52,7 @@ pub struct DeploymentArtifact {
chain_id: ChainId,
contract_id: String,
deployment_size: usize,
deployed_block_height: u32,
deployed_block_height: Option<u32>,
}

impl DeploymentArtifact {
Expand Down Expand Up @@ -442,79 +442,84 @@ pub async fn deploy_pkg(
let chain_info = client.chain_info().await?;
let chain_id = chain_info.consensus_parameters.chain_id();

let deployment_request = client.submit_and_await_commit(&tx).map(|res| match res {
Ok(logs) => match logs {
TransactionStatus::Submitted { .. } => {
bail!("contract {} deployment timed out", &contract_id);
}
TransactionStatus::Success { block_height, .. } => {
let pkg_name = manifest.project_name();
let target = Target::from_str(&chain_info.name).unwrap_or(Target::testnet());
let (contract_url, block_url) = match target.explorer_url() {
Some(explorer_url) => (
format!("{explorer_url}/contract/0x"),
format!("{explorer_url}/block/"),
),
None => ("".to_string(), "".to_string()),
};
println_action_green(
"Finished",
&format!("deploying {pkg_name} {contract_url}{contract_id}"),
);
let block_height_formatted =
match u32::from_str_radix(&block_height.to_string(), 16) {
Ok(decimal) => format!("{block_url}{decimal}"),
Err(_) => block_height.to_string(),
};

println_action_green("Deployed", &format!("in block {block_height_formatted}"));

// If only submitting the transaction, don't wait for the deployment to complete
let contract_id: ContractId = if command.submit_only {
match client.submit(&tx).await {
Ok(transaction_id) => {
// Create a deployment artifact.
let deployment_size = bytecode.len();
let deployment_artifact = DeploymentArtifact {
transaction_id: format!("0x{}", tx.id(&chain_id)),
salt: format!("0x{}", salt),
network_endpoint: node_url.to_string(),
chain_id,
contract_id: format!("0x{}", contract_id),
deployment_size,
deployed_block_height: *block_height,
};

let output_dir = command
.pkg
.output_directory
.as_ref()
.map(PathBuf::from)
.unwrap_or_else(|| default_output_directory(manifest.dir()))
.join("deployments");
deployment_artifact.to_file(&output_dir, pkg_name, contract_id)?;

Ok(contract_id)
create_deployment_artifact(
DeploymentArtifact {
transaction_id: format!("0x{}", transaction_id),
salt: format!("0x{}", salt),
network_endpoint: node_url.to_string(),
chain_id,
contract_id: format!("0x{}", contract_id),
deployment_size: bytecode.len(),
deployed_block_height: None,
},
command,
manifest,
chain_info,
)?;

contract_id
}
e => {
Err(e) => {
bail!(
"contract {} failed to deploy due to an error: {:?}",
&contract_id,
e
)
}
},
Err(e) => bail!("{e}"),
});

// submit contract deployment with a timeout
let contract_id = tokio::time::timeout(
Duration::from_millis(TX_SUBMIT_TIMEOUT_MS),
deployment_request,
)
.await
.with_context(|| {
format!(
"Timed out waiting for contract {} to deploy. The transaction may have been dropped.",
&contract_id
}
} else {
let deployment_request = client.submit_and_await_commit(&tx).map(|res| match res {
Ok(logs) => match logs {
TransactionStatus::Submitted { .. } => {
bail!("contract {} deployment timed out", &contract_id);
}
TransactionStatus::Success { block_height, .. } => {
// Create a deployment artifact.
create_deployment_artifact(
DeploymentArtifact {
transaction_id: format!("0x{}", tx.id(&chain_id)),
salt: format!("0x{}", salt),
network_endpoint: node_url.to_string(),
chain_id,
contract_id: format!("0x{}", contract_id),
deployment_size: bytecode.len(),
deployed_block_height: Some(*block_height),
},
command,
manifest,
chain_info,
)?;

Ok(contract_id)
}
e => {
bail!(
"contract {} failed to deploy due to an error: {:?}",
&contract_id,
e
)
}
},
Err(e) => bail!("{e}"),
});
tokio::time::timeout(
Duration::from_millis(TX_SUBMIT_TIMEOUT_MS),
deployment_request,
)
})??;
.await
.with_context(|| {
format!(
"Timed out waiting for contract {} to deploy. The transaction may have been dropped.",
&contract_id
)
})??
};

Ok(contract_id)
}

Expand Down Expand Up @@ -559,6 +564,55 @@ fn build_opts_from_cmd(cmd: &cmd::Deploy) -> pkg::BuildOpts {
}
}

/// Creates a deployment artifact and writes it to a file.
///
/// This function is used to generate a deployment artifact containing details
/// about the deployment, such as the transaction ID, salt, network endpoint,
/// chain ID, contract ID, deployment size, and deployed block height. It then
/// writes this artifact to a specified output directory.
fn create_deployment_artifact(
luisburigo marked this conversation as resolved.
Show resolved Hide resolved
deployment_artifact: DeploymentArtifact,
cmd: &cmd::Deploy,
manifest: &PackageManifestFile,
chain_info: ChainInfo,
) -> Result<()> {
let contract_id = ContractId::from_str(&deployment_artifact.contract_id).unwrap();
let pkg_name = manifest.project_name();

let target = Target::from_str(&chain_info.name).unwrap_or(Target::testnet());
let (contract_url, block_url) = match target.explorer_url() {
Some(explorer_url) => (
format!("{explorer_url}/contract/0x"),
format!("{explorer_url}/block/"),
),
None => ("".to_string(), "".to_string()),
};
println_action_green(
"Finished",
&format!("deploying {pkg_name} {contract_url}{contract_id}"),
);

let block_height = deployment_artifact.deployed_block_height;
if block_height.is_some() {
let block_height_formatted =
match u32::from_str_radix(&block_height.unwrap().to_string(), 16) {
Ok(decimal) => format!("{block_url}{decimal}"),
Err(_) => block_height.unwrap().to_string(),
};

println_action_green("Deployed", &format!("in block {block_height_formatted}"));
}

let output_dir = cmd
.pkg
.output_directory
.as_ref()
.map(PathBuf::from)
.unwrap_or_else(|| default_output_directory(manifest.dir()))
.join("deployments");
deployment_artifact.to_file(&output_dir, pkg_name, contract_id)
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
41 changes: 41 additions & 0 deletions forc-plugins/forc-client/tests/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,47 @@ async fn test_simple_deploy() {
assert_eq!(contract_ids, expected)
}

#[tokio::test]
async fn test_deploy_submit_only() {
let (mut node, port) = run_node();
let tmp_dir = tempdir().unwrap();
let project_dir = test_data_path().join("standalone_contract");
copy_dir(&project_dir, tmp_dir.path()).unwrap();
patch_manifest_file_with_path_std(tmp_dir.path()).unwrap();

let pkg = Pkg {
path: Some(tmp_dir.path().display().to_string()),
..Default::default()
};

let node_url = format!("http://127.0.0.1:{}/v1/graphql", port);

let target = NodeTarget {
node_url: Some(node_url),
target: None,
testnet: false,
};
let cmd = cmd::Deploy {
pkg,
salt: Some(vec![format!("{}", Salt::default())]),
node: target,
default_signer: true,
submit_only: true,
..Default::default()
};
let contract_ids = deploy(cmd).await.unwrap();
node.kill().unwrap();
let expected = vec![DeployedContract {
id: ContractId::from_str(
"822c8d3672471f64f14f326447793c7377b6e430122db23b622880ccbd8a33ef",
)
.unwrap(),
proxy: None,
}];

assert_eq!(contract_ids, expected)
}

#[tokio::test]
async fn test_deploy_fresh_proxy() {
let (mut node, port) = run_node();
Expand Down
Loading