Skip to content

Commit

Permalink
Substrate: subxt integration tests (#990)
Browse files Browse the repository at this point in the history
Signed-off-by: Raymond Yeh <[email protected]>
  • Loading branch information
extraymond committed Jun 21, 2023
1 parent 0221f20 commit 0170778
Show file tree
Hide file tree
Showing 28 changed files with 4,786 additions and 1 deletion.
37 changes: 36 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ jobs:
- uses: actions/[email protected]
with:
name: solang-linux-x86-64
path: ./target/debug/solang
path: ./target/debug/solang


linux-arm:
name: Linux Arm
Expand Down Expand Up @@ -347,6 +348,40 @@ jobs:
if: always()
run: docker kill ${{steps.substrate.outputs.id}}

substrate-subxt:
name: Substrate Integration test with subxt
runs-on: ubuntu-22.04
needs: linux-x86-64
steps:
- name: Checkout sources
uses: actions/checkout@v2
# We can't run substrate as a github actions service, since it requires
# command line arguments. See https://github.com/actions/runner/pull/1152
- name: Start substrate
run: echo id=$(docker run -d -p 9944:9944 ghcr.io/hyperledger/solang-substrate-ci:e41a9c0 substrate-contracts-node --dev --ws-external) >> $GITHUB_OUTPUT
id: substrate
- uses: actions/download-artifact@master
with:
name: solang-linux-x86-64
path: bin
- run: |
chmod 755 ./bin/solang
echo "$(pwd)/bin" >> $GITHUB_PATH
- name: Install latest rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
override: true
- run: solang compile --target substrate ../substrate/*.sol ../substrate/test/*.sol -o ./contracts/
working-directory: ./integration/subxt-tests
- name: Deploy and test contracts
run: cargo test -- --test-threads=1
working-directory: ./integration/subxt-tests
- name: cleanup
if: always()
run: docker kill ${{steps.substrate.outputs.id}}

vscode:
name: Visual Code Extension
runs-on: solang-ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ Cargo.lock
**/*.rs.bk
bundle.ll

.helix/
.vscode/
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rust-analyzer.linkedProjects": [
"./integration/subxt-tests/Cargo.toml"
]
}
12 changes: 12 additions & 0 deletions integration/substrate/createpair.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity ^0.8.0;
import "./UniswapV2Pair.sol";

contract Creator {
address public pair;

constructor() public {
pair = address(new UniswapV2Pair());
}

}

2 changes: 2 additions & 0 deletions integration/subxt-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
contracts/
target/
28 changes: 28 additions & 0 deletions integration/subxt-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
edition = "2021"
name = "subxt-tests"
version = "0.1.0"
license = "Apache-2.0"

[dependencies]
anyhow = "1.0.71"
async-trait = "0.1.68"
sp-core = "20.0.0"
sp-runtime = "23.0.0"
sp-weights = "19.0.0"
pallet-contracts-primitives = "23.0.0"
hex = "0.4.3"
num-bigint = "0.4.3"
once_cell = "1.17.2"
parity-scale-codec = { version = "3.5.0", features = ["derive"] }
rand = "0.8.5"
serde_json = "1.0.96"
sp-keyring = "23.0.0"
subxt = "0.28.0"
tokio = {version = "1.28.2", features = ["rt-multi-thread", "macros", "time"]}
contract-metadata = "3.0.1"
contract-transcode = "3.0.1"

[workspace]
members = []

137 changes: 137 additions & 0 deletions integration/subxt-tests/src/cases/array_struct_mapping_storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// SPDX-License-Identifier: Apache-2.0

use contract_transcode::ContractMessageTranscoder;
use parity_scale_codec::{Decode, Encode};
use sp_core::{hexdisplay::AsBytesRef, U256};

use crate::{Contract, DeployContract, Execution, ReadContract, WriteContract, API};

