It is a project for Bytom Chrome extension JS SDK https://bytom.github.io/Bytom-JS-SDK
Revision | 2a7ce0006fefa9263634494c4214491ea81c24ff (tree) |
---|---|
Time | 2019-11-29 15:28:32 |
Author | Zhiting Lin <zlin035@uott...> |
Commiter | Zhiting Lin |
update transactions
@@ -41,7 +41,7 @@ | ||
41 | 41 | |
42 | 42 | <section> |
43 | 43 | <article> |
44 | - <pre class="prettyprint source linenums"><code>import {convertArgument, signTransaction1} from '../wasm/func'; | |
44 | + <pre class="prettyprint source linenums"><code>import {convertArgument, signTransaction} from '../wasm/func'; | |
45 | 45 | import { handleApiError, handleAxiosError } from '../utils/http'; |
46 | 46 | import { getDB } from '../db/db'; |
47 | 47 | import keysSDK from "./keys"; |
@@ -205,7 +205,7 @@ transactionSDK.prototype.signTransaction = function(guid, transaction, password) | ||
205 | 205 | } |
206 | 206 | bytom.sdk.keys.getKeyByXPub(e.target.result.rootXPub).then(res => { |
207 | 207 | let pm = {transaction: transaction, password: password, key: res}; |
208 | - signTransaction1(pm).then(res => { | |
208 | + signTransaction(pm).then(res => { | |
209 | 209 | resolve(JSON.parse(res.data)); |
210 | 210 | }).catch(err => { |
211 | 211 | reject(err); |
@@ -42,4 +42,30 @@ accountsApi.prototype.listAddresses = function(Guid) { | ||
42 | 42 | return this.http.request('account/list-addresses', {Guid}); |
43 | 43 | }; |
44 | 44 | |
45 | +/** | |
46 | + * Vapor API Copy Account. | |
47 | + * | |
48 | + * @param {Object} params - Parameters for address create. | |
49 | + * @param {String} params.guid - AccountID, | |
50 | + * @param {String} params.coin - Coin Type Api | |
51 | + * @returns {Promise} Array of Object, with Guid, Address, label. | |
52 | + */ | |
53 | +accountsApi.prototype.copyAccount = function(params) { | |
54 | + return this.http.request('account/copy', params); | |
55 | +}; | |
56 | + | |
57 | +/** | |
58 | + * Vapor API List Wallet. | |
59 | + * | |
60 | + * @param {Object} params - Parameters for Wallet List. | |
61 | + * @param {String} params.start - start, | |
62 | + * @param {String} params.limit - limit | |
63 | + * @returns {Promise} Array of Object, with Guid, m, n, status, signers. | |
64 | + */ | |
65 | +accountsApi.prototype.copyAccount = function(params) { | |
66 | + return this.http.request('account/list-wallets', params); | |
67 | +}; | |
68 | + | |
69 | + | |
70 | + | |
45 | 71 | export default accountsApi; |
\ No newline at end of file |
@@ -1,13 +1,13 @@ | ||
1 | 1 | import axios from 'axios'; |
2 | -import {handleApiError} from './utils/http'; | |
2 | +import {handleApiError, handleAxiosError} from './utils/http'; | |
3 | 3 | |
4 | -const basePath = 'api/v1/btm/'; | |
4 | +// const basePath = 'api/v1/btm'; | |
5 | 5 | |
6 | 6 | export function serverHttp(host) { |
7 | 7 | this.host = host; |
8 | 8 | this.request = function(path, body, net, method) { |
9 | 9 | var config = { |
10 | - url: this.host[net] ? `${this.host[net]}${basePath}${path}`: `${this.host}${basePath}${path}`, | |
10 | + url: this.host[net] ? `${this.host[net]}${path}`: `${this.host}${path}`, | |
11 | 11 | method: method ? method : 'POST' , |
12 | 12 | headers: { |
13 | 13 | Accept: 'application/json', |
@@ -17,7 +17,17 @@ export function serverHttp(host) { | ||
17 | 17 | }; |
18 | 18 | |
19 | 19 | //return Promise |
20 | - return axios.request(config); | |
20 | + return axios.request(config) | |
21 | + .then(function(resp){ | |
22 | + if (resp.status !== 200 || resp.data.code !== 200) { | |
23 | + throw handleApiError(resp); | |
24 | + } else if (resp.data.code === 200) { | |
25 | + return resp.data.result.data; | |
26 | + } | |
27 | + return resp.data; | |
28 | + }).catch(error=>{ | |
29 | + throw handleAxiosError(error); | |
30 | + }); | |
21 | 31 | }; |
22 | 32 | } |
23 | 33 |
@@ -25,7 +35,7 @@ export function http(baseUrl) { | ||
25 | 35 | this.baseUrl = baseUrl; |
26 | 36 | this.request = function(path, body, method) { |
27 | 37 | var config = { |
28 | - url: `${this.baseUrl}${basePath}${path}`, | |
38 | + url: `${this.baseUrl}${path}`, | |
29 | 39 | method: method ? method : 'POST' , |
30 | 40 | headers: { |
31 | 41 | Accept: 'application/json', |
@@ -22,6 +22,7 @@ accountsSDK.prototype.listAccountUseServer = function() { | ||
22 | 22 | let keyRange = IDBKeyRange.only(net); |
23 | 23 | let oc = objectStore.openCursor(keyRange); |
24 | 24 | let ret = []; |
25 | + | |
25 | 26 | oc.onsuccess = function (event) { |
26 | 27 | var cursor = event.target.result; |
27 | 28 | if (cursor) { |
@@ -51,14 +52,7 @@ accountsSDK.prototype.listAccountUseServer = function() { | ||
51 | 52 | */ |
52 | 53 | accountsSDK.prototype.listAddressUseServer = function(guid) { |
53 | 54 | let net = this.bytom.net; |
54 | - let retPromise = new Promise((resolve, reject) => { | |
55 | - this.http.request('account/list-addresses', {guid:guid}, net).then(resp => { | |
56 | - resolve(resp.data.result.data); | |
57 | - }).catch(error => { | |
58 | - reject(handleAxiosError(error)); | |
59 | - }); | |
60 | - }); | |
61 | - return retPromise; | |
55 | + return this.http.request('account/list-addresses', {guid:guid}, net); | |
62 | 56 | }; |
63 | 57 | /** |
64 | 58 | * Create a new address for a wallet. |
@@ -76,11 +70,7 @@ accountsSDK.prototype.createAccountReceiverUseServer = function(guid, label) { | ||
76 | 70 | pm.label = label; |
77 | 71 | } |
78 | 72 | this.http.request('account/new-address', pm, net).then(resp => { |
79 | - if (resp.status !== 200 || resp.data.code !== 200) { | |
80 | - reject(handleApiError(resp)); | |
81 | - return; | |
82 | - } | |
83 | - let dbData = resp.data.result.data; | |
73 | + let dbData = resp; | |
84 | 74 | dbData.guid = guid; |
85 | 75 | dbData.net = net; |
86 | 76 | getDB().then(db => { |
@@ -130,11 +120,7 @@ accountsSDK.prototype.createAccountUseServer = function(rootXPub, alias, label) | ||
130 | 120 | pm.label = label; |
131 | 121 | } |
132 | 122 | that.http.request('account/create', pm, net).then(resp => { |
133 | - if (resp.status !== 200 || resp.data.code !== 200) { | |
134 | - reject(handleApiError(resp)); | |
135 | - return; | |
136 | - } | |
137 | - let dbData = resp.data.result.data; | |
123 | + let dbData = resp; | |
138 | 124 | dbData.rootXPub = rootXPub; |
139 | 125 | dbData.alias = alias; |
140 | 126 | dbData.net = net; |
@@ -173,6 +159,103 @@ accountsSDK.prototype.createAccountUseServer = function(rootXPub, alias, label) | ||
173 | 159 | }; |
174 | 160 | |
175 | 161 | /** |
162 | + * Create a wallet using a public key. Each wallet is identified by a guid. (by server) | |
163 | + * | |
164 | + * @param {String} guid | |
165 | + * @param {String} coin type of coin | |
166 | + * @returns {Promise} | |
167 | + */ | |
168 | +accountsSDK.prototype.copyAccountUseServer = function(guid, coin) { | |
169 | + let net = this.bytom.net; | |
170 | + let that = this; | |
171 | + let retPromise = new Promise((resolve, reject) => { | |
172 | + let pm = {guid}; | |
173 | + if (coin) { | |
174 | + pm.coin = coin; | |
175 | + } | |
176 | + | |
177 | + that.http.request('account/copy', pm, net).then(resp => { | |
178 | + getDB().then(db => { | |
179 | + let objectStore = db.transaction(['accounts-server'], 'readwrite').objectStore('accounts-server'); | |
180 | + let index = objectStore.index('guid'); | |
181 | + let keyRange = IDBKeyRange.only(guid); | |
182 | + let getRequest = index.openCursor(keyRange); | |
183 | + | |
184 | + getRequest.onsuccess = function(e) { | |
185 | + const cursor = e.target.result; | |
186 | + if(cursor){ | |
187 | + const accountObject = cursor.value; | |
188 | + | |
189 | + accountObject.vpAddress = resp.address; | |
190 | + const request = cursor.update(accountObject); | |
191 | + request.onsuccess = function () { | |
192 | + resolve(accountObject); | |
193 | + }; | |
194 | + request.onerror = function () { | |
195 | + reject(request.error); | |
196 | + }; | |
197 | + } | |
198 | + }; | |
199 | + getRequest.onerror = function() { | |
200 | + reject(getRequest.error); | |
201 | + }; | |
202 | + }).catch(err => { | |
203 | + throw (err); | |
204 | + }); | |
205 | + }).catch(err => { | |
206 | + reject(err); | |
207 | + }); | |
208 | + }); | |
209 | + return retPromise; | |
210 | +}; | |
211 | + | |
212 | +/** | |
213 | + * list a wallet using a guid. Each wallet is identified by a guid. (by server) | |
214 | + * | |
215 | + * @param {String} guid | |
216 | + * @param {String} coin type of coin | |
217 | + * @returns {Promise} | |
218 | + */ | |
219 | +accountsSDK.prototype.listVaporAccountUseServer = function(guid) { | |
220 | + let net = this.bytom.net; | |
221 | + let that = this; | |
222 | + let retPromise = new Promise((resolve, reject) => { | |
223 | + that.http.request('account/list-addresses', {guid:guid}, net).then(resp => { | |
224 | + getDB().then(db => { | |
225 | + let objectStore = db.transaction(['accounts-server'], 'readwrite').objectStore('accounts-server'); | |
226 | + let index = objectStore.index('guid'); | |
227 | + let keyRange = IDBKeyRange.only(guid); | |
228 | + let getRequest = index.openCursor(keyRange); | |
229 | + | |
230 | + getRequest.onsuccess = function(e) { | |
231 | + const cursor = e.target.result; | |
232 | + if(cursor){ | |
233 | + const accountObject = cursor.value; | |
234 | + | |
235 | + accountObject.vpAddress = resp[0].address; | |
236 | + const request = cursor.update(accountObject); | |
237 | + request.onsuccess = function () { | |
238 | + resolve(accountObject); | |
239 | + }; | |
240 | + request.onerror = function () { | |
241 | + reject(request.error); | |
242 | + }; | |
243 | + } | |
244 | + }; | |
245 | + getRequest.onerror = function() { | |
246 | + reject(getRequest.error); | |
247 | + }; | |
248 | + }).catch(err => { | |
249 | + throw (err); | |
250 | + }); | |
251 | + }).catch(err => { | |
252 | + reject(err); | |
253 | + }); | |
254 | + }); | |
255 | + return retPromise; | |
256 | +}; | |
257 | + | |
258 | +/** | |
176 | 259 | * create account |
177 | 260 | * |
178 | 261 | * @param {String} alias |
@@ -1,4 +1,4 @@ | ||
1 | -import {createKey, resetKeyPassword, createPubkey} from '../wasm/func'; | |
1 | +import {createKey, resetKeyPassword, createPubkey, signMessage, signTransaction} from '../wasm/func'; | |
2 | 2 | import {getDB} from '../db/db'; |
3 | 3 | |
4 | 4 |
@@ -183,4 +183,62 @@ keysSDK.prototype.createPubkey = function(xpub) { | ||
183 | 183 | return retPromise; |
184 | 184 | }; |
185 | 185 | |
186 | +/** | |
187 | + * Sign Message. | |
188 | + * | |
189 | + * @param {String} message - message. | |
190 | + * @param {String} password - password. | |
191 | + * @param {Object} address - address. | |
192 | + */ | |
193 | +keysSDK.prototype.signMessage = function(message, password, address) { | |
194 | + let retPromise = new Promise((resolve, reject) => { | |
195 | + getDB().then(db => { | |
196 | + let getRequest = db.transaction(['accounts-server'], 'readonly') | |
197 | + .objectStore('accounts-server') | |
198 | + .getAll() | |
199 | + | |
200 | + getRequest.onsuccess = function (e) { | |
201 | + const result = getRequest.result.filter(obj => obj.address === address) | |
202 | + if (result.length === 0) { | |
203 | + reject(new Error('not found address')); | |
204 | + return; | |
205 | + } | |
206 | + | |
207 | + const rootXpub = result[0].rootXPub | |
208 | + let keyObject = db.transaction(['keys'], 'readonly') | |
209 | + .objectStore('keys') | |
210 | + .index('xpub') | |
211 | + .get(rootXpub); | |
212 | + | |
213 | + keyObject.onsuccess = function (e) { | |
214 | + if (!e.target.result) { | |
215 | + reject(new Error('not found xpub')); | |
216 | + return; | |
217 | + } | |
218 | + | |
219 | + let data = {}; | |
220 | + data.message = message; | |
221 | + data.password = password; | |
222 | + data.key = e.target.result.key; | |
223 | + signMessage(data).then((res) => { | |
224 | + let jsonData = JSON.parse(res.data); | |
225 | + resolve(jsonData); | |
226 | + }).catch(error => { | |
227 | + reject(error); | |
228 | + }); | |
229 | + }; | |
230 | + keyObject.onerror = function () { | |
231 | + reject(getRequest.error); | |
232 | + }; | |
233 | + }; | |
234 | + getRequest.onerror = function () { | |
235 | + reject(getRequest.error); | |
236 | + }; | |
237 | + }).catch(error => { | |
238 | + reject(error); | |
239 | + }); | |
240 | + }); | |
241 | + return retPromise; | |
242 | +}; | |
243 | + | |
186 | 244 | export default keysSDK; |
\ No newline at end of file |
@@ -1,5 +1,3 @@ | ||
1 | -import { handleAxiosError } from '../utils/http'; | |
2 | - | |
3 | 1 | function querySDK(bytom) { |
4 | 2 | this.bytom =bytom; |
5 | 3 | this.http = bytom.serverHttp; |
@@ -11,14 +9,7 @@ function querySDK(bytom) { | ||
11 | 9 | */ |
12 | 10 | querySDK.prototype.asset = function(asset_id) { |
13 | 11 | let net = this.bytom.net; |
14 | - let retPromise = new Promise((resolve, reject) => { | |
15 | - this.http.request('q/asset?id=' + asset_id, null, net, 'GET').then(resp => { | |
16 | - resolve(resp.data); | |
17 | - }).catch(err => { | |
18 | - reject(handleAxiosError(err)); | |
19 | - }); | |
20 | - }); | |
21 | - return retPromise; | |
12 | + return this.http.request('q/asset?id=' + asset_id, null, net, 'GET'); | |
22 | 13 | }; |
23 | 14 | |
24 | 15 | /** |
@@ -26,14 +17,7 @@ querySDK.prototype.asset = function(asset_id) { | ||
26 | 17 | */ |
27 | 18 | querySDK.prototype.getblockcount = function() { |
28 | 19 | let net = this.bytom.net; |
29 | - let retPromise = new Promise((resolve, reject) => { | |
30 | - this.http.request('q/chain-status', null, net, 'GET').then(resp => { | |
31 | - resolve(resp.data); | |
32 | - }).catch(err => { | |
33 | - reject(handleAxiosError(err)); | |
34 | - }); | |
35 | - }); | |
36 | - return retPromise; | |
20 | + return this.http.request('q/chain-status', null, net, 'GET'); | |
37 | 21 | }; |
38 | 22 | |
39 | 23 | /** |
@@ -41,14 +25,16 @@ querySDK.prototype.getblockcount = function() { | ||
41 | 25 | */ |
42 | 26 | querySDK.prototype.listUtxo = function(object) { |
43 | 27 | let net = this.bytom.net; |
44 | - let retPromise = new Promise((resolve, reject) => { | |
45 | - this.http.request('q/list-utxos',object, net, 'POST').then(resp => { | |
46 | - resolve(resp.data); | |
47 | - }).catch(err => { | |
48 | - reject(handleAxiosError(err)); | |
49 | - }); | |
50 | - }); | |
51 | - return retPromise; | |
28 | + return this.http.request('q/list-utxos',object, net, 'POST'); | |
29 | +}; | |
30 | + | |
31 | +/** | |
32 | + * Query the vote status. | |
33 | + * vapor only | |
34 | + */ | |
35 | +querySDK.prototype.getVoteStatus = function() { | |
36 | + let net = this.bytom.net; | |
37 | + return this.http.request('q/get-vote-status',null, net, 'GET'); | |
52 | 38 | }; |
53 | 39 | |
54 | 40 | export default querySDK; |
\ No newline at end of file |
@@ -1,7 +1,6 @@ | ||
1 | -import {convertArgument, signTransaction1} from '../wasm/func'; | |
1 | +import {convertArgument, signTransaction} from '../wasm/func'; | |
2 | 2 | import { handleApiError, handleAxiosError } from '../utils/http'; |
3 | 3 | import { getDB } from '../db/db'; |
4 | -import keysSDK from "./keys"; | |
5 | 4 | |
6 | 5 | function transactionSDK(bytom) { |
7 | 6 | this.http = bytom.serverHttp; |
@@ -14,34 +13,34 @@ function transactionSDK(bytom) { | ||
14 | 13 | * |
15 | 14 | * @see https://gist.github.com/HAOYUatHZ/0c7446b8f33e7cddd590256b3824b08f#apiv1btmmerchantlist-transactions |
16 | 15 | * @param {String} guid unique id for each wallet |
17 | - * @param {String} address (optional) if provided, will only return transactions the address is related to | |
16 | + * @param {String} filter (optional) if provided, will only return transactions the filter condition is related to | |
17 | + * @param {String} sort (optional) if provided, will only return transactions the sort condition is related to | |
18 | 18 | * @param {Number} start page start |
19 | 19 | * @param {Number} limit page limit |
20 | 20 | * @returns {Promise} |
21 | 21 | */ |
22 | -transactionSDK.prototype.list = function(guid, address, start, limit) { | |
22 | +transactionSDK.prototype.list = function(guid, filter,sort, start, limit) { | |
23 | 23 | let net = this.bytom.net; |
24 | - let retPromise = new Promise((resolve, reject) => { | |
25 | - let pm = {guid: guid}; | |
26 | - if (address) { | |
27 | - pm.address = address; | |
28 | - } | |
29 | - let url = 'merchant/list-transactions'; | |
30 | - let args = new URLSearchParams(); | |
31 | - if (typeof start !== 'undefined') { | |
32 | - args.append('start', start); | |
33 | - } | |
34 | - if (limit) { | |
35 | - args.append('limit', limit); | |
36 | - } | |
37 | - url = url + '?' + args.toString(); | |
38 | - this.http.request(url, pm, net).then(resp => { | |
39 | - resolve(resp.data); | |
40 | - }).catch(err => { | |
41 | - reject(handleAxiosError(err)); | |
42 | - }); | |
43 | - }); | |
44 | - return retPromise; | |
24 | + let pm = {guid: guid}; | |
25 | + | |
26 | + if (filter) { | |
27 | + pm.filter = filter; | |
28 | + } | |
29 | + | |
30 | + if (sort) { | |
31 | + pm.sort = sort; | |
32 | + } | |
33 | + | |
34 | + let url = 'merchant/list-transactions'; | |
35 | + let args = new URLSearchParams(); | |
36 | + if (typeof start !== 'undefined') { | |
37 | + args.append('start', start); | |
38 | + } | |
39 | + if (limit) { | |
40 | + args.append('limit', limit); | |
41 | + } | |
42 | + url = url + '?' + args.toString(); | |
43 | + return this.http.request(url, pm, net); | |
45 | 44 | }; |
46 | 45 | |
47 | 46 | /** |
@@ -54,19 +53,8 @@ transactionSDK.prototype.list = function(guid, address, start, limit) { | ||
54 | 53 | */ |
55 | 54 | transactionSDK.prototype.submitPayment = function(guid, raw_transaction, signatures) { |
56 | 55 | let net = this.bytom.net; |
57 | - let retPromise = new Promise((resolve, reject) => { | |
58 | - let pm = {guid: guid, raw_transaction: raw_transaction, signatures: signatures}; | |
59 | - this.http.request('merchant/submit-payment', pm, net).then(resp => { | |
60 | - if (resp.status !== 200 || resp.data.code !== 200) { | |
61 | - reject(handleApiError(resp)); | |
62 | - return; | |
63 | - } | |
64 | - resolve(resp.data); | |
65 | - }).catch(err => { | |
66 | - reject(handleAxiosError(err)); | |
67 | - }); | |
68 | - }); | |
69 | - return retPromise; | |
56 | + let pm = {guid: guid, raw_transaction: raw_transaction, signatures: signatures}; | |
57 | + return this.http.request('merchant/submit-payment', pm, net); | |
70 | 58 | }; |
71 | 59 | |
72 | 60 | /** |
@@ -82,27 +70,111 @@ transactionSDK.prototype.submitPayment = function(guid, raw_transaction, signatu | ||
82 | 70 | * @param {Number} confirmations - transaction confirmations |
83 | 71 | * @returns {Promise} |
84 | 72 | */ |
85 | -transactionSDK.prototype.buildPayment = function(guid, to, asset, amount, fee, confirmations) { | |
73 | +transactionSDK.prototype.buildPayment = function(guid, to, asset, amount, fee, confirmations, memo, forbidChainTx) { | |
86 | 74 | let net = this.bytom.net; |
87 | - let retPromise = new Promise((resolve, reject) => { | |
88 | - let pm = {guid: guid, to: to, asset: asset, amount: amount}; | |
89 | - if (fee) { | |
90 | - pm.fee = fee; | |
91 | - } | |
92 | - if (confirmations) { | |
93 | - pm.confirmations = confirmations; | |
94 | - } | |
95 | - this.http.request('merchant/build-payment', pm, net).then(resp => { | |
96 | - if (resp.status !== 200 || resp.data.code !== 200) { | |
97 | - reject(handleApiError(resp)); | |
98 | - return; | |
99 | - } | |
100 | - resolve(resp.data); | |
101 | - }).catch(err => { | |
102 | - reject(handleAxiosError(err)); | |
103 | - }); | |
104 | - }); | |
105 | - return retPromise; | |
75 | + let pm = { | |
76 | + guid: guid, | |
77 | + asset: asset, | |
78 | + recipients: {}, | |
79 | + forbid_chain_tx: false | |
80 | + }; | |
81 | + | |
82 | + pm['recipients'][`${to}`] = amount | |
83 | + | |
84 | + if (memo) { | |
85 | + pm.memo = memo; | |
86 | + } | |
87 | + if (forbidChainTx) { | |
88 | + pm.forbid_chain_tx = forbidChainTx; | |
89 | + } | |
90 | + if (fee) { | |
91 | + pm.fee = fee; | |
92 | + } | |
93 | + if (confirmations) { | |
94 | + pm.confirmations = confirmations; | |
95 | + } | |
96 | + return this.http.request('merchant/build-payment', pm, net); | |
97 | +}; | |
98 | + | |
99 | +/** | |
100 | + * Build a cross chain transaction. | |
101 | + * | |
102 | + * @param {String} guid unique id for each wallet | |
103 | + * @param {String} to destination address | |
104 | + * @param {String} asset hexdecimal asset id | |
105 | + * @param {Number} amount transfer amount | |
106 | + * @param {Number} confirmations - transaction confirmations | |
107 | + * @returns {Promise} | |
108 | + */ | |
109 | +transactionSDK.prototype.buildCrossChain = function(guid, to, asset, amount, confirmations, forbidChainTx) { | |
110 | + let net = this.bytom.net; | |
111 | + | |
112 | + let pm = { | |
113 | + guid: guid, | |
114 | + asset: asset, | |
115 | + recipients: {}, | |
116 | + forbid_chain_tx: false | |
117 | + }; | |
118 | + | |
119 | + pm['recipients'][`${to}`] = amount | |
120 | + | |
121 | + if (forbidChainTx) { | |
122 | + pm.forbid_chain_tx = forbidChainTx; | |
123 | + } | |
124 | + | |
125 | + return this.http.request('merchant/build-crosschain', pm, net); | |
126 | +}; | |
127 | + | |
128 | +/** | |
129 | + * Build a Vote transaction. | |
130 | + * | |
131 | + * @param {String} guid unique id for each wallet | |
132 | + * @param {String} vote pubkey vote | |
133 | + * @param {String} memo memo key | |
134 | + * @param {Number} amount transfer amount | |
135 | + * @param {Number} confirmations - transaction confirmations | |
136 | + * @returns {Promise} | |
137 | + */ | |
138 | +transactionSDK.prototype.buildVote = function(guid, vote, amount, confirmations, memo, forbidChainTx) { | |
139 | + let net = this.bytom.net; | |
140 | + let pm = {guid, vote, amount: amount, forbid_chain_tx: false}; | |
141 | + if (confirmations) { | |
142 | + pm.confirmations = confirmations; | |
143 | + } | |
144 | + if (memo) { | |
145 | + pm.memo = memo; | |
146 | + } | |
147 | + if (forbidChainTx) { | |
148 | + pm.forbid_chain_tx = forbidChainTx; | |
149 | + } | |
150 | + | |
151 | + return this.http.request('merchant/build-vote', pm, net); | |
152 | +}; | |
153 | + | |
154 | +/** | |
155 | + * Build a Veto transaction. | |
156 | + * | |
157 | + * @param {String} guid unique id for each wallet | |
158 | + * @param {String} vote pubkey vote | |
159 | + * @param {String} memo memo key | |
160 | + * @param {Number} amount transfer amount | |
161 | + * @param {Number} confirmations - transaction confirmations | |
162 | + * @returns {Promise} | |
163 | + */ | |
164 | +transactionSDK.prototype.buildVeto = function(guid, vote, amount, confirmations, memo, forbidChainTx) { | |
165 | + let net = this.bytom.net; | |
166 | + let pm = {guid, vote, amount: amount, forbid_chain_tx: false}; | |
167 | + if (confirmations) { | |
168 | + pm.confirmations = confirmations; | |
169 | + } | |
170 | + if (memo) { | |
171 | + pm.memo = memo; | |
172 | + } | |
173 | + if (forbidChainTx) { | |
174 | + pm.forbid_chain_tx = forbidChainTx; | |
175 | + } | |
176 | + | |
177 | + return this.http.request('merchant/build-veto', pm, net); | |
106 | 178 | }; |
107 | 179 | |
108 | 180 | /** |
@@ -115,29 +187,18 @@ transactionSDK.prototype.buildPayment = function(guid, to, asset, amount, fee, c | ||
115 | 187 | */ |
116 | 188 | transactionSDK.prototype.buildTransaction = function(guid, inputs, outputs, fee, confirmations ) { |
117 | 189 | let net = this.bytom.net; |
118 | - let retPromise = new Promise((resolve, reject) => { | |
119 | - let pm = { | |
120 | - guid, | |
121 | - inputs, | |
122 | - outputs, | |
123 | - }; | |
124 | - if (fee) { | |
125 | - pm.fee = fee; | |
126 | - } | |
127 | - if (confirmations) { | |
128 | - pm.confirmations = confirmations; | |
129 | - } | |
130 | - this.http.request('merchant/build-transaction', pm, net).then(resp => { | |
131 | - if (resp.status !== 200 || resp.data.code !== 200) { | |
132 | - reject(handleApiError(resp)); | |
133 | - return; | |
134 | - } | |
135 | - resolve(resp.data); | |
136 | - }).catch(err => { | |
137 | - reject(handleAxiosError(err)); | |
138 | - }); | |
139 | - }); | |
140 | - return retPromise; | |
190 | + let pm = { | |
191 | + guid, | |
192 | + inputs, | |
193 | + outputs, | |
194 | + }; | |
195 | + if (fee) { | |
196 | + pm.fee = fee; | |
197 | + } | |
198 | + if (confirmations) { | |
199 | + pm.confirmations = confirmations; | |
200 | + } | |
201 | + return this.http.request('merchant/build-transaction', pm, net); | |
141 | 202 | }; |
142 | 203 | |
143 | 204 | /** |
@@ -162,7 +223,7 @@ transactionSDK.prototype.signTransaction = function(guid, transaction, password) | ||
162 | 223 | } |
163 | 224 | bytom.sdk.keys.getKeyByXPub(e.target.result.rootXPub).then(res => { |
164 | 225 | let pm = {transaction: transaction, password: password, key: res}; |
165 | - signTransaction1(pm).then(res => { | |
226 | + signTransaction(pm).then(res => { | |
166 | 227 | resolve(JSON.parse(res.data)); |
167 | 228 | }).catch(err => { |
168 | 229 | reject(err); |
@@ -47,24 +47,13 @@ export async function resetKeyPassword(data) { | ||
47 | 47 | return res; |
48 | 48 | } |
49 | 49 | |
50 | -//signTransaction bytom node standard | |
51 | -export async function signTransaction(data) { | |
52 | - await init(); | |
53 | - let res = newWasmResult(); | |
54 | - window.AllFunc.signTransaction(data, res); | |
55 | - await res.wait; | |
56 | - if (res.hasOwnProperty('error')) { | |
57 | - throw new Error(res.error); | |
58 | - } | |
59 | - return res; | |
60 | -} | |
61 | -//signTransaction1 bytom central server standard | |
50 | +//signTransaction bytom central server standard | |
62 | 51 | //see https://gist.github.com/HAOYUatHZ/0c7446b8f33e7cddd590256b3824b08f#apiv1btmmerchantsubmit-payment |
63 | 52 | //see https://github.com/Bytom-Community/Bytom-WebAssembly/blob/master/sdk/transaction.go (wasm implement) |
64 | -export async function signTransaction1(data) { | |
53 | +export async function signTransaction(data) { | |
65 | 54 | await init(); |
66 | 55 | let res = newWasmResult(); |
67 | - window.AllFunc.signTransaction1(data, res); | |
56 | + window.AllFunc.signTransaction(data, res); | |
68 | 57 | await res.wait; |
69 | 58 | if (res.hasOwnProperty('error')) { |
70 | 59 | throw new Error(res.error); |
@@ -116,6 +105,17 @@ export async function createPubkey(data) { | ||
116 | 105 | return res; |
117 | 106 | } |
118 | 107 | |
108 | +export async function signMessage(data) { | |
109 | + await init(); | |
110 | + let res = newWasmResult(); | |
111 | + window.AllFunc.signMessage(data, res); | |
112 | + await res.wait; | |
113 | + if (res.hasOwnProperty('error')) { | |
114 | + throw new Error(res.error); | |
115 | + } | |
116 | + return res; | |
117 | +} | |
118 | + | |
119 | 119 | export async function convertArgument(data) { |
120 | 120 | await init(); |
121 | 121 | let res = newWasmResult(); |
@@ -1,393 +1,417 @@ | ||
1 | -/*eslint-disable*/ | |
2 | 1 | // Copyright 2018 The Go Authors. All rights reserved. |
3 | 2 | // Use of this source code is governed by a BSD-style |
4 | 3 | // license that can be found in the LICENSE file. |
5 | -//see https://github.com/golang/go/wiki/WebAssembly | |
4 | + | |
6 | 5 | export function LoadWasm() { |
7 | - // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API). | |
8 | - if (typeof window !== "undefined") { | |
9 | - window.global = window; | |
10 | - } else if (typeof self !== "undefined") { | |
11 | - self.global = self; | |
12 | - } else { | |
13 | - throw new Error("cannot export Go (neither window nor self is defined)"); | |
14 | - } | |
15 | - | |
16 | - let outputBuf = ""; | |
17 | - global.fs = { | |
18 | - constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused | |
19 | - writeSync(fd, buf) { | |
20 | - outputBuf += decoder.decode(buf); | |
21 | - const nl = outputBuf.lastIndexOf("\n"); | |
22 | - if (nl != -1) { | |
23 | - console.log(outputBuf.substr(0, nl)); | |
24 | - outputBuf = outputBuf.substr(nl + 1); | |
25 | - } | |
26 | - return buf.length; | |
27 | - }, | |
28 | - openSync(path, flags, mode) { | |
29 | - const err = new Error("not implemented"); | |
30 | - err.code = "ENOSYS"; | |
31 | - throw err; | |
32 | - }, | |
33 | - }; | |
34 | - | |
35 | - const encoder = new TextEncoder("utf-8"); | |
36 | - const decoder = new TextDecoder("utf-8"); | |
37 | - | |
38 | - global.Go = class { | |
39 | - constructor() { | |
40 | - this.argv = ["js"]; | |
41 | - this.env = {}; | |
42 | - this.exit = (code) => { | |
43 | - if (code !== 0) { | |
44 | - console.warn("exit code:", code); | |
45 | - } | |
46 | - if(typeof window.resetWasmStatus == "function") { | |
47 | - window.resetWasmStatus(); | |
48 | - } | |
49 | - }; | |
50 | - this._callbackTimeouts = new Map(); | |
51 | - this._nextCallbackTimeoutID = 1; | |
52 | - | |
53 | - const mem = () => { | |
54 | - // The buffer may change when requesting more memory. | |
55 | - return new DataView(this._inst.exports.mem.buffer); | |
56 | - } | |
57 | - | |
58 | - const setInt64 = (addr, v) => { | |
59 | - mem().setUint32(addr + 0, v, true); | |
60 | - mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); | |
61 | - } | |
62 | - | |
63 | - const getInt64 = (addr) => { | |
64 | - const low = mem().getUint32(addr + 0, true); | |
65 | - const high = mem().getInt32(addr + 4, true); | |
66 | - return low + high * 4294967296; | |
67 | - } | |
68 | - | |
69 | - const loadValue = (addr) => { | |
70 | - const f = mem().getFloat64(addr, true); | |
71 | - if (!isNaN(f)) { | |
72 | - return f; | |
73 | - } | |
74 | - | |
75 | - const id = mem().getUint32(addr, true); | |
76 | - return this._values[id]; | |
77 | - } | |
78 | - | |
79 | - const storeValue = (addr, v) => { | |
80 | - const nanHead = 0x7FF80000; | |
81 | - | |
82 | - if (typeof v === "number") { | |
83 | - if (isNaN(v)) { | |
84 | - mem().setUint32(addr + 4, nanHead, true); | |
85 | - mem().setUint32(addr, 0, true); | |
86 | - return; | |
87 | - } | |
88 | - mem().setFloat64(addr, v, true); | |
89 | - return; | |
90 | - } | |
91 | - | |
92 | - switch (v) { | |
93 | - case undefined: | |
94 | - mem().setUint32(addr + 4, nanHead, true); | |
95 | - mem().setUint32(addr, 1, true); | |
96 | - return; | |
97 | - case null: | |
98 | - mem().setUint32(addr + 4, nanHead, true); | |
99 | - mem().setUint32(addr, 2, true); | |
100 | - return; | |
101 | - case true: | |
102 | - mem().setUint32(addr + 4, nanHead, true); | |
103 | - mem().setUint32(addr, 3, true); | |
104 | - return; | |
105 | - case false: | |
106 | - mem().setUint32(addr + 4, nanHead, true); | |
107 | - mem().setUint32(addr, 4, true); | |
108 | - return; | |
109 | - } | |
110 | - | |
111 | - let ref = this._refs.get(v); | |
112 | - if (ref === undefined) { | |
113 | - ref = this._values.length; | |
114 | - this._values.push(v); | |
115 | - this._refs.set(v, ref); | |
116 | - } | |
117 | - let typeFlag = 0; | |
118 | - switch (typeof v) { | |
119 | - case "string": | |
120 | - typeFlag = 1; | |
121 | - break; | |
122 | - case "symbol": | |
123 | - typeFlag = 2; | |
124 | - break; | |
125 | - case "function": | |
126 | - typeFlag = 3; | |
127 | - break; | |
128 | - } | |
129 | - mem().setUint32(addr + 4, nanHead | typeFlag, true); | |
130 | - mem().setUint32(addr, ref, true); | |
131 | - } | |
132 | - | |
133 | - const loadSlice = (addr) => { | |
134 | - const array = getInt64(addr + 0); | |
135 | - const len = getInt64(addr + 8); | |
136 | - return new Uint8Array(this._inst.exports.mem.buffer, array, len); | |
137 | - } | |
138 | - | |
139 | - const loadSliceOfValues = (addr) => { | |
140 | - const array = getInt64(addr + 0); | |
141 | - const len = getInt64(addr + 8); | |
142 | - const a = new Array(len); | |
143 | - for (let i = 0; i < len; i++) { | |
144 | - a[i] = loadValue(array + i * 8); | |
145 | - } | |
146 | - return a; | |
147 | - } | |
148 | - | |
149 | - const loadString = (addr) => { | |
150 | - const saddr = getInt64(addr + 0); | |
151 | - const len = getInt64(addr + 8); | |
152 | - return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); | |
153 | - } | |
154 | - | |
155 | - const timeOrigin = Date.now() - performance.now(); | |
156 | - this.importObject = { | |
157 | - go: { | |
158 | - // func wasmExit(code int32) | |
159 | - "runtime.wasmExit": (sp) => { | |
160 | - const code = mem().getInt32(sp + 8, true); | |
161 | - this.exited = true; | |
162 | - delete this._inst; | |
163 | - delete this._values; | |
164 | - delete this._refs; | |
165 | - this.exit(code); | |
166 | - }, | |
167 | - | |
168 | - // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) | |
169 | - "runtime.wasmWrite": (sp) => { | |
170 | - const fd = getInt64(sp + 8); | |
171 | - const p = getInt64(sp + 16); | |
172 | - const n = mem().getInt32(sp + 24, true); | |
173 | - fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); | |
174 | - }, | |
175 | - | |
176 | - // func nanotime() int64 | |
177 | - "runtime.nanotime": (sp) => { | |
178 | - setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); | |
179 | - }, | |
180 | - | |
181 | - // func walltime() (sec int64, nsec int32) | |
182 | - "runtime.walltime": (sp) => { | |
183 | - const msec = (new Date).getTime(); | |
184 | - setInt64(sp + 8, msec / 1000); | |
185 | - mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); | |
186 | - }, | |
187 | - | |
188 | - // func scheduleCallback(delay int64) int32 | |
189 | - "runtime.scheduleCallback": (sp) => { | |
190 | - const id = this._nextCallbackTimeoutID; | |
191 | - this._nextCallbackTimeoutID++; | |
192 | - this._callbackTimeouts.set(id, setTimeout( | |
193 | - () => { this._resolveCallbackPromise(); }, | |
194 | - getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early | |
195 | - )); | |
196 | - mem().setInt32(sp + 16, id, true); | |
197 | - }, | |
198 | - | |
199 | - // func clearScheduledCallback(id int32) | |
200 | - "runtime.clearScheduledCallback": (sp) => { | |
201 | - const id = mem().getInt32(sp + 8, true); | |
202 | - clearTimeout(this._callbackTimeouts.get(id)); | |
203 | - this._callbackTimeouts.delete(id); | |
204 | - }, | |
205 | - | |
206 | - // func getRandomData(r []byte) | |
207 | - "runtime.getRandomData": (sp) => { | |
208 | - crypto.getRandomValues(loadSlice(sp + 8)); | |
209 | - }, | |
210 | - | |
211 | - // func stringVal(value string) ref | |
212 | - "syscall/js.stringVal": (sp) => { | |
213 | - storeValue(sp + 24, loadString(sp + 8)); | |
214 | - }, | |
215 | - | |
216 | - // func valueGet(v ref, p string) ref | |
217 | - "syscall/js.valueGet": (sp) => { | |
218 | - storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16))); | |
219 | - }, | |
220 | - | |
221 | - // func valueSet(v ref, p string, x ref) | |
222 | - "syscall/js.valueSet": (sp) => { | |
223 | - Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); | |
224 | - }, | |
225 | - | |
226 | - // func valueIndex(v ref, i int) ref | |
227 | - "syscall/js.valueIndex": (sp) => { | |
228 | - storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); | |
229 | - }, | |
230 | - | |
231 | - // valueSetIndex(v ref, i int, x ref) | |
232 | - "syscall/js.valueSetIndex": (sp) => { | |
233 | - Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); | |
234 | - }, | |
235 | - | |
236 | - // func valueCall(v ref, m string, args []ref) (ref, bool) | |
237 | - "syscall/js.valueCall": (sp) => { | |
238 | - try { | |
239 | - const v = loadValue(sp + 8); | |
240 | - const m = Reflect.get(v, loadString(sp + 16)); | |
241 | - const args = loadSliceOfValues(sp + 32); | |
242 | - storeValue(sp + 56, Reflect.apply(m, v, args)); | |
243 | - mem().setUint8(sp + 64, 1); | |
244 | - } catch (err) { | |
245 | - storeValue(sp + 56, err); | |
246 | - mem().setUint8(sp + 64, 0); | |
247 | - } | |
248 | - }, | |
249 | - | |
250 | - // func valueInvoke(v ref, args []ref) (ref, bool) | |
251 | - "syscall/js.valueInvoke": (sp) => { | |
252 | - try { | |
253 | - const v = loadValue(sp + 8); | |
254 | - const args = loadSliceOfValues(sp + 16); | |
255 | - storeValue(sp + 40, Reflect.apply(v, undefined, args)); | |
256 | - mem().setUint8(sp + 48, 1); | |
257 | - } catch (err) { | |
258 | - storeValue(sp + 40, err); | |
259 | - mem().setUint8(sp + 48, 0); | |
260 | - } | |
261 | - }, | |
262 | - | |
263 | - // func valueNew(v ref, args []ref) (ref, bool) | |
264 | - "syscall/js.valueNew": (sp) => { | |
265 | - try { | |
266 | - const v = loadValue(sp + 8); | |
267 | - const args = loadSliceOfValues(sp + 16); | |
268 | - storeValue(sp + 40, Reflect.construct(v, args)); | |
269 | - mem().setUint8(sp + 48, 1); | |
270 | - } catch (err) { | |
271 | - storeValue(sp + 40, err); | |
272 | - mem().setUint8(sp + 48, 0); | |
273 | - } | |
274 | - }, | |
275 | - | |
276 | - // func valueLength(v ref) int | |
277 | - "syscall/js.valueLength": (sp) => { | |
278 | - setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); | |
279 | - }, | |
280 | - | |
281 | - // valuePrepareString(v ref) (ref, int) | |
282 | - "syscall/js.valuePrepareString": (sp) => { | |
283 | - const str = encoder.encode(String(loadValue(sp + 8))); | |
284 | - storeValue(sp + 16, str); | |
285 | - setInt64(sp + 24, str.length); | |
286 | - }, | |
287 | - | |
288 | - // valueLoadString(v ref, b []byte) | |
289 | - "syscall/js.valueLoadString": (sp) => { | |
290 | - const str = loadValue(sp + 8); | |
291 | - loadSlice(sp + 16).set(str); | |
292 | - }, | |
293 | - | |
294 | - // func valueInstanceOf(v ref, t ref) bool | |
295 | - "syscall/js.valueInstanceOf": (sp) => { | |
296 | - mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); | |
297 | - }, | |
298 | - | |
299 | - "debug": (value) => { | |
300 | - console.log(value); | |
301 | - }, | |
302 | - } | |
303 | - }; | |
304 | - } | |
305 | - | |
306 | - async run(instance) { | |
307 | - this._inst = instance; | |
308 | - this._values = [ // TODO: garbage collection | |
309 | - NaN, | |
310 | - undefined, | |
311 | - null, | |
312 | - true, | |
313 | - false, | |
314 | - global, | |
315 | - this._inst.exports.mem, | |
316 | - this, | |
317 | - ]; | |
318 | - this._refs = new Map(); | |
319 | - this._callbackShutdown = false; | |
320 | - this.exited = false; | |
321 | - | |
322 | - const mem = new DataView(this._inst.exports.mem.buffer) | |
323 | - | |
324 | - // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. | |
325 | - let offset = 4096; | |
326 | - | |
327 | - const strPtr = (str) => { | |
328 | - let ptr = offset; | |
329 | - new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0")); | |
330 | - offset += str.length + (8 - (str.length % 8)); | |
331 | - return ptr; | |
332 | - }; | |
333 | - | |
334 | - const argc = this.argv.length; | |
335 | - | |
336 | - const argvPtrs = []; | |
337 | - this.argv.forEach((arg) => { | |
338 | - argvPtrs.push(strPtr(arg)); | |
339 | - }); | |
340 | - | |
341 | - const keys = Object.keys(this.env).sort(); | |
342 | - argvPtrs.push(keys.length); | |
343 | - keys.forEach((key) => { | |
344 | - argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); | |
345 | - }); | |
346 | - | |
347 | - const argv = offset; | |
348 | - argvPtrs.forEach((ptr) => { | |
349 | - mem.setUint32(offset, ptr, true); | |
350 | - mem.setUint32(offset + 4, 0, true); | |
351 | - offset += 8; | |
352 | - }); | |
353 | - | |
354 | - while (true) { | |
355 | - const callbackPromise = new Promise((resolve) => { | |
356 | - this._resolveCallbackPromise = () => { | |
357 | - if (this.exited) { | |
358 | - throw new Error("bad callback: Go program has already exited"); | |
359 | - } | |
360 | - setTimeout(resolve, 0); // make sure it is asynchronous | |
361 | - }; | |
362 | - }); | |
363 | - this._inst.exports.run(argc, argv); | |
364 | - if (this.exited) { | |
365 | - break; | |
366 | - } | |
367 | - await callbackPromise; | |
368 | - } | |
369 | - } | |
370 | - | |
371 | - static _makeCallbackHelper(id, pendingCallbacks, go) { | |
372 | - return function() { | |
373 | - pendingCallbacks.push({ id: id, args: arguments }); | |
374 | - go._resolveCallbackPromise(); | |
375 | - }; | |
376 | - } | |
377 | - | |
378 | - static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) { | |
379 | - return function(event) { | |
380 | - if (preventDefault) { | |
381 | - event.preventDefault(); | |
382 | - } | |
383 | - if (stopPropagation) { | |
384 | - event.stopPropagation(); | |
385 | - } | |
386 | - if (stopImmediatePropagation) { | |
387 | - event.stopImmediatePropagation(); | |
388 | - } | |
389 | - fn(event); | |
390 | - }; | |
391 | - } | |
392 | - } | |
393 | -}; | |
6 | + if (typeof global !== 'undefined') { | |
7 | + // global already exists | |
8 | + } else if (typeof window !== 'undefined') { | |
9 | + window.global = window; | |
10 | + } else if (typeof self !== 'undefined') { | |
11 | + self.global = self; | |
12 | + } else { | |
13 | + throw new Error('cannot export Go (neither global, window nor self is defined)'); | |
14 | + } | |
15 | + | |
16 | + // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API). | |
17 | + let outputBuf = ''; | |
18 | + global.fs = { | |
19 | + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused | |
20 | + writeSync(fd, buf) { | |
21 | + outputBuf += decoder.decode(buf); | |
22 | + const nl = outputBuf.lastIndexOf('\n'); | |
23 | + if (nl != -1) { | |
24 | + console.log(outputBuf.substr(0, nl)); | |
25 | + outputBuf = outputBuf.substr(nl + 1); | |
26 | + } | |
27 | + return buf.length; | |
28 | + }, | |
29 | + write(fd, buf, offset, length, position, callback) { | |
30 | + if (offset !== 0 || length !== buf.length || position !== null) { | |
31 | + throw new Error('not implemented'); | |
32 | + } | |
33 | + const n = this.writeSync(fd, buf); | |
34 | + callback(null, n); | |
35 | + }, | |
36 | + open(path, flags, mode, callback) { | |
37 | + const err = new Error('not implemented'); | |
38 | + err.code = 'ENOSYS'; | |
39 | + callback(err); | |
40 | + }, | |
41 | + read(fd, buffer, offset, length, position, callback) { | |
42 | + const err = new Error('not implemented'); | |
43 | + err.code = 'ENOSYS'; | |
44 | + callback(err); | |
45 | + }, | |
46 | + fsync(fd, callback) { | |
47 | + callback(null); | |
48 | + }, | |
49 | + }; | |
50 | + | |
51 | + const encoder = new TextEncoder('utf-8'); | |
52 | + const decoder = new TextDecoder('utf-8'); | |
53 | + | |
54 | + global.Go = class { | |
55 | + constructor() { | |
56 | + this.argv = ['js']; | |
57 | + this.env = {}; | |
58 | + this.exit = (code) => { | |
59 | + if (code !== 0) { | |
60 | + console.warn('exit code:', code); | |
61 | + } | |
62 | + }; | |
63 | + this._exitPromise = new Promise((resolve) => { | |
64 | + this._resolveExitPromise = resolve; | |
65 | + }); | |
66 | + this._pendingEvent = null; | |
67 | + this._scheduledTimeouts = new Map(); | |
68 | + this._nextCallbackTimeoutID = 1; | |
69 | + | |
70 | + const mem = () => { | |
71 | + // The buffer may change when requesting more memory. | |
72 | + return new DataView(this._inst.exports.mem.buffer); | |
73 | + }; | |
74 | + | |
75 | + const setInt64 = (addr, v) => { | |
76 | + mem().setUint32(addr + 0, v, true); | |
77 | + mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); | |
78 | + }; | |
79 | + | |
80 | + const getInt64 = (addr) => { | |
81 | + const low = mem().getUint32(addr + 0, true); | |
82 | + const high = mem().getInt32(addr + 4, true); | |
83 | + return low + high * 4294967296; | |
84 | + }; | |
85 | + | |
86 | + const loadValue = (addr) => { | |
87 | + const f = mem().getFloat64(addr, true); | |
88 | + if (f === 0) { | |
89 | + return undefined; | |
90 | + } | |
91 | + if (!isNaN(f)) { | |
92 | + return f; | |
93 | + } | |
94 | + | |
95 | + const id = mem().getUint32(addr, true); | |
96 | + return this._values[id]; | |
97 | + }; | |
98 | + | |
99 | + const storeValue = (addr, v) => { | |
100 | + const nanHead = 0x7FF80000; | |
101 | + | |
102 | + if (typeof v === 'number') { | |
103 | + if (isNaN(v)) { | |
104 | + mem().setUint32(addr + 4, nanHead, true); | |
105 | + mem().setUint32(addr, 0, true); | |
106 | + return; | |
107 | + } | |
108 | + if (v === 0) { | |
109 | + mem().setUint32(addr + 4, nanHead, true); | |
110 | + mem().setUint32(addr, 1, true); | |
111 | + return; | |
112 | + } | |
113 | + mem().setFloat64(addr, v, true); | |
114 | + return; | |
115 | + } | |
116 | + | |
117 | + switch (v) { | |
118 | + case undefined: | |
119 | + mem().setFloat64(addr, 0, true); | |
120 | + return; | |
121 | + case null: | |
122 | + mem().setUint32(addr + 4, nanHead, true); | |
123 | + mem().setUint32(addr, 2, true); | |
124 | + return; | |
125 | + case true: | |
126 | + mem().setUint32(addr + 4, nanHead, true); | |
127 | + mem().setUint32(addr, 3, true); | |
128 | + return; | |
129 | + case false: | |
130 | + mem().setUint32(addr + 4, nanHead, true); | |
131 | + mem().setUint32(addr, 4, true); | |
132 | + return; | |
133 | + } | |
134 | + | |
135 | + let ref = this._refs.get(v); | |
136 | + if (ref === undefined) { | |
137 | + ref = this._values.length; | |
138 | + this._values.push(v); | |
139 | + this._refs.set(v, ref); | |
140 | + } | |
141 | + let typeFlag = 0; | |
142 | + switch (typeof v) { | |
143 | + case 'string': | |
144 | + typeFlag = 1; | |
145 | + break; | |
146 | + case 'symbol': | |
147 | + typeFlag = 2; | |
148 | + break; | |
149 | + case 'function': | |
150 | + typeFlag = 3; | |
151 | + break; | |
152 | + } | |
153 | + mem().setUint32(addr + 4, nanHead | typeFlag, true); | |
154 | + mem().setUint32(addr, ref, true); | |
155 | + }; | |
156 | + | |
157 | + const loadSlice = (addr) => { | |
158 | + const array = getInt64(addr + 0); | |
159 | + const len = getInt64(addr + 8); | |
160 | + return new Uint8Array(this._inst.exports.mem.buffer, array, len); | |
161 | + }; | |
162 | + | |
163 | + const loadSliceOfValues = (addr) => { | |
164 | + const array = getInt64(addr + 0); | |
165 | + const len = getInt64(addr + 8); | |
166 | + const a = new Array(len); | |
167 | + for (let i = 0; i < len; i++) { | |
168 | + a[i] = loadValue(array + i * 8); | |
169 | + } | |
170 | + return a; | |
171 | + }; | |
172 | + | |
173 | + const loadString = (addr) => { | |
174 | + const saddr = getInt64(addr + 0); | |
175 | + const len = getInt64(addr + 8); | |
176 | + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); | |
177 | + }; | |
178 | + | |
179 | + const timeOrigin = Date.now() - performance.now(); | |
180 | + this.importObject = { | |
181 | + go: { | |
182 | + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) | |
183 | + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported | |
184 | + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). | |
185 | + // This changes the SP, thus we have to update the SP used by the imported function. | |
186 | + | |
187 | + // func wasmExit(code int32) | |
188 | + 'runtime.wasmExit': (sp) => { | |
189 | + const code = mem().getInt32(sp + 8, true); | |
190 | + this.exited = true; | |
191 | + delete this._inst; | |
192 | + delete this._values; | |
193 | + delete this._refs; | |
194 | + this.exit(code); | |
195 | + }, | |
196 | + | |
197 | + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) | |
198 | + 'runtime.wasmWrite': (sp) => { | |
199 | + const fd = getInt64(sp + 8); | |
200 | + const p = getInt64(sp + 16); | |
201 | + const n = mem().getInt32(sp + 24, true); | |
202 | + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); | |
203 | + }, | |
204 | + | |
205 | + // func nanotime() int64 | |
206 | + 'runtime.nanotime': (sp) => { | |
207 | + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); | |
208 | + }, | |
209 | + | |
210 | + // func walltime() (sec int64, nsec int32) | |
211 | + 'runtime.walltime': (sp) => { | |
212 | + const msec = (new Date).getTime(); | |
213 | + setInt64(sp + 8, msec / 1000); | |
214 | + mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); | |
215 | + }, | |
216 | + | |
217 | + // func scheduleTimeoutEvent(delay int64) int32 | |
218 | + 'runtime.scheduleTimeoutEvent': (sp) => { | |
219 | + const id = this._nextCallbackTimeoutID; | |
220 | + this._nextCallbackTimeoutID++; | |
221 | + this._scheduledTimeouts.set(id, setTimeout( | |
222 | + () => { this._resume(); }, | |
223 | + getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early | |
224 | + )); | |
225 | + mem().setInt32(sp + 16, id, true); | |
226 | + }, | |
227 | + | |
228 | + // func clearTimeoutEvent(id int32) | |
229 | + 'runtime.clearTimeoutEvent': (sp) => { | |
230 | + const id = mem().getInt32(sp + 8, true); | |
231 | + clearTimeout(this._scheduledTimeouts.get(id)); | |
232 | + this._scheduledTimeouts.delete(id); | |
233 | + }, | |
234 | + | |
235 | + // func getRandomData(r []byte) | |
236 | + 'runtime.getRandomData': (sp) => { | |
237 | + crypto.getRandomValues(loadSlice(sp + 8)); | |
238 | + }, | |
239 | + | |
240 | + // func stringVal(value string) ref | |
241 | + 'syscall/js.stringVal': (sp) => { | |
242 | + storeValue(sp + 24, loadString(sp + 8)); | |
243 | + }, | |
244 | + | |
245 | + // func valueGet(v ref, p string) ref | |
246 | + 'syscall/js.valueGet': (sp) => { | |
247 | + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); | |
248 | + sp = this._inst.exports.getsp(); // see comment above | |
249 | + storeValue(sp + 32, result); | |
250 | + }, | |
251 | + | |
252 | + // func valueSet(v ref, p string, x ref) | |
253 | + 'syscall/js.valueSet': (sp) => { | |
254 | + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); | |
255 | + }, | |
256 | + | |
257 | + // func valueIndex(v ref, i int) ref | |
258 | + 'syscall/js.valueIndex': (sp) => { | |
259 | + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); | |
260 | + }, | |
261 | + | |
262 | + // valueSetIndex(v ref, i int, x ref) | |
263 | + 'syscall/js.valueSetIndex': (sp) => { | |
264 | + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); | |
265 | + }, | |
266 | + | |
267 | + // func valueCall(v ref, m string, args []ref) (ref, bool) | |
268 | + 'syscall/js.valueCall': (sp) => { | |
269 | + try { | |
270 | + const v = loadValue(sp + 8); | |
271 | + const m = Reflect.get(v, loadString(sp + 16)); | |
272 | + const args = loadSliceOfValues(sp + 32); | |
273 | + const result = Reflect.apply(m, v, args); | |
274 | + sp = this._inst.exports.getsp(); // see comment above | |
275 | + storeValue(sp + 56, result); | |
276 | + mem().setUint8(sp + 64, 1); | |
277 | + } catch (err) { | |
278 | + storeValue(sp + 56, err); | |
279 | + mem().setUint8(sp + 64, 0); | |
280 | + } | |
281 | + }, | |
282 | + | |
283 | + // func valueInvoke(v ref, args []ref) (ref, bool) | |
284 | + 'syscall/js.valueInvoke': (sp) => { | |
285 | + try { | |
286 | + const v = loadValue(sp + 8); | |
287 | + const args = loadSliceOfValues(sp + 16); | |
288 | + const result = Reflect.apply(v, undefined, args); | |
289 | + sp = this._inst.exports.getsp(); // see comment above | |
290 | + storeValue(sp + 40, result); | |
291 | + mem().setUint8(sp + 48, 1); | |
292 | + } catch (err) { | |
293 | + storeValue(sp + 40, err); | |
294 | + mem().setUint8(sp + 48, 0); | |
295 | + } | |
296 | + }, | |
297 | + | |
298 | + // func valueNew(v ref, args []ref) (ref, bool) | |
299 | + 'syscall/js.valueNew': (sp) => { | |
300 | + try { | |
301 | + const v = loadValue(sp + 8); | |
302 | + const args = loadSliceOfValues(sp + 16); | |
303 | + const result = Reflect.construct(v, args); | |
304 | + sp = this._inst.exports.getsp(); // see comment above | |
305 | + storeValue(sp + 40, result); | |
306 | + mem().setUint8(sp + 48, 1); | |
307 | + } catch (err) { | |
308 | + storeValue(sp + 40, err); | |
309 | + mem().setUint8(sp + 48, 0); | |
310 | + } | |
311 | + }, | |
312 | + | |
313 | + // func valueLength(v ref) int | |
314 | + 'syscall/js.valueLength': (sp) => { | |
315 | + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); | |
316 | + }, | |
317 | + | |
318 | + // valuePrepareString(v ref) (ref, int) | |
319 | + 'syscall/js.valuePrepareString': (sp) => { | |
320 | + const str = encoder.encode(String(loadValue(sp + 8))); | |
321 | + storeValue(sp + 16, str); | |
322 | + setInt64(sp + 24, str.length); | |
323 | + }, | |
324 | + | |
325 | + // valueLoadString(v ref, b []byte) | |
326 | + 'syscall/js.valueLoadString': (sp) => { | |
327 | + const str = loadValue(sp + 8); | |
328 | + loadSlice(sp + 16).set(str); | |
329 | + }, | |
330 | + | |
331 | + // func valueInstanceOf(v ref, t ref) bool | |
332 | + 'syscall/js.valueInstanceOf': (sp) => { | |
333 | + mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); | |
334 | + }, | |
335 | + | |
336 | + 'debug': (value) => { | |
337 | + console.log(value); | |
338 | + }, | |
339 | + } | |
340 | + }; | |
341 | + } | |
342 | + | |
343 | + async run(instance) { | |
344 | + this._inst = instance; | |
345 | + this._values = [ // TODO: garbage collection | |
346 | + NaN, | |
347 | + 0, | |
348 | + null, | |
349 | + true, | |
350 | + false, | |
351 | + global, | |
352 | + this._inst.exports.mem, | |
353 | + this, | |
354 | + ]; | |
355 | + this._refs = new Map(); | |
356 | + this.exited = false; | |
357 | + | |
358 | + const mem = new DataView(this._inst.exports.mem.buffer); | |
359 | + | |
360 | + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. | |
361 | + let offset = 4096; | |
362 | + | |
363 | + const strPtr = (str) => { | |
364 | + let ptr = offset; | |
365 | + new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + '\0')); | |
366 | + offset += str.length + (8 - (str.length % 8)); | |
367 | + return ptr; | |
368 | + }; | |
369 | + | |
370 | + const argc = this.argv.length; | |
371 | + | |
372 | + const argvPtrs = []; | |
373 | + this.argv.forEach((arg) => { | |
374 | + argvPtrs.push(strPtr(arg)); | |
375 | + }); | |
376 | + | |
377 | + const keys = Object.keys(this.env).sort(); | |
378 | + argvPtrs.push(keys.length); | |
379 | + keys.forEach((key) => { | |
380 | + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); | |
381 | + }); | |
382 | + | |
383 | + const argv = offset; | |
384 | + argvPtrs.forEach((ptr) => { | |
385 | + mem.setUint32(offset, ptr, true); | |
386 | + mem.setUint32(offset + 4, 0, true); | |
387 | + offset += 8; | |
388 | + }); | |
389 | + | |
390 | + this._inst.exports.run(argc, argv); | |
391 | + if (this.exited) { | |
392 | + this._resolveExitPromise(); | |
393 | + } | |
394 | + await this._exitPromise; | |
395 | + } | |
396 | + | |
397 | + _resume() { | |
398 | + if (this.exited) { | |
399 | + throw new Error('Go program has already exited'); | |
400 | + } | |
401 | + this._inst.exports.resume(); | |
402 | + if (this.exited) { | |
403 | + this._resolveExitPromise(); | |
404 | + } | |
405 | + } | |
406 | + | |
407 | + _makeFuncWrapper(id) { | |
408 | + const go = this; | |
409 | + return function () { | |
410 | + const event = { id: id, this: this, args: arguments }; | |
411 | + go._pendingEvent = event; | |
412 | + go._resume(); | |
413 | + return event.result; | |
414 | + }; | |
415 | + } | |
416 | + }; | |
417 | +} |