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

system-test: Refactor NodeJS Test Scenarios to Use js-stellar-sdk WASM Upload/Deploy Methods #91

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ RUN if echo "$JS_STELLAR_SDK_NPM_VERSION" | grep -q '.*file:.*'; then \

ADD *.ts /home/tester/bin/
RUN ["sudo", "chmod", "+x", "/home/tester/bin/invoke.ts"]
RUN ["sudo", "chmod", "+x", "/home/tester/bin/install.ts"]

FROM base as build

Expand Down
2 changes: 1 addition & 1 deletion features/dapp_develop/dapp_develop.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feature: DApp Contract Development
Scenario Outline: DApp developer compiles, installs, deploys and invokes a contract
Given I used cargo to compile example contract <ContractExampleSubPath>
And I used rpc to verify my account is on the network
And I used cli to install contract <ContractExampleSubPath> / <ContractCompiledFileName> on network using my secret key
And I used cli to install contract <ContractExampleSubPath> / <ContractCompiledFileName> on network from tool <Tool> using my secret key
And I used cli to deploy contract <ContractExampleSubPath> / <ContractCompiledFileName> by installed hash using my secret key
When I invoke function <FunctionName> on <ContractName> with request parameters <FunctionParams> from tool <Tool> using my secret key
Then The result should be <Result>
Expand Down
6 changes: 3 additions & 3 deletions features/dapp_develop/dapp_develop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ func deployContractUsingConfigParamsStep(ctx context.Context, contractExamplesSu
return nil
}

