Skip to content

Commit

Permalink
Add sample script to init and call the secure owner API from node.js (m…
Browse files Browse the repository at this point in the history
…imblewimble#335)

* add sample script to init and call the secure owner API from node

* fix xiaojay's name.. thanks xiaojay!
  • Loading branch information
yeastplume authored Feb 18, 2020
1 parent da064ac commit 1ced899
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 0 deletions.
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();


0 comments on commit 1ced899

Please sign in to comment.