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

Add sample script to init and call the secure owner API from node.js #335

Merged
merged 2 commits into from
Feb 18, 2020
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
115 changes: 115 additions & 0 deletions doc/samples/v3_api_node/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions doc/samples/v3_api_node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "node-sample",
"version": "0.0.1",
"description": "Sample of connecting to the secure OwnerAPI via node",
"main": "src/index.js",
"scripts": {
"test": "npm test"
},
"author": "",
"license": "ISC",
"dependencies": {
"jayson": "^3.2.0"
}
}
28 changes: 28 additions & 0 deletions doc/samples/v3_api_node/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Connecting to the wallet's V3 Owner API from Node

This is a small sample with code that demonstrates how to initialize the Wallet V3's Secure API and call API functions through it.

To run this sample:

First run the Owner API:

```.sh
grin-wallet owner_api
```

This sample doesn't use the authentication specified in the wallet's `.api_secret`, so before running the owner_api please ensure api authentication is commented out in `grin-wallet.toml`. Including the authentication token as part of the request is a function of your json-rpc client library of choice, so it's not included in the sample to make setup a bit simpler.

ensure the client url in `src\index.js` is set correctly:

```.sh
const client = jayson.client.http('http://localhost:3420/v3/owner');
```

Then (assuming node.js and npm are installed on the system):

```.sh
npm install
node src/index.json
```

Feel free to play around with the sample, modifying it to call whatever functions you'd like to see in operation!
134 changes: 134 additions & 0 deletions doc/samples/v3_api_node/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* Sample Code for connecting to the V3 Secure API via Node
*
* With thanks to xiaojay of Niffler Wallet:
* https://github.com/grinfans/Niffler/blob/gw3/src/shared/walletv3.js
*
*/

const jayson = require('jayson/promise');
const crypto = require('crypto');

const client = jayson.client.http('http://localhost:3420/v3/owner');

// Demo implementation of using `aes-256-gcm` with node.js's `crypto` lib.
const aes256gcm = (shared_secret) => {
const ALGO = 'aes-256-gcm';

// encrypt returns base64-encoded ciphertext
const encrypt = (str, nonce) => {
let key = Buffer.from(shared_secret, 'hex')
const cipher = crypto.createCipheriv(ALGO, key, nonce)
const enc = Buffer.concat([cipher.update(str, 'utf8'), cipher.final()])
const tag = cipher.getAuthTag()
return Buffer.concat([enc, tag]).toString('base64')
};

// decrypt decodes base64-encoded ciphertext into a utf8-encoded string
const decrypt = (enc, nonce) => {
//key,nonce is all buffer type; data is base64-encoded string
let key = Buffer.from(shared_secret, 'hex')
const data_ = Buffer.from(enc, 'base64')
const decipher = crypto.createDecipheriv(ALGO, key, nonce)
const len = data_.length
const tag = data_.slice(len-16, len)
const text = data_.slice(0, len-16)
decipher.setAuthTag(tag)
const dec = decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');
return dec
};

return {
encrypt,
decrypt,
};
};

class JSONRequestEncrypted {
constructor(id, method, params) {
this.jsonrpc = '2.0'
this.method = method
this.id = id
this.params = params
}

async send(key){
const aesCipher = aes256gcm(key);
const nonce = new Buffer.from(crypto.randomBytes(12));
let enc = aesCipher.encrypt(JSON.stringify(this), nonce);
console.log("Encrypted: " + enc)
let params = {
'nonce': nonce.toString('hex'),
'body_enc': enc,
}
let response = await client.request('encrypted_request_v3', params);

if (response.err) {
throw response.err
}

const nonce2 = Buffer.from(response.result.Ok.nonce, 'hex');
const data = Buffer.from(response.result.Ok.body_enc, 'base64');

let dec = aesCipher.decrypt(data, nonce2)
return dec
}
}

async function initSecure() {
let ecdh = crypto.createECDH('secp256k1')
ecdh.generateKeys()
let publicKey = ecdh.getPublicKey('hex', 'compressed')
const params = {
'ecdh_pubkey': publicKey
}
let response = await client.request('init_secure_api', params);
if (response.err) {
throw response.err
}

return ecdh.computeSecret(response.result.Ok, 'hex', 'hex')
}

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async function main() {
let shared_key = await initSecure();

let response = await new JSONRequestEncrypted(1, 'open_wallet', {
"name": null,
"password": "",
}).send(shared_key);

let token = JSON.parse(response).result.Ok;

let iterations = 1;

for (i=0; i<iterations*2; i+=2) {
let info_response = await new JSONRequestEncrypted(i, 'retrieve_summary_info', {
"token": token,
"refresh_from_node": true,
"minimum_confirmations": 1,
}).send(shared_key)

console.log("Info Response: ", info_response);
await sleep(2000)

let txs_response = await new JSONRequestEncrypted(i+1, 'retrieve_txs', {
"token": token,
"refresh_from_node": true,
"tx_id": null,
"tx_slate_id": null,
}).send(shared_key)

console.log("Txs Response: ", txs_response);
await sleep(2000)
}
}



main();