Skip to content

Commit

Permalink
Merge branch 'main' into install-additional-deb
Browse files Browse the repository at this point in the history
  • Loading branch information
willemneal authored Sep 10, 2024
2 parents fde4b3e + 6b61f2d commit bbad1c2
Show file tree
Hide file tree
Showing 15 changed files with 661 additions and 2 deletions.
1 change: 1 addition & 0 deletions cmd/crates/soroban-test/tests/it/integration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod bindings;
mod cookbook;
mod custom_types;
mod dotenv;
mod fund;
Expand Down
292 changes: 292 additions & 0 deletions cmd/crates/soroban-test/tests/it/integration/cookbook.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
use predicates::prelude::*;
use soroban_cli::config::network::passphrase::LOCAL;
use soroban_test::TestEnv;
use std::fs;
use std::path::PathBuf;

fn parse_command(command: &str) -> Vec<String> {
command
.replace("\\\n", " ")
.split_whitespace()
.map(String::from)
.collect()
}

async fn run_command(
sandbox: &TestEnv,
command: &str,
wasm_path: &str,
wasm_hash: &str,
source: &str,
contract_id: &str,
bob_id: &str,
native_id: &str,
key_xdr: &str,
) -> Result<(), String> {
if command.contains("export") {
return Ok(());
}
let args = parse_command(command);
if args.is_empty() {
return Err("Empty command".to_string());
}
if command.contains("contract asset deploy") {
return Ok(());
}
/*if command.contains("keys generate"){
return Ok(());
}*/
let cmd = args[1].clone();
let mut modified_args: Vec<String> = Vec::new();
let mut skip_next = false;

for (index, arg) in args[2..].iter().enumerate() {
if skip_next {
skip_next = false;
continue;
}

match arg.as_str() {
"--wasm" => {
modified_args.push(arg.to_string());
modified_args.push(wasm_path.to_string());
skip_next = true;
}
"--wasm-hash" => {
modified_args.push(arg.to_string());
modified_args.push(wasm_hash.to_string());
skip_next = true;
}
"--source" | "--source-account" => {
modified_args.push(arg.to_string());
modified_args.push(source.to_string());
skip_next = true;
}
"--contract-id" | "--id" => {
modified_args.push(arg.to_string());
modified_args.push(contract_id.to_string());
skip_next = true;
}
"--network-passphrase" => {
modified_args.push(arg.to_string());
modified_args.push(LOCAL.to_string());
skip_next = true;
}
"--network" => {
modified_args.push(arg.to_string());
modified_args.push("local".to_string());
skip_next = true;
}
"--key-xdr" => {
modified_args.push(arg.to_string());
modified_args.push(key_xdr.to_string());
skip_next = true;
}
"<DURABILITY>" => {
modified_args.push("persistent".to_string());
skip_next = false;
}
"<KEY>" => {
modified_args.push("COUNTER".to_string());
skip_next = false;
}
"<Bob_ID>" => {
modified_args.push(bob_id.to_string());
skip_next = false;
}
"<asset_contract_ID>" => {
modified_args.push(native_id.to_string());
skip_next = false;
}
_ => modified_args.push(arg.to_string()),
}

// If this is the last argument, don't skip the next one
if index == args[2..].len() - 1 {
skip_next = false;
}
}

println!("Executing command: {} {}", cmd, modified_args.join(" "));
let result = sandbox.new_assert_cmd(&cmd).args(&modified_args).assert();

if command.contains("keys generate") {
result
.code(predicates::ord::eq(0).or(predicates::ord::eq(1)))
.stderr(
predicate::str::is_empty().or(predicates::str::contains("Generated new key for")
.or(predicates::str::contains("The identity")
.and(predicates::str::contains("already exists")))),
);
} else if command.contains("contract invoke") {
result
.failure()
.stderr(predicates::str::contains("error: unrecognized subcommand"));
} else if command.contains("contract restore") {
result
.failure()
.stderr(predicates::str::contains("TxSorobanInvalid"));
} else {
result.success();
}

Ok(())
}

async fn test_mdx_file(
sandbox: &TestEnv,
file_path: &str,
wasm_path: &str,
wasm_hash: &str,
source: &str,
contract_id: &str,
bob_id: &str,
native_id: &str,
key_xdr: &str,
) -> Result<(), String> {
let content = fs::read_to_string(file_path)
.map_err(|e| format!("Failed to read file {}: {}", file_path, e))?;

let commands: Vec<&str> = content
.split("```bash")
.skip(1)
.filter_map(|block| block.split("```").next())
.collect();

println!("Testing commands from file: {}", file_path);

for (i, command) in commands.iter().enumerate() {
println!("Running command {}: {}", i + 1, command);
run_command(
sandbox,
command,
wasm_path,
wasm_hash,
source,
contract_id,
bob_id,
native_id,
key_xdr,
)
.await?;
}

Ok(())
}

fn get_repo_root() -> PathBuf {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
let mut path = PathBuf::from(manifest_dir);
for _ in 0..3 {
path.pop();
}
path
}

