From 3625518672ec8a89bb9ff185402c86b2bdc20e75 Mon Sep 17 00:00:00 2001 From: g-homebase Date: Thu, 23 Apr 2020 14:15:34 -0400 Subject: [PATCH 1/7] intial pass on adding wasabi features -WIP --- cli.js | 4 ++-- index.js | 2 ++ index.ts | 1 + src/clients/btcClient.js | 2 ++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cli.js b/cli.js index 614ba4a..81b7d66 100644 --- a/cli.js +++ b/cli.js @@ -1,8 +1,8 @@ const { client: lnclient } = require("./src/clients/lncClient"); const { client: btclient } = require("./src/clients/btcClient"); +const { client: wasabi } = require("./src/clients/wasabiClient"); const ln = lnclient(); const btc = btclient(); - -module.exports = {ln,btc} +module.exports = { ln, btc, wasabi: wasabi() }; diff --git a/index.js b/index.js index 363e0bb..d79433a 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,8 @@ var lncClient_1 = require("./src/clients/lncClient"); exports.lnClient = lncClient_1.client; var otsClient_1 = require("./src/clients/otsClient"); exports.otsClient = otsClient_1.client; +var wasabiClient_1 = require("./src/clients/wasabiClient"); +exports.wasabiClient = wasabiClient_1.client; var cypherNodeHttpTransport_1 = require("./src/transport/cypherNodeHttpTransport"); exports.cypherNodeHttpTransport = cypherNodeHttpTransport_1.default; var cryptoUtil_1 = require("./src/lib/cryptoUtil"); diff --git a/index.ts b/index.ts index 2983de8..fef9f31 100644 --- a/index.ts +++ b/index.ts @@ -1,6 +1,7 @@ export { client as btcClient } from "./src/clients/btcClient"; export { client as lnClient } from "./src/clients/lncClient"; export { client as otsClient } from "./src/clients/otsClient"; +export { client as wasabiClient } from "./src/clients/wasabiClient"; export { default as cypherNodeHttpTransport } from "./src/transport/cypherNodeHttpTransport"; diff --git a/src/clients/btcClient.js b/src/clients/btcClient.js index 6aeef6e..f7761e1 100644 --- a/src/clients/btcClient.js +++ b/src/clients/btcClient.js @@ -245,6 +245,8 @@ exports.client = function (_a) { throw "Label is required to for a pub32 watch"; if (/[^0-9a-zA-Z_i ]/.test(options.label)) throw "Labels must be alpha numeric or _"; + if (!options.nstart || isNaN(options.nstart)) + throw "nstart must be provided and must be a number"; return [4 /*yield*/, post("watchxpub", __assign({ pub32: xpub }, options))]; case 1: result = _a.sent(); From 28fe5b86a285f2ed3a2a56eab4c16fc51bc08c4b Mon Sep 17 00:00:00 2001 From: g-homebase Date: Thu, 23 Apr 2020 14:47:04 -0400 Subject: [PATCH 2/7] Add types --- src/clients/wasabiClient.js | 65 +++++++++++++++++++++++++++++++++++++ src/clients/wasabiClient.ts | 35 ++++++++++++++++++++ src/lib/types/wasabi.d.ts | 45 +++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 src/clients/wasabiClient.js create mode 100644 src/clients/wasabiClient.ts create mode 100644 src/lib/types/wasabi.d.ts diff --git a/src/clients/wasabiClient.js b/src/clients/wasabiClient.js new file mode 100644 index 0000000..754baaf --- /dev/null +++ b/src/clients/wasabiClient.js @@ -0,0 +1,65 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var cypherNodeHttpTransport_1 = __importDefault(require("../transport/cypherNodeHttpTransport")); +exports.client = function (_a) { + var _b = (_a === void 0 ? {} : _a).transport, transport = _b === void 0 ? cypherNodeHttpTransport_1.default() : _b; + var get = transport.get, post = transport.post; + var api = { + getNewAddress: function (label) { + return post("wasabi_getnewaddress", { label: label }); + }, + getBalances: function () { + return get("wasabi_getbalances"); + }, + getTxns: function (instanceId) { + return get("wasabi_gettransactions", instanceId); + }, + spend: function (param) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, post("wasabi_spend", param)]; + }); + }); + } + }; + return api; +}; diff --git a/src/clients/wasabiClient.ts b/src/clients/wasabiClient.ts new file mode 100644 index 0000000..34da3d5 --- /dev/null +++ b/src/clients/wasabiClient.ts @@ -0,0 +1,35 @@ +import cypherNodeHTTPTransport from "../transport/cypherNodeHttpTransport"; +import { ClientConfig } from "../lib/types/clients"; +import { + WasabiClient, + Address, + WasabiGetBalancesPayload, + WasabiGetTxnsPayload, + WasabiNewAddressPayload, + WasabiSpendPayload +} from "../lib/types/wasabi.d"; +export const client = ({ + transport = cypherNodeHTTPTransport() +}: ClientConfig = {}): WasabiClient => { + const { get, post } = transport; + const api = { + getNewAddress(label: string): Promise { + return post("wasabi_getnewaddress", { label }); + }, + getBalances(): Promise { + return get("wasabi_getbalances"); + }, + getTxns(instanceId: number): Promise { + return get("wasabi_gettransactions", instanceId); + }, + async spend(param: { + instanceId?: number; + private?: boolean; + address: Address; + amount: number; + }): Promise { + return post("wasabi_spend", param); + } + }; + return api; +}; diff --git a/src/lib/types/wasabi.d.ts b/src/lib/types/wasabi.d.ts new file mode 100644 index 0000000..0951257 --- /dev/null +++ b/src/lib/types/wasabi.d.ts @@ -0,0 +1,45 @@ +export type Hash = string; // '0000000000000000001c6a6ae90f3f90f9a02098f5f447dc3ee09649097fa2cf' +export type BlockHash = Hash; +export type BlockHeight = number; +export type MerkleRoot = Hash; +export type TxnHash = Hash; +export type TimeStamp = number; +export type TxnHex = string; +export type TxnId = string; +export type Address = string; +export type AddressType = "bech32" | "legacy" | "p2sh-segwit" | "expub"; +export interface WasabiNewAddressPayload { + address: Address; + keyPath: string; //"84'/0'/0'/0/23"; + label: string; //'["unknown"]'; +} +export interface WasabiGetBalancesPayload { + [instanceId: string]: { private: number; total: number }; +} +export interface WasabiTxn { + datetime: string; // "2020-04-23T18:10:36+00:00"; + height: number; //1721643; + amount: number; // 340000; + label: string; // "mytest"; + tx: string; // "220850ec4d8a8daf6ebe9e74f4ab29ffca3392ff03a081c4915a83cb56b9e0e5"; +} +export interface WasabiGetTxnsPayload { + instanceId: number | null; + transactions: [WasabiTxn]; +} +export interface WasabiSpendPayload { + txid: string; + tx: string; +} + +export interface WasabiClient { + getNewAddress(label: string): Promise; + getBalances(): Promise; + getTxns(instanceId: number): Promise; + spend(param: { + instanceId?: number; + private?: boolean; + address: Address; + amount: number; + }): Promise; +} From 27c102acb706f906ae3de1d7b185c0a8c185cb39 Mon Sep 17 00:00:00 2001 From: g-homebase Date: Sun, 26 Apr 2020 13:47:52 -0400 Subject: [PATCH 3/7] Add getunspent coins --- src/clients/wasabiClient.js | 3 +++ src/clients/wasabiClient.ts | 11 +++++++++-- src/lib/types/wasabi.d.ts | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/clients/wasabiClient.js b/src/clients/wasabiClient.js index 754baaf..a49cea9 100644 --- a/src/clients/wasabiClient.js +++ b/src/clients/wasabiClient.js @@ -50,6 +50,9 @@ exports.client = function (_a) { getBalances: function () { return get("wasabi_getbalances"); }, + getUnspentCoins: function (instanceId) { + return get("wasabi_getunspentcoins", instanceId); + }, getTxns: function (instanceId) { return get("wasabi_gettransactions", instanceId); }, diff --git a/src/clients/wasabiClient.ts b/src/clients/wasabiClient.ts index 34da3d5..e29fd35 100644 --- a/src/clients/wasabiClient.ts +++ b/src/clients/wasabiClient.ts @@ -6,8 +6,10 @@ import { WasabiGetBalancesPayload, WasabiGetTxnsPayload, WasabiNewAddressPayload, - WasabiSpendPayload + WasabiSpendPayload, + WasabiGetUnspentCoinsPayload } from "../lib/types/wasabi.d"; + export const client = ({ transport = cypherNodeHTTPTransport() }: ClientConfig = {}): WasabiClient => { @@ -19,7 +21,12 @@ export const client = ({ getBalances(): Promise { return get("wasabi_getbalances"); }, - getTxns(instanceId: number): Promise { + getUnspentCoins( + instanceId?: number + ): Promise { + return get("wasabi_getunspentcoins", instanceId); + }, + getTxns(instanceId?: number): Promise { return get("wasabi_gettransactions", instanceId); }, async spend(param: { diff --git a/src/lib/types/wasabi.d.ts b/src/lib/types/wasabi.d.ts index 0951257..dd8a1a1 100644 --- a/src/lib/types/wasabi.d.ts +++ b/src/lib/types/wasabi.d.ts @@ -27,6 +27,20 @@ export interface WasabiGetTxnsPayload { instanceId: number | null; transactions: [WasabiTxn]; } +export interface WasabiUnspentCoint { + txid: string; //"d914f27248354d5f1886ebf393f2a2ba9fd56a0c67e864cf5e549cdade8b351f"; + index: number; // 0; + amount: number; //15815; + anonymitySet: number; // 9; + confirmed: boolean; // true; + label: string; // ""; + keyPath: string; // "84'/0'/0'/1/1892"; + address: string; // "tb1qlvxadhum3k5nthv8g9nkhze43sk9v727jnhkxj"; +} +export interface WasabiGetUnspentCoinsPayload { + instanceId: number | null; + transactions: [WasabiUnspentCoint]; +} export interface WasabiSpendPayload { txid: string; tx: string; @@ -36,6 +50,7 @@ export interface WasabiClient { getNewAddress(label: string): Promise; getBalances(): Promise; getTxns(instanceId: number): Promise; + getUnspentCoins(instanceId?: number): Promise; spend(param: { instanceId?: number; private?: boolean; From 66f120cb168b410cb848cdf6a45188f31f205758 Mon Sep 17 00:00:00 2001 From: g-homebase Date: Mon, 27 Apr 2020 10:19:37 -0400 Subject: [PATCH 4/7] better spend --- cli.js | 6 ++++-- package.json | 3 ++- src/clients/wasabiClient.js | 4 ++-- src/clients/wasabiClient.ts | 9 +++++---- src/lib/types/wasabi.d.ts | 17 ++++++++++++----- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/cli.js b/cli.js index 81b7d66..c200534 100644 --- a/cli.js +++ b/cli.js @@ -1,8 +1,10 @@ const { client: lnclient } = require("./src/clients/lncClient"); const { client: btclient } = require("./src/clients/btcClient"); -const { client: wasabi } = require("./src/clients/wasabiClient"); +const { client: wasabiClient } = require("./src/clients/wasabiClient"); const ln = lnclient(); const btc = btclient(); +const wasabi = wasabiClient(); +const { log, error } = console; -module.exports = { ln, btc, wasabi: wasabi() }; +module.exports = { ln, btc, wasabi }; diff --git a/package.json b/package.json index 114ed6c..fce7d59 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "test:client:btc": "NODE_TLS_REJECT_UNAUTHORIZED=0 node -r dotenv/config ./node_modules/ava/cli.js ./src/clients/btcClient.spec.js", "test:client:lnc": "NODE_TLS_REJECT_UNAUTHORIZED=0 node -r dotenv/config ./node_modules/ava/cli.js ./src/clients/lncClient.spec.js", "test:client:ots": "NODE_TLS_REJECT_UNAUTHORIZED=0 node -r dotenv/config ./node_modules/ava/cli.js ./src/clients/otsClient.spec.js", - "test": "NODE_TLS_REJECT_UNAUTHORIZED=0 node -r dotenv/config ./node_modules/ava/cli.js ./src/lib/*.spec*.js ./src/clients/*.spec.js" + "test": "NODE_TLS_REJECT_UNAUTHORIZED=0 node -r dotenv/config ./node_modules/ava/cli.js ./src/lib/*.spec*.js ./src/clients/*.spec.js", + "cli": "CYPHERNODE_GATEKEEPER_CERT_CA=$(cat test.pem) node -r dotenv/config -i -e \"$( { return post("wasabi_getnewaddress", { label }); }, - getBalances(): Promise { - return get("wasabi_getbalances"); + getBalances(anonset?: number): Promise { + return get("wasabi_getbalances", anonset); }, getUnspentCoins( instanceId?: number @@ -30,10 +30,11 @@ export const client = ({ return get("wasabi_gettransactions", instanceId); }, async spend(param: { - instanceId?: number; - private?: boolean; address: Address; amount: number; + instanceId?: number; + private?: boolean; + minanonset?: number; }): Promise { return post("wasabi_spend", param); } diff --git a/src/lib/types/wasabi.d.ts b/src/lib/types/wasabi.d.ts index dd8a1a1..bbd4bda 100644 --- a/src/lib/types/wasabi.d.ts +++ b/src/lib/types/wasabi.d.ts @@ -42,19 +42,26 @@ export interface WasabiGetUnspentCoinsPayload { transactions: [WasabiUnspentCoint]; } export interface WasabiSpendPayload { - txid: string; - tx: string; + message: string; + result: + | { + txid: string; + tx: string; + } + | "error"; + event: string; } export interface WasabiClient { getNewAddress(label: string): Promise; - getBalances(): Promise; + getBalances(anonset?: number): Promise; getTxns(instanceId: number): Promise; getUnspentCoins(instanceId?: number): Promise; spend(param: { - instanceId?: number; - private?: boolean; address: Address; amount: number; + instanceId?: number; + private?: boolean; + minanonset?: number; }): Promise; } From accd2865877c220df1d37b6ef83fea3024fbcff4 Mon Sep 17 00:00:00 2001 From: g-homebase Date: Tue, 5 May 2020 08:12:46 -0400 Subject: [PATCH 5/7] Update read me --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 237c1be..d58f173 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# An Isomorphic Javascript SDK for Bitcoin, C-lightning and Opentimestamp Vis-à-vis CypherNode. +# An Isomorphic Javascript SDK for Bitcoin, C-lightning, Wasabi and Opentimestamp Vis-à-vis CypherNode. # Why ? @@ -80,7 +80,8 @@ Contains the individual clients that can be instaniated to access each of Cypher - btcClient.js : All things bitcoin - lncClient.js : All things lighting -- otsClient.js : All this opentimestamp +- otsClient.js : All things opentimestamp +- wasabi.js : All things Wasabi Test files for each client are included that showcase the usage of functionality From b6fccd02677363b89ad68dcbe0a453524274c645 Mon Sep 17 00:00:00 2001 From: g-homebase Date: Tue, 5 May 2020 08:24:11 -0400 Subject: [PATCH 6/7] Make cli auto resolve promises --- cli.js | 13 ++++++++++++- package.json | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cli.js b/cli.js index c200534..2a8fcfd 100644 --- a/cli.js +++ b/cli.js @@ -1,10 +1,21 @@ const { client: lnclient } = require("./src/clients/lncClient"); const { client: btclient } = require("./src/clients/btcClient"); const { client: wasabiClient } = require("./src/clients/wasabiClient"); +const repl=require('repl'); const ln = lnclient(); const btc = btclient(); const wasabi = wasabiClient(); const { log, error } = console; -module.exports = { ln, btc, wasabi }; +function replEvalPromise(cmd,ctx,filename,cb) { + let result=eval(cmd); + if (result instanceof Promise) { + return result + .then(response=>cb(null,response)); + } + return cb(null, result); +} +repl.start({ prompt: 'cyphernode-cli > ', eval: replEvalPromise }); + +// module.exports = { ln, btc, wasabi }; diff --git a/package.json b/package.json index fce7d59..cd88ca9 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "test:client:lnc": "NODE_TLS_REJECT_UNAUTHORIZED=0 node -r dotenv/config ./node_modules/ava/cli.js ./src/clients/lncClient.spec.js", "test:client:ots": "NODE_TLS_REJECT_UNAUTHORIZED=0 node -r dotenv/config ./node_modules/ava/cli.js ./src/clients/otsClient.spec.js", "test": "NODE_TLS_REJECT_UNAUTHORIZED=0 node -r dotenv/config ./node_modules/ava/cli.js ./src/lib/*.spec*.js ./src/clients/*.spec.js", - "cli": "CYPHERNODE_GATEKEEPER_CERT_CA=$(cat test.pem) node -r dotenv/config -i -e \"$( Date: Tue, 5 May 2020 08:26:11 -0400 Subject: [PATCH 7/7] lint cli --- cli.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/cli.js b/cli.js index 2a8fcfd..d4eb8a2 100644 --- a/cli.js +++ b/cli.js @@ -1,21 +1,18 @@ const { client: lnclient } = require("./src/clients/lncClient"); const { client: btclient } = require("./src/clients/btcClient"); const { client: wasabiClient } = require("./src/clients/wasabiClient"); -const repl=require('repl'); +const repl = require("repl"); const ln = lnclient(); const btc = btclient(); const wasabi = wasabiClient(); const { log, error } = console; -function replEvalPromise(cmd,ctx,filename,cb) { - let result=eval(cmd); +const replEvalPromise = (cmd, ctx, filename, cb) => { + let result = eval(cmd); if (result instanceof Promise) { - return result - .then(response=>cb(null,response)); + return result.then(response => cb(null, response)); } return cb(null, result); -} -repl.start({ prompt: 'cyphernode-cli > ', eval: replEvalPromise }); - -// module.exports = { ln, btc, wasabi }; +}; +repl.start({ prompt: "cyphernode-cli > ", eval: replEvalPromise });