From ade85fd96a07f4045bdb15314d0d0bf1a12b67ef Mon Sep 17 00:00:00 2001
From: Yeastplume <yeastplume@protonmail.com>
Date: Mon, 17 Feb 2020 15:28:08 +0000
Subject: [PATCH 1/2] add sample script to init and call the secure owner API
 from node

---
 doc/samples/v3_api_node/package-lock.json | 115 +++++++++++++++++++
 doc/samples/v3_api_node/package.json      |  14 +++
 doc/samples/v3_api_node/readme.md         |  28 +++++
 doc/samples/v3_api_node/src/index.js      | 134 ++++++++++++++++++++++
 4 files changed, 291 insertions(+)
 create mode 100644 doc/samples/v3_api_node/package-lock.json
 create mode 100644 doc/samples/v3_api_node/package.json
 create mode 100644 doc/samples/v3_api_node/readme.md
 create mode 100644 doc/samples/v3_api_node/src/index.js

diff --git a/doc/samples/v3_api_node/package-lock.json b/doc/samples/v3_api_node/package-lock.json
new file mode 100644
index 000000000..16c044e49
--- /dev/null
+++ b/doc/samples/v3_api_node/package-lock.json
@@ -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=="
+    }
+  }
+}
diff --git a/doc/samples/v3_api_node/package.json b/doc/samples/v3_api_node/package.json
new file mode 100644
index 000000000..1126955d3
--- /dev/null
+++ b/doc/samples/v3_api_node/package.json
@@ -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"
+  }
+}
diff --git a/doc/samples/v3_api_node/readme.md b/doc/samples/v3_api_node/readme.md
new file mode 100644
index 000000000..e08ccbfcb
--- /dev/null
+++ b/doc/samples/v3_api_node/readme.md
@@ -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!
diff --git a/doc/samples/v3_api_node/src/index.js b/doc/samples/v3_api_node/src/index.js
new file mode 100644
index 000000000..18cc2211c
--- /dev/null
+++ b/doc/samples/v3_api_node/src/index.js
@@ -0,0 +1,134 @@
+/* Sample Code for connecting to the V3 Secure API via Node
+ *
+ * With thanks t xiajoy 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();
+
+

From 6bc9caacd25596975cf186e3c23782094f09d91c Mon Sep 17 00:00:00 2001
From: Yeastplume <yeastplume@protonmail.com>
Date: Mon, 17 Feb 2020 15:37:26 +0000
Subject: [PATCH 2/2] fix xiaojay's name.. thanks xiaojay!

---
 doc/samples/v3_api_node/src/index.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/samples/v3_api_node/src/index.js b/doc/samples/v3_api_node/src/index.js
index 18cc2211c..6de1c5bec 100644
--- a/doc/samples/v3_api_node/src/index.js
+++ b/doc/samples/v3_api_node/src/index.js
@@ -1,6 +1,6 @@
 /* Sample Code for connecting to the V3 Secure API via Node
  *
- * With thanks t xiajoy of Niffler Wallet:
+ * With thanks to xiaojay of Niffler Wallet:
  * https://github.com/grinfans/Niffler/blob/gw3/src/shared/walletv3.js
  *
  */