#[tokio::test]
async fn case() -> anyhow::Result<()> {
let api = API::new().await?;

let mut contract = Contract::new("./contracts/array_struct_mapping_storage.contract")?;

contract
.deploy(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| t.encode::<_, String>("new", []).unwrap(),
)
.await?;

contract
.call(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| t.encode::<_, _>("setNumber", ["2147483647"]).unwrap(),
)
.await?;

let b_push = |t: &ContractMessageTranscoder| t.encode::<_, String>("push", []).unwrap();

contract
.call(&api, sp_keyring::AccountKeyring::Alice, 0, &b_push)
.await?;

contract
.call(&api, sp_keyring::AccountKeyring::Alice, 0, &b_push)
.await?;

for array_no in 0..2 {
for i in 0..10 {
contract
.call(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| {
t.encode::<_, _>(
"set",
[
format!("{}", array_no),
format!("{}", 102 + i + array_no * 500),
format!("{}", 300331 + i),
],
)
.unwrap()
},
)
.await?;
}
}

for array_no in 0..2 {
for i in 0..10 {
let rs = contract
.try_call(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| {
t.encode::<_, _>(
"get",
[
format!("{}", array_no),
format!("{}", 102 + i + array_no * 500),
],
)
.unwrap()
},
)
.await
.and_then(|v| <U256>::decode(&mut v.as_bytes_ref()).map_err(Into::into))?;

assert_eq!(rs, U256::from(300331_u128 + i));
}
}

contract
.call(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| {
t.encode::<_, _>("rm", [format!("{}", 0), format!("{}", 104)])
.unwrap()
},
)
.await?;

for i in 0..10 {
let rs = contract
.try_call(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| {
t.encode::<_, _>("get", [format!("{}", 0), format!("{}", 102 + i)])
.unwrap()
},
)
.await
.and_then(|v| <U256>::decode(&mut v.as_bytes_ref()).map_err(Into::into))?;

if i != 2 {
assert_eq!(rs, U256::from(300331_u128 + i));
} else {
assert_eq!(rs, U256::zero());
}
}

let rs = contract
.try_call(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| t.encode::<_, String>("number", []).unwrap(),
)
.await
.and_then(|v| <i64>::decode(&mut v.as_bytes_ref()).map_err(Into::into))?;

assert_eq!(rs, 2147483647);

Ok(())
}
107 changes: 107 additions & 0 deletions integration/subxt-tests/src/cases/arrays.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: Apache-2.0

use crate::{Contract, DeployContract, Execution, ReadContract, WriteContract, API};
use contract_transcode::{ContractMessageTranscoder, Value};
use hex::FromHex;

use parity_scale_codec::{Decode, Encode};
use rand::{seq::SliceRandom, thread_rng, Rng};
use sp_core::{crypto::AccountId32, hexdisplay::AsBytesRef};

#[ignore = "contract trapped"]
#[tokio::test]
async fn case() -> anyhow::Result<()> {
let api = API::new().await?;

let mut contract = Contract::new("./contracts/arrays.contract")?;

contract
.deploy(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| t.encode::<_, String>("new", []).unwrap(),
)
.await?;

let mut users = Vec::new();

for i in 0..3 {
let rnd_addr = rand::random::<[u8; 32]>();

let name = format!("name{i}");

let id = u32::from_be_bytes(rand::random::<[u8; 4]>());
let mut perms = Vec::<String>::new();

let mut j: f64 = 0.0;
while j < rand::thread_rng().gen_range(0.0..=3.0) {
j += 1.0;

let p = rand::thread_rng().gen_range(0..8);
perms.push(format!("Perm{}", p + 1));
}

contract
.call(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| {
t.encode(
"addUser",
[
id.to_string(),
format!("0x{}", hex::encode(rnd_addr)),
format!("\"{}\"", name.clone()),
format!("[{}]", perms.join(",")),
],
)
.unwrap()
},
)
.await?;

users.push((name, rnd_addr, id, perms));
}

let (name, addr, id, perms) = users.choose(&mut thread_rng()).unwrap();

let output = contract
.try_call(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| {
t.encode("getUserById", [format!("\"{id}\"")]).unwrap()
},
)
.await?;

let (name, addr, id, perms) =
<(String, AccountId32, u64, Vec<u8>)>::decode(&mut output.as_bytes_ref())?;

if !perms.is_empty() {
let p = perms.choose(&mut thread_rng()).unwrap();

let output = contract
.try_call(
&api,
sp_keyring::AccountKeyring::Alice,
0,
&|t: &ContractMessageTranscoder| {
t.encode(
"hasPermission",
[format!("\"{id}\""), format!("Perm{}", p + 1)],
)
.unwrap()
},
)
.await?;

let has_permission = <bool>::decode(&mut output.as_bytes_ref())?;
assert!(has_permission);
}

Ok(())
}
Loading

0 comments on commit 0170778

Please sign in to comment.