OSDN Git Service

big boom
authorxuhongxin <xuhongxin@luojilab.com>
Wed, 10 Oct 2018 06:14:35 +0000 (14:14 +0800)
committerxuhongxin <xuhongxin@luojilab.com>
Wed, 10 Oct 2018 06:14:35 +0000 (14:14 +0800)
19 files changed:
.gitignore [new file with mode: 0644]
.jshintrc [new file with mode: 0644]
README.md [new file with mode: 0644]
package-lock.json [new file with mode: 0644]
package.json [new file with mode: 0644]
src/api/accounts.js [new file with mode: 0644]
src/api/balances.js [new file with mode: 0644]
src/api/keys.js [new file with mode: 0644]
src/api/transactions.js [new file with mode: 0644]
src/db/db.js [new file with mode: 0644]
src/http.js [new file with mode: 0644]
src/index.js [new file with mode: 0644]
src/sdk/accounts.js [new file with mode: 0644]
src/sdk/keys.js [new file with mode: 0644]
src/utils/byte.js [new file with mode: 0644]
src/utils/hex.js [new file with mode: 0644]
src/utils/uuid.js [new file with mode: 0644]
src/wasm/func.js [new file with mode: 0644]
src/wasm/go.js [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..30bc162
--- /dev/null
@@ -0,0 +1 @@
+/node_modules
\ No newline at end of file
diff --git a/.jshintrc b/.jshintrc
new file mode 100644 (file)
index 0000000..8ab3485
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,3 @@
+{
+    "esversion": 6
+}
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..7658f19
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# bytom-js-sdk
+
+## install
+
+``` bash
+npm install bytom-js-sdk
+```
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644 (file)
index 0000000..9ae545e
--- /dev/null
@@ -0,0 +1,53 @@
+{
+  "name": "bytom-js-sdk",
+  "version": "1.0.2",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "axios": {
+      "version": "0.18.0",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
+      "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
+      "requires": {
+        "follow-redirects": "1.5.7",
+        "is-buffer": "1.1.6"
+      }
+    },
+    "crypto-js": {
+      "version": "3.1.9-1",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz",
+      "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg="
+    },
+    "debug": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+      "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "follow-redirects": {
+      "version": "1.5.7",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.7.tgz",
+      "integrity": "sha512-NONJVIFiX7Z8k2WxfqBjtwqMifx7X42ORLFrOZ2LTKGj71G3C0kfdyTqGqr8fx5zSX6Foo/D95dgGWbPUiwnew==",
+      "requires": {
+        "debug": "3.1.0"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "tweetnacl": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.0.tgz",
+      "integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins="
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644 (file)
index 0000000..2a8b6fc
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "name": "bytom-js-sdk",
+  "version": "1.0.2",
+  "description": "bytom chrome api sdk",
+  "main": "src/index.js",
+  "dependencies": {
+    "axios": "^0.18.0",
+    "crypto-js": "^3.1.9-1",
+    "tweetnacl": "^1.0.0"
+  },
+  "devDependencies": {},
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://gitee.com/eostimeline/bytom-js-sdk.git"
+  },
+  "author": "",
+  "license": "ISC"
+}
diff --git a/src/api/accounts.js b/src/api/accounts.js
new file mode 100644 (file)
index 0000000..2e38d43
--- /dev/null
@@ -0,0 +1,43 @@
+function accountsApi(http) {
+    this.http = http;
+}
+
+/**
+ * Create a new account.
+ * 
+ * @see https://github.com/Bytom/bytom/wiki/API-Reference#create-account
+ * 
+ * @param {Array of String} xpubs - root_xpubs, pubkey array.
+ * @param {Integer} quorum - The number of keys required to sign transactions for the account.
+ * @param {String} alias  - Account alias.
+ */
+accountsApi.prototype.create = function(xpubs, quorum, alias) {
+    return this.http.request('/create-account', {root_xpubs: xpubs, quorum, alias});
+};
+
+/**
+ * List all accounts in the target Bytom node.
+ */
+accountsApi.prototype.listAll = function() {
+    return this.http.request('/list-accounts', {});
+};
+
+/**
+ * List all addresses for one account.
+ * 
+ * @param {String} accountId - id of account.
+ */
+accountsApi.prototype.listAddressesById = function(accountId) {
+    return this.http.request("/list-addresses", {account_id: accountId});
+};
+
+/**
+ * List all addresses for one account.
+ * 
+ * @param {String} accountAlias - alias of account.
+ */
+accountsApi.prototype.listAddressesByAlias = function(accountAlias) {
+    return this.http.request("/list-addresses", {account_alias: accountAlias});
+};
+
+export default accountsApi;
\ No newline at end of file
diff --git a/src/api/balances.js b/src/api/balances.js
new file mode 100644 (file)
index 0000000..64fb039
--- /dev/null
@@ -0,0 +1,14 @@
+
+/**
+ * balances Api
+ */
+function balancesApi(http) {
+    this.http = http;
+}
+
+/**
+ * get balances list
+ */
+balancesApi.prototype.list =function() {
+    return this.http.request('/list-balances', {});
+};
\ No newline at end of file
diff --git a/src/api/keys.js b/src/api/keys.js
new file mode 100644 (file)
index 0000000..0ead666
--- /dev/null
@@ -0,0 +1,15 @@
+function keysApi(http) {
+    this.http = http;
+}
+
+/**
+ * Create a new key.
+ * 
+ * @param {String} alias - User specified, unique identifier.
+ * @param {String} password - User specified, key password.
+ */
+keysApi.prototype.create = function(alias, password) {
+    return this.http.request('/create-key', {alias, password});
+};
+
+export default keysApi;
\ No newline at end of file
diff --git a/src/api/transactions.js b/src/api/transactions.js
new file mode 100644 (file)
index 0000000..27fd06b
--- /dev/null
@@ -0,0 +1,86 @@
+
+/**
+ * transactions api
+ * 
+ * @param {Object} http 
+ */
+function transactionsApi(http) {
+    this.http = http;
+}
+
+/**
+ * List all local transactions by account id.
+ * 
+ * @see https://github.com/Bytom/bytom/wiki/API-Reference#list-transactions
+ * 
+ * @param {String} accountId - Account id.
+ */
+transactionsApi.prototype.listByAccountId = function(accountId) {
+    return this.http.request('/list-transactions', {account_id: accountId});
+};
+
+/**
+ * List local transactions by id.
+ * 
+ * @see https://github.com/Bytom/bytom/wiki/API-Reference#list-transactions
+ * 
+ * @param {String} id - The transaction id.
+ */
+transactionsApi.prototype.listById = function(id) {
+    return this.http.request('/list-transactions', {id});
+};
+
+/**
+ * Build transaction.
+ * 
+ * @see https://github.com/Bytom/bytom/wiki/API-Reference#build-transaction
+ * 
+ * @param {String} baseTransaction - base data for the transaction, default is null.
+ * @param {Object} actions - Set of actions to compose the transaction.
+ * @param {Integer} timeRange - time stamp(block height)is maximum survival time for the transaction, the transaction will be not submit into block after this time stamp.
+ * @param {Integer} ttl - integer of the time to live in milliseconds, it means utxo will be reserved(locked) for builded transaction in this time range, if the transaction will not to be submitted into block, it will be auto unlocked for build transaction again after this ttl time. it will be set to 5 minutes(300 seconds) defaultly when ttl is 0.
+ */
+transactionsApi.prototype.build = function(baseTransaction, actions, timeRange, ttl) {
+    return this.http.request('/build-transaction', {
+        base_transaction: baseTransaction,
+        time_range: timeRange,
+        actions,
+        ttl
+    });
+};
+
+/**
+ * Sign transaction.
+ * 
+ * @see https://github.com/Bytom/bytom/wiki/API-Reference#sign-transaction
+ * 
+ * @param {Object} transaction - The built transaction template.
+ * @param {String} password - signature of the password.
+ */
+transactionsApi.prototype.sign = function(transaction, password) {
+    return this.http.request("/sign-transaction", {transaction, password});
+};
+
+/**
+ * Submit a signed transaction to the blockchain.
+ * 
+ * @see https://github.com/Bytom/bytom/wiki/API-Reference#submit-transaction·
+ * 
+ * @param {String} rawTransaction - raw_transaction of signed transaction.
+ */
+transactionsApi.prototype.submit = function(rawTransaction) {
+    return this.http.request("/submit-transaction", {raw_transaction: rawTransaction});
+};
+
+/**
+ * Estimate consumed neu(1BTM = 10^8NEU) for the transaction.
+ * 
+ * @see https://github.com/Bytom/bytom/wiki/API-Reference#estimate-transaction-gas
+ * 
+ * @param {Object} transaction - builded transaction response.
+ */
+transactionsApi.prototype.estimateGas = function(transaction) {
+    return this.http.request("estimate-transaction-gas", {transaction_template: transaction});
+};
+
+export default transactionsApi;
\ No newline at end of file
diff --git a/src/db/db.js b/src/db/db.js
new file mode 100644 (file)
index 0000000..8351644
--- /dev/null
@@ -0,0 +1,46 @@
+let db;
+let init = false;
+//db version
+const version = 1;
+
+export async function getDB(name) {
+    await initDB();
+    return db;
+}
+
+async function initDB() {
+    if (!init) {
+        let dbpr = new Promise(function(resolve, reject){
+            let request = indexedDB.open("bytom", version);
+            request.onerror = function(event) {
+                reject(new Error("Why didn't you allow my web app to use IndexedDB?"));
+            };
+            request.onsuccess = function(event) {
+                console.log("open IndexedDB onsuccess");
+                db = event.target.result;
+                init = true;
+                resolve();
+            };
+            request.onupgradeneeded = function(event) {
+                db = event.target.result;
+                console.log("onupgradeneeded IndexedDB");
+                if (!db.objectStoreNames.contains('keys')) {
+                    let ObjectStore = db.createObjectStore('keys', { autoIncrement: true });
+                    ObjectStore.createIndex('alias', 'alias', { unique: true });
+                    ObjectStore.createIndex('xpub', 'xpub', { unique: true });
+                }
+                if(!db.objectStoreNames.contains('accounts')) {
+                    let ObjectStore = db.createObjectStore('accounts', { autoIncrement: true });
+                    ObjectStore.createIndex('alias', 'alias', { unique: true });
+                    ObjectStore.createIndex('id', 'id', { unique: true });
+                }
+                if(!db.objectStoreNames.contains('addresses')) {
+                    let ObjectStore = db.createObjectStore('addresses', { autoIncrement: true });
+                    ObjectStore.createIndex('AccountID', 'AccountID', { unique: false });
+                    ObjectStore.createIndex('Address', 'Address', { unique: true });
+                }
+            };
+        });
+        await dbpr;
+    }
+}
\ No newline at end of file
diff --git a/src/http.js b/src/http.js
new file mode 100644 (file)
index 0000000..71e74af
--- /dev/null
@@ -0,0 +1,32 @@
+import axios from 'axios';
+
+function http(baseUrl, token) {
+    this.baseUrl = baseUrl;
+    this.token = token;
+    this.request = function(path, body) {
+        var config = {
+            url: `${this.baseUrl}${path}`,
+            method: 'POST',
+            headers: {
+                Accept: 'application/json',
+            },
+            data: body,
+            timeout: 1000
+        };
+
+        if (this.token) {
+            config.headers.Authorization = `Basic ${btoa(this.token)}`;
+        }
+        //return Promise
+        return axios.request(config).then(function(resp){
+            if (resp.data.status === 'fail') {
+                throw resp.data.msg;
+            } else if (resp.data.status === 'success') {
+                return resp.data.data;
+            }
+            return resp.data;
+        });
+    };
+}
+
+export default http;
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
new file mode 100644 (file)
index 0000000..a717f42
--- /dev/null
@@ -0,0 +1,28 @@
+import keysApi from "./api/keys.js";
+import accountsApi from "./api/accounts.js";
+import transactionsApi from "./api/transactions.js";
+import keysSDK from "./sdk/keys.js";
+import accountsSDK from "./sdk/accounts.js";
+import http from "./http.js";
+
+//todo vue use
+function Bytom(serverHost, wasmPath, baseURL, token) {
+    this.install = function(Vue, options) {
+        Vue.prototype.$Bytom = this;
+    };
+    
+    if(baseURL) {
+        this.http = new http(baseURL, token);
+
+        this.keys = new keysApi(this.http);
+        this.accounts = new accountsApi(this.http);
+        this.transactions = new transactionsApi(this.http);
+    }
+
+    Bytom.wasmPath = wasmPath;
+    this.sdk = {};
+    this.sdk.keys = new keysSDK();
+    this.sdk.accounts = new accountsSDK();
+}
+
+export default Bytom;
\ No newline at end of file
diff --git a/src/sdk/accounts.js b/src/sdk/accounts.js
new file mode 100644 (file)
index 0000000..9944f0b
--- /dev/null
@@ -0,0 +1,97 @@
+import {getDB} from "../db/db";
+import {createAccount, createAccountReceiver} from "../wasm/func";
+
+function accountsSDK(){    
+}
+
+/**
+ * create account
+ * 
+ * @param {String} alias 
+ * @param {Int} quorum 
+ * @param {String} rootXPub 
+ */
+accountsSDK.prototype.createAccount = function(alias, quorum, rootXPub) {
+    let returnPromise = new Promise((resolve, reject) => {
+        getDB().then(db => {
+            let transaction = db.transaction(["accounts"], "readwrite");
+            let objectStore = transaction.objectStore("accounts");
+            let request = objectStore.add({
+                alias:alias,
+            });
+            request.onsuccess = function (event) {
+                let data = {alias:alias, quorum:quorum, rootXPub:rootXPub, nextIndex:request.result};
+                createAccount(data).then(res => {
+                    let JsonData = JSON.parse(res.data);
+                    let putTransaction = db.transaction(["accounts"], "readwrite");
+                    let putObjectStore = putTransaction.objectStore("accounts");
+                    let putRequest = putObjectStore.put(JsonData, request.result);
+                    putRequest.onsuccess = function(e) {
+                        resolve(JsonData);
+                    };
+                    putRequest.onerror = function(e) {
+                        resolve(putRequest.error);
+                    };
+                }).catch(error => {
+                    reject(error);
+                });
+            };
+            request.onerror = function(event) {
+                reject(request.error);
+            };
+        }).catch(error => {
+            reject(error);
+        });
+    });
+    return returnPromise;
+};
+
+/**
+ * 
+ * @param {Object} account createAccount return account Object val
+ * @param {Int} nextIndex 
+ */
+accountsSDK.prototype.createAccountReceiver = function(account) {
+    let returnPromise = new Promise((resolve, reject) => {
+        getDB().then(db => {
+            let transaction = db.transaction(["addresses"], "readwrite");
+            let objectStore = transaction.objectStore("addresses");
+            let request = objectStore.add({
+                account_id:account.id,
+                account_alias:account.alias,
+            });
+            request.onsuccess = function(event) {
+                let data = {account:JSON.stringify(account), nextIndex: request.result};
+                createAccountReceiver(data).then(res => {
+                    let JsonData = JSON.parse(res.data);
+                    let JsonDB = JSON.parse(res.db);
+                    let putTransaction = db.transaction(["addresses"], "readwrite");
+                    let putObjectStore = putTransaction.objectStore("addresses");
+                    let putRequest = null;
+                    for (let key in JsonDB) {
+                        if(!JsonDB.hasOwnProperty(key)) continue;
+                        let putData = JSON.parse(JsonDB[key]);
+                        console.log(putData);
+                        putRequest = putObjectStore.put(putData, request.result);
+                    }
+                    putRequest.onsuccess = function(e) {
+                        resolve(JsonData);
+                    };
+                    putRequest.onerror = function(e) {
+                        resolve(putRequest.error);
+                    };
+                }).catch(error => {
+                    reject(error);
+                });
+            };
+            request.onerror = function(event) {
+                reject(request.error);
+            };
+        }).catch(error => {
+            reject(error);
+        });
+    });
+    return returnPromise;
+};
+
+export default accountsSDK;
\ No newline at end of file
diff --git a/src/sdk/keys.js b/src/sdk/keys.js
new file mode 100644 (file)
index 0000000..e60dc4d
--- /dev/null
@@ -0,0 +1,134 @@
+import {createKey, resetKeyPassword} from "../wasm/func";
+import {getDB} from "../db/db";
+
+function keysSDK() {
+}
+
+/**
+ * reset key password
+ * 
+ * @param {String}} rootXPub 
+ * @param {String} oldPassword 
+ * @param {String} newPassword 
+ */
+keysSDK.prototype.resetKeyPassword = function(rootXPub, oldPassword, newPassword) {
+    let returnPromise = new Promise((resolve, reject) => {
+        let data = {rootXPub: rootXPub, oldPassword:oldPassword, newPassword:newPassword};
+        resetKeyPassword(data).then(res => {
+            getDB().then(db => {
+                let objectStore = db.transaction(['keys'], 'readwrite').objectStore('keys');
+                let index = objectStore.index('xpub');
+                let keyRange = IDBKeyRange.only(rootXPub);
+                let getRequest = index.openCursor(keyRange);
+                getRequest.onsuccess = function (event) {
+                    const cursor = event.target.result;
+                    if(cursor && cursor.value.xpub === rootXPub) {
+                        console.log(cursor);
+                        const updateData = cursor.value;
+                        updateData.key = res.data;
+                        const request = cursor.update(updateData);
+                        request.onsuccess = function() {
+                            resolve(true);
+                        };
+                        request.onerror = function() {
+                            reject(new Error("db update error"));
+                        };
+                    } else {
+                        reject(new Error("db update error: not found by rootXPub"));
+                    }
+                };
+                getRequest.onerror = function (event) {
+                    reject(new Error("db get error"));
+                };
+            }).catch(error => {
+                reject(error);
+            });
+        }).catch(error => {
+            reject(error);
+        });
+    });
+    return returnPromise;
+};
+
+/**
+ * get key by XPub
+ * 
+ * @param {String} xpub 
+ */
+keysSDK.prototype.getKeyByXPub = function(xpub) {
+    let returnPromise = new Promise((resolve, reject) => {
+        getDB().then(db => {
+            let getRequest = db.transaction(['keys'], 'readonly')
+                .objectStore('keys')
+                .index('xpub')
+                .get(xpub);
+            getRequest.onsuccess = function(e) {
+                if(e.target.result) {
+                    resolve(e.target.result.key);
+                } else {
+                    reject(new Error("not found by XPub"));    
+                }
+            };
+            getRequest.onerror = function(e) {
+                reject(new Error("db get error"));
+            };
+        }).catch(error => {
+            reject(error);
+        });
+    });
+    return returnPromise;
+};
+
+/**
+ * Create a new key.
+ * 
+ * @param {String} alias - User specified, unique identifier.
+ * @param {String} password - User specified, key password.
+ */
+keysSDK.prototype.create = function(alias, password) {
+    var normalizedAlias = alias.toLowerCase().trim();
+    let returnPromise = new Promise((resolve, reject) => {
+        getDB().then(db => {
+            let getRequest = db.transaction(['keys'], 'readonly')
+                .objectStore('keys')
+                .index('alias')
+                .get(normalizedAlias);
+            getRequest.onsuccess = function (e) {
+                if (e.target.result) {
+                    reject(new Error("alias already exists"));
+                    return;
+                }
+                let data = {};
+                data.alias = normalizedAlias;
+                data.auth = password;
+                createKey(data).then((res) => {
+                    let jsonData = JSON.parse(res.data);
+                    let dbData = {
+                        key:res.data,
+                        xpub:jsonData.xpub,
+                        alias:alias,
+                    };
+                    let request = db.transaction(['keys'], 'readwrite')
+                        .objectStore('keys')
+                        .add(dbData);
+                    request.onsuccess = function (event) {
+                        resolve({xpub:jsonData.xpub, alias: alias});
+                    };
+                    request.onerror = function (event) {
+                        reject(new Error("db insert error"));
+                    };
+                }).catch(error => {
+                    reject(error);    
+                });
+            };
+            getRequest.onerror = function (event) {
+                reject(new Error("db get error"));
+            };
+        }).catch(error => {
+            reject(error);
+        });
+    });
+    return returnPromise;
+};
+
+export default keysSDK;
\ No newline at end of file
diff --git a/src/utils/byte.js b/src/utils/byte.js
new file mode 100644 (file)
index 0000000..0b21f13
--- /dev/null
@@ -0,0 +1,42 @@
+
+// assumes wordArray is Big-Endian (because it comes from CryptoJS which is all BE)
+// From: https://gist.github.com/getify/7325764
+export function convertWordArrayToUint8Array(wordArray) {
+       var len = wordArray.words.length,
+               u8_array = new Uint8Array(len << 2),
+               offset = 0, word, i
+       ;
+       for (i=0; i<len; i++) {
+               word = wordArray.words[i];
+               u8_array[offset++] = word >> 24;
+               u8_array[offset++] = (word >> 16) & 0xff;
+               u8_array[offset++] = (word >> 8) & 0xff;
+               u8_array[offset++] = word & 0xff;
+       }
+    return u8_array;
+}
+
+export function convertUint8ArrayToWordArray(u8Array) {
+       var words = [], i = 0, len = u8Array.length;
+
+       while (i < len) {
+               words.push(
+                       (u8Array[i++] << 24) |
+                       (u8Array[i++] << 16) |
+                       (u8Array[i++] << 8)  |
+                       (u8Array[i++])
+               );
+       }
+
+       return {
+               sigBytes: words.length * 4,
+               words: words
+       };
+}
+
+// get random Uint8Array
+export function getRandomByte(len) {
+       var array = new Uint8Array(len);
+       window.crypto.getRandomValues(array);
+       return array;
+}
\ No newline at end of file
diff --git a/src/utils/hex.js b/src/utils/hex.js
new file mode 100644 (file)
index 0000000..0da2ac0
--- /dev/null
@@ -0,0 +1,27 @@
+export function byteToHexString(uint8arr) {
+    if (!uint8arr) {
+      return '';
+    }
+    
+    var hexStr = '';
+    for (var i = 0; i < uint8arr.length; i++) {
+      var hex = (uint8arr[i] & 0xff).toString(16);
+      hex = (hex.length === 1) ? '0' + hex : hex;
+      hexStr += hex;
+    }
+    
+    return hexStr.toUpperCase();
+  }
+  
+export function hexStringToByte(str) {
+    if (!str) {
+      return new Uint8Array();
+    }
+    
+    var a = [];
+    for (var i = 0, len = str.length; i < len; i+=2) {
+      a.push(parseInt(str.substr(i,2),16));
+    }
+    
+    return new Uint8Array(a);
+}
\ No newline at end of file
diff --git a/src/utils/uuid.js b/src/utils/uuid.js
new file mode 100644 (file)
index 0000000..f4aed30
--- /dev/null
@@ -0,0 +1,7 @@
+function s4() {
+    return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
+}
+
+export function uuid() {
+    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
+}
\ No newline at end of file
diff --git a/src/wasm/func.js b/src/wasm/func.js
new file mode 100644 (file)
index 0000000..facf44a
--- /dev/null
@@ -0,0 +1,129 @@
+import {LoadWasm} from "./go";
+import Bytom from "../index";
+import keysSDK from "../sdk/keys.js";
+
+//wasm load val
+window.wasmIsLoad = false;
+window.wasmReceived = false;
+window.AllFunc = {};
+
+let AllFuncReceivedResolve;
+let funcReceived = new Promise(resolve => {
+    AllFuncReceivedResolve = resolve;
+});
+
+window.setFuncOver = function() {
+    AllFuncReceivedResolve();
+}
+window.resetWasmStatus = function() {
+    window.wasmIsLoad = false;
+    window.wasmReceived = false;
+    funcReceived = new Promise(resolve => {
+        AllFuncReceivedResolve = resolve;
+    });
+};
+
+//wasm call js func getKeyByXPub
+window.getKeyByXPub = function (XPub) {
+    let keys = new keysSDK();
+    let returnPromise = new Promise((resolve, reject) => {
+        keys.getKeyByXPub(XPub).then(res => {
+            resolve(res);
+        }).catch(error => {
+            reject(error);
+        });
+    });
+    return returnPromise;
+};
+
+export async function resetKeyPassword(data) {
+    await init();
+    let res = newWasmResult();
+    AllFunc.resetKeyPassword(data, res);
+    await res.wait;
+    if (res.hasOwnProperty('error')) {
+        throw new Error(res.error);
+    }
+    return res;
+}
+
+export async function createAccountReceiver(data) {
+    await init();
+    let res = newWasmResult();
+    AllFunc.createAccountReceiver(data, res);
+    await res.wait;
+    if (res.hasOwnProperty('error')) {
+        throw new Error(res.error);
+    }
+    return res;
+}
+
+export async function createAccount(data) {
+    await init();
+    let res = newWasmResult();
+    AllFunc.createAccount(data, res);
+    await res.wait;
+    if (res.hasOwnProperty('error')) {
+        throw new Error(res.error);
+    }
+    return res;
+}
+
+export async function createKey(data) {
+    await init();
+    let res = newWasmResult();
+    AllFunc.createKey(data, res);
+    await res.wait;
+    if (res.hasOwnProperty('error')) {
+        throw new Error(res.error);
+    }
+    return res;
+}
+
+export async function scMulBase(pri) {
+    await init();
+    let res = newWasmResult();
+    AllFunc.scMulBase(pri, res);
+    await res.wait;
+    if (res.hasOwnProperty('error')) {
+        throw new Error(res.error);
+    }
+    return res;
+}
+
+export function newWasmResult() {
+    let res = {};
+    res.wait = new Promise(function(resolve){
+        res.endFunc = resolve;
+    });
+    return res;
+}
+
+async function init() {
+    if (!window.wasmReceived) {
+        load();
+        await funcReceived;
+        window.wasmReceived = true;
+    }
+}
+
+function load(){
+    if(!window.wasmIsLoad) {
+        LoadWasm();
+        if (!WebAssembly.instantiateStreaming) { // polyfill
+            WebAssembly.instantiateStreaming = async (resp, importObject) => {
+                const source = await (await resp).arrayBuffer();
+                return await WebAssembly.instantiate(source, importObject);
+            };
+        }
+        const go = new Go();
+        let mod, inst;
+        WebAssembly.instantiateStreaming(fetch(Bytom.wasmPath), go.importObject).then(async (result) => {
+            mod = result.module;
+            inst = result.instance;
+            const run = go.run(inst);
+            window.wasmIsLoad = true;
+            // await run;
+        });
+    }
+}
\ No newline at end of file
diff --git a/src/wasm/go.js b/src/wasm/go.js
new file mode 100644 (file)
index 0000000..216fddb
--- /dev/null
@@ -0,0 +1,392 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+export function LoadWasm() {
+       // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API).
+       if (typeof window !== "undefined") {
+               window.global = window;
+       } else if (typeof self !== "undefined") {
+               self.global = self;
+       } else {
+               throw new Error("cannot export Go (neither window nor self is defined)");
+       }
+
+       let outputBuf = "";
+       global.fs = {
+               constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
+               writeSync(fd, buf) {
+                       outputBuf += decoder.decode(buf);
+                       const nl = outputBuf.lastIndexOf("\n");
+                       if (nl != -1) {
+                               console.log(outputBuf.substr(0, nl));
+                               outputBuf = outputBuf.substr(nl + 1);
+                       }
+                       return buf.length;
+               },
+               openSync(path, flags, mode) {
+                       const err = new Error("not implemented");
+                       err.code = "ENOSYS";
+                       throw err;
+               },
+       };
+
+       const encoder = new TextEncoder("utf-8");
+       const decoder = new TextDecoder("utf-8");
+
+       global.Go = class {
+               constructor() {
+                       this.argv = ["js"];
+                       this.env = {};
+                       this.exit = (code) => {
+                               if (code !== 0) {
+                                       console.warn("exit code:", code);
+                               }
+                               if(typeof window.resetWasmStatus == "function") {
+                                       window.resetWasmStatus();
+                               }
+                       };
+                       this._callbackTimeouts = new Map();
+                       this._nextCallbackTimeoutID = 1;
+
+                       const mem = () => {
+                               // The buffer may change when requesting more memory.
+                               return new DataView(this._inst.exports.mem.buffer);
+                       }
+
+                       const setInt64 = (addr, v) => {
+                               mem().setUint32(addr + 0, v, true);
+                               mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
+                       }
+
+                       const getInt64 = (addr) => {
+                               const low = mem().getUint32(addr + 0, true);
+                               const high = mem().getInt32(addr + 4, true);
+                               return low + high * 4294967296;
+                       }
+
+                       const loadValue = (addr) => {
+                               const f = mem().getFloat64(addr, true);
+                               if (!isNaN(f)) {
+                                       return f;
+                               }
+
+                               const id = mem().getUint32(addr, true);
+                               return this._values[id];
+                       }
+
+                       const storeValue = (addr, v) => {
+                               const nanHead = 0x7FF80000;
+
+                               if (typeof v === "number") {
+                                       if (isNaN(v)) {
+                                               mem().setUint32(addr + 4, nanHead, true);
+                                               mem().setUint32(addr, 0, true);
+                                               return;
+                                       }
+                                       mem().setFloat64(addr, v, true);
+                                       return;
+                               }
+
+                               switch (v) {
+                                       case undefined:
+                                               mem().setUint32(addr + 4, nanHead, true);
+                                               mem().setUint32(addr, 1, true);
+                                               return;
+                                       case null:
+                                               mem().setUint32(addr + 4, nanHead, true);
+                                               mem().setUint32(addr, 2, true);
+                                               return;
+                                       case true:
+                                               mem().setUint32(addr + 4, nanHead, true);
+                                               mem().setUint32(addr, 3, true);
+                                               return;
+                                       case false:
+                                               mem().setUint32(addr + 4, nanHead, true);
+                                               mem().setUint32(addr, 4, true);
+                                               return;
+                               }
+
+                               let ref = this._refs.get(v);
+                               if (ref === undefined) {
+                                       ref = this._values.length;
+                                       this._values.push(v);
+                                       this._refs.set(v, ref);
+                               }
+                               let typeFlag = 0;
+                               switch (typeof v) {
+                                       case "string":
+                                               typeFlag = 1;
+                                               break;
+                                       case "symbol":
+                                               typeFlag = 2;
+                                               break;
+                                       case "function":
+                                               typeFlag = 3;
+                                               break;
+                               }
+                               mem().setUint32(addr + 4, nanHead | typeFlag, true);
+                               mem().setUint32(addr, ref, true);
+                       }
+
+                       const loadSlice = (addr) => {
+                               const array = getInt64(addr + 0);
+                               const len = getInt64(addr + 8);
+                               return new Uint8Array(this._inst.exports.mem.buffer, array, len);
+                       }
+
+                       const loadSliceOfValues = (addr) => {
+                               const array = getInt64(addr + 0);
+                               const len = getInt64(addr + 8);
+                               const a = new Array(len);
+                               for (let i = 0; i < len; i++) {
+                                       a[i] = loadValue(array + i * 8);
+                               }
+                               return a;
+                       }
+
+                       const loadString = (addr) => {
+                               const saddr = getInt64(addr + 0);
+                               const len = getInt64(addr + 8);
+                               return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
+                       }
+
+                       const timeOrigin = Date.now() - performance.now();
+                       this.importObject = {
+                               go: {
+                                       // func wasmExit(code int32)
+                                       "runtime.wasmExit": (sp) => {
+                                               const code = mem().getInt32(sp + 8, true);
+                                               this.exited = true;
+                                               delete this._inst;
+                                               delete this._values;
+                                               delete this._refs;
+                                               this.exit(code);
+                                       },
+
+                                       // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
+                                       "runtime.wasmWrite": (sp) => {
+                                               const fd = getInt64(sp + 8);
+                                               const p = getInt64(sp + 16);
+                                               const n = mem().getInt32(sp + 24, true);
+                                               fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
+                                       },
+
+                                       // func nanotime() int64
+                                       "runtime.nanotime": (sp) => {
+                                               setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
+                                       },
+
+                                       // func walltime() (sec int64, nsec int32)
+                                       "runtime.walltime": (sp) => {
+                                               const msec = (new Date).getTime();
+                                               setInt64(sp + 8, msec / 1000);
+                                               mem().setInt32(sp + 16, (msec % 1000) * 1000000, true);
+                                       },
+
+                                       // func scheduleCallback(delay int64) int32
+                                       "runtime.scheduleCallback": (sp) => {
+                                               const id = this._nextCallbackTimeoutID;
+                                               this._nextCallbackTimeoutID++;
+                                               this._callbackTimeouts.set(id, setTimeout(
+                                                       () => { this._resolveCallbackPromise(); },
+                                                       getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
+                                               ));
+                                               mem().setInt32(sp + 16, id, true);
+                                       },
+
+                                       // func clearScheduledCallback(id int32)
+                                       "runtime.clearScheduledCallback": (sp) => {
+                                               const id = mem().getInt32(sp + 8, true);
+                                               clearTimeout(this._callbackTimeouts.get(id));
+                                               this._callbackTimeouts.delete(id);
+                                       },
+
+                                       // func getRandomData(r []byte)
+                                       "runtime.getRandomData": (sp) => {
+                                               crypto.getRandomValues(loadSlice(sp + 8));
+                                       },
+
+                                       // func stringVal(value string) ref
+                                       "syscall/js.stringVal": (sp) => {
+                                               storeValue(sp + 24, loadString(sp + 8));
+                                       },
+
+                                       // func valueGet(v ref, p string) ref
+                                       "syscall/js.valueGet": (sp) => {
+                                               storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16)));
+                                       },
+
+                                       // func valueSet(v ref, p string, x ref)
+                                       "syscall/js.valueSet": (sp) => {
+                                               Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
+                                       },
+
+                                       // func valueIndex(v ref, i int) ref
+                                       "syscall/js.valueIndex": (sp) => {
+                                               storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
+                                       },
+
+                                       // valueSetIndex(v ref, i int, x ref)
+                                       "syscall/js.valueSetIndex": (sp) => {
+                                               Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
+                                       },
+
+                                       // func valueCall(v ref, m string, args []ref) (ref, bool)
+                                       "syscall/js.valueCall": (sp) => {
+                                               try {
+                                                       const v = loadValue(sp + 8);
+                                                       const m = Reflect.get(v, loadString(sp + 16));
+                                                       const args = loadSliceOfValues(sp + 32);
+                                                       storeValue(sp + 56, Reflect.apply(m, v, args));
+                                                       mem().setUint8(sp + 64, 1);
+                                               } catch (err) {
+                                                       storeValue(sp + 56, err);
+                                                       mem().setUint8(sp + 64, 0);
+                                               }
+                                       },
+
+                                       // func valueInvoke(v ref, args []ref) (ref, bool)
+                                       "syscall/js.valueInvoke": (sp) => {
+                                               try {
+                                                       const v = loadValue(sp + 8);
+                                                       const args = loadSliceOfValues(sp + 16);
+                                                       storeValue(sp + 40, Reflect.apply(v, undefined, args));
+                                                       mem().setUint8(sp + 48, 1);
+                                               } catch (err) {
+                                                       storeValue(sp + 40, err);
+                                                       mem().setUint8(sp + 48, 0);
+                                               }
+                                       },
+
+                                       // func valueNew(v ref, args []ref) (ref, bool)
+                                       "syscall/js.valueNew": (sp) => {
+                                               try {
+                                                       const v = loadValue(sp + 8);
+                                                       const args = loadSliceOfValues(sp + 16);
+                                                       storeValue(sp + 40, Reflect.construct(v, args));
+                                                       mem().setUint8(sp + 48, 1);
+                                               } catch (err) {
+                                                       storeValue(sp + 40, err);
+                                                       mem().setUint8(sp + 48, 0);
+                                               }
+                                       },
+
+                                       // func valueLength(v ref) int
+                                       "syscall/js.valueLength": (sp) => {
+                                               setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
+                                       },
+
+                                       // valuePrepareString(v ref) (ref, int)
+                                       "syscall/js.valuePrepareString": (sp) => {
+                                               const str = encoder.encode(String(loadValue(sp + 8)));
+                                               storeValue(sp + 16, str);
+                                               setInt64(sp + 24, str.length);
+                                       },
+
+                                       // valueLoadString(v ref, b []byte)
+                                       "syscall/js.valueLoadString": (sp) => {
+                                               const str = loadValue(sp + 8);
+                                               loadSlice(sp + 16).set(str);
+                                       },
+
+                                       // func valueInstanceOf(v ref, t ref) bool
+                                       "syscall/js.valueInstanceOf": (sp) => {
+                                               mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
+                                       },
+
+                                       "debug": (value) => {
+                                               console.log(value);
+                                       },
+                               }
+                       };
+               }
+
+               async run(instance) {
+                       this._inst = instance;
+                       this._values = [ // TODO: garbage collection
+                               NaN,
+                               undefined,
+                               null,
+                               true,
+                               false,
+                               global,
+                               this._inst.exports.mem,
+                               this,
+                       ];
+                       this._refs = new Map();
+                       this._callbackShutdown = false;
+                       this.exited = false;
+
+                       const mem = new DataView(this._inst.exports.mem.buffer)
+
+                       // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
+                       let offset = 4096;
+
+                       const strPtr = (str) => {
+                               let ptr = offset;
+                               new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0"));
+                               offset += str.length + (8 - (str.length % 8));
+                               return ptr;
+                       };
+
+                       const argc = this.argv.length;
+
+                       const argvPtrs = [];
+                       this.argv.forEach((arg) => {
+                               argvPtrs.push(strPtr(arg));
+                       });
+
+                       const keys = Object.keys(this.env).sort();
+                       argvPtrs.push(keys.length);
+                       keys.forEach((key) => {
+                               argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
+                       });
+
+                       const argv = offset;
+                       argvPtrs.forEach((ptr) => {
+                               mem.setUint32(offset, ptr, true);
+                               mem.setUint32(offset + 4, 0, true);
+                               offset += 8;
+                       });
+
+                       while (true) {
+                               const callbackPromise = new Promise((resolve) => {
+                                       this._resolveCallbackPromise = () => {
+                                               if (this.exited) {
+                                                       throw new Error("bad callback: Go program has already exited");
+                                               }
+                                               setTimeout(resolve, 0); // make sure it is asynchronous
+                                       };
+                               });
+                               this._inst.exports.run(argc, argv);
+                               if (this.exited) {
+                                       break;
+                               }
+                               await callbackPromise;
+                       }
+               }
+
+               static _makeCallbackHelper(id, pendingCallbacks, go) {
+                       return function() {
+                               pendingCallbacks.push({ id: id, args: arguments });
+                               go._resolveCallbackPromise();
+                       };
+               }
+
+               static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
+                       return function(event) {
+                               if (preventDefault) {
+                                       event.preventDefault();
+                               }
+                               if (stopPropagation) {
+                                       event.stopPropagation();
+                               }
+                               if (stopImmediatePropagation) {
+                                       event.stopImmediatePropagation();
+                               }
+                               fn(event);
+                       };
+               }
+       }
+};