Skip to content

Commit

Permalink
add MockVm tests and integration tests
Browse files Browse the repository at this point in the history
Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
  • Loading branch information
salaheldinsoliman committed Jun 12, 2024
1 parent 320832c commit 88ff09b
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 26 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,43 @@ jobs:
with:
name: anchor-tests
path: ./target/*.profraw

soroban:
name: Soroban Integration test
runs-on: solang-ubuntu-latest
container: ghcr.io/hyperledger/solang-llvm:ci-7
needs: linux-x86-64
steps:
- name: Checkout sources
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- uses: dtolnay/rust-toolchain@1.74.0
- uses: actions/download-artifact@v3
with:
name: solang-linux-x86-64
path: bin
- name: Solang Compiler
run: |
chmod 755 ./bin/solang
echo "$(pwd)/bin" >> $GITHUB_PATH
- name: Install Soroban
run: cargo install --locked soroban-cli --version 21.0.0-rc.1
- run: npm install
working-directory: ./integration/soroban
- name: Build Solang contracts
run: npm run build
working-directory: ./integration/soroban
- name: Setup Soroban enivronment
run: npm run setup
working-directory: ./integration/soroban
- name: Upload test coverage files
uses: actions/upload-artifact@v3.1.0
with:
name: soroban-tests
path: ./target/*.profraw

solana:
name: Solana Integration test
Expand Down Expand Up @@ -425,6 +462,8 @@ jobs:
with:
name: polkadot-subxt-tests
path: ./target/*.profraw



vscode:
name: Visual Code Extension
Expand Down
55 changes: 55 additions & 0 deletions integration/soroban/counter.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as StellarSdk from '@stellar/stellar-sdk';
import { readFileSync } from 'fs';
import { expect } from 'chai';
import path from 'path';
import { fileURLToPath } from 'url';
import { call_contract_function } from './test_helpers.js';

const __filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(__filename);

describe('Counter', () => {
let keypair;
const server = new StellarSdk.SorobanRpc.Server(
"https://soroban-testnet.stellar.org:443",
);

let contractAddr;
let contract;
before(async () => {

console.log('Setting up counter contract tests...');

// read secret from file
const secret = readFileSync('alice.txt', 'utf8').trim();
keypair = StellarSdk.Keypair.fromSecret(secret);

let contractIdFile = path.join(dirname, '.soroban', 'contract-ids', 'counter.txt');
// read contract address from file
contractAddr = readFileSync(contractIdFile, 'utf8').trim().toString();

// load contract
contract = new StellarSdk.Contract(contractAddr);

// initialize the contract
await call_contract_function("init", server, keypair, contract);

});

it('get correct initial counter', async () => {
// get the count
let count = await call_contract_function("count", server, keypair, contract);
expect(count.toString()).eq("10");
});

it('increment counter', async () => {
// increment the counter
await call_contract_function("increment", server, keypair, contract);

// get the count
let count = await call_contract_function("count", server, keypair, contract);
expect(count.toString()).eq("11");
});
});


23 changes: 23 additions & 0 deletions integration/soroban/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"type": "module",
"dependencies": {
"@stellar/stellar-sdk": "^12.0.1",
"chai": "^5.1.1",
"dotenv": "^16.4.5",
"mocha": "^10.4.0"
},
"scripts": {
"build": "solang compile *.sol --target soroban",
"setup": "node setup.js",
"test": "mocha *.spec.js --timeout 20000"
},
"devDependencies": {
"@eslint/js": "^9.4.0",
"@types/mocha": "^10.0.6",
"eslint": "^9.4.0",
"expect": "^29.7.0",
"globals": "^15.4.0",
"typescript": "^5.4.5"
}
}

63 changes: 63 additions & 0 deletions integration/soroban/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

import 'dotenv/config';
import { mkdirSync, readdirSync} from 'fs';
import { execSync } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';

console.log("###################### Initializing ########################");

// Get dirname (equivalent to the Bash version)
const __filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(__filename);

// variable for later setting pinned version of soroban in "$(dirname/target/bin/soroban)"
const soroban = "soroban"

// Function to execute and log shell commands
function exe(command) {
console.log(command);
execSync(command, { stdio: 'inherit' });
}

function generate_alice() {
exe(`${soroban} keys generate alice --network testnet`);

// get the secret key of alice and put it in alice.txt
exe(`${soroban} keys show alice > alice.txt`);
}


function filenameNoExtension(filename) {
return path.basename(filename, path.extname(filename));
}

function deploy(wasm) {

let contractId = path.join(dirname, '.soroban', 'contract-ids', filenameNoExtension(wasm) + '.txt');

exe(`(${soroban} contract deploy --wasm ${wasm} --ignore-checks --source-account alice --network testnet) > ${contractId}`);
}

function deploy_all() {
const contractsDir = path.join(dirname, '.soroban', 'contract-ids');
mkdirSync(contractsDir, { recursive: true });

const wasmFiles = readdirSync(`${dirname}`).filter(file => file.endsWith('.wasm'));

wasmFiles.forEach(wasmFile => {
deploy(path.join(dirname, wasmFile));
});
}

function add_testnet() {

exe(`${soroban} network add \
--global testnet \
--rpc-url https://soroban-testnet.stellar.org:443 \
--network-passphrase "Test SDF Network ; September 2015"`);
}

add_testnet();
generate_alice();
deploy_all();
55 changes: 55 additions & 0 deletions integration/soroban/test_helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as StellarSdk from '@stellar/stellar-sdk';



async function call_contract_function(method, server, keypair, contract) {

let res;
let builtTransaction = new StellarSdk.TransactionBuilder(await server.getAccount(keypair.publicKey()), {
fee: StellarSdk.BASE_FEE,
networkPassphrase: StellarSdk.Networks.TESTNET,
}).addOperation(contract.call(method)).setTimeout(30).build();

let preparedTransaction = await server.prepareTransaction(builtTransaction);

// Sign the transaction with the source account's keypair.
preparedTransaction.sign(keypair);

try {
let sendResponse = await server.sendTransaction(preparedTransaction);
if (sendResponse.status === "PENDING") {
let getResponse = await server.getTransaction(sendResponse.hash);
// Poll `getTransaction` until the status is not "NOT_FOUND"
while (getResponse.status === "NOT_FOUND") {
console.log("Waiting for transaction confirmation...");
// See if the transaction is complete
getResponse = await server.getTransaction(sendResponse.hash);
// Wait one second
await new Promise((resolve) => setTimeout(resolve, 1000));
}

if (getResponse.status === "SUCCESS") {
// Make sure the transaction's resultMetaXDR is not empty
if (!getResponse.resultMetaXdr) {
throw "Empty resultMetaXDR in getTransaction response";
}
// Find the return value from the contract and return it
let transactionMeta = getResponse.resultMetaXdr;
let returnValue = transactionMeta.v3().sorobanMeta().returnValue();
console.log(`Transaction result: ${returnValue.value()}`);
res = returnValue.value();
} else {
throw `Transaction failed: ${getResponse.resultXdr}`;
}
} else {
throw sendResponse.errorResultXdr;
}
} catch (err) {
// Catch and report any errors we've thrown
console.log("Sending transaction failed");
console.log(err);
}
return res;
}

export { call_contract_function };
2 changes: 1 addition & 1 deletion src/codegen/dispatch/soroban.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub fn function_dispatch(
wrapper_cfg.add(&mut vartab, placeholder);

// set the msb 8 bits of the return value to 6, the return value is 64 bits.

// FIXME: this assumes that the solidity function always returns one value.
let shifted = Expression::ShiftLeft {
loc: pt::Loc::Codegen,
ty: Type::Uint(64),
Expand Down
47 changes: 22 additions & 25 deletions tests/soroban_testcases/math.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: Apache-2.0

use crate::build_solidity;
use soroban_sdk::Val;
use soroban_sdk::{IntoVal, Val};

#[test]
fn math() {
let env = build_solidity(
let runtime = build_solidity(
r#"contract math {
function max(uint64 a, uint64 b) public returns (uint64) {
if (a > b) {
Expand All @@ -17,13 +17,14 @@ fn math() {
}"#,
);

let addr = env.contracts.last().unwrap();
let res = env.invoke_contract(
addr,
"max",
vec![Val::from_payload(5u64), Val::from_payload(4u64)],
);
println!()
let arg: Val = 5_u64.into_val(&runtime.env);
let arg2: Val = 4_u64.into_val(&runtime.env);

let addr = runtime.contracts.last().unwrap();
let res = runtime.invoke_contract(addr, "max", vec![arg, arg2]);

let expected: Val = 5_u64.into_val(&runtime.env);
assert!(expected.shallow_eq(&res));
}

#[test]
Expand Down Expand Up @@ -58,21 +59,17 @@ fn math_same_name() {
);

let addr = src.contracts.last().unwrap();
let res = src.invoke_contract(
addr,
"max_uint64_uint64",
vec![*Val::from_u32(4).as_val(), *Val::from_u32(5).as_val()],
);
assert!(Val::from_u32(5).as_val().shallow_eq(&res));

let res = src.invoke_contract(
addr,
"max_uint64_uint64_uint64",
vec![
*Val::from_u32(4).as_val(),
*Val::from_u32(5).as_val(),
*Val::from_u32(6).as_val(),
],
);
assert!(Val::from_u32(6).as_val().shallow_eq(&res));
let arg1: Val = 5_u64.into_val(&src.env);
let arg2: Val = 4_u64.into_val(&src.env);
let res = src.invoke_contract(addr, "max_uint64_uint64", vec![arg1, arg2]);
let expected: Val = 5_u64.into_val(&src.env);
assert!(expected.shallow_eq(&res));

let arg1: Val = 5_u64.into_val(&src.env);
let arg2: Val = 4_u64.into_val(&src.env);
let arg3: Val = 6_u64.into_val(&src.env);
let res = src.invoke_contract(addr, "max_uint64_uint64_uint64", vec![arg1, arg2, arg3]);
let expected: Val = 6_u64.into_val(&src.env);
assert!(expected.shallow_eq(&res));
}
1 change: 1 addition & 0 deletions tests/soroban_testcases/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// SPDX-License-Identifier: Apache-2.0
mod math;
mod storage;
41 changes: 41 additions & 0 deletions tests/soroban_testcases/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache-2.0

use crate::build_solidity;
use soroban_sdk::{IntoVal, Val};

#[test]
fn counter() {
let src = build_solidity(
r#"contract counter {
uint64 public count = 10;
function increment() public returns (uint64){
count += 1;
return count;
}
function decrement() public returns (uint64){
count -= 1;
return count;
}
}"#,
);

let addr = src.contracts.last().unwrap();

let _res = src.invoke_contract(addr, "init", vec![]);

let res = src.invoke_contract(addr, "count", vec![]);
let expected: Val = 10_u64.into_val(&src.env);
assert!(expected.shallow_eq(&res));

src.invoke_contract(addr, "increment", vec![]);
let res = src.invoke_contract(addr, "count", vec![]);
let expected: Val = 11_u64.into_val(&src.env);
assert!(expected.shallow_eq(&res));

src.invoke_contract(addr, "decrement", vec![]);
let res = src.invoke_contract(addr, "count", vec![]);
let expected: Val = 10_u64.into_val(&src.env);
assert!(expected.shallow_eq(&res));
}

0 comments on commit 88ff09b

Please sign in to comment.