#[cfg(test)]
mod tests {
use soroban_test::AssertExt;

use crate::integration::util::{deploy_hello, HELLO_WORLD};

use super::*;

#[tokio::test]
async fn test_all_mdx_files() {
let sandbox = TestEnv::new();
let wasm = HELLO_WORLD;
let wasm_path = wasm.path();
let wasm_hash = wasm.hash().expect("should exist").to_string();
let source = "test";

sandbox
.new_assert_cmd("keys")
.arg("fund")
.arg(source)
.assert()
.success();

sandbox
.new_assert_cmd("keys")
.arg("generate")
.arg("bob")
.assert()
.success();
let bob_id = sandbox
.new_assert_cmd("keys")
.arg("address")
.arg("bob")
.assert()
.success()
.stdout_as_str();
sandbox
.new_assert_cmd("contract")
.arg("asset")
.arg("deploy")
.arg("--asset")
.arg("native")
.arg("--source-account")
.arg(source)
.output()
.expect("Failed to execute command");
let native_id = sandbox
.new_assert_cmd("contract")
.arg("id")
.arg("asset")
.arg("--asset")
.arg("native")
.arg("--source-account")
.arg(source)
.assert()
.stdout_as_str();
let contract_id = deploy_hello(&sandbox).await;
sandbox
.invoke_with_test(&["--id", &contract_id, "--", "inc"])
.await
.unwrap();
let read_xdr = sandbox
.new_assert_cmd("contract")
.arg("read")
.arg("--id")
.arg(contract_id.clone())
.arg("--output")
.arg("xdr")
.arg("--key")
.arg("COUNTER")
.arg("--source-account")
.arg(source)
.assert()
.stdout_as_str();
let key_xdr = read_xdr.split(',').next().unwrap_or("").trim();
let repo_root = get_repo_root();
let docs_dir = repo_root.join("cookbook");
if !docs_dir.is_dir() {
panic!("docs directory not found");
}

for entry in fs::read_dir(docs_dir).expect("Failed to read docs directory") {
let entry = entry.expect("Failed to read directory entry");
let path = entry.path();

if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("mdx") {
let file_path = path.to_str().unwrap();
match test_mdx_file(
&sandbox,
file_path,
&wasm_path.to_str().unwrap(),
&wasm_hash,
source,
&contract_id,
&bob_id,
&native_id,
&key_xdr,
)
.await
{
Ok(_) => println!("Successfully tested all commands in {}", file_path),
Err(e) => panic!("Error testing {}: {}", file_path, e),
}
}
}
}
}
2 changes: 1 addition & 1 deletion cmd/crates/soroban-test/tests/it/integration/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub async fn extend(sandbox: &TestEnv, id: &str, value: Option<&str>) {
"--durability",
"persistent",
"--ledgers-to-extend",
"100000",
"100001",
];
if let Some(value) = value {
args.push("--key");
Expand Down
1 change: 0 additions & 1 deletion cmd/soroban-cli/src/commands/contract/extend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ impl NetworkRunnable for Cmd {
let network = config.get_network()?;
tracing::trace!(?network);
let keys = self.key.parse_keys(&config.locator, &network)?;
let network = &config.get_network()?;
let client = Client::new(&network.rpc_url)?;
let key = config.key_pair()?;
let extend_to = self.ledgers_to_extend();
Expand Down
61 changes: 61 additions & 0 deletions cookbook/contract-lifecycle.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: Contract Lifecycle
hide_table_of_contents: true
description: Manage the lifecycle of a Stellar smart contract using the CLI
---

To manage the lifecycle of a Stellar smart contract using the CLI, follow these steps:

1. Create an identity for Alice:

```bash
stellar keys generate alice
```

2. Fund the identity:

```bash
stellar keys fund alice
```

3. Deploy a contract:

```bash
stellar contract deploy --wasm /path/to/contract.wasm --source alice --network testnet
```

This will display the resulting contract ID, e.g.:

```
CBB65ZLBQBZL5IYHDHEEPCVUUMFOQUZSQKAJFV36R7TZETCLWGFTRLOQ
```

To learn more about how to build contract `.wasm` files, take a look at our [getting started tutorial](https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup).

4. Initialize the contract:

```bash
stellar contract invoke --id <CONTRACT_ID> --source alice --network testnet -- initialize --param1 value1 --param2 value2
```

5. Invoke a contract function:

```bash
stellar contract invoke --id <CONTRACT_ID> --source alice --network testnet -- function_name --arg1 value1 --arg2 value2
```

6. View the contract's state:

```bash
stellar contract read --id <CONTRACT_ID> --network testnet --source alice --durability <DURABILITY> --key <KEY>
```

Note: `<DURABILITY>` is either `persistent` or `temporary`. `KEY` provides the key of the storage entry being read.

7. Manage expired states:

```bash
stellar contract extend --id <CONTRACT_ID> --ledgers-to-extend 1000 --source alice --network testnet --durability <DURABILITY> --key <KEY>
```

This extends the state of the instance provided by the given key to at least 1000 ledgers from the current ledger.
14 changes: 14 additions & 0 deletions cookbook/deploy-contract.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: Deploy a contract from installed Wasm bytecode
hide_table_of_contents: true
description: Deploy an instance of a compiled contract that is already installed on the network
---

To deploy an instance of a compiled smart contract that has already been installed onto the Stellar network, use the `stellar contract deploy` command:

```bash
stellar contract deploy \
--source S... \
--network testnet \
--wasm-hash <hex-encoded-wasm-hash>
```
Loading

0 comments on commit bbad1c2

Please sign in to comment.