mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-01-20 19:11:09 +03:00
Add sample script to init and call the secure owner API from node.js (#335)
* add sample script to init and call the secure owner API from node * fix xiaojay's name.. thanks xiaojay!
This commit is contained in:
parent
da064acc33
commit
1ced8990b9
4 changed files with 291 additions and 0 deletions
115
doc/samples/v3_api_node/package-lock.json
generated
Normal file
115
doc/samples/v3_api_node/package-lock.json
generated
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
{
|
||||||
|
"name": "node-sample",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/connect": {
|
||||||
|
"version": "3.4.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||||
|
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/express-serve-static-core": {
|
||||||
|
"version": "4.17.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz",
|
||||||
|
"integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/range-parser": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/lodash": {
|
||||||
|
"version": "4.14.149",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
|
||||||
|
"integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ=="
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "12.12.27",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.27.tgz",
|
||||||
|
"integrity": "sha512-odQFl/+B9idbdS0e8IxDl2ia/LP8KZLXhV3BUeI98TrZp0uoIzQPhGd+5EtzHmT0SMOIaPd7jfz6pOHLWTtl7A=="
|
||||||
|
},
|
||||||
|
"@types/range-parser": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
|
||||||
|
},
|
||||||
|
"JSONStream": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
|
||||||
|
"requires": {
|
||||||
|
"jsonparse": "^1.2.0",
|
||||||
|
"through": ">=2.2.7 <3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||||
|
},
|
||||||
|
"es6-promise": {
|
||||||
|
"version": "4.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||||
|
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||||
|
},
|
||||||
|
"es6-promisify": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
|
||||||
|
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
|
||||||
|
"requires": {
|
||||||
|
"es6-promise": "^4.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eyes": {
|
||||||
|
"version": "0.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
|
||||||
|
"integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
|
||||||
|
},
|
||||||
|
"jayson": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jayson/-/jayson-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-DZQnwA57GcStw4soSYB2VntWXFfoWvmSarlaWePDYOWhjxT72PBM4atEBomaTaS1uqk3jFC9UO9AyWjlujo3xw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/connect": "^3.4.32",
|
||||||
|
"@types/express-serve-static-core": "^4.16.9",
|
||||||
|
"@types/lodash": "^4.14.139",
|
||||||
|
"@types/node": "^12.7.7",
|
||||||
|
"JSONStream": "^1.3.1",
|
||||||
|
"commander": "^2.12.2",
|
||||||
|
"es6-promisify": "^5.0.0",
|
||||||
|
"eyes": "^0.1.8",
|
||||||
|
"json-stringify-safe": "^5.0.1",
|
||||||
|
"lodash": "^4.17.15",
|
||||||
|
"uuid": "^3.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json-stringify-safe": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||||
|
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||||
|
},
|
||||||
|
"jsonparse": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
|
||||||
|
"integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
|
||||||
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||||
|
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||||
|
},
|
||||||
|
"through": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
|
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
doc/samples/v3_api_node/package.json
Normal file
14
doc/samples/v3_api_node/package.json
Normal file
|
@ -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
doc/samples/v3_api_node/readme.md
Normal file
28
doc/samples/v3_api_node/readme.md
Normal file
|
@ -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
doc/samples/v3_api_node/src/index.js
Normal file
134
doc/samples/v3_api_node/src/index.js
Normal file
|
@ -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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue