Skip to content

Commit

Permalink
cctl: make deployable contracts runtime args specifiable in the cli
Browse files Browse the repository at this point in the history
  • Loading branch information
marijanp committed Sep 10, 2024
1 parent 7d7aa2d commit b61268c
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 53 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ itertools = "0.13"
nom = "7"
hex = "0.4"
sd-notify = "0.4"
serde = "1"
serde_json = "1"
tokio = { version = "1", features = [ "full", "tracing", "macros" ] }
tempfile = "3"
tracing = "0.1"
Expand Down
20 changes: 4 additions & 16 deletions bin/cctld.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use casper_types::runtime_args;
use clap::Parser;
use sd_notify::NotifyState;
use std::path::PathBuf;
Expand All @@ -10,10 +9,10 @@ pub struct Cli {
#[arg(short, long)]
pub working_dir: Option<PathBuf>,
#[arg(short, long)]
pub deploy_contract: Option<String>,
#[arg(short, long)]
pub deploy_contracts: Option<Vec<cctl::DeployableContract>>,
#[arg(short = 's', long)]
pub chainspec_path: Option<PathBuf>,
#[arg(short, long)]
#[arg(short = 'c', long)]
pub config_path: Option<PathBuf>,
}

Expand All @@ -24,20 +23,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.with_writer(std::io::stderr)
.init();
let cli = Cli::parse();
let deploy_contract = cli.deploy_contract.map(|deploy_contracts_arg| {
match deploy_contracts_arg.split_once(':') {
Some((hash_name, path)) => cctl::DeployableContract {
hash_name: hash_name.to_string(),
// FIXME at some point we want to make this parametrizable
runtime_args: runtime_args! {},
path: PathBuf::from(&path),
},
None => panic!("Error parsing the provided deploy contracts argument."),
}
});
let _network = cctl::CCTLNetwork::run(
cli.working_dir,
deploy_contract,
cli.deploy_contracts,
cli.chainspec_path,
cli.config_path,
)
Expand Down
36 changes: 24 additions & 12 deletions nixos/modules/cctl.nix
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,29 @@ in
'';
};

contract = mkOption {
type = types.nullOr (types.attrsOf types.path);
contracts = mkOption {
default = null;
example = { "contract hash name" = "/path/to/contract.wasm"; };
description = ''
The wasm compiled contract that should be deployed once the network is up and ready.
The name of the attribute should correspond to the contracts hash name when calling
https://docs.rs/casper-contract/latest/casper_contract/contract_api/storage/fn.new_locked_contract.html
'';
type = types.nullOr (types.listOf (types.submodule {
options = {
hash_name = mkOption {
type = types.str;
description = ''
The contracts hash name which was provided when calling
https://docs.rs/casper-contract/latest/casper_contract/contract_api/storage/fn.new_locked_contract.html
'';
};
path = mkOption {
type = types.path;
description = "The wasm compiled contract that should be deployed once the network is up and ready.";
};
runtime_args = mkOption {
default = null;
type = types.nullOr types.attrs;
description = "The runtime arguments expected by this contract.";
};
};
}));
};

};

config = mkIf cfg.enable {
Expand All @@ -83,9 +95,9 @@ in
"--working-dir"
cfg.workingDirectory
]
++ optionals (!builtins.isNull cfg.contract) ([
"--deploy-contract"
] ++ (lib.mapAttrsToList (hash_name: contract_path: "${hash_name}:${contract_path}") cfg.contract))
++ optionals (!builtins.isNull cfg.contracts) ([
"--deploy-contracts"
] ++ (lib.map (contract: builtins.toJSON contract) cfg.contracts))
++ optionals (!builtins.isNull cfg.chainspec) [
"--chainspec-path"
cfg.chainspec
Expand Down
8 changes: 7 additions & 1 deletion nixos/tests/verify-cctl-service.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ nixosTest {
];
services.cctl = {
enable = true;
contract = { "contract-hash" = contractWasm; };
contracts = [
{
hash_name = "contract-hash";
path = contractWasm;
runtime_args = null;
}
];
};
networking.firewall.allowedTCPPorts = [ 80 config.services.cctl.port ];
};
Expand Down
56 changes: 35 additions & 21 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ use anyhow::anyhow;
use backoff::{future::retry, ExponentialBackoff};
use hex::FromHex;
use itertools::{Either, Itertools};
use serde::{Deserialize, Serialize};
use std::env;
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use std::{
fs,
time::{Duration, Instant},
Expand All @@ -21,8 +23,8 @@ use casper_types::{
account::AccountHash,
contracts::ContractHash,
execution::{execution_result_v1::ExecutionResultV1, ExecutionResult},
DeployBuilder, ExecutableDeployItem, Key, PublicKey, RuntimeArgs, SecretKey, StoredValue,
TimeDiff, Timestamp,
runtime_args, DeployBuilder, ExecutableDeployItem, Key, PublicKey, RuntimeArgs, SecretKey,
StoredValue, TimeDiff, Timestamp,
};

use parsers::RawNodeType;
Expand Down Expand Up @@ -68,13 +70,22 @@ pub struct CCTLNetwork {
pub casper_sidecars: Vec<CasperSidecar>,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct DeployableContract {
/// This is the named key under which the contract hash is located
pub hash_name: String,
pub runtime_args: RuntimeArgs,
pub runtime_args: Option<RuntimeArgs>,
pub path: PathBuf,
}

impl FromStr for DeployableContract {
type Err = serde_json::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(s)
}
}

/// Configures the casper-client verbosity level depending on the tracing log level
pub fn casper_client_verbosity() -> Verbosity {
if tracing::enabled!(tracing::Level::TRACE) {
Expand All @@ -98,7 +109,7 @@ impl CCTLNetwork {
/// Ensure that two instances of this function are not running at the same time even in different processes.
pub async fn run(
working_dir: Option<PathBuf>,
contract_to_deploy: Option<DeployableContract>,
contracts_to_deploy: Option<Vec<DeployableContract>>,
chainspec_path: Option<PathBuf>,
config_path: Option<PathBuf>,
) -> anyhow::Result<CCTLNetwork> {
Expand Down Expand Up @@ -218,23 +229,26 @@ impl CCTLNetwork {
let deployer_pkey =
PublicKey::from_file(working_dir.join("assets/users/user-1/public_key.pem"))?;

let (hash_name, contract_hash) = deploy_contract(
&casper_sidecar_rpc_url,
&deployer_skey,
&deployer_pkey.to_account_hash(),
&contract_to_deploy,
)
.await?;
let contracts_dir = working_dir.join("contracts");
fs::create_dir_all(&contracts_dir)?;
fs::write(
contracts_dir.join(hash_name),
// For a ContractHash contract- will always be the prefix
contract_hash
.to_formatted_string()
.strip_prefix("contract-")
.unwrap(),
)?

for contract_to_deploy in contracts_to_deploy {
let (hash_name, contract_hash) = deploy_contract(
&casper_sidecar_rpc_url,
&deployer_skey,
&deployer_pkey.to_account_hash(),
&contract_to_deploy,
)
.await?;
fs::write(
contracts_dir.join(hash_name),
// For a ContractHash contract- will always be the prefix
contract_hash
.to_formatted_string()
.strip_prefix("contract-")
.unwrap(),
)?
}
}
Ok(CCTLNetwork {
working_dir,
Expand Down Expand Up @@ -284,8 +298,8 @@ async fn deploy_contract(
let casper_client_verbosity = casper_client_verbosity();

let contract_bytes = fs::read(path)?;
let contract =
ExecutableDeployItem::new_module_bytes(contract_bytes.into(), runtime_args.clone());
let runtime_args = runtime_args.clone().unwrap_or(runtime_args! {});
let contract = ExecutableDeployItem::new_module_bytes(contract_bytes.into(), runtime_args);
let deploy = DeployBuilder::new(
// TODO ideally make the chain-name configurable
"cspr-dev-cctl",
Expand Down
6 changes: 3 additions & 3 deletions tests/test_cctl_deploys_a_contract_successfully.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::fs;
use std::path::PathBuf;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};

use casper_types::{contracts::ContractHash, runtime_args};
use casper_types::contracts::ContractHash;
use cctl::{CCTLNetwork, DeployableContract};

fn tracing_init() {
Expand All @@ -23,11 +23,11 @@ async fn test_cctl_deploys_a_contract_successfully() {
let hash_name = "contract-hash";
let contract_to_deploy = DeployableContract {
hash_name: hash_name.to_string(),
runtime_args: runtime_args! {},
runtime_args: None,
path: contract_wasm_path,
};

let network = CCTLNetwork::run(None, Some(contract_to_deploy), None, None)
let network = CCTLNetwork::run(None, Some(vec![contract_to_deploy]), None, None)
.await
.unwrap();
let expected_contract_hash_path = network.working_dir.join("contracts").join(hash_name);
Expand Down

0 comments on commit b61268c

Please sign in to comment.