func installContractStep(ctx context.Context, contractExamplesSubPath string, compiledContractFileName string) error {
func installContractStep(ctx context.Context, contractExamplesSubPath string, compiledContractFileName string, tool string) error {

testConfig := ctx.Value(e2e.TestConfigContextKey).(*testConfig)
contractWorkingDirectory := fmt.Sprintf("%s/soroban_examples", testConfig.TestWorkingDir)

var err error
if testConfig.InstalledContractId, err = installContract(compiledContractFileName, contractWorkingDirectory, contractExamplesSubPath, testConfig.E2EConfig); err != nil {
if testConfig.InstalledContractId, err = installContract(compiledContractFileName, contractWorkingDirectory, contractExamplesSubPath, tool, testConfig.E2EConfig); err != nil {
return err
}

Expand Down Expand Up @@ -335,7 +335,7 @@ func initializeScenario(scenarioCtx *godog.ScenarioContext) {
scenarioCtx.Step(`^I used cli to add Network Config ([\S|\s]+) for rpc and standalone$`, createNetworkConfigStep)
scenarioCtx.Step(`^I used cli to add Identity ([\S|\s]+) for my secret key$`, createMyIdentityStep)
scenarioCtx.Step(`^I used cli to deploy contract ([\S|\s]+) / ([\S|\s]+) using Identity ([\S|\s]+) and Network Config ([\S|\s]+)$`, deployContractUsingConfigParamsStep)
scenarioCtx.Step(`^I used cli to install contract ([\S|\s]+) / ([\S|\s]+) on network using my secret key$`, installContractStep)
scenarioCtx.Step(`^I used cli to install contract ([\S|\s]+) / ([\S|\s]+) on network from tool ([\S|\s]+) using my secret key$`, installContractStep)
scenarioCtx.Step(`^I used cli to deploy contract ([\S|\s]+) / ([\S|\s]+) by installed hash using my secret key$`, deployContractStep)
scenarioCtx.Step(`^I used cli to deploy contract ([\S|\s]+) / ([\S|\s]+) using my secret key$`, deployContractStep)
scenarioCtx.Step(`^I used cli to add Identity ([\S|\s]+) for tester secret key$`, createTestAccountIdentityStep)
Expand Down
46 changes: 45 additions & 1 deletion features/dapp_develop/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dapp_develop

import (
"fmt"
"strings"

"github.com/go-cmd/cmd"

Expand Down Expand Up @@ -95,7 +96,50 @@ func deployContractUsingConfigParams(compiledContractFileName string, contractWo
}

// returns the installed contract id
func installContract(compiledContractFileName string, contractWorkingDirectory string, contractExamplesSubPath string, e2eConfig *e2e.E2EConfig) (string, error) {
func installContract(compiledContractFileName string, contractWorkingDirectory string, contractExamplesSubPath string, tool string, e2eConfig *e2e.E2EConfig) (string, error) {
var response string
var err error

switch tool {
case "CLI":
response, err = installContractFromCliTool(compiledContractFileName, contractWorkingDirectory, contractExamplesSubPath, e2eConfig)
case "NODEJS":
response, err = installContractFromNodeJSTool(compiledContractFileName, contractWorkingDirectory, contractExamplesSubPath, e2eConfig)
default:
err = fmt.Errorf("%s tool not supported for invoke yet", tool)
}

if err != nil {
return "", err
}

return response, nil
}

func installContractFromNodeJSTool(compiledContractFileName string, contractWorkingDirectory string, contractExamplesSubPath string, e2eConfig *e2e.E2EConfig) (string, error) {
args := []string{

"--wasm", fmt.Sprintf("./%s/%s/target/wasm32-unknown-unknown/release/%s", contractWorkingDirectory, contractExamplesSubPath, compiledContractFileName),
"--rpc-url", e2eConfig.TargetNetworkRPCURL,
"--source", e2eConfig.TargetNetworkSecretKey,
"--network-passphrase", e2eConfig.TargetNetworkPassPhrase,
}
envCmd := cmd.NewCmd("./install.ts", args...)
status, stdOutLines, err := e2e.RunCommand(envCmd, e2eConfig)
stdOut := strings.TrimSpace(strings.Join(stdOutLines, "\n"))

if status != 0 || err != nil {
return "", fmt.Errorf("nodejs install of example contract %s had error %v, %v, stdout: %v", compiledContractFileName, status, err, stdOut)
}

if stdOut == "" {
return "", fmt.Errorf("nodejs install of example contract %s did not print any response", compiledContractFileName)
}

return stdOut, nil
}

func installContractFromCliTool(compiledContractFileName string, contractWorkingDirectory string, contractExamplesSubPath string, e2eConfig *e2e.E2EConfig) (string, error) {
envCmd := cmd.NewCmd("soroban",
"contract",
"install",
Expand Down
91 changes: 91 additions & 0 deletions install.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env ts-node-script

import * as fs from 'fs';
import { ArgumentParser } from 'argparse';
import {
Keypair,
TransactionBuilder,
SorobanRpc,
scValToNative,
xdr,
Operation,
OperationOptions,
} from '@stellar/stellar-sdk';

const { Server } = SorobanRpc;

async function main() {
const parser = new ArgumentParser({ description: 'Install a contract' })

parser.add_argument('--wasm', { dest: 'wasm', required: true, help: 'Path to wasm binary' });
parser.add_argument('--rpc-url', { dest: 'rpcUrl', required: true, help: 'RPC URL' });
parser.add_argument('--source', { dest: 'source', required: true, help: 'Secret key' });
parser.add_argument('--network-passphrase', { dest: 'networkPassphrase', required: true, help: 'Network passphrase' });

const {
wasm,
rpcUrl,
networkPassphrase,
source,
} = parser.parse_args() as Record<string, string>;


const server = new Server(rpcUrl, { allowHttp: true });
const secretKey = Keypair.fromSecret(source);
const account = secretKey.publicKey();
const sourceAccount = await server.getAccount(account);
const wasmBuffer = fs.readFileSync(wasm);


const options: OperationOptions.InvokeHostFunction = {
"func": xdr.HostFunction.hostFunctionTypeUploadContractWasm(Buffer.from(wasmBuffer)),
"source": account
};
const op = Operation.invokeHostFunction(options);

const originalTxn = new TransactionBuilder(sourceAccount, {
fee: "100",
networkPassphrase
})
.addOperation(op)
.setTimeout(30)
.build();

const txn = await server.prepareTransaction(originalTxn);
txn.sign(secretKey);
const send = await server.sendTransaction(txn);
if (send.errorResult) {
throw new Error(`Transaction failed: ${JSON.stringify(send)}`);
}
let response = await server.getTransaction(send.hash);
for (let i = 0; i < 50; i++) {
switch (response.status) {
case "NOT_FOUND": {
// retry
await new Promise(resolve => setTimeout(resolve, 100));
response = await server.getTransaction(send.hash);
break;
}
case "SUCCESS": {
if (!response.returnValue) {
throw new Error(`No invoke host fn return value provided: ${JSON.stringify(response)}`);
}

const parsed = scValToNative(response.returnValue);
console.log(JSON.stringify(parsed));
return;
}
case "FAILED": {
throw new Error(`Transaction failed: ${JSON.stringify(response)}`);
}
default:
throw new Error(`Unknown transaction status: ${response.status}`);
}
}
throw new Error("Transaction timed out");
}

main().catch(err => {
console.error(JSON.stringify(err));
throw err;
});
Loading
Loading