Skip to content
This repository was archived by the owner on Sep 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #14 from casper-ecosystem/dev/improvements
Browse files Browse the repository at this point in the history
Improvements in key-manager for workshops
  • Loading branch information
hoffmannjan authored Jun 23, 2021
2 parents 56412e3 + a0e93f7 commit 25358cd
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 136 deletions.
50 changes: 35 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,52 @@ The steps below are a quick start if you have already set up your [develoment en
### Set up the Rust toolchain
You need the Rust toolchain to develop smart contracts.
```bash
$ cd contract
$ rustup install $(cat rust-toolchain)
$ rustup target add --toolchain $(cat rust-toolchain) wasm32-unknown-unknown
```

### Compile Account Code (Smart Contracts)
Create a WASM file that will be used by the JS client.
```bash
$ cd contract
$ cargo build --release
```

### Install the JS packages
```bash
$ cd client
$ npm install
```

## Prepare the local `nctl` network
1. Set up [nctl](https://github.com/CasperLabs/casper-node/tree/master/utils/nctl)
2. Update `client/src/utils.js`:
- Set `baseKeyPath` to your nctl faucet key.

## Run
Run the client code.
```bash
$ cd client
$ node src/keys-manager.js
$ node src/keys-manager-set-all.js
## Running prepared client-scenarios

### Installation

Just run `npm i` in `./client`.

### Env configuration

Environment variables needs to be set in `.env` file in `./client`.

```
BASE_KEY_PATH=... # absolute path to keys directory
NODE_URL=... # optional, defaults to standard NCTL address http://localhost:40101/rpc
WASM_PATH=... # optional, defaults to ../contract/target/wasm32-unknown-unknown/release/keys-manager.wasm
NETWORK_NAME=... # optional, defaults to casper-net-1
FUND_AMOUNT=10000000000000 # defaults to 10000000000000 = 10000CSPR
PAYMENT_AMOUNT=100000000000 # defaults to 100000000000 = 100CSPR
TRANSFER_AMOUNT=2500000000 # defaults to 2500000000 = 2.5CSPR
```

You can also run run both scripts providing custom `.env` path by running

`npm run start:atomic dotenv_config_path=./example-env-file`

### Set-all

`npm run start:all`

### Step-by-step

`npm run start:atomic`

### Interactive mode

To run a script in interactive mode just add `interactive` to the above commands.
27 changes: 0 additions & 27 deletions client/README.md

This file was deleted.

190 changes: 109 additions & 81 deletions client/src/key-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,31 @@ const {
CLValueBuilder,
} = require('casper-js-sdk');

const { getAccountFromKeyPair, randomSeed, toAccountHashString, sleep } = require('./utils');
const { getAccountFromKeyPair, randomSeed, toAccountHashString, sleep, pauseAndWaitForKeyPress } = require('./utils');

const FUND_AMOUNT = 10000000000000;
const PAYMENT_AMOUNT = 100000000000;
const FUND_AMOUNT = process.env.FUND_AMOUNT || 10000000000000;
const PAYMENT_AMOUNT = process.env.PAYMENT_AMOUNT || 100000000000;

const NODE_URL = process.env.NODE_URL || 'http://localhost:40101/rpc';
const WASM_PATH = process.env.WASM_PATH || '../contract/target/wasm32-unknown-unknown/release/keys-manager.wasm';
const NETWORK_NAME = process.env.NETWORK_NAME || 'casper-net-1';
const BASE_KEY_PATH = process.env.BASE_KEY_PATH;

// Get a faucet account from provided path
const faucetAccount = getAccountFromKeyPair(BASE_KEY_PATH);

// Create a client connect to Casper Node
let client = new CasperClient(NODE_URL);
// Create a client connected to Casper Node
const client = new CasperClient(NODE_URL);

async function sendDeploy(deploy, signingKeys) {
for(let key of signingKeys){
console.log(`Signed by: ${toAccountHashString(key.publicKey)}`);
deploy = client.signDeploy(deploy, key);
}
let deployHash = await client.putDeploy(deploy);
await printDeploy(deployHash);
}
//
// Helper methods
//

// Helper method for geting a deploy in a defined time period (30s)
async function getDeploy(deployHash) {
let i = 300;
while (i != 0) {
let [deploy, raw] = await client.getDeploy(deployHash);
const [deploy, raw] = await client.getDeploy(deployHash);
if (raw.execution_results.length !== 0){
if (raw.execution_results[0].result.Success) {
return deploy;
Expand All @@ -56,122 +53,153 @@ async function getDeploy(deployHash) {
throw Error('Timeout after ' + i + 's. Something\'s wrong');
}

// Helper method for getting the current state of the account
async function getAccount(publicKey) {
let c = new CasperServiceByJsonRPC(NODE_URL);
let stateRootHash = (await c.getLatestBlockInfo()).block.header.state_root_hash;
let account = await c.getBlockState(
const c = new CasperServiceByJsonRPC(NODE_URL);
const stateRootHash = (await c.getLatestBlockInfo()).block.header.state_root_hash;
const account = await c.getBlockState(
stateRootHash,
'account-hash-' + toAccountHashString(publicKey),
publicKey.toAccountHashStr(),
[]
).then(res => res.Account);
return account;
}

// Helper method for sending deploy and displaying signing keys
async function sendDeploy(deploy, signingKeys) {
for(let key of signingKeys){
console.log(`Signed by: ${key.publicKey.toAccountHashStr()}`);
deploy = client.signDeploy(deploy, key);
}
const deployHash = await client.putDeploy(deploy);
await printDeploy(deployHash);
}

// Helper method to create a new hierarchical deterministic wallet
function randomMasterKey() {
const seed = new Uint8Array(randomSeed());
return client.newHdWallet(seed);
}

// Helper method for printing deploy result
async function printDeploy(deployHash) {
console.log("Deploy hash: " + deployHash);
console.log("Deploy result:");
console.log(DeployUtil.deployToJson(await getDeploy(deployHash)));
}

// Helper method for printing account info
async function printAccount(account) {
console.log("\n[x] Current state of the account:");
console.log(JSON.stringify(await getAccount(account.publicKey), null, 2));
console.log(JSON.parse(JSON.stringify(await getAccount(account.publicKey), null, 2)));
await pauseAndWaitForKeyPress();
}

function randomMasterKey() {
const seed = new Uint8Array(randomSeed());
return client.newHdWallet(seed);
//
// Transfers
//

// Builds native transfer deploy
function transferDeploy(fromAccount, toAccount, amount) {
const deployParams = new DeployUtil.DeployParams(
fromAccount.publicKey,
NETWORK_NAME
);
const transferParams = DeployUtil.ExecutableDeployItem.newTransfer(
amount,
toAccount.publicKey,
null,
1
);
const payment = DeployUtil.standardPayment(PAYMENT_AMOUNT);
return DeployUtil.makeDeploy(deployParams, transferParams, payment);
}

// Helper method for funding the specified account from a faucetAccount
async function fundAccount(account) {
const deploy = transferDeploy(faucetAccount, account, FUND_AMOUNT);
await sendDeploy(deploy, [faucetAccount]);
}

// Key manager
//
// Contract deploy related methods
//

// Builds a deploy that will install key-manager contract on specified account
function buildContractInstallDeploy(baseAccount) {
const deployParams = new DeployUtil.DeployParams(
baseAccount.publicKey,
NETWORK_NAME
);
const session = new Uint8Array(fs.readFileSync(WASM_PATH, null).buffer);
const runtimeArgs = RuntimeArgs.fromMap({});
const sessionModule = DeployUtil.ExecutableDeployItem.newModuleBytes(
session,
runtimeArgs
);
const payment = DeployUtil.standardPayment(PAYMENT_AMOUNT);

function setAll(fromAccount, deployThereshold, keyManagementThreshold, accountWeights) {
let accounts = accountWeights.map(x => x.publicKey);
let weights = accountWeights.map(x => CLValueBuilder.u8(x.weight));
return DeployUtil.makeDeploy(deployParams, sessionModule, payment);
}

// Builds key-manager deploy that takes entrypoint and args
function buildKeyManagerDeploy(baseAccount, entrypoint, args) {
const deployParams = new DeployUtil.DeployParams(
baseAccount.publicKey,
NETWORK_NAME
);
const runtimeArgs = RuntimeArgs.fromMap(args);
const sessionModule = DeployUtil.ExecutableDeployItem.newStoredContractByName(
"keys_manager",
entrypoint,
runtimeArgs
);
const payment = DeployUtil.standardPayment(PAYMENT_AMOUNT);
return DeployUtil.makeDeploy(deployParams, sessionModule, payment);
}

//
// Key-manager contract specific methods
//

// Sets deploy threshold, key management threshold, and weights for the specified accounts
function setAll(fromAccount, deployThreshold, keyManagementThreshold, accountWeights) {
const accounts = accountWeights.map(x => x.publicKey);
const weights = accountWeights.map(x => CLValueBuilder.u8(x.weight));

return buildKeyManagerDeploy(fromAccount, "set_all", {
deployment_thereshold: CLValueBuilder.u8(deployThereshold),
deployment_thereshold: CLValueBuilder.u8(deployThreshold),
key_management_threshold: CLValueBuilder.u8(keyManagementThreshold),
accounts: CLValueBuilder.list(accounts),
weights: CLValueBuilder.list(weights),
});
}

// Sets key with a specified weight
function setKeyWeightDeploy(fromAccount, account, weight) {
return buildKeyManagerDeploy(fromAccount, "set_key_weight", {
account: account.publicKey,
weight: CLValueBuilder.u8(weight)
});
}

// Sets deploys threshold
function setDeploymentThresholdDeploy(fromAccount, weight) {
return buildKeyManagerDeploy(fromAccount, "set_deployment_threshold", {
weight: CLValueBuilder.u8(weight)
});
}

// Sets key-management threshold
function setKeyManagementThresholdDeploy(fromAccount, weight) {
return buildKeyManagerDeploy(fromAccount, "set_key_management_threshold", {
weight: CLValueBuilder.u8(weight)
});
}

function buildKeyManagerDeploy(baseAccount, entrypoint, args) {
let deployParams = new DeployUtil.DeployParams(
baseAccount.publicKey,
NETWORK_NAME
);
let runtimeArgs = RuntimeArgs.fromMap(args);
let sessionModule = DeployUtil.ExecutableDeployItem.newStoredContractByName(
"keys_manager",
entrypoint,
runtimeArgs
);
let payment = DeployUtil.standardPayment(PAYMENT_AMOUNT);
return DeployUtil.makeDeploy(deployParams, sessionModule, payment);
}

function buildContractInstallDeploy(baseAccount) {
let deployParams = new DeployUtil.DeployParams(
baseAccount.publicKey,
NETWORK_NAME
);
const session = new Uint8Array(fs.readFileSync(WASM_PATH, null).buffer);
let runtimeArgs = RuntimeArgs.fromMap({});

let sessionModule = DeployUtil.ExecutableDeployItem.newModuleBytes(
session,
runtimeArgs
);
let payment = DeployUtil.standardPayment(PAYMENT_AMOUNT);
return DeployUtil.makeDeploy(deployParams, sessionModule, payment);
}

// Funding
function transferDeploy(fromAccount, toAccount, amount) {
let deployParams = new DeployUtil.DeployParams(
fromAccount.publicKey,
NETWORK_NAME
);
let transferParams = DeployUtil.ExecutableDeployItem.newTransfer(
amount,
toAccount.publicKey,
null,
1
);
let payment = DeployUtil.standardPayment(PAYMENT_AMOUNT);
return DeployUtil.makeDeploy(deployParams, transferParams, payment);
}

async function fund(account) {
let deploy = transferDeploy(faucetAccount, account, FUND_AMOUNT);
await sendDeploy(deploy, [faucetAccount]);
}

module.exports = {
'randomMasterKey': randomMasterKey,
'toAccountHashString': toAccountHashString,
'fund': fund,
'fundAccount': fundAccount,
'printAccount': printAccount,
'keys': {
'setAll': setAll,
Expand Down
Loading

0 comments on commit 25358cd

Please sign in to comment.