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

Update crates and tests to last state of art #21

Merged
merged 8 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 13 additions & 13 deletions .github/workflows/ci-casper-rust-contract.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ name: ci-casper-rust-contract

on:
push:
branches: [ master ]
branches: [master]
paths-ignore:
- '**.md'

pull_request:
branches: [ master ]
branches: [master]
paths-ignore:
- '**.md'

Expand All @@ -18,14 +18,14 @@ jobs:
os: [ubuntu-20.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
components: rustfmt, clippy
# Needed for gcc install
- run: sudo apt update && sudo apt install -y build-essential
- uses: Swatinem/rust-cache@v1
- run: make prepare
- run: make check-lint
- run: make test
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
components: rustfmt, clippy
# Needed for gcc install
- run: sudo apt update && sudo apt install -y build-essential wabt
- uses: Swatinem/rust-cache@v1
- run: make prepare
- run: make check-lint
- run: make test
10 changes: 5 additions & 5 deletions .github/workflows/nightly-scheduled-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
schedule:
# * is a special character in YAML so you have to quote this string
# runs every day at midnight
- cron: "0 0 * * *"
- cron: '0 0 * * *'

jobs:
nightly-make-test:
Expand All @@ -21,7 +21,7 @@ jobs:
components: rustfmt, clippy

# Needed for gcc install
- run: sudo apt update && sudo apt install -y build-essential
- run: sudo apt update && sudo apt install -y build-essential wabt
- uses: Swatinem/rust-cache@v1
- run: make prepare
- run: make test
Expand All @@ -31,9 +31,9 @@ jobs:
if: always()
with:
status: ${{ job.status }}
notification_title: "*{repo}*"
message_format: "{emoji} *{workflow}* *{status_message}* in <{repo_url}|{repo}@{branch}> on <{commit_url}|{commit_sha}>"
footer: "<{run_url}|View Run>"
notification_title: '*{repo}*'
message_format: '{emoji} *{workflow}* *{status_message}* in <{repo_url}|{repo}@{branch}> on <{commit_url}|{commit_sha}>'
footer: '<{run_url}|View Run>'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Expand Down
30 changes: 16 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,27 @@ prepare:
rustup target add wasm32-unknown-unknown

build-contract:
cargo build --release -p contract --target wasm32-unknown-unknown
cd contract && cargo build --release --target wasm32-unknown-unknown
wasm-strip ./target/wasm32-unknown-unknown/release/contract.wasm

test-only:
cargo test -p tests

copy-wasm-file-to-test:
cp target/wasm32-unknown-unknown/release/contract.wasm tests/wasm

test: build-contract copy-wasm-file-to-test test-only
test: build-contract
mkdir -p tests/wasm
cp ./target/wasm32-unknown-unknown/release/contract.wasm tests/wasm
cd tests && cargo test

clippy:
cargo clippy --all-targets --all -- -D warnings -A renamed_and_removed_lints
cd contract && cargo clippy --all-targets -- -D warnings
cd tests && cargo clippy --all-targets -- -D warnings

check-lint: clippy
cargo fmt --all -- --check
cd contract && cargo fmt -- --check
cd tests && cargo fmt -- --check

lint: clippy
cargo fmt --all

cd contract && cargo fmt
cd tests && cargo fmt

clean:
cargo clean
rm -rf tests/wasm/contract.wasm
cd contract && cargo clean
cd tests && cargo clean
rm -rf tests/wasm
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,31 @@ This session code accepts a message string and stores it in the calling account

**Usage**: This session code expects a runtime argument named `message` of the type `String`.

**Tests**: There are two tests available to test the Hello World session code. The `should_store_hello_world` test verifies the successful path, where a string *hello world* is saved under the `special_value` NamedKey. The `should_error_on_missing_runtime_arg` test verifies that an error is displayed when the runtime argument is missing.
The tests start by initializing the Casper crates and creating a genesis account. Then the contract Wasm is loaded to the session_code object. The deploy_item object is created using the details like payment method, session code arguments, and the account address. The deploy_item object is passed to the execute_request. Finally, the execution engine is invoked to process the execute_request.
**Tests**: There are two tests available to test the Hello World session code. The `should_store_hello_world` test verifies the successful path, where a string _hello world_ is saved under the `special_value` NamedKey. The `should_error_on_missing_runtime_arg` test verifies that an error is displayed when the runtime argument is missing.
The tests start by initializing the Casper crates and creating a genesis account. Then the contract Wasm is loaded to the session_code object. The deploy_item object is created using the details like payment method, session code arguments, and the account address. The deploy_item object is passed to the execute_request. Finally, the execution engine is invoked to process the execute_request.

## Build and Test the Session Code
## Build and Test the Session Code

### Set up the Rust toolchain

You need the Rust toolchain to develop smart contracts.

```bash
make prepare
```

### Compile session code

Compile the session code to WebAssembly (Wasm)

```bash
make build-contract
```

### Test

Run the tests suite.

```bash
make test
```
Expand Down Expand Up @@ -74,7 +80,6 @@ nctl-view-faucet-account

</details>


The following command will help you deploy the session code on the NCTL network. In the following command, the KEY PATH is the path of the faucet account secret key.

```bash
Expand All @@ -84,10 +89,10 @@ casper-client put-deploy \
--secret-key [KEY PATH]/secret_key.pem \
--payment-amount 5000000000000 \
--session-path [CONTRACT PATH]/contract.wasm \
--session-arg "message:string='hello world'"
--session-arg "message:string='hello world'"
```

After the deploy is successful, you can view the new NamedKey `special_value` in the faucet account details.
After the deploy is successful, you can view the new NamedKey `special_value` in the faucet account details.

<details>
<summary>Sample faucet account details after successful deploy</summary>
Expand Down Expand Up @@ -149,7 +154,7 @@ casper-client get-state-root-hash --node-address http://localhost:11101

```json
{
"id": -7547762796950564402,
"id": -7547762796950564402,
"jsonrpc": "2.0",
"result": {
"api_version": "1.0.0",
Expand Down Expand Up @@ -192,6 +197,3 @@ casper-client query-state \
```

</details>



7 changes: 2 additions & 5 deletions contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ authors = ["darthsiroftardis <[email protected]>"]
edition = "2018"

[dependencies]
casper-contract = "1.4.4"
casper-types = "1.5.0"
casper-contract = "3.0.0"
casper-types = "3.0.0"

[[bin]]
name = "contract"
path = "src/main.rs"
bench = false
doctest = false
test = false

[features]
default = ["casper-contract/std", "casper-types/std", "casper-contract/test-support"]
17 changes: 11 additions & 6 deletions contract/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#![no_std]
#![no_main]

extern crate alloc;

use alloc::string::String;
use casper_contract::contract_api::{runtime, storage};
use casper_types::{Key, URef};

const KEY: &str = "special_value";
const ARG_MESSAGE: &str = "message";
const KEY_NAME: &str = "my-key-name";
const RUNTIME_ARG_NAME: &str = "message";

fn store(value: String) {
// Store `value` under a new unforgeable reference.
Expand All @@ -13,14 +17,15 @@ fn store(value: String) {
// Wrap the unforgeable reference in a value of type `Key`.
let value_key: Key = value_ref.into();

// Store this key under the name "special_value" in context-local storage.
runtime::put_key(KEY, value_key);
// Store this key under the name "my-key-name" in caller context
runtime::put_key(KEY_NAME, value_key);
}

// All session code must have a `call` entrypoint.
#[no_mangle]
pub extern "C" fn call() {
// Get the optional first argument supplied to the argument.
let value: String = runtime::get_named_arg(ARG_MESSAGE);
// This contract expects a single runtime argument to be provided. The arg is named "message"
// and will be of type `String`.
let value: String = runtime::get_named_arg(RUNTIME_ARG_NAME);
store(value);
}
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nightly-2022-03-03
nightly-2023-03-25
11 changes: 6 additions & 5 deletions tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ version = "0.1.0"
edition = "2018"

[dev-dependencies]
casper-contract = { version = "1.4.4", default-features = false, features = ["test-support"] }
casper-engine-test-support = { version = "2.2.0", features = ["test-support"] }
casper-execution-engine = "2.0.0"
casper-types = "1.5.0"
casper-engine-test-support = "5.0.0"
casper-contract = { version = "3.0.0", default-features = false, features = [
"test-support",
] }
casper-execution-engine = "5.0.0"
casper-types = "3.0.0"

[[bin]]
name = "integration-tests"
path = "src/integration_tests.rs"
bench = false
doctest = false

76 changes: 32 additions & 44 deletions tests/src/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,24 @@ mod tests {

use casper_engine_test_support::{
DeployItemBuilder, ExecuteRequestBuilder, InMemoryWasmTestBuilder, ARG_AMOUNT,
DEFAULT_ACCOUNT_ADDR, DEFAULT_ACCOUNT_INITIAL_BALANCE, DEFAULT_GENESIS_CONFIG,
DEFAULT_GENESIS_CONFIG_HASH, DEFAULT_PAYMENT, DEFAULT_RUN_GENESIS_REQUEST,
};
use casper_execution_engine::core::engine_state::{
run_genesis_request::RunGenesisRequest, GenesisAccount,
};
use casper_types::{
account::AccountHash, runtime_args, Key, Motes, PublicKey, RuntimeArgs, SecretKey, U512,
DEFAULT_ACCOUNT_ADDR, DEFAULT_PAYMENT, PRODUCTION_RUN_GENESIS_REQUEST,
};
use casper_execution_engine::core::{engine_state::Error as EngineStateError, execution};
use casper_types::{runtime_args, ApiError, Key, RuntimeArgs};

const MY_ACCOUNT: [u8; 32] = [7u8; 32];
// Define `KEY` constant to match that in the contract.
const KEY: &str = "special_value";
// Define `KEY_NAME` constant to match that in the contract.
const KEY_NAME: &str = "my-key-name";
const VALUE: &str = "hello world";
const RUNTIME_ARG_NAME: &str = "message";
const CONTRACT_WASM: &str = "contract.wasm";

#[test]
fn should_store_hello_world() {
// Create keypair.
let secret_key = SecretKey::ed25519_from_bytes(MY_ACCOUNT).unwrap();
let public_key = PublicKey::from(&secret_key);

// Create an AccountHash from a public key.
let account_addr = AccountHash::from(&public_key);
// Create a GenesisAccount.
let account = GenesisAccount::account(
public_key,
Motes::new(U512::from(DEFAULT_ACCOUNT_INITIAL_BALANCE)),
None,
);

let mut genesis_config = DEFAULT_GENESIS_CONFIG.clone();
genesis_config.ee_config_mut().push_account(account);
let mut builder = InMemoryWasmTestBuilder::default();
builder
.run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST)
.commit();

let run_genesis_request = RunGenesisRequest::new(
*DEFAULT_GENESIS_CONFIG_HASH,
genesis_config.protocol_version(),
genesis_config.take_ee_config(),
);
// The test framework checks for compiled Wasm files in '<current working dir>/wasm'. Paths
// relative to the current working dir (e.g. 'wasm/contract.wasm') can also be used, as can
// absolute paths.
Expand All @@ -57,20 +35,17 @@ mod tests {
ARG_AMOUNT => *DEFAULT_PAYMENT
})
.with_session_code(session_code, session_args)
.with_authorization_keys(&[account_addr])
.with_address(account_addr)
.with_authorization_keys(&[*DEFAULT_ACCOUNT_ADDR])
.with_address(*DEFAULT_ACCOUNT_ADDR)
.build();

let execute_request = ExecuteRequestBuilder::from_deploy_item(deploy_item).build();

let mut builder = InMemoryWasmTestBuilder::default();
builder.run_genesis(&run_genesis_request).commit();

// prepare assertions.
let result_of_query = builder.query(
None,
Key::Account(*DEFAULT_ACCOUNT_ADDR),
&[KEY.to_string()],
&[KEY_NAME.to_string()],
);
assert!(result_of_query.is_err());

Expand All @@ -79,7 +54,11 @@ mod tests {

// make assertions
let result_of_query = builder
.query(None, Key::Account(account_addr), &[KEY.to_string()])
.query(
None,
Key::Account(*DEFAULT_ACCOUNT_ADDR),
&[KEY_NAME.to_string()],
)
.expect("should be stored value.")
.as_cl_value()
.expect("should be cl value.")
Expand All @@ -92,25 +71,34 @@ mod tests {

#[test]
fn should_error_on_missing_runtime_arg() {
let secret_key = SecretKey::ed25519_from_bytes(MY_ACCOUNT).unwrap();
let public_key = PublicKey::from(&secret_key);
let account_addr = AccountHash::from(&public_key);

let session_code = PathBuf::from(CONTRACT_WASM);
let session_args = RuntimeArgs::new();

let deploy_item = DeployItemBuilder::new()
.with_empty_payment_bytes(runtime_args! {ARG_AMOUNT => *DEFAULT_PAYMENT})
.with_authorization_keys(&[account_addr])
.with_authorization_keys(&[*DEFAULT_ACCOUNT_ADDR])
.with_address(*DEFAULT_ACCOUNT_ADDR)
.with_session_code(session_code, session_args)
.build();

let execute_request = ExecuteRequestBuilder::from_deploy_item(deploy_item).build();

let mut builder = InMemoryWasmTestBuilder::default();
builder.run_genesis(&DEFAULT_RUN_GENESIS_REQUEST).commit();
builder
.run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST)
.commit();
builder.exec(execute_request).commit().expect_failure();

let actual_error = builder.get_error().expect("must have error");
assert!(
matches!(
actual_error,
EngineStateError::Exec(execution::Error::Revert(ApiError::MissingArgument))
),
"Expected {:?}, received {:?}",
EngineStateError::Exec(execution::Error::Revert(ApiError::MissingArgument)),
actual_error
);
}
}

Expand Down
Loading