diff --git a/.gitignore b/.gitignore
index 4e3b3d7..50e1fd7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ node_modules/
coverage
.vscode
yarn-error.log
-tests/fixtures
\ No newline at end of file
+tests/fixtures
+dist
\ No newline at end of file
diff --git a/tests/MG_sigs.spec.js b/__test__/MG_sigs.spec.ts
similarity index 69%
rename from tests/MG_sigs.spec.js
rename to __test__/MG_sigs.spec.ts
index b1b05fb..387d64c 100644
--- a/tests/MG_sigs.spec.js
+++ b/__test__/MG_sigs.spec.ts
@@ -1,3 +1,13 @@
+import {
+ skGen,
+ ge_scalarmult_base,
+ identity,
+ ge_scalarmult,
+ hashToPoint,
+ MLSAG_Gen,
+ MLSAG_ver,
+} from "cryptonote_utils";
+
// Copyright (c) 2014-2018, MyMonero.com
//
// All rights reserved.
@@ -26,21 +36,19 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const monero_utils = require("../").monero_utils;
-
it("MG_sigs", () => {
- function skvGen(len) {
- let skVec = [];
+ function skvGen(len: number) {
+ const skVec: string[] = [];
for (let i = 0; i < len; i++) {
- skVec.push(monero_utils.skGen());
+ skVec.push(skGen());
}
return skVec;
}
//initializes a key matrix;
//first parameter is rows,
//second is columns
- function keyMInit(rows, cols) {
- let rv = [];
+ function keyMInit(cols: number) {
+ let rv: string[][] = [];
for (let i = 0; i < cols; i++) {
rv.push([]);
}
@@ -53,7 +61,7 @@ it("MG_sigs", () => {
let N = 3; // cols
let R = 2; // rows
- let xm = keyMInit(R, N); // = [[None]*N] #just used to generate test public keys
+ let xm = keyMInit(N); // = [[None]*N] #just used to generate test public keys
let sk = skvGen(R);
// [
@@ -63,15 +71,15 @@ it("MG_sigs", () => {
// [pubkeyn, commitmentn]]
// // Gen creates a signature which proves that for some column in the keymatrix "pk"
// the signer knows a secret key for each row in that column
- let P = keyMInit(R, N); // = keyM[[None]*N] #stores the public keys;
+ let P = keyMInit(N); // = keyM[[None]*N] #stores the public keys;
let ind = 2;
let i = 0;
for (j = 0; j < R; j++) {
for (i = 0; i < N; i++) {
- xm[i][j] = monero_utils.skGen();
- P[i][j] = monero_utils.ge_scalarmult_base(xm[i][j]); // generate fake [pubkey, commit]
+ xm[i][j] = skGen();
+ P[i][j] = ge_scalarmult_base(xm[i][j]); // generate fake [pubkey, commit]
}
}
@@ -80,34 +88,27 @@ it("MG_sigs", () => {
sk[j] = xm[ind][j];
}
- let message = monero_utils.identity();
- let kimg = monero_utils.ge_scalarmult(
- monero_utils.hashToPoint(P[ind][0]),
- sk[0],
- );
- let rv = monero_utils.MLSAG_Gen(message, P, sk, kimg, ind);
- let c = monero_utils.MLSAG_ver(message, P, rv, kimg);
+ let message = identity();
+ let kimg = ge_scalarmult(hashToPoint(P[ind][0]), sk[0]);
+ let rv = MLSAG_Gen(message, P, sk, kimg, ind);
+ let c = MLSAG_ver(message, P, rv, kimg);
expect(c).toEqual(true);
- xtmp = skvGen(R);
- xm = keyMInit(R, N); // = [[None]*N] #just used to generate test public keys
+ xm = keyMInit(N); // = [[None]*N] #just used to generate test public keys
sk = skvGen(R);
for (j = 0; j < R; j++) {
for (i = 0; i < N; i++) {
- xm[i][j] = monero_utils.skGen();
- P[i][j] = monero_utils.ge_scalarmult_base(xm[i][j]); // generate fake [pubkey, commit]
+ xm[i][j] = skGen();
+ P[i][j] = ge_scalarmult_base(xm[i][j]); // generate fake [pubkey, commit]
}
}
sk[1] = skGen(); //assume we don't know one of the private keys..
- kimg = monero_utils.ge_scalarmult(
- monero_utils.hashToPoint(P[ind][0]),
- sk[0],
- );
- rv = monero_utils.MLSAG_Gen(message, P, sk, kimg, ind);
- c = monero_utils.MLSAG_ver(message, P, rv, kimg);
+ kimg = ge_scalarmult(hashToPoint(P[ind][0]), sk[0]);
+ rv = MLSAG_Gen(message, P, sk, kimg, ind);
+ c = MLSAG_ver(message, P, rv, kimg);
expect(c).toEqual(false);
});
diff --git a/tests/borromean/borromean_1.spec.js b/__test__/borromean/borromean_1.spec.ts
similarity index 86%
rename from tests/borromean/borromean_1.spec.js
rename to __test__/borromean/borromean_1.spec.ts
index f9ded42..a514647 100644
--- a/tests/borromean/borromean_1.spec.js
+++ b/__test__/borromean/borromean_1.spec.ts
@@ -1,3 +1,6 @@
+import { generate_parameters } from "./test_parameters";
+import { genBorromean, verifyBorromean } from "cryptonote_utils";
+
// Copyright (c) 2014-2018, MyMonero.com
//
// All rights reserved.
@@ -26,13 +29,11 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const monero_utils = require("../../").monero_utils;
-const { generate_parameters } = require("./test_parameters");
const { indi, P1v, P2v, xv, N } = generate_parameters();
it("borromean_3", () => {
// #true one
- const bb = monero_utils.genBorromean(xv, [P1v, P2v], indi, 2, N); /*?.*/
- const valid = monero_utils.verifyBorromean(bb, P1v, P2v); /*?.*/
+ const bb = genBorromean(xv, [P1v, P2v], indi, 2, N); /*?.*/
+ const valid = verifyBorromean(bb, P1v, P2v); /*?.*/
expect(valid).toBe(true);
});
diff --git a/tests/borromean/borromean_2.spec.js b/__test__/borromean/borromean_2.spec.ts
similarity index 87%
rename from tests/borromean/borromean_2.spec.js
rename to __test__/borromean/borromean_2.spec.ts
index 669fcc9..b33698c 100644
--- a/tests/borromean/borromean_2.spec.js
+++ b/__test__/borromean/borromean_2.spec.ts
@@ -26,14 +26,14 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const monero_utils = require("../../").monero_utils;
-const { generate_parameters } = require("./test_parameters");
+import { generate_parameters } from "./test_parameters";
+import { genBorromean, verifyBorromean } from "cryptonote_utils";
const { indi, P1v, P2v, xv, N } = generate_parameters();
it("borromean_2", () => {
//#false one
indi[3] = `${(+indi[3] + 1) % 2}`;
- const bb = monero_utils.genBorromean(xv, [P1v, P2v], indi, 2, N); /*?.*/
- const valid = monero_utils.verifyBorromean(bb, P1v, P2v); /*?.*/
+ const bb = genBorromean(xv, [P1v, P2v], indi, 2, N); /*?.*/
+ const valid = verifyBorromean(bb, P1v, P2v); /*?.*/
expect(valid).toBe(false);
});
diff --git a/tests/borromean/borromean_3.spec.js b/__test__/borromean/borromean_3.spec.ts
similarity index 87%
rename from tests/borromean/borromean_3.spec.js
rename to __test__/borromean/borromean_3.spec.ts
index 3ba8366..2593df3 100644
--- a/tests/borromean/borromean_3.spec.js
+++ b/__test__/borromean/borromean_3.spec.ts
@@ -26,8 +26,8 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const monero_utils = require("../../").monero_utils;
-const { generate_parameters } = require("./test_parameters");
+import { generate_parameters } from "./test_parameters";
+import { genBorromean, verifyBorromean } from "cryptonote_utils";
const { indi, P1v, P2v, xv, N } = generate_parameters();
it("borromean_3", () => {
@@ -35,7 +35,7 @@ it("borromean_3", () => {
indi[3] = `${(+indi[3] + 1) % 2}`;
indi[3] = `${(+indi[3] + 1) % 2}`;
- const bb = monero_utils.genBorromean(xv, [P1v, P2v], indi, 2, N); /*?.*/
- const valid = monero_utils.verifyBorromean(bb, P1v, P2v); /*?.*/
+ const bb = genBorromean(xv, [P1v, P2v], indi, 2, N); /*?.*/
+ const valid = verifyBorromean(bb, P1v, P2v); /*?.*/
expect(valid).toBe(true);
});
diff --git a/tests/borromean/borromean_4.spec.js b/__test__/borromean/borromean_4.spec.ts
similarity index 86%
rename from tests/borromean/borromean_4.spec.js
rename to __test__/borromean/borromean_4.spec.ts
index e0591ee..68822d8 100644
--- a/tests/borromean/borromean_4.spec.js
+++ b/__test__/borromean/borromean_4.spec.ts
@@ -1,3 +1,5 @@
+import { genBorromean, verifyBorromean } from "cryptonote_utils";
+
// Copyright (c) 2014-2018, MyMonero.com
//
// All rights reserved.
@@ -26,13 +28,12 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const monero_utils = require("../../").monero_utils;
-const { generate_parameters } = require("./test_parameters");
+import { generate_parameters } from "./test_parameters";
const { indi, P1v, P2v, xv, N } = generate_parameters();
it("borromean_4", () => {
// #false one
- const bb = monero_utils.genBorromean(xv, [P2v, P1v], indi, 2, N); /*?.*/
- const valid = monero_utils.verifyBorromean(bb, P1v, P2v); /*?.*/
+ const bb = genBorromean(xv, [P2v, P1v], indi, 2, N); /*?.*/
+ const valid = verifyBorromean(bb, P1v, P2v); /*?.*/
expect(valid).toBe(false);
});
diff --git a/tests/borromean/test_parameters.js b/__test__/borromean/test_parameters.ts
similarity index 71%
rename from tests/borromean/test_parameters.js
rename to __test__/borromean/test_parameters.ts
index b94c82d..c9c743d 100644
--- a/tests/borromean/test_parameters.js
+++ b/__test__/borromean/test_parameters.ts
@@ -26,36 +26,35 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const monero_utils = require("../../").monero_utils;
-const { randomBytes } = require("crypto");
+import { randomBytes } from "crypto";
+import {
+ skGen,
+ ge_scalarmult_base,
+ ge_add,
+ ge_sub,
+ H2,
+} from "cryptonote_utils";
+import { padLeft } from "cryptonote_utils/formatters";
function randomBit() {
// get random 8 bits in hex
const rand8bits = randomBytes(1).toString("hex");
// take 4 bits "nibble" and convert to binary
// then take last index
- return monero_utils.padLeft(
- parseInt(rand8bits[0], 16).toString(2),
- 4,
- 0,
- )[3];
+ return padLeft(parseInt(rand8bits[0], 16).toString(2), 4, "0")[3];
}
//Tests for Borromean signatures
//#boro true one, false one, C != sum Ci, and one out of the range..
const N = 64;
-let xv = [], // vector of secret keys, 1 per ring (nrings)
- P1v = [], //key64, arr of commitments Ci
- P2v = [], //key64
- indi = []; // vector of secret indexes, 1 per ring (nrings), can be a string
-
-let indi_2 = [];
-let indi_3 = [];
-let indi_4 = [];
+let xv: string[] = [], // vector of secret keys, 1 per ring (nrings)
+ P1v: string[] = [], //key64, arr of commitments Ci
+ P2v: string[] = [], //key64
+ indi: string[] = []; // vector of secret indexes, 1 per ring (nrings), can be a string
let generated = false;
-function generate_parameters() {
+export function generate_parameters() {
if (generated) {
const indiCopy = [...indi];
@@ -64,20 +63,18 @@ function generate_parameters() {
for (let j = 0; j < N; j++) {
indi[j] = randomBit(); /*?.*/
- xv[j] = monero_utils.skGen(); /*?.*/
+ xv[j] = skGen(); /*?.*/
if (+indi[j] === 0) {
- P1v[j] = monero_utils.ge_scalarmult_base(xv[j]); /*?.*/
+ P1v[j] = ge_scalarmult_base(xv[j]); /*?.*/
} else {
- P1v[j] = monero_utils.ge_scalarmult_base(xv[j]); // calculate aG = xv[j].G /*?.*/
- P1v[j] = monero_utils.ge_add(P1v[j], monero_utils.H2[j]); // calculate aG + H2 /*?.*/
+ P1v[j] = ge_scalarmult_base(xv[j]); // calculate aG = xv[j].G /*?.*/
+ P1v[j] = ge_add(P1v[j], H2[j]); // calculate aG + H2 /*?.*/
}
- P2v[j] = monero_utils.ge_sub(P1v[j], monero_utils.H2[j]); /*?.*/
+ P2v[j] = ge_sub(P1v[j], H2[j]); /*?.*/
}
generated = true;
return { xv, P1v, P2v, indi, N };
}
}
-
-module.exports = { generate_parameters };
diff --git a/tests/cryptonote_utils.spec.js b/__test__/cryptonote_utils.spec.ts
similarity index 68%
rename from tests/cryptonote_utils.spec.js
rename to __test__/cryptonote_utils.spec.ts
index df8622e..7a162cb 100644
--- a/tests/cryptonote_utils.spec.js
+++ b/__test__/cryptonote_utils.spec.ts
@@ -26,122 +26,105 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"use strict";
-const mymonero = require("../");
-const assert = require("assert");
+import {
+ cn_fast_hash,
+ NetType,
+ valid_hex,
+ generate_key_derivation,
+ decode_address,
+ hash_to_scalar,
+ derivation_to_scalar,
+ derive_public_key,
+ derive_subaddress_public_key,
+} from "cryptonote_utils";
-var public_key =
+const public_key =
"904e49462268d771cc1649084c35aa1296bfb214880fe2e7f373620a3e2ba597";
-var private_key =
+const private_key =
"52aa4c69b93b780885c9d7f51e6fd5795904962c61a2e07437e130784846f70d";
-var nettype = mymonero.nettype_utils.network_type.MAINNET;
+const nettype = NetType.MAINNET;
describe("cryptonote_utils tests", function() {
it("is valid hex", function() {
- var valid = mymonero.monero_utils.valid_hex(private_key);
- assert.strictEqual(valid, true);
+ const valid = valid_hex(private_key);
+ expect(valid).toEqual(true);
});
it("fast hash / keccak-256", function() {
- var hash = mymonero.monero_utils.cn_fast_hash(
- private_key,
- private_key.length,
- );
- assert.equal(
- hash,
+ const hash = cn_fast_hash(private_key);
+ expect(hash).toEqual(
"64997ff54f0d82ee87d51e971a0329d4315481eaeb4ad2403c65d5843480c414",
);
});
it("generate key derivation", function() {
- var derivation = mymonero.monero_utils.generate_key_derivation(
- public_key,
- private_key,
- );
- assert.equal(
- derivation,
+ const derivation = generate_key_derivation(public_key, private_key);
+ expect(derivation).toEqual(
"591c749f1868c58f37ec3d2a9d2f08e7f98417ac4f8131e3a57c1fd71273ad00",
);
});
it("decode mainnet primary address", function() {
- var decoded = mymonero.monero_utils.decode_address(
+ const decoded = decode_address(
"49qwWM9y7j1fvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNRHuCbTr",
nettype,
);
- var expected = {
+ const expected = {
spend:
"d8f1e81ecbe25ce8b596d426fb02fe7b1d4bb8d14c06b3d3e371a60eeea99534",
view:
"576f0e61e250d941746ed147f602b5eb1ea250ca385b028a935e166e18f74bd7",
};
- assert.deepEqual(decoded, expected);
+ expect(decoded).toEqual(expected);
});
it("decode mainnet integrated address", function() {
- var decoded = mymonero.monero_utils.decode_address(
+ const decoded = decode_address(
"4KYcX9yTizXfvaBK684Y5sMbN8MZ3XwDLcSaqcKwjh5W9kn9qFigPBNBwzdq6TCAm2gKxQWrdZuEZQBMjQodi9cNd3mZpgrjXBKMx9ee7c",
nettype,
);
- var expected = {
+ const expected = {
spend:
"d8f1e81ecbe25ce8b596d426fb02fe7b1d4bb8d14c06b3d3e371a60eeea99534",
view:
"576f0e61e250d941746ed147f602b5eb1ea250ca385b028a935e166e18f74bd7",
intPaymentId: "83eab71fbee84eb9",
};
- assert.deepEqual(decoded, expected);
+ expect(decoded).toEqual(expected);
});
it("hash_to_scalar", function() {
- var scalar = mymonero.monero_utils.hash_to_scalar(private_key);
- assert.equal(
- scalar,
+ const scalar = hash_to_scalar(private_key);
+ expect(scalar).toEqual(
"77c5899835aa6f96b13827f43b094abf315481eaeb4ad2403c65d5843480c404",
);
});
it("derivation_to_scalar", function() {
- var derivation = mymonero.monero_utils.generate_key_derivation(
- public_key,
- private_key,
- );
- var scalar = mymonero.monero_utils.derivation_to_scalar(derivation, 1);
- assert.equal(
- scalar,
+ const derivation = generate_key_derivation(public_key, private_key);
+ const scalar = derivation_to_scalar(derivation, 1);
+ expect(scalar).toEqual(
"201ce3c258e09eeb6132ec266d24ee1ca957828f384ce052d5bc217c2c55160d",
);
});
it("derive public key", function() {
- var derivation = mymonero.monero_utils.generate_key_derivation(
- public_key,
- private_key,
- );
- var output_key = mymonero.monero_utils.derive_public_key(
- derivation,
- 1,
- public_key,
- );
- assert.equal(
- output_key,
+ const derivation = generate_key_derivation(public_key, private_key);
+ const output_key = derive_public_key(derivation, 1, public_key);
+ expect(output_key).toEqual(
"da26518ddb54cde24ccfc59f36df13bbe9bdfcb4ef1b223d9ab7bef0a50c8be3",
);
});
it("derive subaddress public key", function() {
- var derivation = mymonero.monero_utils.generate_key_derivation(
- public_key,
- private_key,
- );
- var subaddress_public_key = mymonero.monero_utils.derive_subaddress_public_key(
+ const derivation = generate_key_derivation(public_key, private_key);
+ const subaddress_public_key = derive_subaddress_public_key(
public_key,
derivation,
1,
);
- assert.equal(
- subaddress_public_key,
+ expect(subaddress_public_key).toEqual(
"dfc9e4a0039e913204c1c0f78e954a7ec7ce291d8ffe88265632f0da9d8de1be",
);
});
diff --git a/tests/ecdh.spec.js b/__test__/ecdh.spec.ts
similarity index 75%
rename from tests/ecdh.spec.js
rename to __test__/ecdh.spec.ts
index 5ade692..8ff1ef0 100644
--- a/tests/ecdh.spec.js
+++ b/__test__/ecdh.spec.ts
@@ -1,3 +1,6 @@
+import { BigInt } from "biginteger";
+import { skGen, d2s, encode_rct_ecdh, decode_rct_ecdh } from "cryptonote_utils";
+
// Copyright (c) 2014-2018, MyMonero.com
//
// All rights reserved.
@@ -26,29 +29,26 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const JSBigInt = require("../cryptonote_utils/biginteger").BigInteger;
-const monero_utils = require("../").monero_utils;
-
it("ecdh_roundtrip", () => {
const test_amounts = [
- new JSBigInt(1),
- new JSBigInt(1),
- new JSBigInt(2),
- new JSBigInt(3),
- new JSBigInt(4),
- new JSBigInt(5),
- new JSBigInt(10000),
+ new BigInt(1),
+ new BigInt(1),
+ new BigInt(2),
+ new BigInt(3),
+ new BigInt(4),
+ new BigInt(5),
+ new BigInt(10000),
- new JSBigInt("10000000000000000000"),
- new JSBigInt("10203040506070809000"),
+ new BigInt("10000000000000000000"),
+ new BigInt("10203040506070809000"),
- new JSBigInt("123456789123456789"),
+ new BigInt("123456789123456789"),
];
for (const amount of test_amounts) {
- const k = monero_utils.skGen();
- const scalar = monero_utils.skGen(); /*?*/
- const amt = monero_utils.d2s(amount.toString());
+ const k = skGen();
+ const scalar = skGen(); /*?*/
+ const amt = d2s(amount.toString());
const t0 = {
mask: scalar,
amount: amt,
@@ -57,9 +57,9 @@ it("ecdh_roundtrip", () => {
// both are strings so we can shallow copy
let t1 = { ...t0 };
- t1 = monero_utils.encode_rct_ecdh(t1, k);
+ t1 = encode_rct_ecdh(t1, k);
- t1 = monero_utils.decode_rct_ecdh(t1, k);
+ t1 = decode_rct_ecdh(t1, k);
expect(t1.mask).toEqual(t0.mask);
expect(t1.amount).toEqual(t0.amount);
}
diff --git a/__test__/fixtures/log-modified.log b/__test__/fixtures/log-modified.log
new file mode 100644
index 0000000..7420c1c
--- /dev/null
+++ b/__test__/fixtures/log-modified.log
@@ -0,0 +1,669 @@
+send_coins.js?2:200 Parsed destinations:
+[
+ {
+ "address":"46WhbYQYsJzdp3BVigsbFpFTG3ahq7QbcWJX8w9m48HUitAdQkqfJWDPfZUTWnqTv9jafzXwzbdWpWjh6HQeoBFUTTWVtuA",
+ "amount":{"_d":[0,1000],"_s":1}
+ }
+]
+
+send_coins.js?2:201 Total before fee: 0.01
+send_coins.js?2:229 Destinations:
+send_coins.js?2:232 46WhbYQYsJzdp3BVigsbFpFTG3ahq7QbcWJX8w9m48HUitAdQkqfJWDPfZUTWnqTv9jafzXwzbdWpWjh6HQeoBFUTTWVtuA: 0.010000000000
+
+
+send_coins.js?2:285 Output used as mixin (c0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b/4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7)
+send_coins.js?2:289 Unspent outs:
+[
+ {
+ "amount":"296850150000",
+ "public_key":"af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d",
+ "index":0,
+ "global_index":6251022,
+ "rct":"35ca97e4550d966247076aa1ca015e8ebcdc0d63594f48ad18cdd806c789b2f970273a47742ff06fa3549a004f97927dd89b0b126655bc2680ce3ed3bbb88e05f444932916512b22d51042a876337e9014f120a46cd103915b812779bda41e06",
+ "tx_id":4751309,
+ "tx_hash":"3153c385e55f1577cad35ab8327f1acf179a82a3c33691beac2d3dfb6c5189f9",
+ "tx_pub_key":"5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf",
+ "tx_prefix_hash":"1c1ae56c6a558eb759c2382f3ffca3a557a6399d6133113aa5d8488cfa517c58",
+ "spend_key_images":["4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7"],
+ "timestamp":"2018-07-06T00:25:48Z",
+ "height":1600105
+ }
+]
+
+send_coins.js?2:389 Balance required: 0.01223054 XMR
+send_coins.js?2:367 Selecting outputs to use. Current total: 0 target: 0.01223054
+send_coins.js?2:373 Using output: 0.29685015 -
+{
+ "amount":"296850150000",
+ "public_key":"af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d",
+ "index":0,
+ "global_index":6251022,
+ "rct":"35ca97e4550d966247076aa1ca015e8ebcdc0d63594f48ad18cdd806c789b2f970273a47742ff06fa3549a004f97927dd89b0b126655bc2680ce3ed3bbb88e05f444932916512b22d51042a876337e9014f120a46cd103915b812779bda41e06",
+ "tx_id":4751309,
+ "tx_hash":"3153c385e55f1577cad35ab8327f1acf179a82a3c33691beac2d3dfb6c5189f9",
+ "tx_pub_key":"5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf",
+ "tx_prefix_hash":"1c1ae56c6a558eb759c2382f3ffca3a557a6399d6133113aa5d8488cfa517c58",
+ "spend_key_images":["4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7"],
+ "timestamp":"2018-07-06T00:25:48Z",
+ "height":1600105
+}
+
+send_coins.js?2:433 Sending change of 0.28461961 XMR to 47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX
+send_coins.js?2:458 Fetching mix outs...
+send_coins.js?2:481 Destinations:
+cn_util.js?3:1851 46WhbYQYsJzdp3BVigsbFpFTG3ahq7QbcWJX8w9m48HUitAdQkqfJWDPfZUTWnqTv9jafzXwzbdWpWjh6HQeoBFUTTWVtuA: 0.010000000000
+cn_util.js?3:1851 47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX: 0.284619610000
+send_coins.js?2:488 Decomposed destinations:
+cn_util.js?3:1851 46WhbYQYsJzdp3BVigsbFpFTG3ahq7QbcWJX8w9m48HUitAdQkqfJWDPfZUTWnqTv9jafzXwzbdWpWjh6HQeoBFUTTWVtuA: 0.010000000000
+cn_util.js?3:1851 47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX: 0.284619610000
+
+cn_util.js?3:1741 Selected transfers:
+[
+ {
+ "amount": "296850150000",
+ "public_key": "af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d",
+ "index": 0,
+ "global_index": 6251022,
+ "rct": "35ca97e4550d966247076aa1ca015e8ebcdc0d63594f48ad18cdd806c789b2f970273a47742ff06fa3549a004f97927dd89b0b126655bc2680ce3ed3bbb88e05f444932916512b22d51042a876337e9014f120a46cd103915b812779bda41e06",
+ "tx_id": 4751309,
+ "tx_hash": "3153c385e55f1577cad35ab8327f1acf179a82a3c33691beac2d3dfb6c5189f9",
+ "tx_pub_key": "5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf",
+ "tx_prefix_hash": "1c1ae56c6a558eb759c2382f3ffca3a557a6399d6133113aa5d8488cfa517c58",
+ "spend_key_images": [
+ "4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7"
+ ],
+ "timestamp": "2018-07-06T00:25:48Z",
+ "height": 1600105
+ }
+]
+
+
+
+cn_util.js?3:1812 sources:
+[
+ {
+ "outputs": [
+ {
+ "index": "2080088",
+ "key": "e3b02dc808097301664c989a4faf7c5f2ec8976777b74dbe4b8cfecf8c44ca11",
+ "commit": "bdeba6a579f63f9dfe344ae4754c8db083e1f87a75956bb2c90f93210b3d02a1"
+ },
+ {
+ "index": "2799561",
+ "key": "69d3eb381e3a43fd41b19cc41004866c00274e9daeb07de3303bf7a949719d50",
+ "commit": "733270f6b344c77dc8e463a95524fb90626874c2d8ae15b02578fe8404be8491"
+ },
+ {
+ "index": "4203500",
+ "key": "4d9eed420a1844a32e588317c7a713fbe59d9281b45b7f3504b9bda307c2c3c1",
+ "commit": "dc1afdf383ca54068da77efcd29ae628882db2fc93adf8c1e4a96da7d066d1a1"
+ },
+ {
+ "index": "4281075",
+ "key": "7c82f043d142a221f9f0a5f9bc4337270a58e3f2d8d4dfb6f8619842bfe0d1c0",
+ "commit": "636a54cdae73d0517334a02cdc9b6c6081c261e250f510284c1289ca3ed5e26c"
+ },
+ {
+ "index": "5980172",
+ "key": "3fb2a5f3016c21721349e27f98ba734bf3d0cb577ba4640fe5db5904a2bb146e",
+ "commit": "df525ea20e203f3d0c6ed5caf9e083e679ce6daee56c992216ec39849b12aaac"
+ },
+ {
+ "index": "6002676",
+ "key": "bcfd234d10151e10eff22c7b84bcf7064f9a35c3e0c53dd315394cd66d1ddc8e",
+ "commit": "5206721c74d96343283bb82ad0abde561f10913e1217b3826ed5cf33fe7c5355"
+ },
+ {
+ "index": "6251022",
+ "key": "af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d",
+ "commit": "35ca97e4550d966247076aa1ca015e8ebcdc0d63594f48ad18cdd806c789b2f9"
+ }
+ ],
+ "amount": "296850150000",
+ "real_out_tx_key": "5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf",
+ "real_out": 6,
+ "real_out_in_tx": 0,
+ "mask": "70273a47742ff06fa3549a004f97927dd89b0b126655bc2680ce3ed3bbb88e05",
+ "key_image": "c0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b",
+ "in_ephemeral": {
+ "pub": "af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d",
+ "sec": "0b645a7041b8709a977fba1c34e70b5ca3eb8511a023142442f4cd0516064f07",
+ "mask": "61b1c6138c261599e8e40f3e437212b36d3d48fee73daeb502b141a26f777008"
+ }
+ }
+]
+
+cn_util.js?3:1560
+{
+ sec: "b9a57d04249992a0faca9a7da08109f31c5955bb8bd60a3dc2872abb1bd49b05",
+ pub: "c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df3"
+}
+
+
+cn_util.js?3:1593 Sources:
+cn_util.js?3:1597 0: 0.296850150000
+cn_util.js?3:1234 Time take for range proof 0: 922
+cn_util.js?3:1234 Time take for range proof 1: 904
+
+cn_util.js?3:1700
+{
+ "unlock_time": 0,
+ "version": 2,
+ "extra": "01c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df3",
+ "vin": [
+ {
+ "type": "input_to_key",
+ "amount": "0",
+ "k_image": "c0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b",
+ "key_offsets": [
+ "2080088",
+ "719473",
+ "1403939",
+ "77575",
+ "1699097",
+ "22504",
+ "248346"
+ ]
+ }
+ ],
+ "vout": [
+ {
+ "amount": "0",
+ "target": {
+ "type": "txout_to_key",
+ "key": "105f078f9332cf8fb2390be2f6be39abf40992dcd33daf0e3fbd9b02a23755b1"
+ }
+ },
+ {
+ "amount": "0",
+ "target": {
+ "type": "txout_to_key",
+ "key": "d4b94e2516134889b321d6f06792322debb0d311c761c6fc1fc4e699fbb87d31"
+ }
+ }
+ ],
+ "rct_signatures": {
+ "type": 1,
+ "message": "6ffddc62bc7c37b8b2c5dfca7fe2c8be1fc8099d86a4e779946c58dc8f5f2128",
+ "outPk": [
+ "200554164113b97acd5bb67b91ee69296bf15fff6001d7ce77374afffa68fb6b",
+ "6464fb83555790fac27de59e3fd2cb8d27384ee923ad99de0dfe2e498104b31e"
+ ],
+ "p": {
+ "rangeSigs": [
+ {
+ "Ci": [
+ "02836fed5f531a64dd41aad00a1dc39ec13b49d1ebd7a2a6b5867d7d71db0e50",
+ "a393ec33f28531624bdfa3ddab00ab9239b2bd54ea858920f34c26430045898f",
+ "52b419f7f04f7b3b8e604bf11b20ed058e8ecdeae5be4845aa5f14f16c1f953d",
+ "8e653029b087bcd4ab9953c1937cee84f81c393285c8bc3341a5eb5bae580b5d",
+ "55cf6f0ee2afbea795764bf5d4ed0735367a0bf65b6f69732449e3dfec547d93",
+ "d22056ada1eb1c35c2ca1924e07c0e6120b818d6941755efc4dae89d0cc03976",
+ "2bf5e003763a01910d434d3f0c3e71af286c55d30c9aadb2a480b8d0938ce43c",
+ "202968df06659dc14e713dd6b2899be1501718e3003be5220b7e90034244ab3e",
+ "9db020600bdb9b2a17b9d87e150356cb3f3a7150bf305d03efb0aab95cf43e5c",
+ "f124a8f5db6c37b81f7841b81a7cc5c2ed6d3b8124ff95f9981fc93d732dbf47",
+ "0403a44e0f46ffff9bbef09e2fedbf9b42600918567617ea28d8f42e6b1d764a",
+ "340ea317a4462b1083062b29560d25f2b35a82bfdf000033f8a0db28e7314d4d",
+ "784f6b9d0df70928cc30b94119cf02085884a237b2ff7470e299726d06f430f7",
+ "2c73a1727446f060a3d98ee0c867ea162c197bacce848ebc423f3dc6c4835e8b",
+ "bce299084ecf73139b024c33f215bc132fe77cf326e2da2cd18b3008bf16447f",
+ "f4b2a4a5679987564d369e8936270b4adb3128e6e88eb75205f9de36088fde3a",
+ "7d294c7c38cf0d0bb02c1160a6695550423737668f1646ca65b93d95547097a2",
+ "fa8bd70f74a623e5a3c9c3c6b70097bde54853b7517c76c0d36e59cabce4bb69",
+ "1bff14524d2a0eac2155a9af73a4e87f0d6ec67d4a27840ed0b7134fa93d7515",
+ "88a77416daca2b68d054a7e74dc752a9506090e79596c9b558f085853055d878",
+ "52c251baa10a9607b86c009ba3ecba6ed2457d499128f26fe632c39e5ccc46fb",
+ "4b7e225ee282d9883abaed6e531b4f844df3d5cb547152742d0a0744e40fb928",
+ "4637a34dbd3a4ff4dae9a07904b36e539f56eec6ce7baf51ed8e8189b242effc",
+ "3bc60908d6b62739b6d6e8206e4223160eb293eeea2992c0dcaf60c73632ce19",
+ "6441692c1f5131526818f5bff1cd83a75e414e595e13a082a47985bd505cf256",
+ "f5ec516ae79c1117f9f7820594d696a7714d2151450127dee286f8d18de13517",
+ "b24eda31f18547032200489505d2f53101c962641b7275b8a48296f339402f7f",
+ "48ac2674f669841615752eaf495a6dac6bd4d6cae58be8c779890d201904221e",
+ "f4a1a0f87c523b027fa43f52f37ea06f8a19070fe04f7533dfb529d802a22d7d",
+ "299f3abb6974611456060915173bcca396febe1bc943150131d94a2406565957",
+ "950fea0f11421c7ae2b7e900f46a8c947b7e5e724bd981c00a1c93070a6dd51b",
+ "f406b876c5141f7fb5bd84cb788dda0b822bdc4263e500c85c079aee409cc00e",
+ "411628ef38f9c948a3ca7989efe4592d7702aa846b3a1cfbfc5e4a1801ad6a63",
+ "386bdaa004af3bd3182a082ae5f17d30862c5a0de12078aba63b51ad2ad9c695",
+ "257e90cf997464da236ace2f90020547003d30e8f63998ab705aa56fd4031397",
+ "d36526e09faa72c4789380a4df227104cba21542a16f64b19ba96a84103513d9",
+ "4336e5077c76eb3a301e3bcdf507c9a0ab6b7f98d6533d6cdfe04f72d2404247",
+ "2cd55b68ff9d9e1d8155c21e7df17066751a99ccc5147d0a828053f77b059879",
+ "160522a71a95f14e058a51bf443264700870541f074c182b1ca3d1250b6e0337",
+ "7dea966eeba2bd820d680f35dc7226e4a8faebc88da83b49da2b5e7dc897dfc7",
+ "998961a5fb82372fc03ddcbb6043284c80358ec0f2bf9cba7e3bbce8696dd16b",
+ "c32819e8182b5528b3ac82df464b286b944a13704dc17befb80ed61d24a2b240",
+ "7fd2bbf0fd1e547b1437db807e113c9e514048342558691bb18c8e2fb7318a5d",
+ "d730f570722b73446476e28fe2c85f1fb908cf4898025d519f075bc16c0db7c5",
+ "80f8d5eab655c7fb355eff1463026b61b092594a971e573cbc71f2c6fd237821",
+ "71dd7c5a41d980ac1830093a2ba8be6de21cfe8a07463deb4428bcf1382326c0",
+ "2edf5e1cc806779da45ac34d5c0db4f2382f5e08be2c31fcdfd93be10f4f6c08",
+ "42938f9080674ac1ce67acacb5727ac6bfc7995b29f02e324e59f291a5bb4751",
+ "f66dd6200ca2c9c8af94a1d6c5531e6d332d8a91c4e00c21fc1248cede72d88c",
+ "8fa61f98513bddd06b910456ee60635081b1627a0e01c67e8312f0e0fed42145",
+ "e9c514547cf5b5bc53634a11729b9eb6ddd217568a49772e18e020d0d5ddd222",
+ "0d938c753a044fb4bd49d3b2002e951849e32ad1f37a7058750c627f4a94b03d",
+ "048a524eee29bc8de8ea1f76255b402b63f9b4ff050aec4af6c6ff1ed7cdd62d",
+ "5f9e38e5deb73068b2a49a460adc7b98f64e72729ffe7d63662e25ea421d7399",
+ "5c22633e12ba5892f6c2b1dfdbe1f85bf5c22208bf1399bcefb5f9357db7c84b",
+ "ca879fbddc966d7adad62121490505b5b95cbd545753fb900ddf50a6a6588828",
+ "f8f95252b9c7aaf321303f19c496bfa2af90104416a10771ca02d7daf9204daa",
+ "3c0d9c8eccc7fa80234995db0869947e7412e5443851495e89259f92bee8e5b3",
+ "3c51e08f3dcfdcfb2a3b989ba51c16e820ca2f3bbd34c913d8ffd75d36447bb2",
+ "539e2f424297ac39ac5090463289f332a4555c6243315c3e2c241c5a2534c08c",
+ "6397ee82dffd9b8b2dca1d64ca3cc29cbd07e67bf095ab47273a7a70b59cbe93",
+ "6cd018be9d0c3f8a224c405de0a58cc5bc2cac7b6e6f514089b036eee6b0904b",
+ "1979e6077b6132d9fd669788f5031d3f88107a857e35117054f9196a197e97ce",
+ "89507701e23077c93b98401ad75a8a30a77317952fca30ec28eef2179699f93d"
+ ],
+ "bsig": {
+ "s": [
+ [
+ "094164e2b93cb85abd82ed7afc408fad29b1ae74fe2383a08cc4a154686e2e08",
+ "b59d3a7a56337281c7de9edb43dc6cb3eb90afe399b23d65c9ca1c542e542b0c",
+ "2b976ab45e2e11e27816623ce4349206c2a4c8b7540f394c868685267eefa60a",
+ "e93f2fda2b1fd5835506acf9d7c77b0ca08e1cc89569df1ddf8a5688a630810e",
+ "b57b4a9cc83bcc39e7c4ea8069afc2c5f97cf41bf4894d7cd3ad9fee9dcb630c",
+ "ad06176bcb8d5cbed6a09a16451ea8ed80fd21e16aa136a720b76b8a6b03ff02",
+ "0f92ec55b78668ac41c5423de75eb4bfbc6c2c20abb74e7a82ed9d2d53510003",
+ "bdc18588724e2ecc6afcbfb3d8765f15b00a006306778c04d305dd99b90ac10e",
+ "92ac7473a379ba999316ba893d0ff320788ecc754632bc2bd565e0838ffc9a01",
+ "adab99709eb702deb1c350954857856de88d5b5d007919c225f6d70e1b89a906",
+ "7d40bb9918e657d8dd8741453877ce22b13a5442ebf963757bd48488f15b7b0e",
+ "0c96c3fa4118c6beaf72693c64308b9343472784d965e04f8b304ee3ebdb4405",
+ "d04d70208389b5200b771cae428b88be6fe91970e7ec0c2b9409f16a582c6a0b",
+ "9b9278f65bb6401b18e5fe1031f52c8b036bd5afd0e984cfd3cf4c650a994004",
+ "0331067730c9e58688286fa2533a4815eb5289f8860664d47b55f4507a7e5b05",
+ "40d8b2d3a98a02caf4109de34289f04ca6b316f7a682328f3cef057bab3b7705",
+ "e25fdbf9b361023602c9fe45c9d47615cd388b251d4cc13c667e2c8235e1bb01",
+ "06374aa1ff0e61f1121d47abeaf0ac767d1bb1a0de2afbe706c1014ec8d14600",
+ "1d727248b4db192c5da6699d3a32b09652922908900267fd2e1982e1f05a0208",
+ "7c6ade39bab5b1bcdc2f6cf03d455f8221f9d937fadcb87c4070262bde23ca0e",
+ "df3422c0787a2135b741d68f5b2dedf689f455f200378e57603ab0704c9ffc0c",
+ "6f0c0d13b284111698cf0bae7b0faa9ae341a25eacbd9adc502df4a12705c50b",
+ "bb3d708c72e496bb9913852765c676ae683a04479b93e117e9cc0cacb403720e",
+ "8e685a2a6a8cba27590a0c1dd0f1c08022ea3590acf423121a3f1c8abfcb7302",
+ "bc40bcff2cbceb474f0bbb400d74f17e76e9e665a7a0ad2aff650fb8ee41c605",
+ "d45ab263f78d60a67567e2bdd2085cb321f348b27f92c2e570e0396a25950908",
+ "32aa144fa375474f3ef8d95295f46f0c7cfa8058a4ff11bb0dfdec1c6ba9f608",
+ "73f1a08aa38b6417ecd09cf92b1547eb6a49a694f0f6ed57fa157b6eedca4609",
+ "9248eccd40954c5bebc6f2bbd5de96ae3e2efe842a251a8651231bac54bc1205",
+ "2a1962e4dbdc3ff1797e1ddf401d070a9b9dd2ea2ff3ae2e207d10c02c13e50c",
+ "a90ae0d17418051108f06a77fcbafa80fbbf563e334da38624525e1d203bd100",
+ "18ce2730e92157810ef2c092a51dce81cdcab79dc271a47749b8dc1fd6d84d0e",
+ "4e0e97ee23d09c70c2d2882858bc7a318ab516a1c9f3291824c5ed79baff2c05",
+ "d9d93f18348c5fbffd2337c3ae6973eacbf67aeeda71ea745fd635da419fac03",
+ "296b62aa4b3f4c554b7452db9ca1887250bc8c507d042db2b9b7c81d10184108",
+ "63d23ad52e74b68b13a1d0b17fec9ac5043cf22452b98f2c05328707d3b51508",
+ "645918528f4712d553db4e03325e11ac0a3b3794e74884ebc3e2d63a8c2a2a0a",
+ "f1dab421831e8336538d8441a5145eac4830ac0a7fe5cfab808e6fc23c7c4300",
+ "a33fd2bcbfe92dc9c47d173af9be7f1e50da97ce5d88c36724e4dff79f6d9b05",
+ "9b47fbf1fa33e259b339f1f74c9831473b86e7767f4cef199c7b53d260f00001",
+ "fe22ce255569940f6b460c6aa9eccb1afc191f786f00256aca97d7b465ad8e0c",
+ "e21b01b36099a8326edd3d509bcc49b37c69fdd04d8716df564c42d01f589804",
+ "824fab7c5a13b31e41ec62321f42ace7e7c301abd04a29c4692eb526a9f5bc0c",
+ "fc47845963028fe9bcac2619255251235ff7be950066e41933d86114c4799c0f",
+ "94394c9b48e34a39908cd2c676fe6ee42d21a09b2c148506e9cf72bba00aa702",
+ "0850b7d693ef97ce9074adc966a258c87841aa76468e10cc2474f58690b08601",
+ "a7d7620a3f6c826422ebd339b47e0566a4ef9d74c91f8733e68da260ff052700",
+ "dd03dd70ff28bb938ac6c420eb8942d31e0222a2c4c5e07339503f33baf16507",
+ "4813beabf4ae379d0d093c16438e5c9e08c06d2909221278c398698a2a45710c",
+ "1ac2954ffb0064229d43b1686dff63189c9def06fbc84d52c6dd1b45785bee01",
+ "c43ba993220032fb7530872ebb42cd1b8d1b56cc98784ac01b242a0859ad5f05",
+ "56be67160b9b6a676b3f5b36040947b4c8d97cb2125ee429a785f1798052780f",
+ "a7acb882cfff3842d52df35bd957f462c1b493cb64852f8d3479b1d930e6cb04",
+ "2dacc0b2112af2fa9be170d164bef97f69cf0c7bb428f3677f8431aeb300b10d",
+ "0b89d7dc92f2ee2f1f60a7e93cd7ac88b6f71b4c83f2df1c4f1df217795af501",
+ "e274a6b4ba98db41785a1268a5c26093cd7799e2fc97fca3832465aa0ca8a80b",
+ "241240171cc310cbe3835eb71edab3ca918b62a883f27be003be1984c9fced06",
+ "8d3cfa28d895556eb48d6f6f21fea79f6818297df8a030202ddf87c4b38d5202",
+ "270555a24fa46c4b124af481e0a791336efc5d87d16191f7a73686dc330cd00a",
+ "72431571eaa9a6866d114c2abf5a9599dbd8c6b8c6ab41ad155329b20bd9d302",
+ "b896f7ef3859598c1f7312149d49c85fc32734aec69311a1f7241713c902ca0a",
+ "578f78aaecc9f9783095216d583312116983c97da812058ba197a73b7b00a403",
+ "823f17e7d12a20d8d2af791623984d8a5d42e8b81cc33d2075706f188b96240e",
+ "0c70da64295b99aba8a1ca9a692f908224766dd4ab85b21c2e4a3561d92da70b"
+ ],
+ [
+ "b3fcb96c7067379745497875654d4135f2a367f9760e1101e1b7c8e45bd78801",
+ "e7166d6480bcd5628467993ee7c11b651a3e97afffcb302fe91d6cf55036bc0a",
+ "cc531d9d6c3dc509c2878ed7378a82ecb182da633ab8d7e08241da069afb8b06",
+ "e63c93d5e36e33e4ba022fe2352061f55f4e5115d048e4c36ab56d9d558daf00",
+ "15a8ec96436dd72e4ee610e0806adbb382a7ef9d3dd9774ed070abeec25d460e",
+ "304ebc4b2d097bfa0f6982abe3f7490f0374a278f1464505e0233a1195e7af01",
+ "ae7e06f814cb7266ca60722c53e2d3eb66a5a1f8cff91bad0da26b5716ab7106",
+ "fceb4b4a1a613014804c5284e7c8d785512730c7f109d4c225f9fa00cff4d10c",
+ "f7f120e75d1c37c5e5b4e9117f30fb30c3cbcf86a33225fb4f63f2a207168204",
+ "5dfb733111c50730556e62bd49a471b71c9421b312e8805358ba53af071b6103",
+ "99e917fa1b4687093313e330b453dd98b3f7866bcbe737f5e11f4bd1c0043b09",
+ "75b84a92c4cfef856688345ebf4ba84b95aaad4ef573774e448d7ea93f6ba109",
+ "37ec41a174bc0965d667918f608b6358d424628859d0933cccdf51ec7d55f906",
+ "438342b45be0e9796bb57225e7bb184602fb97dfb48260bcfa427f787a759c0f",
+ "5a2a8929697f47c854bebca692175efb36276a5f9fb92437836e65d87bb3b70a",
+ "09663579a35c801c1aa654eb28517013b5e877a003be057b875b35df2fe22709",
+ "0c58358156115c37d72c290912ee288600cdabb492312227fe659dc238df6e03",
+ "def5668fd22573e63b245586cd10bc66d68942e01bc9dfb9d152a1a7898ce002",
+ "daef4fbe574991fe37e0b8b69ff269e098b7b1640a47b370fb38f410eb0d5703",
+ "536bbe4bcc900df2a526d80a413b9c9ed3ab75124ff7198d3a44e2ca0cce410f",
+ "edf5f2ef7b33f93b43a26f4d30751f2bfb4b13ca42a3105196bb17e5c2653e0c",
+ "00adac72a44baa60b2d2411fb239d0d0dfd68251d362bb04ab0a3cc169cf6104",
+ "9bdc73cf11aee0b90f7b26cbcdab987873ab69542cdef0bb8c125da7034e320d",
+ "c9bc4f84a9827cd927165c3455ae9fd8f5325e606b58e3cb6db67bc46b434c0b",
+ "f78b1ef5530c0996205b419dbde1a8b1a9ed21abf38e3c5a16d34d6091a1e406",
+ "5c79fe8d78b95184e779434993a52c23e6e9c6db90e9db5b90e97a29def79302",
+ "922934dd5413a93d183cd83ad88ab3d439ef39753c84588a8269a74f0d197208",
+ "0fbb35971f624d31041842ed430c70d09171c20f0f00e0d0c4e8ef8149b9af03",
+ "8846db4d5b2d8a586e5f1b34803e52cab395311ac5bac25f2ce3cb2203d80703",
+ "54da89b48c24c0da18b4db9f87d210388ec2655f48fcc42bcb78c367bef2aa0c",
+ "543f7e562adc23a6b22445ca1389156587c0af0fa16aeaa1aa5e6508228e9c04",
+ "620aafcb6408e66ad86f0258cdd04a376ca1f41a623e7cf36a800d5da9158302",
+ "9bb46e73b038d27c370d948571c442f65302d09c1562ba3ebde7e7e803f32602",
+ "799726043b23a4eca27e62986009cc9e4419fe95cece9504cabdf6d92f01f105",
+ "beee7e9ece3fb49b495922bfc23337e53baafebcad53a250b66d168a87b3a406",
+ "4440985fe7becf778fadbbf8eb4d01c024039a4cdfd5f27edab16f4702b4c205",
+ "90dce3157b8c727316ba8c25de3cab3112c385f8ca0895e5c775e5f34c17520c",
+ "c8cf80d28be593f186ad7f6f4666f702df2566c16a5d5e1aad21694774f41f00",
+ "03fbcb4502fcb5e1deff0fa2a458f6ff88bbc91cfa2e21cb42b0077da5d43007",
+ "d68610e05172ac51036ab6a52140e14e1707dc325fe8efcc39c74883780e9404",
+ "bcb78fdf38b2763ddd7458db79ffaf28305aef1534d7ec4112316a8acfbe3504",
+ "277ef2e7dd36cc72869f1d53689d904cabd23085165d2933ea15fa491da5ed01",
+ "f00468c47ba2185b241405d89952922a74c02ceb8617543b70f353a62f017b02",
+ "3dc53beb7b858019493aa1d2bdcf2d6eb7683d453d62dfd391b189c296bee006",
+ "d7c85bd22dd02bf6448cb7af0460cc319913f9fbc357875bf5396f9e33fab505",
+ "0aeb928f76e42175bb300f1e254f981d8bf22079c66b03169e4d04879495e80a",
+ "395187afeb4d7e73edf5d4320ce49cc4774560697ed4c58e8b4214fd03d3e808",
+ "d666a22ab6104dbafa95c6c0db8af94ea49f24fbd5c36d00fc009c451ce09708",
+ "3f84b72f2e5b9b1dec327c4f14d43da764ad36c5e8e8389453755fb77157dd03",
+ "f9e97ed405828144904fb6a2f578e1d39f7f30621d9a483291ce50391c53690a",
+ "76b217cdc7d7ac7a1ca3add04d303a920713db149ca022ad49a83e72ea7fba0c",
+ "6c29a44633d1c056ddbffcfd43cd1ad4b810cddf796ba994ddda1d63cebf8705",
+ "e743ff022e0ecd99912346a9c2b892e5e4ebc3e52b98e60262d96f3367717703",
+ "49b69d7560d297a73658c22441c5ce1b294065ff4f3da35227f7684e3a445001",
+ "910975c311f461abd40ee2b56ab71f105770a4bc1a581b1221495023353aaa01",
+ "d4bd115f5a8d9ba37b3b41dab0cb181f07e60624b755e79e1abfc7da826a6d08",
+ "cab5edf981c43ec78bdcbf74313af449bbfe3b4806a205de6adbd2249015aa0b",
+ "0efe4e92b667f66f2b8899084941fce413edc2e4a5ab44c066236c6b2a527707",
+ "fe52311427d13f55a87cb3a361b196e0faecc106ca6bf40e968b057f64cde106",
+ "b2b446d99da75974b26266573f1a641b4c7b477c61b370116d63267471132806",
+ "f9bdb81ab2a81f03165e2a5b2d32f1f2b2cbe86d95e3c6f8bf8ac6a2296ccd0c",
+ "2e6412ed8f74082f073b6bc2dd63e59ead35112c5c9362610a6561531b931b09",
+ "f21e5bef71a01433b29402e008ecebaadda2bd8bed28d0cdf910ffd573434c0a",
+ "c5781c5eee63cd99e97b5b319cd3925607dbb371a360e2d5e070f161a063f109"
+ ]
+ ],
+ "ee": "46793313125f55f30bbabb52e61db65e5ae507cef64f08f501417edef3da5e02"
+ }
+ },
+ {
+ "Ci": [
+ "524227a74d9da9b38d2ddeb8649eb9d46bafca1453fb6ab89112544597bf2dd8",
+ "55511f88a12a3000d7410572e9705c11b7fcfbeb9b1e2317d274526555e81048",
+ "40abf75d25f845b7262d8b97d359ec39019b7e79750d3924564a757f61d0739e",
+ "dc3b52509ccd4822e0a009ff01e33619090961ffee25f22fb6132f3273d4a2be",
+ "46de6f8ef4bbb8105fd7ca8329d986901e77ddc8364f56a841c66eccd7c29d34",
+ "893077d396aa37f39e5157e56029141ed64a21ef75be7509c4787df1ad05e310",
+ "6117d83208dffda5818ad4b9214f8d98525b01b283ac7b1e10342f546147dfc1",
+ "e7f3cf1e0108070c30f760d0fa2a9681bf480b329ec479a7b70c6d90e6dd8d3b",
+ "0292dade0859548bf22cf4c808691e59acb8948f14a5eafcb81e15a3f0af341b",
+ "165712b33dcfa798c4fdc96d576a144d6186911e1efa3b39cea48adb7e325479",
+ "e0a1d07e78c8eef802096af323acfe1f0dc5f09eddb6fdc484dd73b09ef8be8e",
+ "302758bdcddd89c671361d9b56857efc8486e4d1639b0b0a70546f8323db524b",
+ "6b95350a947a9f2b929badd89da73ccb4c46e36eb9f994030a6eb36a6615f047",
+ "1ba343eb5b8eb80b7e07523cb99431029b36db074d8e8643c417b80ef6e2cee0",
+ "94a39a04be94c9d08d4632548574d71e758440fb5bc26a628d19aec66d263c72",
+ "72a551228be488c132df8c56b961e203ea465354fff1931763bca9609b9392e8",
+ "b3290e60f8df264746081f9400ed506e5ac9b15da69625fd774d3f3450ba9b8e",
+ "bec6b6cca9822ed85d7fc1cf6dbdada1cb34970cf87cc80b4d0ce3e9b973a125",
+ "d761f03d35508d209a5c37b17c8b4d6d5064d4c8df72763285cadda3b56e6c6e",
+ "8b5b75b719c41974164fd7af9e2478cf0f8a8ee7f663b3e60570779ca891a939",
+ "f7f8cd30d7115a1da50b6099d1817986ec9e16459563c26e6714b3bb4d169462",
+ "c6b8469680ac8a833d1f5a7a3fb4a3320ea262c3dd7d0ba79a7bbde853287e50",
+ "5e1979a376b2a2d925f9dca00fdf29d3b76484e5928433e716b186badab0a9ef",
+ "be1562bd221f7275e46520c3a70f931a45cf22bcf455d9a45f65d0b15f44631c",
+ "8e79f4c6ff9365027db2d4b322a62dd9eb3fcda81e30721970730ae5732216f2",
+ "5d311cf39877fcf0a6e258f786b5a617b9cc2b00eec15ba2d3ca12bc6a59189a",
+ "c688d1a7e14cafb9060b516438ff68ff6f6084664ca56787d2be64104a20ae61",
+ "459c8df34f473beb2f99efd5a16c3cb008aaa79902e9d7a48e91365d8e411f76",
+ "98bad64eb84c061d98954d4b98bc1c6f2de13c9dcd0a1354fb25081bce08fae4",
+ "030628f061af5d731bb3b4bc9ff12e311daebb60005413b27dca8eec97eddda8",
+ "8b55fa193767f679fc012e6404f97ab5b7efc52dff2c0e523b8a8ab0f4ca0dee",
+ "fbadb9fb79bd57d49bf07d1f9f9d12b09931860df0444cadbfcce259f6e505ea",
+ "baa4b7625ab3ae7d72c60b06ec95c07981b7552483d385a17d6179f6106e86c8",
+ "609cbf2d948e16fb41e195b0a93f250b296ddcfe20a5bd50a46c5d7f5a0f284b",
+ "df8dae11f26821fdaaaeaa3a11fdce58240685ab2709caed3feab3cd5b38d150",
+ "d2cf728a23339fdf6f5f501d08bac9ed7538a98610c933cada9b5307814fc855",
+ "8fa44b2042edd8ea2777147a026260569cb893a9b302e4756ea008abdae8cc65",
+ "a6962e9845d8ceba5e9f0e723d950f839c9975f10b60725d37361bd2ce557fa6",
+ "8a6e9a6069d9727a98fa47fda31deabecc5c6f48fa57884283166ab5d7b49185",
+ "98a01bed5c3d178b07494d7ad6fd7fc2717f0703ea0ff1965e2c77c63f8e999d",
+ "c40035a91123df458822509becbcc22386349dea118d70efd59843a4eb3dc9be",
+ "c704626d93b8286d709c46a9171f02255ad28581d1a2e385f177e9ccf01b9f73",
+ "2b50ef827e812e09c819dc082beb168dfe07739f976023d208cbcc54b76e9f0c",
+ "a7b7ec2ddf15fe23195241837d5bc0c690261b46f05b7020bbae7f59ef42c046",
+ "a7aa498a245b11d04f6fea6c0bf565de3cc419d74f00ae2dc2c6311bab6c8f04",
+ "100ab1f61412d7f8a5a615136ad7e8a4785d7717f34b32af22f0d677b019f6ce",
+ "64bd0c94a6c6ef2f704df82d21c34f63ae6d6ff32a27e2205dfb077d2d8f5bbb",
+ "6f2135f2e771f31dbb4e98be4d9fa86a204acc1bf7f8ad76c800f6ac2009916d",
+ "e7d3fe6a7201b2c0c260c9a24bc45516f2244f95e836f30e8ac041d46f0ed08c",
+ "99de3d62779195c599a3ad158ff9a6efecacdb7924a2146c472f551b9160aebb",
+ "256dce0236fc7d46be77cf93c9e80ab021bea750e0d9b4b4cda04ed408909ee4",
+ "49da45a63e06ffaf7e311b125f2fef63c2ba9b6d2f3bdb5b0d04c117908308cd",
+ "37665cc6775ef4d49ce555d3286671bb1d150b16276e38233e425c3230a31a3b",
+ "f7d5946e6e13327519880ab9ceca4259f966826dcdeb30eed9a993971d9961da",
+ "3e98e0c60f7c3f415707ecabe41920b1a20723a7a99ce84a255ecf97d5b004ae",
+ "0062f108a76c5df2cb9b4b5a548a8b82468bc1ddc3b62f0f98f60d8b5e933fda",
+ "2f11ceae7340c8ca55ce5a0d0dcce3ed1a558456698350f818a035f4b9d43e7a",
+ "fd2dad4678a6029e68d99f1ec4bb68aba853cf408a84bac2d379b47a9a9fec4c",
+ "3ed22207d08edc7feae97451b4257a876186bdb1095ff6a404b3dca3bf5170d0",
+ "127b93b23a2b88651a6a546677536a6ff3cdddf5247c98290cdc09d5600c6693",
+ "bd736f080cda67770a209827ebfecdf9c86ff131cd3a17b39188348db4ca043f",
+ "aba0bdf151be4bee2b0559ed43ea7d5c4bd5ca6287e5b82f896a2a561c915b7a",
+ "af0ab0c6a238fafa262aff20c9aaa6b47e25e7c811e12ac2e9ad879214804a9c",
+ "7bbb5bcde50c7ed7fb094ae029f869b3a441e0750473f3faa1d614456adc475a"
+ ],
+ "bsig": {
+ "s": [
+ [
+ "d1d07b998118930c481260cffe5d098a81048b0853bc957f61c376a392bc740d",
+ "4bc083e9bc4b62ee04daee640c5339121174af2f2c3c78d716d97497c0518d0a",
+ "7468d5ab25b59fab995c815f65550d52567ab22ce7215eadb2a5bf07ff899100",
+ "7d381ad3b18f9af8307e97282b681c37a74cc1083bc079e49b79d08e8d15d602",
+ "b65d2f153c27e54407d80a4dbda35acc29220996375f7819919ebd9ec6999a0a",
+ "a1ee2b5a2f8cd95c1ed40ed310ec102b872a2f0ae40a383036398a467a845003",
+ "2b8dc50fb3aead263d9004dee7f10e12aea154ab97f04f4a016d39b97742d00c",
+ "837350002903c83f056c5d64316c7aafd83fe355a64802fbef14516b197dfa00",
+ "1695103d494c48359e58bbcd358b0eb20d1ef42e3184594fcf51443ad3cfb404",
+ "7ced01f064c638698c690b9179b61aa68d369e35d5ac8bc65dae702cbc74890d",
+ "cbedb029dd98055213866576a3d5e0617d063dc2470e8d8e5b83574b5687b209",
+ "90a81ccea7787be7d38ba9f21b5ba3443049ddd82328d4e8fbe6b2817bf9380b",
+ "46f9e643f6a593081f7f1d5d97c69b0fe58c9cc6074c373f8a71c90a12604b08",
+ "84716acb458e7dd490b5f0057d917e586c69cafe9fbe47f69bd64c7e61b5b80a",
+ "6825520c5327772c2ea8d7450c3d764c4d08cdaa1562a1d990eb0878134d3501",
+ "2482c2f5c4ec38fe5161e00eaf669187f2ec98584a6735a9341cf66b4827dc0b",
+ "f512a1d4ead70ad08445b1a9af017ac6b375f9c5f0103d0755e1c13046d2c102",
+ "a041f2c79577ed00c330e1a7c8e3a6d474dbf6d9ef9c4e3217cc3fe611967a0b",
+ "c5b96d0815ad46b04d043dd39ade61805d12aff54f4f60ca89f2ad77433c3908",
+ "22c633e75cba3a385958fe1015c502315ce63e440baab882dbeb4ba118994103",
+ "676b9e83e6f6d2d53db075f662469e733778dd8082c36cb246b03f9701c58001",
+ "5d55bf2ca24a02bd3888eb95597f654d04e84fcd79aefa00125091db32898f05",
+ "45a6b7695838c01d0be144395d40af9d8e69b0ce2f1326bcc3d57a244be24000",
+ "31cbf5ff387e2439cc3b492466343d35a6a7ae244c0abdcada6cd0e37fb8170a",
+ "d2c7582aafefc50fe74f4f05bd1b1aa0a0391c18b2e54f7b8078517ce5072305",
+ "e4a509c5ceb5165a148a78c67fc1de9d97d4a7c0a9e6d152b47e2320721c200c",
+ "830a015279a3d84acd32f7070af60f82db8fe6f035973f4250dba240999a5005",
+ "f96f27a773fc9e7b68558f5f98699aeee07c645097111ec1aaabdefea7746904",
+ "82af54c967848b2cd65ebd7f9bd4657292864f46e4620210ae41c1642357eb09",
+ "85f8b1dd6e9d87fcca833964873224c9128e899acd85f74ee625809e9f045907",
+ "e717b4e15183bf2fc55a1cae983526e833255b1a38a532c3319ae67428e6b604",
+ "f1aa90a29afe47935795d9b629abe0654a9375268d11f91a7f0e13a74c9a380a",
+ "e66988a69672f086bb939df9590f0e495364a72c8cb298e98bf04a0709722f00",
+ "449422874e793b6029a95e26724609dc06fe9086f6ae690b34dc1b29427b4303",
+ "50a43678dfb0ddde1db147da17c2e6b3fd44dbe186dabc204f47ab241c2f560d",
+ "4c103099635d550200103c05232988f48838d4b56b401242852c4be6ef991203",
+ "fa32db1bac264e7663b866f21314c05d3eb006a4fc2e00fabf30b4197b5edc06",
+ "b8777c5560b10549622a28ef19132a9e3960f54f3c449c3039985b24be69e80f",
+ "17df718ef65afb8fbd4b1d6eb3fa982590962d8863720a6f7080f6d73251360a",
+ "62b8674d157eb1a2d313631aa446635ec453090c75cb7470cbe60044f329130b",
+ "829cf76313e13a5a58dd5378ba3d1bd7c7a6b931e63f233204c7caf9d4fb8d0b",
+ "036910d5fd088dff93e7f6a43888096b38b5e0d39bb94b2c570e94258ba8f601",
+ "43f516aef8442b816bcf88ce21eb62bf58874644dfed54d13ecaec398846950a",
+ "01b76ab69ed1324cdda07cf14831bf6554f1ad9952defc2186c66da67a626504",
+ "9e01bb01a69c67b81bc0977ce81b13cd3058ca82bc212d17514ac19cc40c9703",
+ "cd8a3c8253d0163d494a9bce334b798aaeddece4c546509ed61953f8ed357c0e",
+ "861c7bd6ec632e7831b57a9d796b0513a715ab7348db38f3dcfc8474a82b8107",
+ "6e69287de84cc3bb1e8326f467b961f0dddcd70a9d104cfad472d3661bada40b",
+ "1aafdc9b6fcaf21ba61e8e5367aba0374fb927c375f90b87350bb0e0dbd8e105",
+ "bbc62c5fc32f99f4bed49baa2f3db5a4705c4d2feaf3cd244d4d28d6f6a1400b",
+ "5384e8f695e1449db4ef23f7b77618ed7a366eef3ae38d0b36aa19541a5f1d0b",
+ "26c006bdaca798027322f8294933ad9d877fb79cce3cd756b94ed2cd311cb40d",
+ "b8cd46406725c9e7fd92893cdba0d1001796a286d9b18707f4ee433f90665607",
+ "f60f5caadfa08817e1ab0f440b26dfb5163469c871bd7e0bfda5597597a2d402",
+ "65d270c8303c48231c97b1807ddd444105aa6e195df4c208a0d07ab7721e4909",
+ "6e2b8edcaab76fc8729fd3bab94895518e3549dfcfe5238ef3f92e912e23bc0f",
+ "032af4ad5abedd0c10613e50c26c9522902074202bd2c93ca60a7fdf4c5dff06",
+ "e27594188bd5c6fed2874df4acf8b6d2f412959c2f274ffba18d040909985f09",
+ "038124541070db29455f8d58c33323798cb03cfa2dc0298a856e64d4d7eb190f",
+ "9c47bbbf7c075e8cc3ac5f980cad86287d1940dd8dc0ace92241ddb49d454605",
+ "cd9229fe4080ca8e4b34bf3c35c3b95436d64f61d321a30f402f2695b6452d04",
+ "f4aa87a45504333b39c487f05fab5ac541893bcccaccc27313293c20da1df10a",
+ "1fe1edcb7962bd4c9b211472947196e01ee99d2d3f518710241dfc5f06a3ea0f",
+ "53b36bc3696808aeba745a58c8c8b27f5bfeda5676a95596cfc24e7aebec110f"
+ ],
+ [
+ "851e464baf54513501af9262618d44cb81eff6d451d2ef90868a0b76fee1ae0f",
+ "2392eca80014d625cf976c763f86186cf984943a0324bb38b8d23d97a2dae700",
+ "2590aa56836825bee4770d74ded76b9187ed26ce8795d96e4c211056b7d8380f",
+ "3e2ee2b7f4b37342968ebb1706e85233ad8fa15a39cda0417f86dacd35829a00",
+ "53eec03966ac62876503efabf4407da1fd5eb13b542cbfa9ee755725cfe9c30f",
+ "1235645468cbe11d875c5ebf96363b25abefa920867af543bcbc4148d5f1e50a",
+ "c520452ced483e258ffb7ace14531c5c7de112f4827c594cbdaa373e52f54506",
+ "833713e2d2fab34ecb5228c7c1c1cf402be69654c5ace0376d34ef7339383f0a",
+ "2243fba2d420eb964593f871e18f5764824976f2f4c28f8624d9921237d45106",
+ "8ed944427dd822c8789064dbcdadca5c14b766706c7c3ac034875d44e94bf604",
+ "7e1e989bc3506445c88c08b7805bf5a9ff3bf1636b93e98f039cb762d667ae02",
+ "eca0fa2e30835f037dc359ce12edaafd46f064e0aa513d1e3239e0b63af87909",
+ "0a56c3d72748837b84bbd59a55a15c15b4e44d110532a020b9da108bfcf9530c",
+ "3e1a49bf0c90143ecdd1324ad2beb6a4e1d033f46ceb918f4259d5b17bad3304",
+ "e40fa0c77edf3afee63799c94b470a4dc3c89d18a43dc8dcd31aac6d33a23801",
+ "a3548cd48aae4fcc39d0c2775ea1a26290f6d7c6d49506ad8772b1a356a8170b",
+ "d0be3d0d451aa618d6312d6be5a86a2a398ad051a7e5660a454a4c45da41630f",
+ "c9e3af071abd46b85a07749d1ee6287a4afb898f7c49655763e34a9ab47b9d0d",
+ "750e5c0b8526960a576d55862c3e75f97716f211903d808ec860f41361129c0f",
+ "c8cf440475e64a92338a37ccdef482ad7d9ad75a0a94bfb9007db701548dad00",
+ "97312ecdb25219b47f5f44a707846ff2c180198747102dad0e37f3bdda0cba06",
+ "0aa8fafcabd48672879641167142c0024458aa118c2b6189ff95812c56515c06",
+ "adf80b43718fe494419e883abe0f33e36d4e18f6cdfeb8edb121b81d0db1770f",
+ "2705face099e6bbdae0cd485d486a8c2018d49bfcd956704f669edb352807c08",
+ "6c9edd1d0fac0eefed63077d6022aff763c8d34e51d69b26016740638b477606",
+ "f68a07a3089aca2204035f772b06945823375fff78fd21fa554b16688ecb210b",
+ "7291f1617493c6913bae6c90eda5935cc53a50c933e7b3b453b68f3871885b07",
+ "3c7a25563a66104e4dc28007990565ec9e2a3382f9924c6a63953af4929d4d0c",
+ "d3282c0bb4c5cb3f2aa4c1d88751f15a5cb7ae8687034b6ab38d46b76b0bcf09",
+ "67ba42db29eb1d7c458e923e6c6474e79ef469e92805b30a3a6e6e9101c53e0c",
+ "e747701d014c338171df6ba79f8aa6234a49e78a8039e2ea9b671feddffa7d03",
+ "cba531d055be1808d6fc2a6e229087b77a3be35e40ab4a6f378c216ea05d1900",
+ "cd1aa21bf3bf0afe40e95c406000b49a429bdeeb563f32b87bedc1adc6e5e501",
+ "c051a69901925053f064ae1e8054e34500b97eff9f974292eafbbd85715c2500",
+ "e45a20f16556e09b7bb530764582a59ec40a4af28d95e6fdfea3f10ce35e2b04",
+ "16e209c92a5045b8d7af4762cebf2d6d97b8960d9e9565e0549a2488a9e8b50c",
+ "985f5395774fb807d49a0f84e0983fec0e32fb5342a3704c5d198dddc945d806",
+ "c3262dc93f96a1b1a09c2bd461f324c068aae59d5473deba8ba6036ad96c670f",
+ "42667ada96da901567e544157b12f56159bb7c3f83b90abd1b9e7eb948f2e702",
+ "c9eca93744830f0dff8b7bc73a1458b0f8d40e679afd1e22b09c93f2c9729602",
+ "c6c81026f8f02991c094a38ed0e6062508974a8bc69e8d6e6741a45eabc7db06",
+ "65f8a2d879de17fdaf7ad1b210f337b90aec1c1c3676f244aa3852a77c05db0c",
+ "05b9936bc10f0a327602aa2109bdd115f8a3e1081c1b0c56649638207d2dd908",
+ "6c6ab885512fec9650ba288df5133c9dc87cb7fe81919fafca783f1252c29303",
+ "32559bbafcdc830223837a7b78a7ce137e9f4a4e3e7d1195a494fa6d0ca9a106",
+ "59c0df4ee0c9db91dba17acad08e5f6ccae71012e2c6e4b37894520fffe9620d",
+ "66a72a1e4f04e5fa2abe93ef7f4b2e9e837c9e0f9d962b5c80f5e83408746103",
+ "58576c23403b2d742c587e3827d17c6f41b31f9da4030207aaa2f4c32059f401",
+ "7bfa4c324be2ac00a0aeabc7756ff2e16bdf39ed6b0c313f01b1acce8bc2b408",
+ "a0cb3eaf9af9b68dd17f258c0bf0c536b19040d6bebc0a9dac7fbc59398b5208",
+ "91a741c4183a851c1cbcf3f78f30924d85ea2d907a6ac8db9a1455e4c672800a",
+ "88629853fefb86800fec6631d77edf591d56199d5980bf3c86d9254773e02508",
+ "be6a488b032686134049415bb194ad11ab8e4b1e6304274091beccc795e7b604",
+ "649f106b395bef6f30a1dca330561709cdf7295557410d040543c279e169a908",
+ "dc3d94c4809f0a7dd92ad8cefb014f5d47681b21d6b955fa894eff5b57cc5d0d",
+ "1b08c01c4e892af363e6560a02212edc1fa645cdbce380b3e010d3c7f473960e",
+ "c86e8ab8d2c235f594cbb2c2fa62f27b4ea4ab9796b8534688fd9819ea529c02",
+ "3ab370c6e482633801e3d588f3a4433cef5aaeb633547e922dbb35410af8a803",
+ "156b64daf9633636063a37c4adc43e492023e09137099a715a690a480119f60d",
+ "9c2f1a110a6590ff1b7774e0fe3e4a7419226448634c129aa1213e560814cf0b",
+ "28d28fbd4029688f0d920a5f45c2a06bdea6c38f731a8bf741ba106bb8a3ac0e",
+ "35fd8c015995d55c0733a18e66705dc84dab552aff7d2ae654b903f6fc9f1305",
+ "da5a5bafbfbb22df241e211416fa902eaec589c7a22d6bf160a884e5534c5e0a",
+ "685c312527c037f3196d4122f4d42c5a683d0e555af2a271d8d68faea80dfa04"
+ ]
+ ],
+ "ee": "bc27bdbca4d4b3c14ae0745ea453174a6e6e3b3014dbd23ac3c8a413b5e29d08"
+ }
+ }
+ ],
+ "MGs": [
+ {
+ "ss": [
+ [
+ "02cdd7aaf9c48b45161eb2a105ea4b9f03d676fd34f69bac593777a74578ef0a",
+ "de13db1564e4172576fe454edeae5bc523fd6048d4bcb5a5716f6da4bf01fd05"
+ ],
+ [
+ "738c182aed9dc6a0fe13584c520f2bde1ac54e3c10b34985fc9711f4b8dae504",
+ "6b92929afb4c8ba3f7ca1c662b78abbfaf2febf4f4184c1f115d552ca0872c0a"
+ ],
+ [
+ "f06b61516dbbe09c3b086712e3eef9a0a0f5c890417b4fcf656f1b4cdddeba0f",
+ "5a63d3b2ce929f6c3032387b4d4bafef61fb5611f846da35d4e3e11fea261507"
+ ],
+ [
+ "0aa485e0828b38359cc30426796db346825191141a5b14211937b12924e0a507",
+ "4932707d8f1e76c3c80d6a3c64518dd3691c5d5252ba28df354dbb231da4f40a"
+ ],
+ [
+ "b801a29f5769a3ad6866582966b40896d943f3b62f711988613b1ab59bc0cc0d",
+ "6dd2ea50fd4afa1ffd250339cfc0484693d119961d7b4f17dc6116c62adc7d07"
+ ],
+ [
+ "6ed6834db8886b1620aff0dcd5ddac2f0492f45a7c0b448a668688ad6cb1d101",
+ "c451dd8cc0c762b544849ca04153936549ae3b39112dbe2ea96bf0b210ec8408"
+ ],
+ [
+ "10b47be58637baa446509b07c58ffa1fb66585fd4cb01df05291fd4d7d258804",
+ "83bb8ac4e0505a7cc5119ee45948309eae12ea41a8b83e0a612acc25338e4b00"
+ ]
+ ],
+ "cc": "8187156b0971c145f9d6214afd896b20a15edba78f7a087c23a30d894f688908"
+ }
+ ]
+ },
+ "ecdhInfo": [
+ {
+ "mask": "417e2c0f2fe4bfe629ad37a477089a98f91c98e59c54931dd53adc358a7f7808",
+ "amount": "64294cb7a3af9d977519ba3b9af993613e9c5f95c7ad3926fef81e603a1f3b03"
+ },
+ {
+ "mask": "67826fabd56cd9596b110c32aff6a41a4eefc4309a43264609f1b69f52331c01",
+ "amount": "0db6b4fb75210d33417fc5700eaf5ae67f4f5930b9e4148824deafaf82cb8301"
+ }
+ ],
+ "txnFee": "2230540000",
+ "pseudoOuts": []
+ }
+}
+
+
+
+send_coins.js?2:504 raw_tx and hash:
+send_coins.js?2:505
+{
+ "raw": "020001020007d8fa7ef1f42ba3d85587de0499da67e8af019a940fc0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b020002105f078f9332cf8fb2390be2f6be39abf40992dcd33daf0e3fbd9b02a23755b10002d4b94e2516134889b321d6f06792322debb0d311c761c6fc1fc4e699fbb87d312101c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df301e0adcda708417e2c0f2fe4bfe629ad37a477089a98f91c98e59c54931dd53adc358a7f780864294cb7a3af9d977519ba3b9af993613e9c5f95c7ad3926fef81e603a1f3b0367826fabd56cd9596b110c32aff6a41a4eefc4309a43264609f1b69f52331c010db6b4fb75210d33417fc5700eaf5ae67f4f5930b9e4148824deafaf82cb8301200554164113b97acd5bb67b91ee69296bf15fff6001d7ce77374afffa68fb6b6464fb83555790fac27de59e3fd2cb8d27384ee923ad99de0dfe2e498104b31e094164e2b93cb85abd82ed7afc408fad29b1ae74fe2383a08cc4a154686e2e08b59d3a7a56337281c7de9edb43dc6cb3eb90afe399b23d65c9ca1c542e542b0c2b976ab45e2e11e27816623ce4349206c2a4c8b7540f394c868685267eefa60ae93f2fda2b1fd5835506acf9d7c77b0ca08e1cc89569df1ddf8a5688a630810eb57b4a9cc83bcc39e7c4ea8069afc2c5f97cf41bf4894d7cd3ad9fee9dcb630cad06176bcb8d5cbed6a09a16451ea8ed80fd21e16aa136a720b76b8a6b03ff020f92ec55b78668ac41c5423de75eb4bfbc6c2c20abb74e7a82ed9d2d53510003bdc18588724e2ecc6afcbfb3d8765f15b00a006306778c04d305dd99b90ac10e92ac7473a379ba999316ba893d0ff320788ecc754632bc2bd565e0838ffc9a01adab99709eb702deb1c350954857856de88d5b5d007919c225f6d70e1b89a9067d40bb9918e657d8dd8741453877ce22b13a5442ebf963757bd48488f15b7b0e0c96c3fa4118c6beaf72693c64308b9343472784d965e04f8b304ee3ebdb4405d04d70208389b5200b771cae428b88be6fe91970e7ec0c2b9409f16a582c6a0b9b9278f65bb6401b18e5fe1031f52c8b036bd5afd0e984cfd3cf4c650a9940040331067730c9e58688286fa2533a4815eb5289f8860664d47b55f4507a7e5b0540d8b2d3a98a02caf4109de34289f04ca6b316f7a682328f3cef057bab3b7705e25fdbf9b361023602c9fe45c9d47615cd388b251d4cc13c667e2c8235e1bb0106374aa1ff0e61f1121d47abeaf0ac767d1bb1a0de2afbe706c1014ec8d146001d727248b4db192c5da6699d3a32b09652922908900267fd2e1982e1f05a02087c6ade39bab5b1bcdc2f6cf03d455f8221f9d937fadcb87c4070262bde23ca0edf3422c0787a2135b741d68f5b2dedf689f455f200378e57603ab0704c9ffc0c6f0c0d13b284111698cf0bae7b0faa9ae341a25eacbd9adc502df4a12705c50bbb3d708c72e496bb9913852765c676ae683a04479b93e117e9cc0cacb403720e8e685a2a6a8cba27590a0c1dd0f1c08022ea3590acf423121a3f1c8abfcb7302bc40bcff2cbceb474f0bbb400d74f17e76e9e665a7a0ad2aff650fb8ee41c605d45ab263f78d60a67567e2bdd2085cb321f348b27f92c2e570e0396a2595090832aa144fa375474f3ef8d95295f46f0c7cfa8058a4ff11bb0dfdec1c6ba9f60873f1a08aa38b6417ecd09cf92b1547eb6a49a694f0f6ed57fa157b6eedca46099248eccd40954c5bebc6f2bbd5de96ae3e2efe842a251a8651231bac54bc12052a1962e4dbdc3ff1797e1ddf401d070a9b9dd2ea2ff3ae2e207d10c02c13e50ca90ae0d17418051108f06a77fcbafa80fbbf563e334da38624525e1d203bd10018ce2730e92157810ef2c092a51dce81cdcab79dc271a47749b8dc1fd6d84d0e4e0e97ee23d09c70c2d2882858bc7a318ab516a1c9f3291824c5ed79baff2c05d9d93f18348c5fbffd2337c3ae6973eacbf67aeeda71ea745fd635da419fac03296b62aa4b3f4c554b7452db9ca1887250bc8c507d042db2b9b7c81d1018410863d23ad52e74b68b13a1d0b17fec9ac5043cf22452b98f2c05328707d3b51508645918528f4712d553db4e03325e11ac0a3b3794e74884ebc3e2d63a8c2a2a0af1dab421831e8336538d8441a5145eac4830ac0a7fe5cfab808e6fc23c7c4300a33fd2bcbfe92dc9c47d173af9be7f1e50da97ce5d88c36724e4dff79f6d9b059b47fbf1fa33e259b339f1f74c9831473b86e7767f4cef199c7b53d260f00001fe22ce255569940f6b460c6aa9eccb1afc191f786f00256aca97d7b465ad8e0ce21b01b36099a8326edd3d509bcc49b37c69fdd04d8716df564c42d01f589804824fab7c5a13b31e41ec62321f42ace7e7c301abd04a29c4692eb526a9f5bc0cfc47845963028fe9bcac2619255251235ff7be950066e41933d86114c4799c0f94394c9b48e34a39908cd2c676fe6ee42d21a09b2c148506e9cf72bba00aa7020850b7d693ef97ce9074adc966a258c87841aa76468e10cc2474f58690b08601a7d7620a3f6c826422ebd339b47e0566a4ef9d74c91f8733e68da260ff052700dd03dd70ff28bb938ac6c420eb8942d31e0222a2c4c5e07339503f33baf165074813beabf4ae379d0d093c16438e5c9e08c06d2909221278c398698a2a45710c1ac2954ffb0064229d43b1686dff63189c9def06fbc84d52c6dd1b45785bee01c43ba993220032fb7530872ebb42cd1b8d1b56cc98784ac01b242a0859ad5f0556be67160b9b6a676b3f5b36040947b4c8d97cb2125ee429a785f1798052780fa7acb882cfff3842d52df35bd957f462c1b493cb64852f8d3479b1d930e6cb042dacc0b2112af2fa9be170d164bef97f69cf0c7bb428f3677f8431aeb300b10d0b89d7dc92f2ee2f1f60a7e93cd7ac88b6f71b4c83f2df1c4f1df217795af501e274a6b4ba98db41785a1268a5c26093cd7799e2fc97fca3832465aa0ca8a80b241240171cc310cbe3835eb71edab3ca918b62a883f27be003be1984c9fced068d3cfa28d895556eb48d6f6f21fea79f6818297df8a030202ddf87c4b38d5202270555a24fa46c4b124af481e0a791336efc5d87d16191f7a73686dc330cd00a72431571eaa9a6866d114c2abf5a9599dbd8c6b8c6ab41ad155329b20bd9d302b896f7ef3859598c1f7312149d49c85fc32734aec69311a1f7241713c902ca0a578f78aaecc9f9783095216d583312116983c97da812058ba197a73b7b00a403823f17e7d12a20d8d2af791623984d8a5d42e8b81cc33d2075706f188b96240e0c70da64295b99aba8a1ca9a692f908224766dd4ab85b21c2e4a3561d92da70bb3fcb96c7067379745497875654d4135f2a367f9760e1101e1b7c8e45bd78801e7166d6480bcd5628467993ee7c11b651a3e97afffcb302fe91d6cf55036bc0acc531d9d6c3dc509c2878ed7378a82ecb182da633ab8d7e08241da069afb8b06e63c93d5e36e33e4ba022fe2352061f55f4e5115d048e4c36ab56d9d558daf0015a8ec96436dd72e4ee610e0806adbb382a7ef9d3dd9774ed070abeec25d460e304ebc4b2d097bfa0f6982abe3f7490f0374a278f1464505e0233a1195e7af01ae7e06f814cb7266ca60722c53e2d3eb66a5a1f8cff91bad0da26b5716ab7106fceb4b4a1a613014804c5284e7c8d785512730c7f109d4c225f9fa00cff4d10cf7f120e75d1c37c5e5b4e9117f30fb30c3cbcf86a33225fb4f63f2a2071682045dfb733111c50730556e62bd49a471b71c9421b312e8805358ba53af071b610399e917fa1b4687093313e330b453dd98b3f7866bcbe737f5e11f4bd1c0043b0975b84a92c4cfef856688345ebf4ba84b95aaad4ef573774e448d7ea93f6ba10937ec41a174bc0965d667918f608b6358d424628859d0933cccdf51ec7d55f906438342b45be0e9796bb57225e7bb184602fb97dfb48260bcfa427f787a759c0f5a2a8929697f47c854bebca692175efb36276a5f9fb92437836e65d87bb3b70a09663579a35c801c1aa654eb28517013b5e877a003be057b875b35df2fe227090c58358156115c37d72c290912ee288600cdabb492312227fe659dc238df6e03def5668fd22573e63b245586cd10bc66d68942e01bc9dfb9d152a1a7898ce002daef4fbe574991fe37e0b8b69ff269e098b7b1640a47b370fb38f410eb0d5703536bbe4bcc900df2a526d80a413b9c9ed3ab75124ff7198d3a44e2ca0cce410fedf5f2ef7b33f93b43a26f4d30751f2bfb4b13ca42a3105196bb17e5c2653e0c00adac72a44baa60b2d2411fb239d0d0dfd68251d362bb04ab0a3cc169cf61049bdc73cf11aee0b90f7b26cbcdab987873ab69542cdef0bb8c125da7034e320dc9bc4f84a9827cd927165c3455ae9fd8f5325e606b58e3cb6db67bc46b434c0bf78b1ef5530c0996205b419dbde1a8b1a9ed21abf38e3c5a16d34d6091a1e4065c79fe8d78b95184e779434993a52c23e6e9c6db90e9db5b90e97a29def79302922934dd5413a93d183cd83ad88ab3d439ef39753c84588a8269a74f0d1972080fbb35971f624d31041842ed430c70d09171c20f0f00e0d0c4e8ef8149b9af038846db4d5b2d8a586e5f1b34803e52cab395311ac5bac25f2ce3cb2203d8070354da89b48c24c0da18b4db9f87d210388ec2655f48fcc42bcb78c367bef2aa0c543f7e562adc23a6b22445ca1389156587c0af0fa16aeaa1aa5e6508228e9c04620aafcb6408e66ad86f0258cdd04a376ca1f41a623e7cf36a800d5da91583029bb46e73b038d27c370d948571c442f65302d09c1562ba3ebde7e7e803f32602799726043b23a4eca27e62986009cc9e4419fe95cece9504cabdf6d92f01f105beee7e9ece3fb49b495922bfc23337e53baafebcad53a250b66d168a87b3a4064440985fe7becf778fadbbf8eb4d01c024039a4cdfd5f27edab16f4702b4c20590dce3157b8c727316ba8c25de3cab3112c385f8ca0895e5c775e5f34c17520cc8cf80d28be593f186ad7f6f4666f702df2566c16a5d5e1aad21694774f41f0003fbcb4502fcb5e1deff0fa2a458f6ff88bbc91cfa2e21cb42b0077da5d43007d68610e05172ac51036ab6a52140e14e1707dc325fe8efcc39c74883780e9404bcb78fdf38b2763ddd7458db79ffaf28305aef1534d7ec4112316a8acfbe3504277ef2e7dd36cc72869f1d53689d904cabd23085165d2933ea15fa491da5ed01f00468c47ba2185b241405d89952922a74c02ceb8617543b70f353a62f017b023dc53beb7b858019493aa1d2bdcf2d6eb7683d453d62dfd391b189c296bee006d7c85bd22dd02bf6448cb7af0460cc319913f9fbc357875bf5396f9e33fab5050aeb928f76e42175bb300f1e254f981d8bf22079c66b03169e4d04879495e80a395187afeb4d7e73edf5d4320ce49cc4774560697ed4c58e8b4214fd03d3e808d666a22ab6104dbafa95c6c0db8af94ea49f24fbd5c36d00fc009c451ce097083f84b72f2e5b9b1dec327c4f14d43da764ad36c5e8e8389453755fb77157dd03f9e97ed405828144904fb6a2f578e1d39f7f30621d9a483291ce50391c53690a76b217cdc7d7ac7a1ca3add04d303a920713db149ca022ad49a83e72ea7fba0c6c29a44633d1c056ddbffcfd43cd1ad4b810cddf796ba994ddda1d63cebf8705e743ff022e0ecd99912346a9c2b892e5e4ebc3e52b98e60262d96f336771770349b69d7560d297a73658c22441c5ce1b294065ff4f3da35227f7684e3a445001910975c311f461abd40ee2b56ab71f105770a4bc1a581b1221495023353aaa01d4bd115f5a8d9ba37b3b41dab0cb181f07e60624b755e79e1abfc7da826a6d08cab5edf981c43ec78bdcbf74313af449bbfe3b4806a205de6adbd2249015aa0b0efe4e92b667f66f2b8899084941fce413edc2e4a5ab44c066236c6b2a527707fe52311427d13f55a87cb3a361b196e0faecc106ca6bf40e968b057f64cde106b2b446d99da75974b26266573f1a641b4c7b477c61b370116d63267471132806f9bdb81ab2a81f03165e2a5b2d32f1f2b2cbe86d95e3c6f8bf8ac6a2296ccd0c2e6412ed8f74082f073b6bc2dd63e59ead35112c5c9362610a6561531b931b09f21e5bef71a01433b29402e008ecebaadda2bd8bed28d0cdf910ffd573434c0ac5781c5eee63cd99e97b5b319cd3925607dbb371a360e2d5e070f161a063f10946793313125f55f30bbabb52e61db65e5ae507cef64f08f501417edef3da5e0202836fed5f531a64dd41aad00a1dc39ec13b49d1ebd7a2a6b5867d7d71db0e50a393ec33f28531624bdfa3ddab00ab9239b2bd54ea858920f34c26430045898f52b419f7f04f7b3b8e604bf11b20ed058e8ecdeae5be4845aa5f14f16c1f953d8e653029b087bcd4ab9953c1937cee84f81c393285c8bc3341a5eb5bae580b5d55cf6f0ee2afbea795764bf5d4ed0735367a0bf65b6f69732449e3dfec547d93d22056ada1eb1c35c2ca1924e07c0e6120b818d6941755efc4dae89d0cc039762bf5e003763a01910d434d3f0c3e71af286c55d30c9aadb2a480b8d0938ce43c202968df06659dc14e713dd6b2899be1501718e3003be5220b7e90034244ab3e9db020600bdb9b2a17b9d87e150356cb3f3a7150bf305d03efb0aab95cf43e5cf124a8f5db6c37b81f7841b81a7cc5c2ed6d3b8124ff95f9981fc93d732dbf470403a44e0f46ffff9bbef09e2fedbf9b42600918567617ea28d8f42e6b1d764a340ea317a4462b1083062b29560d25f2b35a82bfdf000033f8a0db28e7314d4d784f6b9d0df70928cc30b94119cf02085884a237b2ff7470e299726d06f430f72c73a1727446f060a3d98ee0c867ea162c197bacce848ebc423f3dc6c4835e8bbce299084ecf73139b024c33f215bc132fe77cf326e2da2cd18b3008bf16447ff4b2a4a5679987564d369e8936270b4adb3128e6e88eb75205f9de36088fde3a7d294c7c38cf0d0bb02c1160a6695550423737668f1646ca65b93d95547097a2fa8bd70f74a623e5a3c9c3c6b70097bde54853b7517c76c0d36e59cabce4bb691bff14524d2a0eac2155a9af73a4e87f0d6ec67d4a27840ed0b7134fa93d751588a77416daca2b68d054a7e74dc752a9506090e79596c9b558f085853055d87852c251baa10a9607b86c009ba3ecba6ed2457d499128f26fe632c39e5ccc46fb4b7e225ee282d9883abaed6e531b4f844df3d5cb547152742d0a0744e40fb9284637a34dbd3a4ff4dae9a07904b36e539f56eec6ce7baf51ed8e8189b242effc3bc60908d6b62739b6d6e8206e4223160eb293eeea2992c0dcaf60c73632ce196441692c1f5131526818f5bff1cd83a75e414e595e13a082a47985bd505cf256f5ec516ae79c1117f9f7820594d696a7714d2151450127dee286f8d18de13517b24eda31f18547032200489505d2f53101c962641b7275b8a48296f339402f7f48ac2674f669841615752eaf495a6dac6bd4d6cae58be8c779890d201904221ef4a1a0f87c523b027fa43f52f37ea06f8a19070fe04f7533dfb529d802a22d7d299f3abb6974611456060915173bcca396febe1bc943150131d94a2406565957950fea0f11421c7ae2b7e900f46a8c947b7e5e724bd981c00a1c93070a6dd51bf406b876c5141f7fb5bd84cb788dda0b822bdc4263e500c85c079aee409cc00e411628ef38f9c948a3ca7989efe4592d7702aa846b3a1cfbfc5e4a1801ad6a63386bdaa004af3bd3182a082ae5f17d30862c5a0de12078aba63b51ad2ad9c695257e90cf997464da236ace2f90020547003d30e8f63998ab705aa56fd4031397d36526e09faa72c4789380a4df227104cba21542a16f64b19ba96a84103513d94336e5077c76eb3a301e3bcdf507c9a0ab6b7f98d6533d6cdfe04f72d24042472cd55b68ff9d9e1d8155c21e7df17066751a99ccc5147d0a828053f77b059879160522a71a95f14e058a51bf443264700870541f074c182b1ca3d1250b6e03377dea966eeba2bd820d680f35dc7226e4a8faebc88da83b49da2b5e7dc897dfc7998961a5fb82372fc03ddcbb6043284c80358ec0f2bf9cba7e3bbce8696dd16bc32819e8182b5528b3ac82df464b286b944a13704dc17befb80ed61d24a2b2407fd2bbf0fd1e547b1437db807e113c9e514048342558691bb18c8e2fb7318a5dd730f570722b73446476e28fe2c85f1fb908cf4898025d519f075bc16c0db7c580f8d5eab655c7fb355eff1463026b61b092594a971e573cbc71f2c6fd23782171dd7c5a41d980ac1830093a2ba8be6de21cfe8a07463deb4428bcf1382326c02edf5e1cc806779da45ac34d5c0db4f2382f5e08be2c31fcdfd93be10f4f6c0842938f9080674ac1ce67acacb5727ac6bfc7995b29f02e324e59f291a5bb4751f66dd6200ca2c9c8af94a1d6c5531e6d332d8a91c4e00c21fc1248cede72d88c8fa61f98513bddd06b910456ee60635081b1627a0e01c67e8312f0e0fed42145e9c514547cf5b5bc53634a11729b9eb6ddd217568a49772e18e020d0d5ddd2220d938c753a044fb4bd49d3b2002e951849e32ad1f37a7058750c627f4a94b03d048a524eee29bc8de8ea1f76255b402b63f9b4ff050aec4af6c6ff1ed7cdd62d5f9e38e5deb73068b2a49a460adc7b98f64e72729ffe7d63662e25ea421d73995c22633e12ba5892f6c2b1dfdbe1f85bf5c22208bf1399bcefb5f9357db7c84bca879fbddc966d7adad62121490505b5b95cbd545753fb900ddf50a6a6588828f8f95252b9c7aaf321303f19c496bfa2af90104416a10771ca02d7daf9204daa3c0d9c8eccc7fa80234995db0869947e7412e5443851495e89259f92bee8e5b33c51e08f3dcfdcfb2a3b989ba51c16e820ca2f3bbd34c913d8ffd75d36447bb2539e2f424297ac39ac5090463289f332a4555c6243315c3e2c241c5a2534c08c6397ee82dffd9b8b2dca1d64ca3cc29cbd07e67bf095ab47273a7a70b59cbe936cd018be9d0c3f8a224c405de0a58cc5bc2cac7b6e6f514089b036eee6b0904b1979e6077b6132d9fd669788f5031d3f88107a857e35117054f9196a197e97ce89507701e23077c93b98401ad75a8a30a77317952fca30ec28eef2179699f93dd1d07b998118930c481260cffe5d098a81048b0853bc957f61c376a392bc740d4bc083e9bc4b62ee04daee640c5339121174af2f2c3c78d716d97497c0518d0a7468d5ab25b59fab995c815f65550d52567ab22ce7215eadb2a5bf07ff8991007d381ad3b18f9af8307e97282b681c37a74cc1083bc079e49b79d08e8d15d602b65d2f153c27e54407d80a4dbda35acc29220996375f7819919ebd9ec6999a0aa1ee2b5a2f8cd95c1ed40ed310ec102b872a2f0ae40a383036398a467a8450032b8dc50fb3aead263d9004dee7f10e12aea154ab97f04f4a016d39b97742d00c837350002903c83f056c5d64316c7aafd83fe355a64802fbef14516b197dfa001695103d494c48359e58bbcd358b0eb20d1ef42e3184594fcf51443ad3cfb4047ced01f064c638698c690b9179b61aa68d369e35d5ac8bc65dae702cbc74890dcbedb029dd98055213866576a3d5e0617d063dc2470e8d8e5b83574b5687b20990a81ccea7787be7d38ba9f21b5ba3443049ddd82328d4e8fbe6b2817bf9380b46f9e643f6a593081f7f1d5d97c69b0fe58c9cc6074c373f8a71c90a12604b0884716acb458e7dd490b5f0057d917e586c69cafe9fbe47f69bd64c7e61b5b80a6825520c5327772c2ea8d7450c3d764c4d08cdaa1562a1d990eb0878134d35012482c2f5c4ec38fe5161e00eaf669187f2ec98584a6735a9341cf66b4827dc0bf512a1d4ead70ad08445b1a9af017ac6b375f9c5f0103d0755e1c13046d2c102a041f2c79577ed00c330e1a7c8e3a6d474dbf6d9ef9c4e3217cc3fe611967a0bc5b96d0815ad46b04d043dd39ade61805d12aff54f4f60ca89f2ad77433c390822c633e75cba3a385958fe1015c502315ce63e440baab882dbeb4ba118994103676b9e83e6f6d2d53db075f662469e733778dd8082c36cb246b03f9701c580015d55bf2ca24a02bd3888eb95597f654d04e84fcd79aefa00125091db32898f0545a6b7695838c01d0be144395d40af9d8e69b0ce2f1326bcc3d57a244be2400031cbf5ff387e2439cc3b492466343d35a6a7ae244c0abdcada6cd0e37fb8170ad2c7582aafefc50fe74f4f05bd1b1aa0a0391c18b2e54f7b8078517ce5072305e4a509c5ceb5165a148a78c67fc1de9d97d4a7c0a9e6d152b47e2320721c200c830a015279a3d84acd32f7070af60f82db8fe6f035973f4250dba240999a5005f96f27a773fc9e7b68558f5f98699aeee07c645097111ec1aaabdefea774690482af54c967848b2cd65ebd7f9bd4657292864f46e4620210ae41c1642357eb0985f8b1dd6e9d87fcca833964873224c9128e899acd85f74ee625809e9f045907e717b4e15183bf2fc55a1cae983526e833255b1a38a532c3319ae67428e6b604f1aa90a29afe47935795d9b629abe0654a9375268d11f91a7f0e13a74c9a380ae66988a69672f086bb939df9590f0e495364a72c8cb298e98bf04a0709722f00449422874e793b6029a95e26724609dc06fe9086f6ae690b34dc1b29427b430350a43678dfb0ddde1db147da17c2e6b3fd44dbe186dabc204f47ab241c2f560d4c103099635d550200103c05232988f48838d4b56b401242852c4be6ef991203fa32db1bac264e7663b866f21314c05d3eb006a4fc2e00fabf30b4197b5edc06b8777c5560b10549622a28ef19132a9e3960f54f3c449c3039985b24be69e80f17df718ef65afb8fbd4b1d6eb3fa982590962d8863720a6f7080f6d73251360a62b8674d157eb1a2d313631aa446635ec453090c75cb7470cbe60044f329130b829cf76313e13a5a58dd5378ba3d1bd7c7a6b931e63f233204c7caf9d4fb8d0b036910d5fd088dff93e7f6a43888096b38b5e0d39bb94b2c570e94258ba8f60143f516aef8442b816bcf88ce21eb62bf58874644dfed54d13ecaec398846950a01b76ab69ed1324cdda07cf14831bf6554f1ad9952defc2186c66da67a6265049e01bb01a69c67b81bc0977ce81b13cd3058ca82bc212d17514ac19cc40c9703cd8a3c8253d0163d494a9bce334b798aaeddece4c546509ed61953f8ed357c0e861c7bd6ec632e7831b57a9d796b0513a715ab7348db38f3dcfc8474a82b81076e69287de84cc3bb1e8326f467b961f0dddcd70a9d104cfad472d3661bada40b1aafdc9b6fcaf21ba61e8e5367aba0374fb927c375f90b87350bb0e0dbd8e105bbc62c5fc32f99f4bed49baa2f3db5a4705c4d2feaf3cd244d4d28d6f6a1400b5384e8f695e1449db4ef23f7b77618ed7a366eef3ae38d0b36aa19541a5f1d0b26c006bdaca798027322f8294933ad9d877fb79cce3cd756b94ed2cd311cb40db8cd46406725c9e7fd92893cdba0d1001796a286d9b18707f4ee433f90665607f60f5caadfa08817e1ab0f440b26dfb5163469c871bd7e0bfda5597597a2d40265d270c8303c48231c97b1807ddd444105aa6e195df4c208a0d07ab7721e49096e2b8edcaab76fc8729fd3bab94895518e3549dfcfe5238ef3f92e912e23bc0f032af4ad5abedd0c10613e50c26c9522902074202bd2c93ca60a7fdf4c5dff06e27594188bd5c6fed2874df4acf8b6d2f412959c2f274ffba18d040909985f09038124541070db29455f8d58c33323798cb03cfa2dc0298a856e64d4d7eb190f9c47bbbf7c075e8cc3ac5f980cad86287d1940dd8dc0ace92241ddb49d454605cd9229fe4080ca8e4b34bf3c35c3b95436d64f61d321a30f402f2695b6452d04f4aa87a45504333b39c487f05fab5ac541893bcccaccc27313293c20da1df10a1fe1edcb7962bd4c9b211472947196e01ee99d2d3f518710241dfc5f06a3ea0f53b36bc3696808aeba745a58c8c8b27f5bfeda5676a95596cfc24e7aebec110f851e464baf54513501af9262618d44cb81eff6d451d2ef90868a0b76fee1ae0f2392eca80014d625cf976c763f86186cf984943a0324bb38b8d23d97a2dae7002590aa56836825bee4770d74ded76b9187ed26ce8795d96e4c211056b7d8380f3e2ee2b7f4b37342968ebb1706e85233ad8fa15a39cda0417f86dacd35829a0053eec03966ac62876503efabf4407da1fd5eb13b542cbfa9ee755725cfe9c30f1235645468cbe11d875c5ebf96363b25abefa920867af543bcbc4148d5f1e50ac520452ced483e258ffb7ace14531c5c7de112f4827c594cbdaa373e52f54506833713e2d2fab34ecb5228c7c1c1cf402be69654c5ace0376d34ef7339383f0a2243fba2d420eb964593f871e18f5764824976f2f4c28f8624d9921237d451068ed944427dd822c8789064dbcdadca5c14b766706c7c3ac034875d44e94bf6047e1e989bc3506445c88c08b7805bf5a9ff3bf1636b93e98f039cb762d667ae02eca0fa2e30835f037dc359ce12edaafd46f064e0aa513d1e3239e0b63af879090a56c3d72748837b84bbd59a55a15c15b4e44d110532a020b9da108bfcf9530c3e1a49bf0c90143ecdd1324ad2beb6a4e1d033f46ceb918f4259d5b17bad3304e40fa0c77edf3afee63799c94b470a4dc3c89d18a43dc8dcd31aac6d33a23801a3548cd48aae4fcc39d0c2775ea1a26290f6d7c6d49506ad8772b1a356a8170bd0be3d0d451aa618d6312d6be5a86a2a398ad051a7e5660a454a4c45da41630fc9e3af071abd46b85a07749d1ee6287a4afb898f7c49655763e34a9ab47b9d0d750e5c0b8526960a576d55862c3e75f97716f211903d808ec860f41361129c0fc8cf440475e64a92338a37ccdef482ad7d9ad75a0a94bfb9007db701548dad0097312ecdb25219b47f5f44a707846ff2c180198747102dad0e37f3bdda0cba060aa8fafcabd48672879641167142c0024458aa118c2b6189ff95812c56515c06adf80b43718fe494419e883abe0f33e36d4e18f6cdfeb8edb121b81d0db1770f2705face099e6bbdae0cd485d486a8c2018d49bfcd956704f669edb352807c086c9edd1d0fac0eefed63077d6022aff763c8d34e51d69b26016740638b477606f68a07a3089aca2204035f772b06945823375fff78fd21fa554b16688ecb210b7291f1617493c6913bae6c90eda5935cc53a50c933e7b3b453b68f3871885b073c7a25563a66104e4dc28007990565ec9e2a3382f9924c6a63953af4929d4d0cd3282c0bb4c5cb3f2aa4c1d88751f15a5cb7ae8687034b6ab38d46b76b0bcf0967ba42db29eb1d7c458e923e6c6474e79ef469e92805b30a3a6e6e9101c53e0ce747701d014c338171df6ba79f8aa6234a49e78a8039e2ea9b671feddffa7d03cba531d055be1808d6fc2a6e229087b77a3be35e40ab4a6f378c216ea05d1900cd1aa21bf3bf0afe40e95c406000b49a429bdeeb563f32b87bedc1adc6e5e501c051a69901925053f064ae1e8054e34500b97eff9f974292eafbbd85715c2500e45a20f16556e09b7bb530764582a59ec40a4af28d95e6fdfea3f10ce35e2b0416e209c92a5045b8d7af4762cebf2d6d97b8960d9e9565e0549a2488a9e8b50c985f5395774fb807d49a0f84e0983fec0e32fb5342a3704c5d198dddc945d806c3262dc93f96a1b1a09c2bd461f324c068aae59d5473deba8ba6036ad96c670f42667ada96da901567e544157b12f56159bb7c3f83b90abd1b9e7eb948f2e702c9eca93744830f0dff8b7bc73a1458b0f8d40e679afd1e22b09c93f2c9729602c6c81026f8f02991c094a38ed0e6062508974a8bc69e8d6e6741a45eabc7db0665f8a2d879de17fdaf7ad1b210f337b90aec1c1c3676f244aa3852a77c05db0c05b9936bc10f0a327602aa2109bdd115f8a3e1081c1b0c56649638207d2dd9086c6ab885512fec9650ba288df5133c9dc87cb7fe81919fafca783f1252c2930332559bbafcdc830223837a7b78a7ce137e9f4a4e3e7d1195a494fa6d0ca9a10659c0df4ee0c9db91dba17acad08e5f6ccae71012e2c6e4b37894520fffe9620d66a72a1e4f04e5fa2abe93ef7f4b2e9e837c9e0f9d962b5c80f5e8340874610358576c23403b2d742c587e3827d17c6f41b31f9da4030207aaa2f4c32059f4017bfa4c324be2ac00a0aeabc7756ff2e16bdf39ed6b0c313f01b1acce8bc2b408a0cb3eaf9af9b68dd17f258c0bf0c536b19040d6bebc0a9dac7fbc59398b520891a741c4183a851c1cbcf3f78f30924d85ea2d907a6ac8db9a1455e4c672800a88629853fefb86800fec6631d77edf591d56199d5980bf3c86d9254773e02508be6a488b032686134049415bb194ad11ab8e4b1e6304274091beccc795e7b604649f106b395bef6f30a1dca330561709cdf7295557410d040543c279e169a908dc3d94c4809f0a7dd92ad8cefb014f5d47681b21d6b955fa894eff5b57cc5d0d1b08c01c4e892af363e6560a02212edc1fa645cdbce380b3e010d3c7f473960ec86e8ab8d2c235f594cbb2c2fa62f27b4ea4ab9796b8534688fd9819ea529c023ab370c6e482633801e3d588f3a4433cef5aaeb633547e922dbb35410af8a803156b64daf9633636063a37c4adc43e492023e09137099a715a690a480119f60d9c2f1a110a6590ff1b7774e0fe3e4a7419226448634c129aa1213e560814cf0b28d28fbd4029688f0d920a5f45c2a06bdea6c38f731a8bf741ba106bb8a3ac0e35fd8c015995d55c0733a18e66705dc84dab552aff7d2ae654b903f6fc9f1305da5a5bafbfbb22df241e211416fa902eaec589c7a22d6bf160a884e5534c5e0a685c312527c037f3196d4122f4d42c5a683d0e555af2a271d8d68faea80dfa04bc27bdbca4d4b3c14ae0745ea453174a6e6e3b3014dbd23ac3c8a413b5e29d08524227a74d9da9b38d2ddeb8649eb9d46bafca1453fb6ab89112544597bf2dd855511f88a12a3000d7410572e9705c11b7fcfbeb9b1e2317d274526555e8104840abf75d25f845b7262d8b97d359ec39019b7e79750d3924564a757f61d0739edc3b52509ccd4822e0a009ff01e33619090961ffee25f22fb6132f3273d4a2be46de6f8ef4bbb8105fd7ca8329d986901e77ddc8364f56a841c66eccd7c29d34893077d396aa37f39e5157e56029141ed64a21ef75be7509c4787df1ad05e3106117d83208dffda5818ad4b9214f8d98525b01b283ac7b1e10342f546147dfc1e7f3cf1e0108070c30f760d0fa2a9681bf480b329ec479a7b70c6d90e6dd8d3b0292dade0859548bf22cf4c808691e59acb8948f14a5eafcb81e15a3f0af341b165712b33dcfa798c4fdc96d576a144d6186911e1efa3b39cea48adb7e325479e0a1d07e78c8eef802096af323acfe1f0dc5f09eddb6fdc484dd73b09ef8be8e302758bdcddd89c671361d9b56857efc8486e4d1639b0b0a70546f8323db524b6b95350a947a9f2b929badd89da73ccb4c46e36eb9f994030a6eb36a6615f0471ba343eb5b8eb80b7e07523cb99431029b36db074d8e8643c417b80ef6e2cee094a39a04be94c9d08d4632548574d71e758440fb5bc26a628d19aec66d263c7272a551228be488c132df8c56b961e203ea465354fff1931763bca9609b9392e8b3290e60f8df264746081f9400ed506e5ac9b15da69625fd774d3f3450ba9b8ebec6b6cca9822ed85d7fc1cf6dbdada1cb34970cf87cc80b4d0ce3e9b973a125d761f03d35508d209a5c37b17c8b4d6d5064d4c8df72763285cadda3b56e6c6e8b5b75b719c41974164fd7af9e2478cf0f8a8ee7f663b3e60570779ca891a939f7f8cd30d7115a1da50b6099d1817986ec9e16459563c26e6714b3bb4d169462c6b8469680ac8a833d1f5a7a3fb4a3320ea262c3dd7d0ba79a7bbde853287e505e1979a376b2a2d925f9dca00fdf29d3b76484e5928433e716b186badab0a9efbe1562bd221f7275e46520c3a70f931a45cf22bcf455d9a45f65d0b15f44631c8e79f4c6ff9365027db2d4b322a62dd9eb3fcda81e30721970730ae5732216f25d311cf39877fcf0a6e258f786b5a617b9cc2b00eec15ba2d3ca12bc6a59189ac688d1a7e14cafb9060b516438ff68ff6f6084664ca56787d2be64104a20ae61459c8df34f473beb2f99efd5a16c3cb008aaa79902e9d7a48e91365d8e411f7698bad64eb84c061d98954d4b98bc1c6f2de13c9dcd0a1354fb25081bce08fae4030628f061af5d731bb3b4bc9ff12e311daebb60005413b27dca8eec97eddda88b55fa193767f679fc012e6404f97ab5b7efc52dff2c0e523b8a8ab0f4ca0deefbadb9fb79bd57d49bf07d1f9f9d12b09931860df0444cadbfcce259f6e505eabaa4b7625ab3ae7d72c60b06ec95c07981b7552483d385a17d6179f6106e86c8609cbf2d948e16fb41e195b0a93f250b296ddcfe20a5bd50a46c5d7f5a0f284bdf8dae11f26821fdaaaeaa3a11fdce58240685ab2709caed3feab3cd5b38d150d2cf728a23339fdf6f5f501d08bac9ed7538a98610c933cada9b5307814fc8558fa44b2042edd8ea2777147a026260569cb893a9b302e4756ea008abdae8cc65a6962e9845d8ceba5e9f0e723d950f839c9975f10b60725d37361bd2ce557fa68a6e9a6069d9727a98fa47fda31deabecc5c6f48fa57884283166ab5d7b4918598a01bed5c3d178b07494d7ad6fd7fc2717f0703ea0ff1965e2c77c63f8e999dc40035a91123df458822509becbcc22386349dea118d70efd59843a4eb3dc9bec704626d93b8286d709c46a9171f02255ad28581d1a2e385f177e9ccf01b9f732b50ef827e812e09c819dc082beb168dfe07739f976023d208cbcc54b76e9f0ca7b7ec2ddf15fe23195241837d5bc0c690261b46f05b7020bbae7f59ef42c046a7aa498a245b11d04f6fea6c0bf565de3cc419d74f00ae2dc2c6311bab6c8f04100ab1f61412d7f8a5a615136ad7e8a4785d7717f34b32af22f0d677b019f6ce64bd0c94a6c6ef2f704df82d21c34f63ae6d6ff32a27e2205dfb077d2d8f5bbb6f2135f2e771f31dbb4e98be4d9fa86a204acc1bf7f8ad76c800f6ac2009916de7d3fe6a7201b2c0c260c9a24bc45516f2244f95e836f30e8ac041d46f0ed08c99de3d62779195c599a3ad158ff9a6efecacdb7924a2146c472f551b9160aebb256dce0236fc7d46be77cf93c9e80ab021bea750e0d9b4b4cda04ed408909ee449da45a63e06ffaf7e311b125f2fef63c2ba9b6d2f3bdb5b0d04c117908308cd37665cc6775ef4d49ce555d3286671bb1d150b16276e38233e425c3230a31a3bf7d5946e6e13327519880ab9ceca4259f966826dcdeb30eed9a993971d9961da3e98e0c60f7c3f415707ecabe41920b1a20723a7a99ce84a255ecf97d5b004ae0062f108a76c5df2cb9b4b5a548a8b82468bc1ddc3b62f0f98f60d8b5e933fda2f11ceae7340c8ca55ce5a0d0dcce3ed1a558456698350f818a035f4b9d43e7afd2dad4678a6029e68d99f1ec4bb68aba853cf408a84bac2d379b47a9a9fec4c3ed22207d08edc7feae97451b4257a876186bdb1095ff6a404b3dca3bf5170d0127b93b23a2b88651a6a546677536a6ff3cdddf5247c98290cdc09d5600c6693bd736f080cda67770a209827ebfecdf9c86ff131cd3a17b39188348db4ca043faba0bdf151be4bee2b0559ed43ea7d5c4bd5ca6287e5b82f896a2a561c915b7aaf0ab0c6a238fafa262aff20c9aaa6b47e25e7c811e12ac2e9ad879214804a9c7bbb5bcde50c7ed7fb094ae029f869b3a441e0750473f3faa1d614456adc475a02cdd7aaf9c48b45161eb2a105ea4b9f03d676fd34f69bac593777a74578ef0ade13db1564e4172576fe454edeae5bc523fd6048d4bcb5a5716f6da4bf01fd05738c182aed9dc6a0fe13584c520f2bde1ac54e3c10b34985fc9711f4b8dae5046b92929afb4c8ba3f7ca1c662b78abbfaf2febf4f4184c1f115d552ca0872c0af06b61516dbbe09c3b086712e3eef9a0a0f5c890417b4fcf656f1b4cdddeba0f5a63d3b2ce929f6c3032387b4d4bafef61fb5611f846da35d4e3e11fea2615070aa485e0828b38359cc30426796db346825191141a5b14211937b12924e0a5074932707d8f1e76c3c80d6a3c64518dd3691c5d5252ba28df354dbb231da4f40ab801a29f5769a3ad6866582966b40896d943f3b62f711988613b1ab59bc0cc0d6dd2ea50fd4afa1ffd250339cfc0484693d119961d7b4f17dc6116c62adc7d076ed6834db8886b1620aff0dcd5ddac2f0492f45a7c0b448a668688ad6cb1d101c451dd8cc0c762b544849ca04153936549ae3b39112dbe2ea96bf0b210ec840810b47be58637baa446509b07c58ffa1fb66585fd4cb01df05291fd4d7d25880483bb8ac4e0505a7cc5119ee45948309eae12ea41a8b83e0a612acc25338e4b008187156b0971c145f9d6214afd896b20a15edba78f7a087c23a30d894f688908",
+ "hash": "4da46a03139ab26295dba21cabcb5f6efc74b165f1df183e83d55169b6902f0b"
+}
+
+
+send_coins.js?2:303 13192 bytes <= 13 KB (current fee: 0.002230540000)
+send_coins.js?2:313 Successful tx generation, submitting tx
+send_coins.js?2:314 Tx hash: 4da46a03139ab26295dba21cabcb5f6efc74b165f1df183e83d55169b6902f0b
+send_coins.js?2:323 Successfully submitted tx
diff --git a/__test__/fixtures/mymonero.com-1530837258185.log b/__test__/fixtures/mymonero.com-1530837258185.log
new file mode 100644
index 0000000..10d8d9a
--- /dev/null
+++ b/__test__/fixtures/mymonero.com-1530837258185.log
@@ -0,0 +1,100 @@
+mymonero.com/:9 X-Frame-Options may only be set via an HTTP header sent along with a document. It may not be set inside .
+crypto.js?1:1 pre-main prep time: 0 ms
+modal.js?1:36 Showing modal: login
+modal.js?1:41 Hiding modal: login
+modal.js?1:36 Showing modal: idle-warning
+modal.js?1:36 Showing modal: idle-timeout
+cn_util.js?3:694 Uncaught TypeError: Cannot read property 'length' of undefined
+ at cnUtil.generate_key_image (cn_util.js?3:694)
+ at Object.accountService.cachedKeyImage (account.js?5:181)
+ at account.js?1:95
+cnUtil.generate_key_image @ cn_util.js?3:694
+accountService.cachedKeyImage @ account.js?5:181
+(anonymous) @ account.js?1:95
+setTimeout (async)
+(anonymous) @ account.js?1:94
+(anonymous) @ account.js?1:105
+(anonymous) @ angular.js:9433
+(anonymous) @ angular.js:13318
+$eval @ angular.js:14570
+$digest @ angular.js:14386
+$apply @ angular.js:14675
+l @ angular.js:9725
+F @ angular.js:9915
+C.onload @ angular.js:9856
+load (async)
+(anonymous) @ angular.js:9839
+n @ angular.js:9694
+f @ angular.js:9406
+(anonymous) @ angular.js:13318
+$eval @ angular.js:14570
+$digest @ angular.js:14386
+$apply @ angular.js:14675
+(anonymous) @ angular.js:10455
+setInterval (async)
+e @ angular.js:10446
+(anonymous) @ account.js?1:181
+e @ angular.js:4219
+w.instance @ angular.js:8516
+(anonymous) @ angular.js:7762
+r @ angular.js:334
+I @ angular.js:7761
+g @ angular.js:7140
+g @ angular.js:7143
+(anonymous) @ angular.js:7019
+(anonymous) @ angular.js:1460
+$eval @ angular.js:14570
+$apply @ angular.js:14669
+(anonymous) @ angular.js:1458
+e @ angular.js:4219
+d @ angular.js:1456
+tc @ angular.js:1476
+Jd @ angular.js:1370
+(anonymous) @ angular.js:26446
+j @ jquery.js:3099
+fireWith @ jquery.js:3211
+ready @ jquery.js:3417
+I @ jquery.js:3433
+modal.js?1:41 Hiding modal: idle-warning
+modal.js?1:41 Hiding modal: idle-timeout
+modal.js?1:36 Showing modal: login
+modal.js?1:41 Hiding modal: login
+send_coins.js?2:200 Parsed destinations: [{"address":"46WhbYQYsJzdp3BVigsbFpFTG3ahq7QbcWJX8w9m48HUitAdQkqfJWDPfZUTWnqTv9jafzXwzbdWpWjh6HQeoBFUTTWVtuA","amount":{"_d":[0,1000],"_s":1}}]
+send_coins.js?2:201 Total before fee: 0.01
+send_coins.js?2:229 Destinations:
+send_coins.js?2:232 46WhbYQYsJzdp3BVigsbFpFTG3ahq7QbcWJX8w9m48HUitAdQkqfJWDPfZUTWnqTv9jafzXwzbdWpWjh6HQeoBFUTTWVtuA: 0.010000000000
+webflow.js:1171 Uncaught TypeError: Cannot read property 'handler' of undefined
+ at HTMLFormElement. (webflow.js:1171)
+ at HTMLDocument.dispatch (jquery.js:4435)
+ at HTMLDocument.r.handle (jquery.js:4121)
+(anonymous) @ webflow.js:1171
+dispatch @ jquery.js:4435
+r.handle @ jquery.js:4121
+send_coins.js?2:285 Output used as mixin (c0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b/4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7)
+send_coins.js?2:289 Unspent outs: [{"amount":"296850150000","public_key":"af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d","index":0,"global_index":6251022,"rct":"35ca97e4550d966247076aa1ca015e8ebcdc0d63594f48ad18cdd806c789b2f970273a47742ff06fa3549a004f97927dd89b0b126655bc2680ce3ed3bbb88e05f444932916512b22d51042a876337e9014f120a46cd103915b812779bda41e06","tx_id":4751309,"tx_hash":"3153c385e55f1577cad35ab8327f1acf179a82a3c33691beac2d3dfb6c5189f9","tx_pub_key":"5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf","tx_prefix_hash":"1c1ae56c6a558eb759c2382f3ffca3a557a6399d6133113aa5d8488cfa517c58","spend_key_images":["4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7"],"timestamp":"2018-07-06T00:25:48Z","height":1600105}]
+send_coins.js?2:389 Balance required: 0.01223054 XMR
+send_coins.js?2:367 Selecting outputs to use. Current total: 0 target: 0.01223054
+send_coins.js?2:373 Using output: 0.29685015 - {"amount":"296850150000","public_key":"af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d","index":0,"global_index":6251022,"rct":"35ca97e4550d966247076aa1ca015e8ebcdc0d63594f48ad18cdd806c789b2f970273a47742ff06fa3549a004f97927dd89b0b126655bc2680ce3ed3bbb88e05f444932916512b22d51042a876337e9014f120a46cd103915b812779bda41e06","tx_id":4751309,"tx_hash":"3153c385e55f1577cad35ab8327f1acf179a82a3c33691beac2d3dfb6c5189f9","tx_pub_key":"5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf","tx_prefix_hash":"1c1ae56c6a558eb759c2382f3ffca3a557a6399d6133113aa5d8488cfa517c58","spend_key_images":["4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7"],"timestamp":"2018-07-06T00:25:48Z","height":1600105}
+send_coins.js?2:433 Sending change of 0.28461961 XMR to 47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX
+send_coins.js?2:458 Fetching mix outs...
+send_coins.js?2:481 Destinations:
+cn_util.js?3:1851 46WhbYQYsJzdp3BVigsbFpFTG3ahq7QbcWJX8w9m48HUitAdQkqfJWDPfZUTWnqTv9jafzXwzbdWpWjh6HQeoBFUTTWVtuA: 0.010000000000
+cn_util.js?3:1851 47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX: 0.284619610000
+send_coins.js?2:488 Decomposed destinations:
+cn_util.js?3:1851 46WhbYQYsJzdp3BVigsbFpFTG3ahq7QbcWJX8w9m48HUitAdQkqfJWDPfZUTWnqTv9jafzXwzbdWpWjh6HQeoBFUTTWVtuA: 0.010000000000
+cn_util.js?3:1851 47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX: 0.284619610000
+cn_util.js?3:1741 Selected transfers: [{…}]0: amount: "296850150000"global_index: 6251022height: 1600105index: 0public_key: "af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d"rct: "35ca97e4550d966247076aa1ca015e8ebcdc0d63594f48ad18cdd806c789b2f970273a47742ff06fa3549a004f97927dd89b0b126655bc2680ce3ed3bbb88e05f444932916512b22d51042a876337e9014f120a46cd103915b812779bda41e06"spend_key_images: Array(1)0: "4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7"length: 1__proto__: Array(0)timestamp: "2018-07-06T00:25:48Z"tx_hash: "3153c385e55f1577cad35ab8327f1acf179a82a3c33691beac2d3dfb6c5189f9"tx_id: 4751309tx_prefix_hash: "1c1ae56c6a558eb759c2382f3ffca3a557a6399d6133113aa5d8488cfa517c58"tx_pub_key: "5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf"__proto__: Objectlength: 1__proto__: Array(0)
+cn_util.js?3:1812 sources: [{…}]0: amount: "296850150000"in_ephemeral: mask: "61b1c6138c261599e8e40f3e437212b36d3d48fee73daeb502b141a26f777008"pub: "af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d"sec: "0b645a7041b8709a977fba1c34e70b5ca3eb8511a023142442f4cd0516064f07"__proto__: Objectkey_image: "c0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b"mask: "70273a47742ff06fa3549a004f97927dd89b0b126655bc2680ce3ed3bbb88e05"outputs: Array(7)0: {index: "2080088", key: "e3b02dc808097301664c989a4faf7c5f2ec8976777b74dbe4b8cfecf8c44ca11", commit: "bdeba6a579f63f9dfe344ae4754c8db083e1f87a75956bb2c90f93210b3d02a1"}commit: "bdeba6a579f63f9dfe344ae4754c8db083e1f87a75956bb2c90f93210b3d02a1"index: "2080088"key: "e3b02dc808097301664c989a4faf7c5f2ec8976777b74dbe4b8cfecf8c44ca11"__proto__: Object1: {index: "2799561", key: "69d3eb381e3a43fd41b19cc41004866c00274e9daeb07de3303bf7a949719d50", commit: "733270f6b344c77dc8e463a95524fb90626874c2d8ae15b02578fe8404be8491"}2: {index: "4203500", key: "4d9eed420a1844a32e588317c7a713fbe59d9281b45b7f3504b9bda307c2c3c1", commit: "dc1afdf383ca54068da77efcd29ae628882db2fc93adf8c1e4a96da7d066d1a1"}3: {index: "4281075", key: "7c82f043d142a221f9f0a5f9bc4337270a58e3f2d8d4dfb6f8619842bfe0d1c0", commit: "636a54cdae73d0517334a02cdc9b6c6081c261e250f510284c1289ca3ed5e26c"}4: {index: "5980172", key: "3fb2a5f3016c21721349e27f98ba734bf3d0cb577ba4640fe5db5904a2bb146e", commit: "df525ea20e203f3d0c6ed5caf9e083e679ce6daee56c992216ec39849b12aaac"}5: {index: "6002676", key: "bcfd234d10151e10eff22c7b84bcf7064f9a35c3e0c53dd315394cd66d1ddc8e", commit: "5206721c74d96343283bb82ad0abde561f10913e1217b3826ed5cf33fe7c5355"}6: {index: "6251022", key: "af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d", commit: "35ca97e4550d966247076aa1ca015e8ebcdc0d63594f48ad18cdd806c789b2f9"}length: 7__proto__: Array(0)real_out: 6real_out_in_tx: 0real_out_tx_key: "5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf"__proto__: Objectlength: 1__proto__: Array(0)
+cn_util.js?3:1560 {sec: "b9a57d04249992a0faca9a7da08109f31c5955bb8bd60a3dc2872abb1bd49b05", pub: "c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df3"}pub: "c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df3"sec: "b9a57d04249992a0faca9a7da08109f31c5955bb8bd60a3dc2872abb1bd49b05"__proto__: Object
+cn_util.js?3:1593 Sources:
+cn_util.js?3:1597 0: 0.296850150000
+cn_util.js?3:1234 Time take for range proof 0: 922
+cn_util.js?3:1234 Time take for range proof 1: 904
+cn_util.js?3:1700 {unlock_time: 0, version: 2, extra: "01c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df3", vin: Array(1), vout: Array(2), …}extra: "01c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df3"rct_signatures: ecdhInfo: Array(2)0: amount: "64294cb7a3af9d977519ba3b9af993613e9c5f95c7ad3926fef81e603a1f3b03"mask: "417e2c0f2fe4bfe629ad37a477089a98f91c98e59c54931dd53adc358a7f7808"__proto__: Object1: amount: "0db6b4fb75210d33417fc5700eaf5ae67f4f5930b9e4148824deafaf82cb8301"mask: "67826fabd56cd9596b110c32aff6a41a4eefc4309a43264609f1b69f52331c01"__proto__: Objectlength: 2__proto__: Array(0)message: "6ffddc62bc7c37b8b2c5dfca7fe2c8be1fc8099d86a4e779946c58dc8f5f2128"outPk: Array(2)0: "200554164113b97acd5bb67b91ee69296bf15fff6001d7ce77374afffa68fb6b"1: "6464fb83555790fac27de59e3fd2cb8d27384ee923ad99de0dfe2e498104b31e"length: 2__proto__: Array(0)p: MGs: Array(1)0: cc: "8187156b0971c145f9d6214afd896b20a15edba78f7a087c23a30d894f688908"ss: Array(7)0: (2) ["02cdd7aaf9c48b45161eb2a105ea4b9f03d676fd34f69bac593777a74578ef0a", "de13db1564e4172576fe454edeae5bc523fd6048d4bcb5a5716f6da4bf01fd05"]0: "02cdd7aaf9c48b45161eb2a105ea4b9f03d676fd34f69bac593777a74578ef0a"1: "de13db1564e4172576fe454edeae5bc523fd6048d4bcb5a5716f6da4bf01fd05"length: 2__proto__: Array(0)1: (2) ["738c182aed9dc6a0fe13584c520f2bde1ac54e3c10b34985fc9711f4b8dae504", "6b92929afb4c8ba3f7ca1c662b78abbfaf2febf4f4184c1f115d552ca0872c0a"]2: (2) ["f06b61516dbbe09c3b086712e3eef9a0a0f5c890417b4fcf656f1b4cdddeba0f", "5a63d3b2ce929f6c3032387b4d4bafef61fb5611f846da35d4e3e11fea261507"]3: (2) ["0aa485e0828b38359cc30426796db346825191141a5b14211937b12924e0a507", "4932707d8f1e76c3c80d6a3c64518dd3691c5d5252ba28df354dbb231da4f40a"]4: (2) ["b801a29f5769a3ad6866582966b40896d943f3b62f711988613b1ab59bc0cc0d", "6dd2ea50fd4afa1ffd250339cfc0484693d119961d7b4f17dc6116c62adc7d07"]5: (2) ["6ed6834db8886b1620aff0dcd5ddac2f0492f45a7c0b448a668688ad6cb1d101", "c451dd8cc0c762b544849ca04153936549ae3b39112dbe2ea96bf0b210ec8408"]6: (2) ["10b47be58637baa446509b07c58ffa1fb66585fd4cb01df05291fd4d7d258804", "83bb8ac4e0505a7cc5119ee45948309eae12ea41a8b83e0a612acc25338e4b00"]length: 7__proto__: Array(0)__proto__: Objectlength: 1__proto__: Array(0)rangeSigs: Array(2)0: Ci: Array(64)0: "02836fed5f531a64dd41aad00a1dc39ec13b49d1ebd7a2a6b5867d7d71db0e50"1: "a393ec33f28531624bdfa3ddab00ab9239b2bd54ea858920f34c26430045898f"2: "52b419f7f04f7b3b8e604bf11b20ed058e8ecdeae5be4845aa5f14f16c1f953d"3: "8e653029b087bcd4ab9953c1937cee84f81c393285c8bc3341a5eb5bae580b5d"4: "55cf6f0ee2afbea795764bf5d4ed0735367a0bf65b6f69732449e3dfec547d93"5: "d22056ada1eb1c35c2ca1924e07c0e6120b818d6941755efc4dae89d0cc03976"6: "2bf5e003763a01910d434d3f0c3e71af286c55d30c9aadb2a480b8d0938ce43c"7: "202968df06659dc14e713dd6b2899be1501718e3003be5220b7e90034244ab3e"8: "9db020600bdb9b2a17b9d87e150356cb3f3a7150bf305d03efb0aab95cf43e5c"9: "f124a8f5db6c37b81f7841b81a7cc5c2ed6d3b8124ff95f9981fc93d732dbf47"10: "0403a44e0f46ffff9bbef09e2fedbf9b42600918567617ea28d8f42e6b1d764a"11: "340ea317a4462b1083062b29560d25f2b35a82bfdf000033f8a0db28e7314d4d"12: "784f6b9d0df70928cc30b94119cf02085884a237b2ff7470e299726d06f430f7"13: "2c73a1727446f060a3d98ee0c867ea162c197bacce848ebc423f3dc6c4835e8b"14: "bce299084ecf73139b024c33f215bc132fe77cf326e2da2cd18b3008bf16447f"15: "f4b2a4a5679987564d369e8936270b4adb3128e6e88eb75205f9de36088fde3a"16: "7d294c7c38cf0d0bb02c1160a6695550423737668f1646ca65b93d95547097a2"17: "fa8bd70f74a623e5a3c9c3c6b70097bde54853b7517c76c0d36e59cabce4bb69"18: "1bff14524d2a0eac2155a9af73a4e87f0d6ec67d4a27840ed0b7134fa93d7515"19: "88a77416daca2b68d054a7e74dc752a9506090e79596c9b558f085853055d878"20: "52c251baa10a9607b86c009ba3ecba6ed2457d499128f26fe632c39e5ccc46fb"21: "4b7e225ee282d9883abaed6e531b4f844df3d5cb547152742d0a0744e40fb928"22: "4637a34dbd3a4ff4dae9a07904b36e539f56eec6ce7baf51ed8e8189b242effc"23: "3bc60908d6b62739b6d6e8206e4223160eb293eeea2992c0dcaf60c73632ce19"24: "6441692c1f5131526818f5bff1cd83a75e414e595e13a082a47985bd505cf256"25: "f5ec516ae79c1117f9f7820594d696a7714d2151450127dee286f8d18de13517"26: "b24eda31f18547032200489505d2f53101c962641b7275b8a48296f339402f7f"27: "48ac2674f669841615752eaf495a6dac6bd4d6cae58be8c779890d201904221e"28: "f4a1a0f87c523b027fa43f52f37ea06f8a19070fe04f7533dfb529d802a22d7d"29: "299f3abb6974611456060915173bcca396febe1bc943150131d94a2406565957"30: "950fea0f11421c7ae2b7e900f46a8c947b7e5e724bd981c00a1c93070a6dd51b"31: "f406b876c5141f7fb5bd84cb788dda0b822bdc4263e500c85c079aee409cc00e"32: "411628ef38f9c948a3ca7989efe4592d7702aa846b3a1cfbfc5e4a1801ad6a63"33: "386bdaa004af3bd3182a082ae5f17d30862c5a0de12078aba63b51ad2ad9c695"34: "257e90cf997464da236ace2f90020547003d30e8f63998ab705aa56fd4031397"35: "d36526e09faa72c4789380a4df227104cba21542a16f64b19ba96a84103513d9"36: "4336e5077c76eb3a301e3bcdf507c9a0ab6b7f98d6533d6cdfe04f72d2404247"37: "2cd55b68ff9d9e1d8155c21e7df17066751a99ccc5147d0a828053f77b059879"38: "160522a71a95f14e058a51bf443264700870541f074c182b1ca3d1250b6e0337"39: "7dea966eeba2bd820d680f35dc7226e4a8faebc88da83b49da2b5e7dc897dfc7"40: "998961a5fb82372fc03ddcbb6043284c80358ec0f2bf9cba7e3bbce8696dd16b"41: "c32819e8182b5528b3ac82df464b286b944a13704dc17befb80ed61d24a2b240"42: "7fd2bbf0fd1e547b1437db807e113c9e514048342558691bb18c8e2fb7318a5d"43: "d730f570722b73446476e28fe2c85f1fb908cf4898025d519f075bc16c0db7c5"44: "80f8d5eab655c7fb355eff1463026b61b092594a971e573cbc71f2c6fd237821"45: "71dd7c5a41d980ac1830093a2ba8be6de21cfe8a07463deb4428bcf1382326c0"46: "2edf5e1cc806779da45ac34d5c0db4f2382f5e08be2c31fcdfd93be10f4f6c08"47: "42938f9080674ac1ce67acacb5727ac6bfc7995b29f02e324e59f291a5bb4751"48: "f66dd6200ca2c9c8af94a1d6c5531e6d332d8a91c4e00c21fc1248cede72d88c"49: "8fa61f98513bddd06b910456ee60635081b1627a0e01c67e8312f0e0fed42145"50: "e9c514547cf5b5bc53634a11729b9eb6ddd217568a49772e18e020d0d5ddd222"51: "0d938c753a044fb4bd49d3b2002e951849e32ad1f37a7058750c627f4a94b03d"52: "048a524eee29bc8de8ea1f76255b402b63f9b4ff050aec4af6c6ff1ed7cdd62d"53: "5f9e38e5deb73068b2a49a460adc7b98f64e72729ffe7d63662e25ea421d7399"54: "5c22633e12ba5892f6c2b1dfdbe1f85bf5c22208bf1399bcefb5f9357db7c84b"55: "ca879fbddc966d7adad62121490505b5b95cbd545753fb900ddf50a6a6588828"56: "f8f95252b9c7aaf321303f19c496bfa2af90104416a10771ca02d7daf9204daa"57: "3c0d9c8eccc7fa80234995db0869947e7412e5443851495e89259f92bee8e5b3"58: "3c51e08f3dcfdcfb2a3b989ba51c16e820ca2f3bbd34c913d8ffd75d36447bb2"59: "539e2f424297ac39ac5090463289f332a4555c6243315c3e2c241c5a2534c08c"60: "6397ee82dffd9b8b2dca1d64ca3cc29cbd07e67bf095ab47273a7a70b59cbe93"61: "6cd018be9d0c3f8a224c405de0a58cc5bc2cac7b6e6f514089b036eee6b0904b"62: "1979e6077b6132d9fd669788f5031d3f88107a857e35117054f9196a197e97ce"63: "89507701e23077c93b98401ad75a8a30a77317952fca30ec28eef2179699f93d"length: 64__proto__: Array(0)bsig: ee: "46793313125f55f30bbabb52e61db65e5ae507cef64f08f501417edef3da5e02"s: Array(2)0: Array(64)0: "094164e2b93cb85abd82ed7afc408fad29b1ae74fe2383a08cc4a154686e2e08"1: "b59d3a7a56337281c7de9edb43dc6cb3eb90afe399b23d65c9ca1c542e542b0c"2: "2b976ab45e2e11e27816623ce4349206c2a4c8b7540f394c868685267eefa60a"3: "e93f2fda2b1fd5835506acf9d7c77b0ca08e1cc89569df1ddf8a5688a630810e"4: "b57b4a9cc83bcc39e7c4ea8069afc2c5f97cf41bf4894d7cd3ad9fee9dcb630c"5: "ad06176bcb8d5cbed6a09a16451ea8ed80fd21e16aa136a720b76b8a6b03ff02"6: "0f92ec55b78668ac41c5423de75eb4bfbc6c2c20abb74e7a82ed9d2d53510003"7: "bdc18588724e2ecc6afcbfb3d8765f15b00a006306778c04d305dd99b90ac10e"8: "92ac7473a379ba999316ba893d0ff320788ecc754632bc2bd565e0838ffc9a01"9: "adab99709eb702deb1c350954857856de88d5b5d007919c225f6d70e1b89a906"10: "7d40bb9918e657d8dd8741453877ce22b13a5442ebf963757bd48488f15b7b0e"11: "0c96c3fa4118c6beaf72693c64308b9343472784d965e04f8b304ee3ebdb4405"12: "d04d70208389b5200b771cae428b88be6fe91970e7ec0c2b9409f16a582c6a0b"13: "9b9278f65bb6401b18e5fe1031f52c8b036bd5afd0e984cfd3cf4c650a994004"14: "0331067730c9e58688286fa2533a4815eb5289f8860664d47b55f4507a7e5b05"15: "40d8b2d3a98a02caf4109de34289f04ca6b316f7a682328f3cef057bab3b7705"16: "e25fdbf9b361023602c9fe45c9d47615cd388b251d4cc13c667e2c8235e1bb01"17: "06374aa1ff0e61f1121d47abeaf0ac767d1bb1a0de2afbe706c1014ec8d14600"18: "1d727248b4db192c5da6699d3a32b09652922908900267fd2e1982e1f05a0208"19: "7c6ade39bab5b1bcdc2f6cf03d455f8221f9d937fadcb87c4070262bde23ca0e"20: "df3422c0787a2135b741d68f5b2dedf689f455f200378e57603ab0704c9ffc0c"21: "6f0c0d13b284111698cf0bae7b0faa9ae341a25eacbd9adc502df4a12705c50b"22: "bb3d708c72e496bb9913852765c676ae683a04479b93e117e9cc0cacb403720e"23: "8e685a2a6a8cba27590a0c1dd0f1c08022ea3590acf423121a3f1c8abfcb7302"24: "bc40bcff2cbceb474f0bbb400d74f17e76e9e665a7a0ad2aff650fb8ee41c605"25: "d45ab263f78d60a67567e2bdd2085cb321f348b27f92c2e570e0396a25950908"26: "32aa144fa375474f3ef8d95295f46f0c7cfa8058a4ff11bb0dfdec1c6ba9f608"27: "73f1a08aa38b6417ecd09cf92b1547eb6a49a694f0f6ed57fa157b6eedca4609"28: "9248eccd40954c5bebc6f2bbd5de96ae3e2efe842a251a8651231bac54bc1205"29: "2a1962e4dbdc3ff1797e1ddf401d070a9b9dd2ea2ff3ae2e207d10c02c13e50c"30: "a90ae0d17418051108f06a77fcbafa80fbbf563e334da38624525e1d203bd100"31: "18ce2730e92157810ef2c092a51dce81cdcab79dc271a47749b8dc1fd6d84d0e"32: "4e0e97ee23d09c70c2d2882858bc7a318ab516a1c9f3291824c5ed79baff2c05"33: "d9d93f18348c5fbffd2337c3ae6973eacbf67aeeda71ea745fd635da419fac03"34: "296b62aa4b3f4c554b7452db9ca1887250bc8c507d042db2b9b7c81d10184108"35: "63d23ad52e74b68b13a1d0b17fec9ac5043cf22452b98f2c05328707d3b51508"36: "645918528f4712d553db4e03325e11ac0a3b3794e74884ebc3e2d63a8c2a2a0a"37: "f1dab421831e8336538d8441a5145eac4830ac0a7fe5cfab808e6fc23c7c4300"38: "a33fd2bcbfe92dc9c47d173af9be7f1e50da97ce5d88c36724e4dff79f6d9b05"39: "9b47fbf1fa33e259b339f1f74c9831473b86e7767f4cef199c7b53d260f00001"40: "fe22ce255569940f6b460c6aa9eccb1afc191f786f00256aca97d7b465ad8e0c"41: "e21b01b36099a8326edd3d509bcc49b37c69fdd04d8716df564c42d01f589804"42: "824fab7c5a13b31e41ec62321f42ace7e7c301abd04a29c4692eb526a9f5bc0c"43: "fc47845963028fe9bcac2619255251235ff7be950066e41933d86114c4799c0f"44: "94394c9b48e34a39908cd2c676fe6ee42d21a09b2c148506e9cf72bba00aa702"45: "0850b7d693ef97ce9074adc966a258c87841aa76468e10cc2474f58690b08601"46: "a7d7620a3f6c826422ebd339b47e0566a4ef9d74c91f8733e68da260ff052700"47: "dd03dd70ff28bb938ac6c420eb8942d31e0222a2c4c5e07339503f33baf16507"48: "4813beabf4ae379d0d093c16438e5c9e08c06d2909221278c398698a2a45710c"49: "1ac2954ffb0064229d43b1686dff63189c9def06fbc84d52c6dd1b45785bee01"50: "c43ba993220032fb7530872ebb42cd1b8d1b56cc98784ac01b242a0859ad5f05"51: "56be67160b9b6a676b3f5b36040947b4c8d97cb2125ee429a785f1798052780f"52: "a7acb882cfff3842d52df35bd957f462c1b493cb64852f8d3479b1d930e6cb04"53: "2dacc0b2112af2fa9be170d164bef97f69cf0c7bb428f3677f8431aeb300b10d"54: "0b89d7dc92f2ee2f1f60a7e93cd7ac88b6f71b4c83f2df1c4f1df217795af501"55: "e274a6b4ba98db41785a1268a5c26093cd7799e2fc97fca3832465aa0ca8a80b"56: "241240171cc310cbe3835eb71edab3ca918b62a883f27be003be1984c9fced06"57: "8d3cfa28d895556eb48d6f6f21fea79f6818297df8a030202ddf87c4b38d5202"58: "270555a24fa46c4b124af481e0a791336efc5d87d16191f7a73686dc330cd00a"59: "72431571eaa9a6866d114c2abf5a9599dbd8c6b8c6ab41ad155329b20bd9d302"60: "b896f7ef3859598c1f7312149d49c85fc32734aec69311a1f7241713c902ca0a"61: "578f78aaecc9f9783095216d583312116983c97da812058ba197a73b7b00a403"62: "823f17e7d12a20d8d2af791623984d8a5d42e8b81cc33d2075706f188b96240e"63: "0c70da64295b99aba8a1ca9a692f908224766dd4ab85b21c2e4a3561d92da70b"length: 64__proto__: Array(0)1: Array(64)0: "b3fcb96c7067379745497875654d4135f2a367f9760e1101e1b7c8e45bd78801"1: "e7166d6480bcd5628467993ee7c11b651a3e97afffcb302fe91d6cf55036bc0a"2: "cc531d9d6c3dc509c2878ed7378a82ecb182da633ab8d7e08241da069afb8b06"3: "e63c93d5e36e33e4ba022fe2352061f55f4e5115d048e4c36ab56d9d558daf00"4: "15a8ec96436dd72e4ee610e0806adbb382a7ef9d3dd9774ed070abeec25d460e"5: "304ebc4b2d097bfa0f6982abe3f7490f0374a278f1464505e0233a1195e7af01"6: "ae7e06f814cb7266ca60722c53e2d3eb66a5a1f8cff91bad0da26b5716ab7106"7: "fceb4b4a1a613014804c5284e7c8d785512730c7f109d4c225f9fa00cff4d10c"8: "f7f120e75d1c37c5e5b4e9117f30fb30c3cbcf86a33225fb4f63f2a207168204"9: "5dfb733111c50730556e62bd49a471b71c9421b312e8805358ba53af071b6103"10: "99e917fa1b4687093313e330b453dd98b3f7866bcbe737f5e11f4bd1c0043b09"11: "75b84a92c4cfef856688345ebf4ba84b95aaad4ef573774e448d7ea93f6ba109"12: "37ec41a174bc0965d667918f608b6358d424628859d0933cccdf51ec7d55f906"13: "438342b45be0e9796bb57225e7bb184602fb97dfb48260bcfa427f787a759c0f"14: "5a2a8929697f47c854bebca692175efb36276a5f9fb92437836e65d87bb3b70a"15: "09663579a35c801c1aa654eb28517013b5e877a003be057b875b35df2fe22709"16: "0c58358156115c37d72c290912ee288600cdabb492312227fe659dc238df6e03"17: "def5668fd22573e63b245586cd10bc66d68942e01bc9dfb9d152a1a7898ce002"18: "daef4fbe574991fe37e0b8b69ff269e098b7b1640a47b370fb38f410eb0d5703"19: "536bbe4bcc900df2a526d80a413b9c9ed3ab75124ff7198d3a44e2ca0cce410f"20: "edf5f2ef7b33f93b43a26f4d30751f2bfb4b13ca42a3105196bb17e5c2653e0c"21: "00adac72a44baa60b2d2411fb239d0d0dfd68251d362bb04ab0a3cc169cf6104"22: "9bdc73cf11aee0b90f7b26cbcdab987873ab69542cdef0bb8c125da7034e320d"23: "c9bc4f84a9827cd927165c3455ae9fd8f5325e606b58e3cb6db67bc46b434c0b"24: "f78b1ef5530c0996205b419dbde1a8b1a9ed21abf38e3c5a16d34d6091a1e406"25: "5c79fe8d78b95184e779434993a52c23e6e9c6db90e9db5b90e97a29def79302"26: "922934dd5413a93d183cd83ad88ab3d439ef39753c84588a8269a74f0d197208"27: "0fbb35971f624d31041842ed430c70d09171c20f0f00e0d0c4e8ef8149b9af03"28: "8846db4d5b2d8a586e5f1b34803e52cab395311ac5bac25f2ce3cb2203d80703"29: "54da89b48c24c0da18b4db9f87d210388ec2655f48fcc42bcb78c367bef2aa0c"30: "543f7e562adc23a6b22445ca1389156587c0af0fa16aeaa1aa5e6508228e9c04"31: "620aafcb6408e66ad86f0258cdd04a376ca1f41a623e7cf36a800d5da9158302"32: "9bb46e73b038d27c370d948571c442f65302d09c1562ba3ebde7e7e803f32602"33: "799726043b23a4eca27e62986009cc9e4419fe95cece9504cabdf6d92f01f105"34: "beee7e9ece3fb49b495922bfc23337e53baafebcad53a250b66d168a87b3a406"35: "4440985fe7becf778fadbbf8eb4d01c024039a4cdfd5f27edab16f4702b4c205"36: "90dce3157b8c727316ba8c25de3cab3112c385f8ca0895e5c775e5f34c17520c"37: "c8cf80d28be593f186ad7f6f4666f702df2566c16a5d5e1aad21694774f41f00"38: "03fbcb4502fcb5e1deff0fa2a458f6ff88bbc91cfa2e21cb42b0077da5d43007"39: "d68610e05172ac51036ab6a52140e14e1707dc325fe8efcc39c74883780e9404"40: "bcb78fdf38b2763ddd7458db79ffaf28305aef1534d7ec4112316a8acfbe3504"41: "277ef2e7dd36cc72869f1d53689d904cabd23085165d2933ea15fa491da5ed01"42: "f00468c47ba2185b241405d89952922a74c02ceb8617543b70f353a62f017b02"43: "3dc53beb7b858019493aa1d2bdcf2d6eb7683d453d62dfd391b189c296bee006"44: "d7c85bd22dd02bf6448cb7af0460cc319913f9fbc357875bf5396f9e33fab505"45: "0aeb928f76e42175bb300f1e254f981d8bf22079c66b03169e4d04879495e80a"46: "395187afeb4d7e73edf5d4320ce49cc4774560697ed4c58e8b4214fd03d3e808"47: "d666a22ab6104dbafa95c6c0db8af94ea49f24fbd5c36d00fc009c451ce09708"48: "3f84b72f2e5b9b1dec327c4f14d43da764ad36c5e8e8389453755fb77157dd03"49: "f9e97ed405828144904fb6a2f578e1d39f7f30621d9a483291ce50391c53690a"50: "76b217cdc7d7ac7a1ca3add04d303a920713db149ca022ad49a83e72ea7fba0c"51: "6c29a44633d1c056ddbffcfd43cd1ad4b810cddf796ba994ddda1d63cebf8705"52: "e743ff022e0ecd99912346a9c2b892e5e4ebc3e52b98e60262d96f3367717703"53: "49b69d7560d297a73658c22441c5ce1b294065ff4f3da35227f7684e3a445001"54: "910975c311f461abd40ee2b56ab71f105770a4bc1a581b1221495023353aaa01"55: "d4bd115f5a8d9ba37b3b41dab0cb181f07e60624b755e79e1abfc7da826a6d08"56: "cab5edf981c43ec78bdcbf74313af449bbfe3b4806a205de6adbd2249015aa0b"57: "0efe4e92b667f66f2b8899084941fce413edc2e4a5ab44c066236c6b2a527707"58: "fe52311427d13f55a87cb3a361b196e0faecc106ca6bf40e968b057f64cde106"59: "b2b446d99da75974b26266573f1a641b4c7b477c61b370116d63267471132806"60: "f9bdb81ab2a81f03165e2a5b2d32f1f2b2cbe86d95e3c6f8bf8ac6a2296ccd0c"61: "2e6412ed8f74082f073b6bc2dd63e59ead35112c5c9362610a6561531b931b09"62: "f21e5bef71a01433b29402e008ecebaadda2bd8bed28d0cdf910ffd573434c0a"63: "c5781c5eee63cd99e97b5b319cd3925607dbb371a360e2d5e070f161a063f109"length: 64__proto__: Array(0)length: 2__proto__: Array(0)__proto__: Object__proto__: Object1: Ci: Array(64)0: "524227a74d9da9b38d2ddeb8649eb9d46bafca1453fb6ab89112544597bf2dd8"1: "55511f88a12a3000d7410572e9705c11b7fcfbeb9b1e2317d274526555e81048"2: "40abf75d25f845b7262d8b97d359ec39019b7e79750d3924564a757f61d0739e"3: "dc3b52509ccd4822e0a009ff01e33619090961ffee25f22fb6132f3273d4a2be"4: "46de6f8ef4bbb8105fd7ca8329d986901e77ddc8364f56a841c66eccd7c29d34"5: "893077d396aa37f39e5157e56029141ed64a21ef75be7509c4787df1ad05e310"6: "6117d83208dffda5818ad4b9214f8d98525b01b283ac7b1e10342f546147dfc1"7: "e7f3cf1e0108070c30f760d0fa2a9681bf480b329ec479a7b70c6d90e6dd8d3b"8: "0292dade0859548bf22cf4c808691e59acb8948f14a5eafcb81e15a3f0af341b"9: "165712b33dcfa798c4fdc96d576a144d6186911e1efa3b39cea48adb7e325479"10: "e0a1d07e78c8eef802096af323acfe1f0dc5f09eddb6fdc484dd73b09ef8be8e"11: "302758bdcddd89c671361d9b56857efc8486e4d1639b0b0a70546f8323db524b"12: "6b95350a947a9f2b929badd89da73ccb4c46e36eb9f994030a6eb36a6615f047"13: "1ba343eb5b8eb80b7e07523cb99431029b36db074d8e8643c417b80ef6e2cee0"14: "94a39a04be94c9d08d4632548574d71e758440fb5bc26a628d19aec66d263c72"15: "72a551228be488c132df8c56b961e203ea465354fff1931763bca9609b9392e8"16: "b3290e60f8df264746081f9400ed506e5ac9b15da69625fd774d3f3450ba9b8e"17: "bec6b6cca9822ed85d7fc1cf6dbdada1cb34970cf87cc80b4d0ce3e9b973a125"18: "d761f03d35508d209a5c37b17c8b4d6d5064d4c8df72763285cadda3b56e6c6e"19: "8b5b75b719c41974164fd7af9e2478cf0f8a8ee7f663b3e60570779ca891a939"20: "f7f8cd30d7115a1da50b6099d1817986ec9e16459563c26e6714b3bb4d169462"21: "c6b8469680ac8a833d1f5a7a3fb4a3320ea262c3dd7d0ba79a7bbde853287e50"22: "5e1979a376b2a2d925f9dca00fdf29d3b76484e5928433e716b186badab0a9ef"23: "be1562bd221f7275e46520c3a70f931a45cf22bcf455d9a45f65d0b15f44631c"24: "8e79f4c6ff9365027db2d4b322a62dd9eb3fcda81e30721970730ae5732216f2"25: "5d311cf39877fcf0a6e258f786b5a617b9cc2b00eec15ba2d3ca12bc6a59189a"26: "c688d1a7e14cafb9060b516438ff68ff6f6084664ca56787d2be64104a20ae61"27: "459c8df34f473beb2f99efd5a16c3cb008aaa79902e9d7a48e91365d8e411f76"28: "98bad64eb84c061d98954d4b98bc1c6f2de13c9dcd0a1354fb25081bce08fae4"29: "030628f061af5d731bb3b4bc9ff12e311daebb60005413b27dca8eec97eddda8"30: "8b55fa193767f679fc012e6404f97ab5b7efc52dff2c0e523b8a8ab0f4ca0dee"31: "fbadb9fb79bd57d49bf07d1f9f9d12b09931860df0444cadbfcce259f6e505ea"32: "baa4b7625ab3ae7d72c60b06ec95c07981b7552483d385a17d6179f6106e86c8"33: "609cbf2d948e16fb41e195b0a93f250b296ddcfe20a5bd50a46c5d7f5a0f284b"34: "df8dae11f26821fdaaaeaa3a11fdce58240685ab2709caed3feab3cd5b38d150"35: "d2cf728a23339fdf6f5f501d08bac9ed7538a98610c933cada9b5307814fc855"36: "8fa44b2042edd8ea2777147a026260569cb893a9b302e4756ea008abdae8cc65"37: "a6962e9845d8ceba5e9f0e723d950f839c9975f10b60725d37361bd2ce557fa6"38: "8a6e9a6069d9727a98fa47fda31deabecc5c6f48fa57884283166ab5d7b49185"39: "98a01bed5c3d178b07494d7ad6fd7fc2717f0703ea0ff1965e2c77c63f8e999d"40: "c40035a91123df458822509becbcc22386349dea118d70efd59843a4eb3dc9be"41: "c704626d93b8286d709c46a9171f02255ad28581d1a2e385f177e9ccf01b9f73"42: "2b50ef827e812e09c819dc082beb168dfe07739f976023d208cbcc54b76e9f0c"43: "a7b7ec2ddf15fe23195241837d5bc0c690261b46f05b7020bbae7f59ef42c046"44: "a7aa498a245b11d04f6fea6c0bf565de3cc419d74f00ae2dc2c6311bab6c8f04"45: "100ab1f61412d7f8a5a615136ad7e8a4785d7717f34b32af22f0d677b019f6ce"46: "64bd0c94a6c6ef2f704df82d21c34f63ae6d6ff32a27e2205dfb077d2d8f5bbb"47: "6f2135f2e771f31dbb4e98be4d9fa86a204acc1bf7f8ad76c800f6ac2009916d"48: "e7d3fe6a7201b2c0c260c9a24bc45516f2244f95e836f30e8ac041d46f0ed08c"49: "99de3d62779195c599a3ad158ff9a6efecacdb7924a2146c472f551b9160aebb"50: "256dce0236fc7d46be77cf93c9e80ab021bea750e0d9b4b4cda04ed408909ee4"51: "49da45a63e06ffaf7e311b125f2fef63c2ba9b6d2f3bdb5b0d04c117908308cd"52: "37665cc6775ef4d49ce555d3286671bb1d150b16276e38233e425c3230a31a3b"53: "f7d5946e6e13327519880ab9ceca4259f966826dcdeb30eed9a993971d9961da"54: "3e98e0c60f7c3f415707ecabe41920b1a20723a7a99ce84a255ecf97d5b004ae"55: "0062f108a76c5df2cb9b4b5a548a8b82468bc1ddc3b62f0f98f60d8b5e933fda"56: "2f11ceae7340c8ca55ce5a0d0dcce3ed1a558456698350f818a035f4b9d43e7a"57: "fd2dad4678a6029e68d99f1ec4bb68aba853cf408a84bac2d379b47a9a9fec4c"58: "3ed22207d08edc7feae97451b4257a876186bdb1095ff6a404b3dca3bf5170d0"59: "127b93b23a2b88651a6a546677536a6ff3cdddf5247c98290cdc09d5600c6693"60: "bd736f080cda67770a209827ebfecdf9c86ff131cd3a17b39188348db4ca043f"61: "aba0bdf151be4bee2b0559ed43ea7d5c4bd5ca6287e5b82f896a2a561c915b7a"62: "af0ab0c6a238fafa262aff20c9aaa6b47e25e7c811e12ac2e9ad879214804a9c"63: "7bbb5bcde50c7ed7fb094ae029f869b3a441e0750473f3faa1d614456adc475a"length: 64__proto__: Array(0)bsig: ee: "bc27bdbca4d4b3c14ae0745ea453174a6e6e3b3014dbd23ac3c8a413b5e29d08"s: Array(2)0: Array(64)0: "d1d07b998118930c481260cffe5d098a81048b0853bc957f61c376a392bc740d"1: "4bc083e9bc4b62ee04daee640c5339121174af2f2c3c78d716d97497c0518d0a"2: "7468d5ab25b59fab995c815f65550d52567ab22ce7215eadb2a5bf07ff899100"3: "7d381ad3b18f9af8307e97282b681c37a74cc1083bc079e49b79d08e8d15d602"4: "b65d2f153c27e54407d80a4dbda35acc29220996375f7819919ebd9ec6999a0a"5: "a1ee2b5a2f8cd95c1ed40ed310ec102b872a2f0ae40a383036398a467a845003"6: "2b8dc50fb3aead263d9004dee7f10e12aea154ab97f04f4a016d39b97742d00c"7: "837350002903c83f056c5d64316c7aafd83fe355a64802fbef14516b197dfa00"8: "1695103d494c48359e58bbcd358b0eb20d1ef42e3184594fcf51443ad3cfb404"9: "7ced01f064c638698c690b9179b61aa68d369e35d5ac8bc65dae702cbc74890d"10: "cbedb029dd98055213866576a3d5e0617d063dc2470e8d8e5b83574b5687b209"11: "90a81ccea7787be7d38ba9f21b5ba3443049ddd82328d4e8fbe6b2817bf9380b"12: "46f9e643f6a593081f7f1d5d97c69b0fe58c9cc6074c373f8a71c90a12604b08"13: "84716acb458e7dd490b5f0057d917e586c69cafe9fbe47f69bd64c7e61b5b80a"14: "6825520c5327772c2ea8d7450c3d764c4d08cdaa1562a1d990eb0878134d3501"15: "2482c2f5c4ec38fe5161e00eaf669187f2ec98584a6735a9341cf66b4827dc0b"16: "f512a1d4ead70ad08445b1a9af017ac6b375f9c5f0103d0755e1c13046d2c102"17: "a041f2c79577ed00c330e1a7c8e3a6d474dbf6d9ef9c4e3217cc3fe611967a0b"18: "c5b96d0815ad46b04d043dd39ade61805d12aff54f4f60ca89f2ad77433c3908"19: "22c633e75cba3a385958fe1015c502315ce63e440baab882dbeb4ba118994103"20: "676b9e83e6f6d2d53db075f662469e733778dd8082c36cb246b03f9701c58001"21: "5d55bf2ca24a02bd3888eb95597f654d04e84fcd79aefa00125091db32898f05"22: "45a6b7695838c01d0be144395d40af9d8e69b0ce2f1326bcc3d57a244be24000"23: "31cbf5ff387e2439cc3b492466343d35a6a7ae244c0abdcada6cd0e37fb8170a"24: "d2c7582aafefc50fe74f4f05bd1b1aa0a0391c18b2e54f7b8078517ce5072305"25: "e4a509c5ceb5165a148a78c67fc1de9d97d4a7c0a9e6d152b47e2320721c200c"26: "830a015279a3d84acd32f7070af60f82db8fe6f035973f4250dba240999a5005"27: "f96f27a773fc9e7b68558f5f98699aeee07c645097111ec1aaabdefea7746904"28: "82af54c967848b2cd65ebd7f9bd4657292864f46e4620210ae41c1642357eb09"29: "85f8b1dd6e9d87fcca833964873224c9128e899acd85f74ee625809e9f045907"30: "e717b4e15183bf2fc55a1cae983526e833255b1a38a532c3319ae67428e6b604"31: "f1aa90a29afe47935795d9b629abe0654a9375268d11f91a7f0e13a74c9a380a"32: "e66988a69672f086bb939df9590f0e495364a72c8cb298e98bf04a0709722f00"33: "449422874e793b6029a95e26724609dc06fe9086f6ae690b34dc1b29427b4303"34: "50a43678dfb0ddde1db147da17c2e6b3fd44dbe186dabc204f47ab241c2f560d"35: "4c103099635d550200103c05232988f48838d4b56b401242852c4be6ef991203"36: "fa32db1bac264e7663b866f21314c05d3eb006a4fc2e00fabf30b4197b5edc06"37: "b8777c5560b10549622a28ef19132a9e3960f54f3c449c3039985b24be69e80f"38: "17df718ef65afb8fbd4b1d6eb3fa982590962d8863720a6f7080f6d73251360a"39: "62b8674d157eb1a2d313631aa446635ec453090c75cb7470cbe60044f329130b"40: "829cf76313e13a5a58dd5378ba3d1bd7c7a6b931e63f233204c7caf9d4fb8d0b"41: "036910d5fd088dff93e7f6a43888096b38b5e0d39bb94b2c570e94258ba8f601"42: "43f516aef8442b816bcf88ce21eb62bf58874644dfed54d13ecaec398846950a"43: "01b76ab69ed1324cdda07cf14831bf6554f1ad9952defc2186c66da67a626504"44: "9e01bb01a69c67b81bc0977ce81b13cd3058ca82bc212d17514ac19cc40c9703"45: "cd8a3c8253d0163d494a9bce334b798aaeddece4c546509ed61953f8ed357c0e"46: "861c7bd6ec632e7831b57a9d796b0513a715ab7348db38f3dcfc8474a82b8107"47: "6e69287de84cc3bb1e8326f467b961f0dddcd70a9d104cfad472d3661bada40b"48: "1aafdc9b6fcaf21ba61e8e5367aba0374fb927c375f90b87350bb0e0dbd8e105"49: "bbc62c5fc32f99f4bed49baa2f3db5a4705c4d2feaf3cd244d4d28d6f6a1400b"50: "5384e8f695e1449db4ef23f7b77618ed7a366eef3ae38d0b36aa19541a5f1d0b"51: "26c006bdaca798027322f8294933ad9d877fb79cce3cd756b94ed2cd311cb40d"52: "b8cd46406725c9e7fd92893cdba0d1001796a286d9b18707f4ee433f90665607"53: "f60f5caadfa08817e1ab0f440b26dfb5163469c871bd7e0bfda5597597a2d402"54: "65d270c8303c48231c97b1807ddd444105aa6e195df4c208a0d07ab7721e4909"55: "6e2b8edcaab76fc8729fd3bab94895518e3549dfcfe5238ef3f92e912e23bc0f"56: "032af4ad5abedd0c10613e50c26c9522902074202bd2c93ca60a7fdf4c5dff06"57: "e27594188bd5c6fed2874df4acf8b6d2f412959c2f274ffba18d040909985f09"58: "038124541070db29455f8d58c33323798cb03cfa2dc0298a856e64d4d7eb190f"59: "9c47bbbf7c075e8cc3ac5f980cad86287d1940dd8dc0ace92241ddb49d454605"60: "cd9229fe4080ca8e4b34bf3c35c3b95436d64f61d321a30f402f2695b6452d04"61: "f4aa87a45504333b39c487f05fab5ac541893bcccaccc27313293c20da1df10a"62: "1fe1edcb7962bd4c9b211472947196e01ee99d2d3f518710241dfc5f06a3ea0f"63: "53b36bc3696808aeba745a58c8c8b27f5bfeda5676a95596cfc24e7aebec110f"length: 64__proto__: Array(0)1: Array(64)0: "851e464baf54513501af9262618d44cb81eff6d451d2ef90868a0b76fee1ae0f"1: "2392eca80014d625cf976c763f86186cf984943a0324bb38b8d23d97a2dae700"2: "2590aa56836825bee4770d74ded76b9187ed26ce8795d96e4c211056b7d8380f"3: "3e2ee2b7f4b37342968ebb1706e85233ad8fa15a39cda0417f86dacd35829a00"4: "53eec03966ac62876503efabf4407da1fd5eb13b542cbfa9ee755725cfe9c30f"5: "1235645468cbe11d875c5ebf96363b25abefa920867af543bcbc4148d5f1e50a"6: "c520452ced483e258ffb7ace14531c5c7de112f4827c594cbdaa373e52f54506"7: "833713e2d2fab34ecb5228c7c1c1cf402be69654c5ace0376d34ef7339383f0a"8: "2243fba2d420eb964593f871e18f5764824976f2f4c28f8624d9921237d45106"9: "8ed944427dd822c8789064dbcdadca5c14b766706c7c3ac034875d44e94bf604"10: "7e1e989bc3506445c88c08b7805bf5a9ff3bf1636b93e98f039cb762d667ae02"11: "eca0fa2e30835f037dc359ce12edaafd46f064e0aa513d1e3239e0b63af87909"12: "0a56c3d72748837b84bbd59a55a15c15b4e44d110532a020b9da108bfcf9530c"13: "3e1a49bf0c90143ecdd1324ad2beb6a4e1d033f46ceb918f4259d5b17bad3304"14: "e40fa0c77edf3afee63799c94b470a4dc3c89d18a43dc8dcd31aac6d33a23801"15: "a3548cd48aae4fcc39d0c2775ea1a26290f6d7c6d49506ad8772b1a356a8170b"16: "d0be3d0d451aa618d6312d6be5a86a2a398ad051a7e5660a454a4c45da41630f"17: "c9e3af071abd46b85a07749d1ee6287a4afb898f7c49655763e34a9ab47b9d0d"18: "750e5c0b8526960a576d55862c3e75f97716f211903d808ec860f41361129c0f"19: "c8cf440475e64a92338a37ccdef482ad7d9ad75a0a94bfb9007db701548dad00"20: "97312ecdb25219b47f5f44a707846ff2c180198747102dad0e37f3bdda0cba06"21: "0aa8fafcabd48672879641167142c0024458aa118c2b6189ff95812c56515c06"22: "adf80b43718fe494419e883abe0f33e36d4e18f6cdfeb8edb121b81d0db1770f"23: "2705face099e6bbdae0cd485d486a8c2018d49bfcd956704f669edb352807c08"24: "6c9edd1d0fac0eefed63077d6022aff763c8d34e51d69b26016740638b477606"25: "f68a07a3089aca2204035f772b06945823375fff78fd21fa554b16688ecb210b"26: "7291f1617493c6913bae6c90eda5935cc53a50c933e7b3b453b68f3871885b07"27: "3c7a25563a66104e4dc28007990565ec9e2a3382f9924c6a63953af4929d4d0c"28: "d3282c0bb4c5cb3f2aa4c1d88751f15a5cb7ae8687034b6ab38d46b76b0bcf09"29: "67ba42db29eb1d7c458e923e6c6474e79ef469e92805b30a3a6e6e9101c53e0c"30: "e747701d014c338171df6ba79f8aa6234a49e78a8039e2ea9b671feddffa7d03"31: "cba531d055be1808d6fc2a6e229087b77a3be35e40ab4a6f378c216ea05d1900"32: "cd1aa21bf3bf0afe40e95c406000b49a429bdeeb563f32b87bedc1adc6e5e501"33: "c051a69901925053f064ae1e8054e34500b97eff9f974292eafbbd85715c2500"34: "e45a20f16556e09b7bb530764582a59ec40a4af28d95e6fdfea3f10ce35e2b04"35: "16e209c92a5045b8d7af4762cebf2d6d97b8960d9e9565e0549a2488a9e8b50c"36: "985f5395774fb807d49a0f84e0983fec0e32fb5342a3704c5d198dddc945d806"37: "c3262dc93f96a1b1a09c2bd461f324c068aae59d5473deba8ba6036ad96c670f"38: "42667ada96da901567e544157b12f56159bb7c3f83b90abd1b9e7eb948f2e702"39: "c9eca93744830f0dff8b7bc73a1458b0f8d40e679afd1e22b09c93f2c9729602"40: "c6c81026f8f02991c094a38ed0e6062508974a8bc69e8d6e6741a45eabc7db06"41: "65f8a2d879de17fdaf7ad1b210f337b90aec1c1c3676f244aa3852a77c05db0c"42: "05b9936bc10f0a327602aa2109bdd115f8a3e1081c1b0c56649638207d2dd908"43: "6c6ab885512fec9650ba288df5133c9dc87cb7fe81919fafca783f1252c29303"44: "32559bbafcdc830223837a7b78a7ce137e9f4a4e3e7d1195a494fa6d0ca9a106"45: "59c0df4ee0c9db91dba17acad08e5f6ccae71012e2c6e4b37894520fffe9620d"46: "66a72a1e4f04e5fa2abe93ef7f4b2e9e837c9e0f9d962b5c80f5e83408746103"47: "58576c23403b2d742c587e3827d17c6f41b31f9da4030207aaa2f4c32059f401"48: "7bfa4c324be2ac00a0aeabc7756ff2e16bdf39ed6b0c313f01b1acce8bc2b408"49: "a0cb3eaf9af9b68dd17f258c0bf0c536b19040d6bebc0a9dac7fbc59398b5208"50: "91a741c4183a851c1cbcf3f78f30924d85ea2d907a6ac8db9a1455e4c672800a"51: "88629853fefb86800fec6631d77edf591d56199d5980bf3c86d9254773e02508"52: "be6a488b032686134049415bb194ad11ab8e4b1e6304274091beccc795e7b604"53: "649f106b395bef6f30a1dca330561709cdf7295557410d040543c279e169a908"54: "dc3d94c4809f0a7dd92ad8cefb014f5d47681b21d6b955fa894eff5b57cc5d0d"55: "1b08c01c4e892af363e6560a02212edc1fa645cdbce380b3e010d3c7f473960e"56: "c86e8ab8d2c235f594cbb2c2fa62f27b4ea4ab9796b8534688fd9819ea529c02"57: "3ab370c6e482633801e3d588f3a4433cef5aaeb633547e922dbb35410af8a803"58: "156b64daf9633636063a37c4adc43e492023e09137099a715a690a480119f60d"59: "9c2f1a110a6590ff1b7774e0fe3e4a7419226448634c129aa1213e560814cf0b"60: "28d28fbd4029688f0d920a5f45c2a06bdea6c38f731a8bf741ba106bb8a3ac0e"61: "35fd8c015995d55c0733a18e66705dc84dab552aff7d2ae654b903f6fc9f1305"62: "da5a5bafbfbb22df241e211416fa902eaec589c7a22d6bf160a884e5534c5e0a"63: "685c312527c037f3196d4122f4d42c5a683d0e555af2a271d8d68faea80dfa04"length: 64__proto__: Array(0)length: 2__proto__: Array(0)__proto__: Object__proto__: Objectlength: 2__proto__: Array(0)__proto__: ObjectpseudoOuts: Array(0)length: 0__proto__: Array(0)txnFee: "2230540000"type: 1__proto__: Objectunlock_time: 0version: 2vin: Array(1)0: amount: "0"k_image: "c0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b"key_offsets: Array(7)0: "2080088"1: "719473"2: "1403939"3: "77575"4: "1699097"5: "22504"6: "248346"length: 7__proto__: Array(0)type: "input_to_key"__proto__: Objectlength: 1__proto__: Array(0)vout: Array(2)0: amount: "0"target: key: "105f078f9332cf8fb2390be2f6be39abf40992dcd33daf0e3fbd9b02a23755b1"type: "txout_to_key"__proto__: Object__proto__: Object1: amount: "0"target: key: "d4b94e2516134889b321d6f06792322debb0d311c761c6fc1fc4e699fbb87d31"type: "txout_to_key"__proto__: Object__proto__: Objectlength: 2__proto__: Array(0)__proto__: Object
+send_coins.js?2:495 signed tx: {"unlock_time":0,"version":2,"extra":"01c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df3","vin":[{"type":"input_to_key","amount":"0","k_image":"c0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b","key_offsets":["2080088","719473","1403939","77575","1699097","22504","248346"]}],"vout":[{"amount":"0","target":{"type":"txout_to_key","key":"105f078f9332cf8fb2390be2f6be39abf40992dcd33daf0e3fbd9b02a23755b1"}},{"amount":"0","target":{"type":"txout_to_key","key":"d4b94e2516134889b321d6f06792322debb0d311c761c6fc1fc4e699fbb87d31"}}],"rct_signatures":{"type":1,"message":"6ffddc62bc7c37b8b2c5dfca7fe2c8be1fc8099d86a4e779946c58dc8f5f2128","outPk":["200554164113b97acd5bb67b91ee69296bf15fff6001d7ce77374afffa68fb6b","6464fb83555790fac27de59e3fd2cb8d27384ee923ad99de0dfe2e498104b31e"],"p":{"rangeSigs":[{"Ci":["02836fed5f531a64dd41aad00a1dc39ec13b49d1ebd7a2a6b5867d7d71db0e50","a393ec33f28531624bdfa3ddab00ab9239b2bd54ea858920f34c26430045898f","52b419f7f04f7b3b8e604bf11b20ed058e8ecdeae5be4845aa5f14f16c1f953d","8e653029b087bcd4ab9953c1937cee84f81c393285c8bc3341a5eb5bae580b5d","55cf6f0ee2afbea795764bf5d4ed0735367a0bf65b6f69732449e3dfec547d93","d22056ada1eb1c35c2ca1924e07c0e6120b818d6941755efc4dae89d0cc03976","2bf5e003763a01910d434d3f0c3e71af286c55d30c9aadb2a480b8d0938ce43c","202968df06659dc14e713dd6b2899be1501718e3003be5220b7e90034244ab3e","9db020600bdb9b2a17b9d87e150356cb3f3a7150bf305d03efb0aab95cf43e5c","f124a8f5db6c37b81f7841b81a7cc5c2ed6d3b8124ff95f9981fc93d732dbf47","0403a44e0f46ffff9bbef09e2fedbf9b42600918567617ea28d8f42e6b1d764a","340ea317a4462b1083062b29560d25f2b35a82bfdf000033f8a0db28e7314d4d","784f6b9d0df70928cc30b94119cf02085884a237b2ff7470e299726d06f430f7","2c73a1727446f060a3d98ee0c867ea162c197bacce848ebc423f3dc6c4835e8b","bce299084ecf73139b024c33f215bc132fe77cf326e2da2cd18b3008bf16447f","f4b2a4a5679987564d369e8936270b4adb3128e6e88eb75205f9de36088fde3a","7d294c7c38cf0d0bb02c1160a6695550423737668f1646ca65b93d95547097a2","fa8bd70f74a623e5a3c9c3c6b70097bde54853b7517c76c0d36e59cabce4bb69","1bff14524d2a0eac2155a9af73a4e87f0d6ec67d4a27840ed0b7134fa93d7515","88a77416daca2b68d054a7e74dc752a9506090e79596c9b558f085853055d878","52c251baa10a9607b86c009ba3ecba6ed2457d499128f26fe632c39e5ccc46fb","4b7e225ee282d9883abaed6e531b4f844df3d5cb547152742d0a0744e40fb928","4637a34dbd3a4ff4dae9a07904b36e539f56eec6ce7baf51ed8e8189b242effc","3bc60908d6b62739b6d6e8206e4223160eb293eeea2992c0dcaf60c73632ce19","6441692c1f5131526818f5bff1cd83a75e414e595e13a082a47985bd505cf256","f5ec516ae79c1117f9f7820594d696a7714d2151450127dee286f8d18de13517","b24eda31f18547032200489505d2f53101c962641b7275b8a48296f339402f7f","48ac2674f669841615752eaf495a6dac6bd4d6cae58be8c779890d201904221e","f4a1a0f87c523b027fa43f52f37ea06f8a19070fe04f7533dfb529d802a22d7d","299f3abb6974611456060915173bcca396febe1bc943150131d94a2406565957","950fea0f11421c7ae2b7e900f46a8c947b7e5e724bd981c00a1c93070a6dd51b","f406b876c5141f7fb5bd84cb788dda0b822bdc4263e500c85c079aee409cc00e","411628ef38f9c948a3ca7989efe4592d7702aa846b3a1cfbfc5e4a1801ad6a63","386bdaa004af3bd3182a082ae5f17d30862c5a0de12078aba63b51ad2ad9c695","257e90cf997464da236ace2f90020547003d30e8f63998ab705aa56fd4031397","d36526e09faa72c4789380a4df227104cba21542a16f64b19ba96a84103513d9","4336e5077c76eb3a301e3bcdf507c9a0ab6b7f98d6533d6cdfe04f72d2404247","2cd55b68ff9d9e1d8155c21e7df17066751a99ccc5147d0a828053f77b059879","160522a71a95f14e058a51bf443264700870541f074c182b1ca3d1250b6e0337","7dea966eeba2bd820d680f35dc7226e4a8faebc88da83b49da2b5e7dc897dfc7","998961a5fb82372fc03ddcbb6043284c80358ec0f2bf9cba7e3bbce8696dd16b","c32819e8182b5528b3ac82df464b286b944a13704dc17befb80ed61d24a2b240","7fd2bbf0fd1e547b1437db807e113c9e514048342558691bb18c8e2fb7318a5d","d730f570722b73446476e28fe2c85f1fb908cf4898025d519f075bc16c0db7c5","80f8d5eab655c7fb355eff1463026b61b092594a971e573cbc71f2c6fd237821","71dd7c5a41d980ac1830093a2ba8be6de21cfe8a07463deb4428bcf1382326c0","2edf5e1cc806779da45ac34d5c0db4f2382f5e08be2c31fcdfd93be10f4f6c08","42938f9080674ac1ce67acacb5727ac6bfc7995b29f02e324e59f291a5bb4751","f66dd6200ca2c9c8af94a1d6c5531e6d332d8a91c4e00c21fc1248cede72d88c","8fa61f98513bddd06b910456ee60635081b1627a0e01c67e8312f0e0fed42145","e9c514547cf5b5bc53634a11729b9eb6ddd217568a49772e18e020d0d5ddd222","0d938c753a044fb4bd49d3b2002e951849e32ad1f37a7058750c627f4a94b03d","048a524eee29bc8de8ea1f76255b402b63f9b4ff050aec4af6c6ff1ed7cdd62d","5f9e38e5deb73068b2a49a460adc7b98f64e72729ffe7d63662e25ea421d7399","5c22633e12ba5892f6c2b1dfdbe1f85bf5c22208bf1399bcefb5f9357db7c84b","ca879fbddc966d7adad62121490505b5b95cbd545753fb900ddf50a6a6588828","f8f95252b9c7aaf321303f19c496bfa2af90104416a10771ca02d7daf9204daa","3c0d9c8eccc7fa80234995db0869947e7412e5443851495e89259f92bee8e5b3","3c51e08f3dcfdcfb2a3b989ba51c16e820ca2f3bbd34c913d8ffd75d36447bb2","539e2f424297ac39ac5090463289f332a4555c6243315c3e2c241c5a2534c08c","6397ee82dffd9b8b2dca1d64ca3cc29cbd07e67bf095ab47273a7a70b59cbe93","6cd018be9d0c3f8a224c405de0a58cc5bc2cac7b6e6f514089b036eee6b0904b","1979e6077b6132d9fd669788f5031d3f88107a857e35117054f9196a197e97ce","89507701e23077c93b98401ad75a8a30a77317952fca30ec28eef2179699f93d"],"bsig":{"s":[["094164e2b93cb85abd82ed7afc408fad29b1ae74fe2383a08cc4a154686e2e08","b59d3a7a56337281c7de9edb43dc6cb3eb90afe399b23d65c9ca1c542e542b0c","2b976ab45e2e11e27816623ce4349206c2a4c8b7540f394c868685267eefa60a","e93f2fda2b1fd5835506acf9d7c77b0ca08e1cc89569df1ddf8a5688a630810e","b57b4a9cc83bcc39e7c4ea8069afc2c5f97cf41bf4894d7cd3ad9fee9dcb630c","ad06176bcb8d5cbed6a09a16451ea8ed80fd21e16aa136a720b76b8a6b03ff02","0f92ec55b78668ac41c5423de75eb4bfbc6c2c20abb74e7a82ed9d2d53510003","bdc18588724e2ecc6afcbfb3d8765f15b00a006306778c04d305dd99b90ac10e","92ac7473a379ba999316ba893d0ff320788ecc754632bc2bd565e0838ffc9a01","adab99709eb702deb1c350954857856de88d5b5d007919c225f6d70e1b89a906","7d40bb9918e657d8dd8741453877ce22b13a5442ebf963757bd48488f15b7b0e","0c96c3fa4118c6beaf72693c64308b9343472784d965e04f8b304ee3ebdb4405","d04d70208389b5200b771cae428b88be6fe91970e7ec0c2b9409f16a582c6a0b","9b9278f65bb6401b18e5fe1031f52c8b036bd5afd0e984cfd3cf4c650a994004","0331067730c9e58688286fa2533a4815eb5289f8860664d47b55f4507a7e5b05","40d8b2d3a98a02caf4109de34289f04ca6b316f7a682328f3cef057bab3b7705","e25fdbf9b361023602c9fe45c9d47615cd388b251d4cc13c667e2c8235e1bb01","06374aa1ff0e61f1121d47abeaf0ac767d1bb1a0de2afbe706c1014ec8d14600","1d727248b4db192c5da6699d3a32b09652922908900267fd2e1982e1f05a0208","7c6ade39bab5b1bcdc2f6cf03d455f8221f9d937fadcb87c4070262bde23ca0e","df3422c0787a2135b741d68f5b2dedf689f455f200378e57603ab0704c9ffc0c","6f0c0d13b284111698cf0bae7b0faa9ae341a25eacbd9adc502df4a12705c50b","bb3d708c72e496bb9913852765c676ae683a04479b93e117e9cc0cacb403720e","8e685a2a6a8cba27590a0c1dd0f1c08022ea3590acf423121a3f1c8abfcb7302","bc40bcff2cbceb474f0bbb400d74f17e76e9e665a7a0ad2aff650fb8ee41c605","d45ab263f78d60a67567e2bdd2085cb321f348b27f92c2e570e0396a25950908","32aa144fa375474f3ef8d95295f46f0c7cfa8058a4ff11bb0dfdec1c6ba9f608","73f1a08aa38b6417ecd09cf92b1547eb6a49a694f0f6ed57fa157b6eedca4609","9248eccd40954c5bebc6f2bbd5de96ae3e2efe842a251a8651231bac54bc1205","2a1962e4dbdc3ff1797e1ddf401d070a9b9dd2ea2ff3ae2e207d10c02c13e50c","a90ae0d17418051108f06a77fcbafa80fbbf563e334da38624525e1d203bd100","18ce2730e92157810ef2c092a51dce81cdcab79dc271a47749b8dc1fd6d84d0e","4e0e97ee23d09c70c2d2882858bc7a318ab516a1c9f3291824c5ed79baff2c05","d9d93f18348c5fbffd2337c3ae6973eacbf67aeeda71ea745fd635da419fac03","296b62aa4b3f4c554b7452db9ca1887250bc8c507d042db2b9b7c81d10184108","63d23ad52e74b68b13a1d0b17fec9ac5043cf22452b98f2c05328707d3b51508","645918528f4712d553db4e03325e11ac0a3b3794e74884ebc3e2d63a8c2a2a0a","f1dab421831e8336538d8441a5145eac4830ac0a7fe5cfab808e6fc23c7c4300","a33fd2bcbfe92dc9c47d173af9be7f1e50da97ce5d88c36724e4dff79f6d9b05","9b47fbf1fa33e259b339f1f74c9831473b86e7767f4cef199c7b53d260f00001","fe22ce255569940f6b460c6aa9eccb1afc191f786f00256aca97d7b465ad8e0c","e21b01b36099a8326edd3d509bcc49b37c69fdd04d8716df564c42d01f589804","824fab7c5a13b31e41ec62321f42ace7e7c301abd04a29c4692eb526a9f5bc0c","fc47845963028fe9bcac2619255251235ff7be950066e41933d86114c4799c0f","94394c9b48e34a39908cd2c676fe6ee42d21a09b2c148506e9cf72bba00aa702","0850b7d693ef97ce9074adc966a258c87841aa76468e10cc2474f58690b08601","a7d7620a3f6c826422ebd339b47e0566a4ef9d74c91f8733e68da260ff052700","dd03dd70ff28bb938ac6c420eb8942d31e0222a2c4c5e07339503f33baf16507","4813beabf4ae379d0d093c16438e5c9e08c06d2909221278c398698a2a45710c","1ac2954ffb0064229d43b1686dff63189c9def06fbc84d52c6dd1b45785bee01","c43ba993220032fb7530872ebb42cd1b8d1b56cc98784ac01b242a0859ad5f05","56be67160b9b6a676b3f5b36040947b4c8d97cb2125ee429a785f1798052780f","a7acb882cfff3842d52df35bd957f462c1b493cb64852f8d3479b1d930e6cb04","2dacc0b2112af2fa9be170d164bef97f69cf0c7bb428f3677f8431aeb300b10d","0b89d7dc92f2ee2f1f60a7e93cd7ac88b6f71b4c83f2df1c4f1df217795af501","e274a6b4ba98db41785a1268a5c26093cd7799e2fc97fca3832465aa0ca8a80b","241240171cc310cbe3835eb71edab3ca918b62a883f27be003be1984c9fced06","8d3cfa28d895556eb48d6f6f21fea79f6818297df8a030202ddf87c4b38d5202","270555a24fa46c4b124af481e0a791336efc5d87d16191f7a73686dc330cd00a","72431571eaa9a6866d114c2abf5a9599dbd8c6b8c6ab41ad155329b20bd9d302","b896f7ef3859598c1f7312149d49c85fc32734aec69311a1f7241713c902ca0a","578f78aaecc9f9783095216d583312116983c97da812058ba197a73b7b00a403","823f17e7d12a20d8d2af791623984d8a5d42e8b81cc33d2075706f188b96240e","0c70da64295b99aba8a1ca9a692f908224766dd4ab85b21c2e4a3561d92da70b"],["b3fcb96c7067379745497875654d4135f2a367f9760e1101e1b7c8e45bd78801","e7166d6480bcd5628467993ee7c11b651a3e97afffcb302fe91d6cf55036bc0a","cc531d9d6c3dc509c2878ed7378a82ecb182da633ab8d7e08241da069afb8b06","e63c93d5e36e33e4ba022fe2352061f55f4e5115d048e4c36ab56d9d558daf00","15a8ec96436dd72e4ee610e0806adbb382a7ef9d3dd9774ed070abeec25d460e","304ebc4b2d097bfa0f6982abe3f7490f0374a278f1464505e0233a1195e7af01","ae7e06f814cb7266ca60722c53e2d3eb66a5a1f8cff91bad0da26b5716ab7106","fceb4b4a1a613014804c5284e7c8d785512730c7f109d4c225f9fa00cff4d10c","f7f120e75d1c37c5e5b4e9117f30fb30c3cbcf86a33225fb4f63f2a207168204","5dfb733111c50730556e62bd49a471b71c9421b312e8805358ba53af071b6103","99e917fa1b4687093313e330b453dd98b3f7866bcbe737f5e11f4bd1c0043b09","75b84a92c4cfef856688345ebf4ba84b95aaad4ef573774e448d7ea93f6ba109","37ec41a174bc0965d667918f608b6358d424628859d0933cccdf51ec7d55f906","438342b45be0e9796bb57225e7bb184602fb97dfb48260bcfa427f787a759c0f","5a2a8929697f47c854bebca692175efb36276a5f9fb92437836e65d87bb3b70a","09663579a35c801c1aa654eb28517013b5e877a003be057b875b35df2fe22709","0c58358156115c37d72c290912ee288600cdabb492312227fe659dc238df6e03","def5668fd22573e63b245586cd10bc66d68942e01bc9dfb9d152a1a7898ce002","daef4fbe574991fe37e0b8b69ff269e098b7b1640a47b370fb38f410eb0d5703","536bbe4bcc900df2a526d80a413b9c9ed3ab75124ff7198d3a44e2ca0cce410f","edf5f2ef7b33f93b43a26f4d30751f2bfb4b13ca42a3105196bb17e5c2653e0c","00adac72a44baa60b2d2411fb239d0d0dfd68251d362bb04ab0a3cc169cf6104","9bdc73cf11aee0b90f7b26cbcdab987873ab69542cdef0bb8c125da7034e320d","c9bc4f84a9827cd927165c3455ae9fd8f5325e606b58e3cb6db67bc46b434c0b","f78b1ef5530c0996205b419dbde1a8b1a9ed21abf38e3c5a16d34d6091a1e406","5c79fe8d78b95184e779434993a52c23e6e9c6db90e9db5b90e97a29def79302","922934dd5413a93d183cd83ad88ab3d439ef39753c84588a8269a74f0d197208","0fbb35971f624d31041842ed430c70d09171c20f0f00e0d0c4e8ef8149b9af03","8846db4d5b2d8a586e5f1b34803e52cab395311ac5bac25f2ce3cb2203d80703","54da89b48c24c0da18b4db9f87d210388ec2655f48fcc42bcb78c367bef2aa0c","543f7e562adc23a6b22445ca1389156587c0af0fa16aeaa1aa5e6508228e9c04","620aafcb6408e66ad86f0258cdd04a376ca1f41a623e7cf36a800d5da9158302","9bb46e73b038d27c370d948571c442f65302d09c1562ba3ebde7e7e803f32602","799726043b23a4eca27e62986009cc9e4419fe95cece9504cabdf6d92f01f105","beee7e9ece3fb49b495922bfc23337e53baafebcad53a250b66d168a87b3a406","4440985fe7becf778fadbbf8eb4d01c024039a4cdfd5f27edab16f4702b4c205","90dce3157b8c727316ba8c25de3cab3112c385f8ca0895e5c775e5f34c17520c","c8cf80d28be593f186ad7f6f4666f702df2566c16a5d5e1aad21694774f41f00","03fbcb4502fcb5e1deff0fa2a458f6ff88bbc91cfa2e21cb42b0077da5d43007","d68610e05172ac51036ab6a52140e14e1707dc325fe8efcc39c74883780e9404","bcb78fdf38b2763ddd7458db79ffaf28305aef1534d7ec4112316a8acfbe3504","277ef2e7dd36cc72869f1d53689d904cabd23085165d2933ea15fa491da5ed01","f00468c47ba2185b241405d89952922a74c02ceb8617543b70f353a62f017b02","3dc53beb7b858019493aa1d2bdcf2d6eb7683d453d62dfd391b189c296bee006","d7c85bd22dd02bf6448cb7af0460cc319913f9fbc357875bf5396f9e33fab505","0aeb928f76e42175bb300f1e254f981d8bf22079c66b03169e4d04879495e80a","395187afeb4d7e73edf5d4320ce49cc4774560697ed4c58e8b4214fd03d3e808","d666a22ab6104dbafa95c6c0db8af94ea49f24fbd5c36d00fc009c451ce09708","3f84b72f2e5b9b1dec327c4f14d43da764ad36c5e8e8389453755fb77157dd03","f9e97ed405828144904fb6a2f578e1d39f7f30621d9a483291ce50391c53690a","76b217cdc7d7ac7a1ca3add04d303a920713db149ca022ad49a83e72ea7fba0c","6c29a44633d1c056ddbffcfd43cd1ad4b810cddf796ba994ddda1d63cebf8705","e743ff022e0ecd99912346a9c2b892e5e4ebc3e52b98e60262d96f3367717703","49b69d7560d297a73658c22441c5ce1b294065ff4f3da35227f7684e3a445001","910975c311f461abd40ee2b56ab71f105770a4bc1a581b1221495023353aaa01","d4bd115f5a8d9ba37b3b41dab0cb181f07e60624b755e79e1abfc7da826a6d08","cab5edf981c43ec78bdcbf74313af449bbfe3b4806a205de6adbd2249015aa0b","0efe4e92b667f66f2b8899084941fce413edc2e4a5ab44c066236c6b2a527707","fe52311427d13f55a87cb3a361b196e0faecc106ca6bf40e968b057f64cde106","b2b446d99da75974b26266573f1a641b4c7b477c61b370116d63267471132806","f9bdb81ab2a81f03165e2a5b2d32f1f2b2cbe86d95e3c6f8bf8ac6a2296ccd0c","2e6412ed8f74082f073b6bc2dd63e59ead35112c5c9362610a6561531b931b09","f21e5bef71a01433b29402e008ecebaadda2bd8bed28d0cdf910ffd573434c0a","c5781c5eee63cd99e97b5b319cd3925607dbb371a360e2d5e070f161a063f109"]],"ee":"46793313125f55f30bbabb52e61db65e5ae507cef64f08f501417edef3da5e02"}},{"Ci":["524227a74d9da9b38d2ddeb8649eb9d46bafca1453fb6ab89112544597bf2dd8","55511f88a12a3000d7410572e9705c11b7fcfbeb9b1e2317d274526555e81048","40abf75d25f845b7262d8b97d359ec39019b7e79750d3924564a757f61d0739e","dc3b52509ccd4822e0a009ff01e33619090961ffee25f22fb6132f3273d4a2be","46de6f8ef4bbb8105fd7ca8329d986901e77ddc8364f56a841c66eccd7c29d34","893077d396aa37f39e5157e56029141ed64a21ef75be7509c4787df1ad05e310","6117d83208dffda5818ad4b9214f8d98525b01b283ac7b1e10342f546147dfc1","e7f3cf1e0108070c30f760d0fa2a9681bf480b329ec479a7b70c6d90e6dd8d3b","0292dade0859548bf22cf4c808691e59acb8948f14a5eafcb81e15a3f0af341b","165712b33dcfa798c4fdc96d576a144d6186911e1efa3b39cea48adb7e325479","e0a1d07e78c8eef802096af323acfe1f0dc5f09eddb6fdc484dd73b09ef8be8e","302758bdcddd89c671361d9b56857efc8486e4d1639b0b0a70546f8323db524b","6b95350a947a9f2b929badd89da73ccb4c46e36eb9f994030a6eb36a6615f047","1ba343eb5b8eb80b7e07523cb99431029b36db074d8e8643c417b80ef6e2cee0","94a39a04be94c9d08d4632548574d71e758440fb5bc26a628d19aec66d263c72","72a551228be488c132df8c56b961e203ea465354fff1931763bca9609b9392e8","b3290e60f8df264746081f9400ed506e5ac9b15da69625fd774d3f3450ba9b8e","bec6b6cca9822ed85d7fc1cf6dbdada1cb34970cf87cc80b4d0ce3e9b973a125","d761f03d35508d209a5c37b17c8b4d6d5064d4c8df72763285cadda3b56e6c6e","8b5b75b719c41974164fd7af9e2478cf0f8a8ee7f663b3e60570779ca891a939","f7f8cd30d7115a1da50b6099d1817986ec9e16459563c26e6714b3bb4d169462","c6b8469680ac8a833d1f5a7a3fb4a3320ea262c3dd7d0ba79a7bbde853287e50","5e1979a376b2a2d925f9dca00fdf29d3b76484e5928433e716b186badab0a9ef","be1562bd221f7275e46520c3a70f931a45cf22bcf455d9a45f65d0b15f44631c","8e79f4c6ff9365027db2d4b322a62dd9eb3fcda81e30721970730ae5732216f2","5d311cf39877fcf0a6e258f786b5a617b9cc2b00eec15ba2d3ca12bc6a59189a","c688d1a7e14cafb9060b516438ff68ff6f6084664ca56787d2be64104a20ae61","459c8df34f473beb2f99efd5a16c3cb008aaa79902e9d7a48e91365d8e411f76","98bad64eb84c061d98954d4b98bc1c6f2de13c9dcd0a1354fb25081bce08fae4","030628f061af5d731bb3b4bc9ff12e311daebb60005413b27dca8eec97eddda8","8b55fa193767f679fc012e6404f97ab5b7efc52dff2c0e523b8a8ab0f4ca0dee","fbadb9fb79bd57d49bf07d1f9f9d12b09931860df0444cadbfcce259f6e505ea","baa4b7625ab3ae7d72c60b06ec95c07981b7552483d385a17d6179f6106e86c8","609cbf2d948e16fb41e195b0a93f250b296ddcfe20a5bd50a46c5d7f5a0f284b","df8dae11f26821fdaaaeaa3a11fdce58240685ab2709caed3feab3cd5b38d150","d2cf728a23339fdf6f5f501d08bac9ed7538a98610c933cada9b5307814fc855","8fa44b2042edd8ea2777147a026260569cb893a9b302e4756ea008abdae8cc65","a6962e9845d8ceba5e9f0e723d950f839c9975f10b60725d37361bd2ce557fa6","8a6e9a6069d9727a98fa47fda31deabecc5c6f48fa57884283166ab5d7b49185","98a01bed5c3d178b07494d7ad6fd7fc2717f0703ea0ff1965e2c77c63f8e999d","c40035a91123df458822509becbcc22386349dea118d70efd59843a4eb3dc9be","c704626d93b8286d709c46a9171f02255ad28581d1a2e385f177e9ccf01b9f73","2b50ef827e812e09c819dc082beb168dfe07739f976023d208cbcc54b76e9f0c","a7b7ec2ddf15fe23195241837d5bc0c690261b46f05b7020bbae7f59ef42c046","a7aa498a245b11d04f6fea6c0bf565de3cc419d74f00ae2dc2c6311bab6c8f04","100ab1f61412d7f8a5a615136ad7e8a4785d7717f34b32af22f0d677b019f6ce","64bd0c94a6c6ef2f704df82d21c34f63ae6d6ff32a27e2205dfb077d2d8f5bbb","6f2135f2e771f31dbb4e98be4d9fa86a204acc1bf7f8ad76c800f6ac2009916d","e7d3fe6a7201b2c0c260c9a24bc45516f2244f95e836f30e8ac041d46f0ed08c","99de3d62779195c599a3ad158ff9a6efecacdb7924a2146c472f551b9160aebb","256dce0236fc7d46be77cf93c9e80ab021bea750e0d9b4b4cda04ed408909ee4","49da45a63e06ffaf7e311b125f2fef63c2ba9b6d2f3bdb5b0d04c117908308cd","37665cc6775ef4d49ce555d3286671bb1d150b16276e38233e425c3230a31a3b","f7d5946e6e13327519880ab9ceca4259f966826dcdeb30eed9a993971d9961da","3e98e0c60f7c3f415707ecabe41920b1a20723a7a99ce84a255ecf97d5b004ae","0062f108a76c5df2cb9b4b5a548a8b82468bc1ddc3b62f0f98f60d8b5e933fda","2f11ceae7340c8ca55ce5a0d0dcce3ed1a558456698350f818a035f4b9d43e7a","fd2dad4678a6029e68d99f1ec4bb68aba853cf408a84bac2d379b47a9a9fec4c","3ed22207d08edc7feae97451b4257a876186bdb1095ff6a404b3dca3bf5170d0","127b93b23a2b88651a6a546677536a6ff3cdddf5247c98290cdc09d5600c6693","bd736f080cda67770a209827ebfecdf9c86ff131cd3a17b39188348db4ca043f","aba0bdf151be4bee2b0559ed43ea7d5c4bd5ca6287e5b82f896a2a561c915b7a","af0ab0c6a238fafa262aff20c9aaa6b47e25e7c811e12ac2e9ad879214804a9c","7bbb5bcde50c7ed7fb094ae029f869b3a441e0750473f3faa1d614456adc475a"],"bsig":{"s":[["d1d07b998118930c481260cffe5d098a81048b0853bc957f61c376a392bc740d","4bc083e9bc4b62ee04daee640c5339121174af2f2c3c78d716d97497c0518d0a","7468d5ab25b59fab995c815f65550d52567ab22ce7215eadb2a5bf07ff899100","7d381ad3b18f9af8307e97282b681c37a74cc1083bc079e49b79d08e8d15d602","b65d2f153c27e54407d80a4dbda35acc29220996375f7819919ebd9ec6999a0a","a1ee2b5a2f8cd95c1ed40ed310ec102b872a2f0ae40a383036398a467a845003","2b8dc50fb3aead263d9004dee7f10e12aea154ab97f04f4a016d39b97742d00c","837350002903c83f056c5d64316c7aafd83fe355a64802fbef14516b197dfa00","1695103d494c48359e58bbcd358b0eb20d1ef42e3184594fcf51443ad3cfb404","7ced01f064c638698c690b9179b61aa68d369e35d5ac8bc65dae702cbc74890d","cbedb029dd98055213866576a3d5e0617d063dc2470e8d8e5b83574b5687b209","90a81ccea7787be7d38ba9f21b5ba3443049ddd82328d4e8fbe6b2817bf9380b","46f9e643f6a593081f7f1d5d97c69b0fe58c9cc6074c373f8a71c90a12604b08","84716acb458e7dd490b5f0057d917e586c69cafe9fbe47f69bd64c7e61b5b80a","6825520c5327772c2ea8d7450c3d764c4d08cdaa1562a1d990eb0878134d3501","2482c2f5c4ec38fe5161e00eaf669187f2ec98584a6735a9341cf66b4827dc0b","f512a1d4ead70ad08445b1a9af017ac6b375f9c5f0103d0755e1c13046d2c102","a041f2c79577ed00c330e1a7c8e3a6d474dbf6d9ef9c4e3217cc3fe611967a0b","c5b96d0815ad46b04d043dd39ade61805d12aff54f4f60ca89f2ad77433c3908","22c633e75cba3a385958fe1015c502315ce63e440baab882dbeb4ba118994103","676b9e83e6f6d2d53db075f662469e733778dd8082c36cb246b03f9701c58001","5d55bf2ca24a02bd3888eb95597f654d04e84fcd79aefa00125091db32898f05","45a6b7695838c01d0be144395d40af9d8e69b0ce2f1326bcc3d57a244be24000","31cbf5ff387e2439cc3b492466343d35a6a7ae244c0abdcada6cd0e37fb8170a","d2c7582aafefc50fe74f4f05bd1b1aa0a0391c18b2e54f7b8078517ce5072305","e4a509c5ceb5165a148a78c67fc1de9d97d4a7c0a9e6d152b47e2320721c200c","830a015279a3d84acd32f7070af60f82db8fe6f035973f4250dba240999a5005","f96f27a773fc9e7b68558f5f98699aeee07c645097111ec1aaabdefea7746904","82af54c967848b2cd65ebd7f9bd4657292864f46e4620210ae41c1642357eb09","85f8b1dd6e9d87fcca833964873224c9128e899acd85f74ee625809e9f045907","e717b4e15183bf2fc55a1cae983526e833255b1a38a532c3319ae67428e6b604","f1aa90a29afe47935795d9b629abe0654a9375268d11f91a7f0e13a74c9a380a","e66988a69672f086bb939df9590f0e495364a72c8cb298e98bf04a0709722f00","449422874e793b6029a95e26724609dc06fe9086f6ae690b34dc1b29427b4303","50a43678dfb0ddde1db147da17c2e6b3fd44dbe186dabc204f47ab241c2f560d","4c103099635d550200103c05232988f48838d4b56b401242852c4be6ef991203","fa32db1bac264e7663b866f21314c05d3eb006a4fc2e00fabf30b4197b5edc06","b8777c5560b10549622a28ef19132a9e3960f54f3c449c3039985b24be69e80f","17df718ef65afb8fbd4b1d6eb3fa982590962d8863720a6f7080f6d73251360a","62b8674d157eb1a2d313631aa446635ec453090c75cb7470cbe60044f329130b","829cf76313e13a5a58dd5378ba3d1bd7c7a6b931e63f233204c7caf9d4fb8d0b","036910d5fd088dff93e7f6a43888096b38b5e0d39bb94b2c570e94258ba8f601","43f516aef8442b816bcf88ce21eb62bf58874644dfed54d13ecaec398846950a","01b76ab69ed1324cdda07cf14831bf6554f1ad9952defc2186c66da67a626504","9e01bb01a69c67b81bc0977ce81b13cd3058ca82bc212d17514ac19cc40c9703","cd8a3c8253d0163d494a9bce334b798aaeddece4c546509ed61953f8ed357c0e","861c7bd6ec632e7831b57a9d796b0513a715ab7348db38f3dcfc8474a82b8107","6e69287de84cc3bb1e8326f467b961f0dddcd70a9d104cfad472d3661bada40b","1aafdc9b6fcaf21ba61e8e5367aba0374fb927c375f90b87350bb0e0dbd8e105","bbc62c5fc32f99f4bed49baa2f3db5a4705c4d2feaf3cd244d4d28d6f6a1400b","5384e8f695e1449db4ef23f7b77618ed7a366eef3ae38d0b36aa19541a5f1d0b","26c006bdaca798027322f8294933ad9d877fb79cce3cd756b94ed2cd311cb40d","b8cd46406725c9e7fd92893cdba0d1001796a286d9b18707f4ee433f90665607","f60f5caadfa08817e1ab0f440b26dfb5163469c871bd7e0bfda5597597a2d402","65d270c8303c48231c97b1807ddd444105aa6e195df4c208a0d07ab7721e4909","6e2b8edcaab76fc8729fd3bab94895518e3549dfcfe5238ef3f92e912e23bc0f","032af4ad5abedd0c10613e50c26c9522902074202bd2c93ca60a7fdf4c5dff06","e27594188bd5c6fed2874df4acf8b6d2f412959c2f274ffba18d040909985f09","038124541070db29455f8d58c33323798cb03cfa2dc0298a856e64d4d7eb190f","9c47bbbf7c075e8cc3ac5f980cad86287d1940dd8dc0ace92241ddb49d454605","cd9229fe4080ca8e4b34bf3c35c3b95436d64f61d321a30f402f2695b6452d04","f4aa87a45504333b39c487f05fab5ac541893bcccaccc27313293c20da1df10a","1fe1edcb7962bd4c9b211472947196e01ee99d2d3f518710241dfc5f06a3ea0f","53b36bc3696808aeba745a58c8c8b27f5bfeda5676a95596cfc24e7aebec110f"],["851e464baf54513501af9262618d44cb81eff6d451d2ef90868a0b76fee1ae0f","2392eca80014d625cf976c763f86186cf984943a0324bb38b8d23d97a2dae700","2590aa56836825bee4770d74ded76b9187ed26ce8795d96e4c211056b7d8380f","3e2ee2b7f4b37342968ebb1706e85233ad8fa15a39cda0417f86dacd35829a00","53eec03966ac62876503efabf4407da1fd5eb13b542cbfa9ee755725cfe9c30f","1235645468cbe11d875c5ebf96363b25abefa920867af543bcbc4148d5f1e50a","c520452ced483e258ffb7ace14531c5c7de112f4827c594cbdaa373e52f54506","833713e2d2fab34ecb5228c7c1c1cf402be69654c5ace0376d34ef7339383f0a","2243fba2d420eb964593f871e18f5764824976f2f4c28f8624d9921237d45106","8ed944427dd822c8789064dbcdadca5c14b766706c7c3ac034875d44e94bf604","7e1e989bc3506445c88c08b7805bf5a9ff3bf1636b93e98f039cb762d667ae02","eca0fa2e30835f037dc359ce12edaafd46f064e0aa513d1e3239e0b63af87909","0a56c3d72748837b84bbd59a55a15c15b4e44d110532a020b9da108bfcf9530c","3e1a49bf0c90143ecdd1324ad2beb6a4e1d033f46ceb918f4259d5b17bad3304","e40fa0c77edf3afee63799c94b470a4dc3c89d18a43dc8dcd31aac6d33a23801","a3548cd48aae4fcc39d0c2775ea1a26290f6d7c6d49506ad8772b1a356a8170b","d0be3d0d451aa618d6312d6be5a86a2a398ad051a7e5660a454a4c45da41630f","c9e3af071abd46b85a07749d1ee6287a4afb898f7c49655763e34a9ab47b9d0d","750e5c0b8526960a576d55862c3e75f97716f211903d808ec860f41361129c0f","c8cf440475e64a92338a37ccdef482ad7d9ad75a0a94bfb9007db701548dad00","97312ecdb25219b47f5f44a707846ff2c180198747102dad0e37f3bdda0cba06","0aa8fafcabd48672879641167142c0024458aa118c2b6189ff95812c56515c06","adf80b43718fe494419e883abe0f33e36d4e18f6cdfeb8edb121b81d0db1770f","2705face099e6bbdae0cd485d486a8c2018d49bfcd956704f669edb352807c08","6c9edd1d0fac0eefed63077d6022aff763c8d34e51d69b26016740638b477606","f68a07a3089aca2204035f772b06945823375fff78fd21fa554b16688ecb210b","7291f1617493c6913bae6c90eda5935cc53a50c933e7b3b453b68f3871885b07","3c7a25563a66104e4dc28007990565ec9e2a3382f9924c6a63953af4929d4d0c","d3282c0bb4c5cb3f2aa4c1d88751f15a5cb7ae8687034b6ab38d46b76b0bcf09","67ba42db29eb1d7c458e923e6c6474e79ef469e92805b30a3a6e6e9101c53e0c","e747701d014c338171df6ba79f8aa6234a49e78a8039e2ea9b671feddffa7d03","cba531d055be1808d6fc2a6e229087b77a3be35e40ab4a6f378c216ea05d1900","cd1aa21bf3bf0afe40e95c406000b49a429bdeeb563f32b87bedc1adc6e5e501","c051a69901925053f064ae1e8054e34500b97eff9f974292eafbbd85715c2500","e45a20f16556e09b7bb530764582a59ec40a4af28d95e6fdfea3f10ce35e2b04","16e209c92a5045b8d7af4762cebf2d6d97b8960d9e9565e0549a2488a9e8b50c","985f5395774fb807d49a0f84e0983fec0e32fb5342a3704c5d198dddc945d806","c3262dc93f96a1b1a09c2bd461f324c068aae59d5473deba8ba6036ad96c670f","42667ada96da901567e544157b12f56159bb7c3f83b90abd1b9e7eb948f2e702","c9eca93744830f0dff8b7bc73a1458b0f8d40e679afd1e22b09c93f2c9729602","c6c81026f8f02991c094a38ed0e6062508974a8bc69e8d6e6741a45eabc7db06","65f8a2d879de17fdaf7ad1b210f337b90aec1c1c3676f244aa3852a77c05db0c","05b9936bc10f0a327602aa2109bdd115f8a3e1081c1b0c56649638207d2dd908","6c6ab885512fec9650ba288df5133c9dc87cb7fe81919fafca783f1252c29303","32559bbafcdc830223837a7b78a7ce137e9f4a4e3e7d1195a494fa6d0ca9a106","59c0df4ee0c9db91dba17acad08e5f6ccae71012e2c6e4b37894520fffe9620d","66a72a1e4f04e5fa2abe93ef7f4b2e9e837c9e0f9d962b5c80f5e83408746103","58576c23403b2d742c587e3827d17c6f41b31f9da4030207aaa2f4c32059f401","7bfa4c324be2ac00a0aeabc7756ff2e16bdf39ed6b0c313f01b1acce8bc2b408","a0cb3eaf9af9b68dd17f258c0bf0c536b19040d6bebc0a9dac7fbc59398b5208","91a741c4183a851c1cbcf3f78f30924d85ea2d907a6ac8db9a1455e4c672800a","88629853fefb86800fec6631d77edf591d56199d5980bf3c86d9254773e02508","be6a488b032686134049415bb194ad11ab8e4b1e6304274091beccc795e7b604","649f106b395bef6f30a1dca330561709cdf7295557410d040543c279e169a908","dc3d94c4809f0a7dd92ad8cefb014f5d47681b21d6b955fa894eff5b57cc5d0d","1b08c01c4e892af363e6560a02212edc1fa645cdbce380b3e010d3c7f473960e","c86e8ab8d2c235f594cbb2c2fa62f27b4ea4ab9796b8534688fd9819ea529c02","3ab370c6e482633801e3d588f3a4433cef5aaeb633547e922dbb35410af8a803","156b64daf9633636063a37c4adc43e492023e09137099a715a690a480119f60d","9c2f1a110a6590ff1b7774e0fe3e4a7419226448634c129aa1213e560814cf0b","28d28fbd4029688f0d920a5f45c2a06bdea6c38f731a8bf741ba106bb8a3ac0e","35fd8c015995d55c0733a18e66705dc84dab552aff7d2ae654b903f6fc9f1305","da5a5bafbfbb22df241e211416fa902eaec589c7a22d6bf160a884e5534c5e0a","685c312527c037f3196d4122f4d42c5a683d0e555af2a271d8d68faea80dfa04"]],"ee":"bc27bdbca4d4b3c14ae0745ea453174a6e6e3b3014dbd23ac3c8a413b5e29d08"}}],"MGs":[{"ss":[["02cdd7aaf9c48b45161eb2a105ea4b9f03d676fd34f69bac593777a74578ef0a","de13db1564e4172576fe454edeae5bc523fd6048d4bcb5a5716f6da4bf01fd05"],["738c182aed9dc6a0fe13584c520f2bde1ac54e3c10b34985fc9711f4b8dae504","6b92929afb4c8ba3f7ca1c662b78abbfaf2febf4f4184c1f115d552ca0872c0a"],["f06b61516dbbe09c3b086712e3eef9a0a0f5c890417b4fcf656f1b4cdddeba0f","5a63d3b2ce929f6c3032387b4d4bafef61fb5611f846da35d4e3e11fea261507"],["0aa485e0828b38359cc30426796db346825191141a5b14211937b12924e0a507","4932707d8f1e76c3c80d6a3c64518dd3691c5d5252ba28df354dbb231da4f40a"],["b801a29f5769a3ad6866582966b40896d943f3b62f711988613b1ab59bc0cc0d","6dd2ea50fd4afa1ffd250339cfc0484693d119961d7b4f17dc6116c62adc7d07"],["6ed6834db8886b1620aff0dcd5ddac2f0492f45a7c0b448a668688ad6cb1d101","c451dd8cc0c762b544849ca04153936549ae3b39112dbe2ea96bf0b210ec8408"],["10b47be58637baa446509b07c58ffa1fb66585fd4cb01df05291fd4d7d258804","83bb8ac4e0505a7cc5119ee45948309eae12ea41a8b83e0a612acc25338e4b00"]],"cc":"8187156b0971c145f9d6214afd896b20a15edba78f7a087c23a30d894f688908"}]},"ecdhInfo":[{"mask":"417e2c0f2fe4bfe629ad37a477089a98f91c98e59c54931dd53adc358a7f7808","amount":"64294cb7a3af9d977519ba3b9af993613e9c5f95c7ad3926fef81e603a1f3b03"},{"mask":"67826fabd56cd9596b110c32aff6a41a4eefc4309a43264609f1b69f52331c01","amount":"0db6b4fb75210d33417fc5700eaf5ae67f4f5930b9e4148824deafaf82cb8301"}],"txnFee":"2230540000","pseudoOuts":[]}}
+send_coins.js?2:504 raw_tx and hash:
+send_coins.js?2:505 {raw: "020001020007d8fa7ef1f42ba3d85587de0499da67e8af019a…5f9d6214afd896b20a15edba78f7a087c23a30d894f688908", hash: "4da46a03139ab26295dba21cabcb5f6efc74b165f1df183e83d55169b6902f0b"}hash: "4da46a03139ab26295dba21cabcb5f6efc74b165f1df183e83d55169b6902f0b"raw: "020001020007d8fa7ef1f42ba3d85587de0499da67e8af019a940fc0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b020002105f078f9332cf8fb2390be2f6be39abf40992dcd33daf0e3fbd9b02a23755b10002d4b94e2516134889b321d6f06792322debb0d311c761c6fc1fc4e699fbb87d312101c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df301e0adcda708417e2c0f2fe4bfe629ad37a477089a98f91c98e59c54931dd53adc358a7f780864294cb7a3af9d977519ba3b9af993613e9c5f95c7ad3926fef81e603a1f3b0367826fabd56cd9596b110c32aff6a41a4eefc4309a43264609f1b69f52331c010db6b4fb75210d33417fc5700eaf5ae67f4f5930b9e4148824deafaf82cb8301200554164113b97acd5bb67b91ee69296bf15fff6001d7ce77374afffa68fb6b6464fb83555790fac27de59e3fd2cb8d27384ee923ad99de0dfe2e498104b31e094164e2b93cb85abd82ed7afc408fad29b1ae74fe2383a08cc4a154686e2e08b59d3a7a56337281c7de9edb43dc6cb3eb90afe399b23d65c9ca1c542e542b0c2b976ab45e2e11e27816623ce4349206c2a4c8b7540f394c868685267eefa60ae93f2fda2b1fd5835506acf9d7c77b0ca08e1cc89569df1ddf8a5688a630810eb57b4a9cc83bcc39e7c4ea8069afc2c5f97cf41bf4894d7cd3ad9fee9dcb630cad06176bcb8d5cbed6a09a16451ea8ed80fd21e16aa136a720b76b8a6b03ff020f92ec55b78668ac41c5423de75eb4bfbc6c2c20abb74e7a82ed9d2d53510003bdc18588724e2ecc6afcbfb3d8765f15b00a006306778c04d305dd99b90ac10e92ac7473a379ba999316ba893d0ff320788ecc754632bc2bd565e0838ffc9a01adab99709eb702deb1c350954857856de88d5b5d007919c225f6d70e1b89a9067d40bb9918e657d8dd8741453877ce22b13a5442ebf963757bd48488f15b7b0e0c96c3fa4118c6beaf72693c64308b9343472784d965e04f8b304ee3ebdb4405d04d70208389b5200b771cae428b88be6fe91970e7ec0c2b9409f16a582c6a0b9b9278f65bb6401b18e5fe1031f52c8b036bd5afd0e984cfd3cf4c650a9940040331067730c9e58688286fa2533a4815eb5289f8860664d47b55f4507a7e5b0540d8b2d3a98a02caf4109de34289f04ca6b316f7a682328f3cef057bab3b7705e25fdbf9b361023602c9fe45c9d47615cd388b251d4cc13c667e2c8235e1bb0106374aa1ff0e61f1121d47abeaf0ac767d1bb1a0de2afbe706c1014ec8d146001d727248b4db192c5da6699d3a32b09652922908900267fd2e1982e1f05a02087c6ade39bab5b1bcdc2f6cf03d455f8221f9d937fadcb87c4070262bde23ca0edf3422c0787a2135b741d68f5b2dedf689f455f200378e57603ab0704c9ffc0c6f0c0d13b284111698cf0bae7b0faa9ae341a25eacbd9adc502df4a12705c50bbb3d708c72e496bb9913852765c676ae683a04479b93e117e9cc0cacb403720e8e685a2a6a8cba27590a0c1dd0f1c08022ea3590acf423121a3f1c8abfcb7302bc40bcff2cbceb474f0bbb400d74f17e76e9e665a7a0ad2aff650fb8ee41c605d45ab263f78d60a67567e2bdd2085cb321f348b27f92c2e570e0396a2595090832aa144fa375474f3ef8d95295f46f0c7cfa8058a4ff11bb0dfdec1c6ba9f60873f1a08aa38b6417ecd09cf92b1547eb6a49a694f0f6ed57fa157b6eedca46099248eccd40954c5bebc6f2bbd5de96ae3e2efe842a251a8651231bac54bc12052a1962e4dbdc3ff1797e1ddf401d070a9b9dd2ea2ff3ae2e207d10c02c13e50ca90ae0d17418051108f06a77fcbafa80fbbf563e334da38624525e1d203bd10018ce2730e92157810ef2c092a51dce81cdcab79dc271a47749b8dc1fd6d84d0e4e0e97ee23d09c70c2d2882858bc7a318ab516a1c9f3291824c5ed79baff2c05d9d93f18348c5fbffd2337c3ae6973eacbf67aeeda71ea745fd635da419fac03296b62aa4b3f4c554b7452db9ca1887250bc8c507d042db2b9b7c81d1018410863d23ad52e74b68b13a1d0b17fec9ac5043cf22452b98f2c05328707d3b51508645918528f4712d553db4e03325e11ac0a3b3794e74884ebc3e2d63a8c2a2a0af1dab421831e8336538d8441a5145eac4830ac0a7fe5cfab808e6fc23c7c4300a33fd2bcbfe92dc9c47d173af9be7f1e50da97ce5d88c36724e4dff79f6d9b059b47fbf1fa33e259b339f1f74c9831473b86e7767f4cef199c7b53d260f00001fe22ce255569940f6b460c6aa9eccb1afc191f786f00256aca97d7b465ad8e0ce21b01b36099a8326edd3d509bcc49b37c69fdd04d8716df564c42d01f589804824fab7c5a13b31e41ec62321f42ace7e7c301abd04a29c4692eb526a9f5bc0cfc47845963028fe9bcac2619255251235ff7be950066e41933d86114c4799c0f94394c9b48e34a39908cd2c676fe6ee42d21a09b2c148506e9cf72bba00aa7020850b7d693ef97ce9074adc966a258c87841aa76468e10cc2474f58690b08601a7d7620a3f6c826422ebd339b47e0566a4ef9d74c91f8733e68da260ff052700dd03dd70ff28bb938ac6c420eb8942d31e0222a2c4c5e07339503f33baf165074813beabf4ae379d0d093c16438e5c9e08c06d2909221278c398698a2a45710c1ac2954ffb0064229d43b1686dff63189c9def06fbc84d52c6dd1b45785bee01c43ba993220032fb7530872ebb42cd1b8d1b56cc98784ac01b242a0859ad5f0556be67160b9b6a676b3f5b36040947b4c8d97cb2125ee429a785f1798052780fa7acb882cfff3842d52df35bd957f462c1b493cb64852f8d3479b1d930e6cb042dacc0b2112af2fa9be170d164bef97f69cf0c7bb428f3677f8431aeb300b10d0b89d7dc92f2ee2f1f60a7e93cd7ac88b6f71b4c83f2df1c4f1df217795af501e274a6b4ba98db41785a1268a5c26093cd7799e2fc97fca3832465aa0ca8a80b241240171cc310cbe3835eb71edab3ca918b62a883f27be003be1984c9fced068d3cfa28d895556eb48d6f6f21fea79f6818297df8a030202ddf87c4b38d5202270555a24fa46c4b124af481e0a791336efc5d87d16191f7a73686dc330cd00a72431571eaa9a6866d114c2abf5a9599dbd8c6b8c6ab41ad155329b20bd9d302b896f7ef3859598c1f7312149d49c85fc32734aec69311a1f7241713c902ca0a578f78aaecc9f9783095216d583312116983c97da812058ba197a73b7b00a403823f17e7d12a20d8d2af791623984d8a5d42e8b81cc33d2075706f188b96240e0c70da64295b99aba8a1ca9a692f908224766dd4ab85b21c2e4a3561d92da70bb3fcb96c7067379745497875654d4135f2a367f9760e1101e1b7c8e45bd78801e7166d6480bcd5628467993ee7c11b651a3e97afffcb302fe91d6cf55036bc0acc531d9d6c3dc509c2878ed7378a82ecb182da633ab8d7e08241da06…7bf2dd855511f88a12a3000d7410572e9705c11b7fcfbeb9b1e2317d274526555e8104840abf75d25f845b7262d8b97d359ec39019b7e79750d3924564a757f61d0739edc3b52509ccd4822e0a009ff01e33619090961ffee25f22fb6132f3273d4a2be46de6f8ef4bbb8105fd7ca8329d986901e77ddc8364f56a841c66eccd7c29d34893077d396aa37f39e5157e56029141ed64a21ef75be7509c4787df1ad05e3106117d83208dffda5818ad4b9214f8d98525b01b283ac7b1e10342f546147dfc1e7f3cf1e0108070c30f760d0fa2a9681bf480b329ec479a7b70c6d90e6dd8d3b0292dade0859548bf22cf4c808691e59acb8948f14a5eafcb81e15a3f0af341b165712b33dcfa798c4fdc96d576a144d6186911e1efa3b39cea48adb7e325479e0a1d07e78c8eef802096af323acfe1f0dc5f09eddb6fdc484dd73b09ef8be8e302758bdcddd89c671361d9b56857efc8486e4d1639b0b0a70546f8323db524b6b95350a947a9f2b929badd89da73ccb4c46e36eb9f994030a6eb36a6615f0471ba343eb5b8eb80b7e07523cb99431029b36db074d8e8643c417b80ef6e2cee094a39a04be94c9d08d4632548574d71e758440fb5bc26a628d19aec66d263c7272a551228be488c132df8c56b961e203ea465354fff1931763bca9609b9392e8b3290e60f8df264746081f9400ed506e5ac9b15da69625fd774d3f3450ba9b8ebec6b6cca9822ed85d7fc1cf6dbdada1cb34970cf87cc80b4d0ce3e9b973a125d761f03d35508d209a5c37b17c8b4d6d5064d4c8df72763285cadda3b56e6c6e8b5b75b719c41974164fd7af9e2478cf0f8a8ee7f663b3e60570779ca891a939f7f8cd30d7115a1da50b6099d1817986ec9e16459563c26e6714b3bb4d169462c6b8469680ac8a833d1f5a7a3fb4a3320ea262c3dd7d0ba79a7bbde853287e505e1979a376b2a2d925f9dca00fdf29d3b76484e5928433e716b186badab0a9efbe1562bd221f7275e46520c3a70f931a45cf22bcf455d9a45f65d0b15f44631c8e79f4c6ff9365027db2d4b322a62dd9eb3fcda81e30721970730ae5732216f25d311cf39877fcf0a6e258f786b5a617b9cc2b00eec15ba2d3ca12bc6a59189ac688d1a7e14cafb9060b516438ff68ff6f6084664ca56787d2be64104a20ae61459c8df34f473beb2f99efd5a16c3cb008aaa79902e9d7a48e91365d8e411f7698bad64eb84c061d98954d4b98bc1c6f2de13c9dcd0a1354fb25081bce08fae4030628f061af5d731bb3b4bc9ff12e311daebb60005413b27dca8eec97eddda88b55fa193767f679fc012e6404f97ab5b7efc52dff2c0e523b8a8ab0f4ca0deefbadb9fb79bd57d49bf07d1f9f9d12b09931860df0444cadbfcce259f6e505eabaa4b7625ab3ae7d72c60b06ec95c07981b7552483d385a17d6179f6106e86c8609cbf2d948e16fb41e195b0a93f250b296ddcfe20a5bd50a46c5d7f5a0f284bdf8dae11f26821fdaaaeaa3a11fdce58240685ab2709caed3feab3cd5b38d150d2cf728a23339fdf6f5f501d08bac9ed7538a98610c933cada9b5307814fc8558fa44b2042edd8ea2777147a026260569cb893a9b302e4756ea008abdae8cc65a6962e9845d8ceba5e9f0e723d950f839c9975f10b60725d37361bd2ce557fa68a6e9a6069d9727a98fa47fda31deabecc5c6f48fa57884283166ab5d7b4918598a01bed5c3d178b07494d7ad6fd7fc2717f0703ea0ff1965e2c77c63f8e999dc40035a91123df458822509becbcc22386349dea118d70efd59843a4eb3dc9bec704626d93b8286d709c46a9171f02255ad28581d1a2e385f177e9ccf01b9f732b50ef827e812e09c819dc082beb168dfe07739f976023d208cbcc54b76e9f0ca7b7ec2ddf15fe23195241837d5bc0c690261b46f05b7020bbae7f59ef42c046a7aa498a245b11d04f6fea6c0bf565de3cc419d74f00ae2dc2c6311bab6c8f04100ab1f61412d7f8a5a615136ad7e8a4785d7717f34b32af22f0d677b019f6ce64bd0c94a6c6ef2f704df82d21c34f63ae6d6ff32a27e2205dfb077d2d8f5bbb6f2135f2e771f31dbb4e98be4d9fa86a204acc1bf7f8ad76c800f6ac2009916de7d3fe6a7201b2c0c260c9a24bc45516f2244f95e836f30e8ac041d46f0ed08c99de3d62779195c599a3ad158ff9a6efecacdb7924a2146c472f551b9160aebb256dce0236fc7d46be77cf93c9e80ab021bea750e0d9b4b4cda04ed408909ee449da45a63e06ffaf7e311b125f2fef63c2ba9b6d2f3bdb5b0d04c117908308cd37665cc6775ef4d49ce555d3286671bb1d150b16276e38233e425c3230a31a3bf7d5946e6e13327519880ab9ceca4259f966826dcdeb30eed9a993971d9961da3e98e0c60f7c3f415707ecabe41920b1a20723a7a99ce84a255ecf97d5b004ae0062f108a76c5df2cb9b4b5a548a8b82468bc1ddc3b62f0f98f60d8b5e933fda2f11ceae7340c8ca55ce5a0d0dcce3ed1a558456698350f818a035f4b9d43e7afd2dad4678a6029e68d99f1ec4bb68aba853cf408a84bac2d379b47a9a9fec4c3ed22207d08edc7feae97451b4257a876186bdb1095ff6a404b3dca3bf5170d0127b93b23a2b88651a6a546677536a6ff3cdddf5247c98290cdc09d5600c6693bd736f080cda67770a209827ebfecdf9c86ff131cd3a17b39188348db4ca043faba0bdf151be4bee2b0559ed43ea7d5c4bd5ca6287e5b82f896a2a561c915b7aaf0ab0c6a238fafa262aff20c9aaa6b47e25e7c811e12ac2e9ad879214804a9c7bbb5bcde50c7ed7fb094ae029f869b3a441e0750473f3faa1d614456adc475a02cdd7aaf9c48b45161eb2a105ea4b9f03d676fd34f69bac593777a74578ef0ade13db1564e4172576fe454edeae5bc523fd6048d4bcb5a5716f6da4bf01fd05738c182aed9dc6a0fe13584c520f2bde1ac54e3c10b34985fc9711f4b8dae5046b92929afb4c8ba3f7ca1c662b78abbfaf2febf4f4184c1f115d552ca0872c0af06b61516dbbe09c3b086712e3eef9a0a0f5c890417b4fcf656f1b4cdddeba0f5a63d3b2ce929f6c3032387b4d4bafef61fb5611f846da35d4e3e11fea2615070aa485e0828b38359cc30426796db346825191141a5b14211937b12924e0a5074932707d8f1e76c3c80d6a3c64518dd3691c5d5252ba28df354dbb231da4f40ab801a29f5769a3ad6866582966b40896d943f3b62f711988613b1ab59bc0cc0d6dd2ea50fd4afa1ffd250339cfc0484693d119961d7b4f17dc6116c62adc7d076ed6834db8886b1620aff0dcd5ddac2f0492f45a7c0b448a668688ad6cb1d101c451dd8cc0c762b544849ca04153936549ae3b39112dbe2ea96bf0b210ec840810b47be58637baa446509b07c58ffa1fb66585fd4cb01df05291fd4d7d25880483bb8ac4e0505a7cc5119ee45948309eae12ea41a8b83e0a612acc25338e4b008187156b0971c145f9d6214afd896b20a15edba78f7a087c23a30d894f688908"__proto__: Object
+send_coins.js?2:303 13192 bytes <= 13 KB (current fee: 0.002230540000)
+send_coins.js?2:313 Successful tx generation, submitting tx
+send_coins.js?2:314 Tx hash: 4da46a03139ab26295dba21cabcb5f6efc74b165f1df183e83d55169b6902f0b
+send_coins.js?2:323 Successfully submitted tx
diff --git a/__test__/fixtures/requests-modified.json b/__test__/fixtures/requests-modified.json
new file mode 100644
index 0000000..935d6bd
--- /dev/null
+++ b/__test__/fixtures/requests-modified.json
@@ -0,0 +1,499 @@
+{
+ "log": {
+ "version": "1.2",
+ "creator": {
+ "name": "WebInspector",
+ "version": "537.36"
+ },
+ "pages": [],
+ "entries": [
+ {
+ "startedDateTime": "2018-07-06T00:25:11.160Z",
+ "time": 159.51527599937253,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/login",
+ "httpVersion": "http/2.0",
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"withCredentials\":true,\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\",\"create_account\":true}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "cookies": [],
+ "content": {
+ "size": 26,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 361
+ },
+ "cache": {},
+
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+
+ {
+ "startedDateTime": "2018-07-06T00:25:11.487Z",
+ "time": 159.68929200007187,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_txs",
+ "httpVersion": "http/2.0",
+
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "cookies": [],
+ "content": {
+ "size": 1218,
+ "mimeType": "application/json",
+ "data": {
+ "total_received": "296850150000",
+ "scanned_height": 4819440,
+ "scanned_block_height": 1610359,
+ "start_height": 4751265,
+ "transaction_height": 4819440,
+ "transactions": [
+ {
+ "id": 4751309,
+ "hash":
+ "3153c385e55f1577cad35ab8327f1acf179a82a3c33691beac2d3dfb6c5189f9",
+ "timestamp": "2018-06-21T21:33:42Z",
+ "total_received": "296850150000",
+ "total_sent": "0",
+ "unlock_time": 0,
+ "height": 1600105,
+ "coinbase": false,
+ "mempool": false,
+ "mixin": 6
+ },
+ {
+ "id": 4754424,
+ "hash":
+ "b047570a0e8192204a7f47e8025f6a2593f47d7fa5def75ab2a927d9c1d0f2b9",
+ "timestamp": "2018-06-22T12:19:09Z",
+ "total_received": "0",
+ "total_sent": "296850150000",
+ "unlock_time": 0,
+ "height": 1600561,
+ "spent_outputs": [
+ {
+ "amount": "296850150000",
+ "key_image":
+ "4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7",
+ "tx_pub_key":
+ "5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf",
+ "out_index": 0,
+ "mixin": 25
+ }
+ ],
+ "coinbase": false,
+ "mempool": false,
+ "mixin": 25
+ }
+ ],
+ "blockchain_height": 1610359
+ }
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1555
+ },
+ "cache": {},
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:17.450Z",
+ "time": 165.1763129986066,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "cookies": [],
+ "content": {
+ "size": 958,
+ "mimeType": "application/json",
+ "data": {
+ "locked_funds": "0",
+ "total_received": "296850150000",
+ "total_sent": "296850150000",
+ "scanned_height": 4819440,
+ "scanned_block_height": 1610359,
+ "start_height": 4751265,
+ "transaction_height": 4819440,
+ "blockchain_height": 1610359,
+ "spent_outputs": [
+ {
+ "amount": "296850150000",
+ "key_image":
+ "4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7",
+ "tx_pub_key":
+ "5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf",
+ "out_index": 0,
+ "mixin": 25
+ }
+ ],
+ "rates": {
+ "AUD": 184.86,
+ "BRL": 545.54,
+ "BTC": 0.02091,
+ "CAD": 182.49,
+ "CHF": 136.67,
+ "CNY": 938.86,
+ "EUR": 116.82,
+ "GBP": 105.13,
+ "HKD": 1107.27,
+ "INR": 9757.02,
+ "JPY": 15489.53,
+ "KRW": 151000,
+ "MXN": 2640.93,
+ "NOK": 1148.19,
+ "NZD": 219.2,
+ "SEK": 1217.07,
+ "SGD": 187.42,
+ "TRY": 634.73,
+ "USD": 136.94,
+ "RUB": 8588.71,
+ "ZAR": 1925.7
+ }
+ }
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1294
+ },
+ "cache": {},
+
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+
+ {
+ "startedDateTime": "2018-07-06T00:25:48.523Z",
+ "time": 155.7823879979951,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_unspent_outs",
+ "httpVersion": "http/2.0",
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\",\"amount\":\"0\",\"mixin\":6,\"use_dust\":false,\"dust_threshold\":\"10000000000\"}"
+ }
+ },
+
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "cookies": [],
+ "content": {
+ "size": 935,
+ "mimeType": "application/json",
+ "data": {
+ "per_kb_fee": 171580000,
+ "amount": "296850150000",
+ "outputs": [
+ {
+ "amount": "296850150000",
+ "public_key":
+ "af40d936c933909f7bdd795e70129ce7a5779bbf9b44c4ae603ed826a397ca9d",
+ "index": 0,
+ "global_index": 6251022,
+ "rct":
+ "35ca97e4550d966247076aa1ca015e8ebcdc0d63594f48ad18cdd806c789b2f970273a47742ff06fa3549a004f97927dd89b0b126655bc2680ce3ed3bbb88e05f444932916512b22d51042a876337e9014f120a46cd103915b812779bda41e06",
+ "tx_id": 4751309,
+ "tx_hash":
+ "3153c385e55f1577cad35ab8327f1acf179a82a3c33691beac2d3dfb6c5189f9",
+ "tx_pub_key":
+ "5998bb39ed954dd90a3bde725b4e283160832aa668fcde400976759e84e84fcf",
+ "tx_prefix_hash":
+ "1c1ae56c6a558eb759c2382f3ffca3a557a6399d6133113aa5d8488cfa517c58",
+ "spend_key_images": [
+ "4e6f85b47b282a388c15075746d2a9e3fdab4e0dbfbcb4251ae50cbbb91363b7"
+ ],
+ "timestamp": "2018-07-06T00:25:48Z",
+ "height": 1600105
+ }
+ ]
+ }
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1271
+ },
+ "cache": {},
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:48.837Z",
+ "time": 158.4292709991605,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_random_outs",
+ "httpVersion": "http/2.0",
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text": "{\"amounts\":[\"0\"],\"count\":7}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "cookies": [],
+ "content": {
+ "size": 1723,
+ "mimeType": "application/json",
+ "data": {
+ "amount_outs": [
+ {
+ "amount": "0",
+ "outputs": [
+ {
+ "global_index": "2080088",
+ "public_key":
+ "e3b02dc808097301664c989a4faf7c5f2ec8976777b74dbe4b8cfecf8c44ca11",
+ "rct":
+ "bdeba6a579f63f9dfe344ae4754c8db083e1f87a75956bb2c90f93210b3d02a1"
+ },
+ {
+ "global_index": "6002676",
+ "public_key":
+ "bcfd234d10151e10eff22c7b84bcf7064f9a35c3e0c53dd315394cd66d1ddc8e",
+ "rct":
+ "5206721c74d96343283bb82ad0abde561f10913e1217b3826ed5cf33fe7c5355"
+ },
+ {
+ "global_index": "5980172",
+ "public_key":
+ "3fb2a5f3016c21721349e27f98ba734bf3d0cb577ba4640fe5db5904a2bb146e",
+ "rct":
+ "df525ea20e203f3d0c6ed5caf9e083e679ce6daee56c992216ec39849b12aaac"
+ },
+ {
+ "global_index": "6349826",
+ "public_key":
+ "85290b8d45e551067fdcb0ec89942e2aafe5b702dee8a03ce3670a2243775d31",
+ "rct":
+ "c85b7fe5e0fe219f9585d8808f6a32ed71eaeb4b663a525034a79b4d662237d6"
+ },
+ {
+ "global_index": "4281075",
+ "public_key":
+ "7c82f043d142a221f9f0a5f9bc4337270a58e3f2d8d4dfb6f8619842bfe0d1c0",
+ "rct":
+ "636a54cdae73d0517334a02cdc9b6c6081c261e250f510284c1289ca3ed5e26c"
+ },
+ {
+ "global_index": "2799561",
+ "public_key":
+ "69d3eb381e3a43fd41b19cc41004866c00274e9daeb07de3303bf7a949719d50",
+ "rct":
+ "733270f6b344c77dc8e463a95524fb90626874c2d8ae15b02578fe8404be8491"
+ },
+ {
+ "global_index": "4203500",
+ "public_key":
+ "4d9eed420a1844a32e588317c7a713fbe59d9281b45b7f3504b9bda307c2c3c1",
+ "rct":
+ "dc1afdf383ca54068da77efcd29ae628882db2fc93adf8c1e4a96da7d066d1a1"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 2069
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.1862710000007937,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.17300000000000004,
+ "wait": 156.43599999916879,
+ "receive": 0.6339999999909196,
+ "_blocked_queueing": 0.27100000079371966
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+
+ {
+ "startedDateTime": "2018-07-06T00:25:51.274Z",
+ "time": 401.30272299839635,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/submit_raw_tx",
+ "httpVersion": "http/2.0",
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\",\"tx\":\"020001020007d8fa7ef1f42ba3d85587de0499da67e8af019a940fc0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b020002105f078f9332cf8fb2390be2f6be39abf40992dcd33daf0e3fbd9b02a23755b10002d4b94e2516134889b321d6f06792322debb0d311c761c6fc1fc4e699fbb87d312101c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df301e0adcda708417e2c0f2fe4bfe629ad37a477089a98f91c98e59c54931dd53adc358a7f780864294cb7a3af9d977519ba3b9af993613e9c5f95c7ad3926fef81e603a1f3b0367826fabd56cd9596b110c32aff6a41a4eefc4309a43264609f1b69f52331c010db6b4fb75210d33417fc5700eaf5ae67f4f5930b9e4148824deafaf82cb8301200554164113b97acd5bb67b91ee69296bf15fff6001d7ce77374afffa68fb6b6464fb83555790fac27de59e3fd2cb8d27384ee923ad99de0dfe2e498104b31e094164e2b93cb85abd82ed7afc408fad29b1ae74fe2383a08cc4a154686e2e08b59d3a7a56337281c7de9edb43dc6cb3eb90afe399b23d65c9ca1c542e542b0c2b976ab45e2e11e27816623ce4349206c2a4c8b7540f394c868685267eefa60ae93f2fda2b1fd5835506acf9d7c77b0ca08e1cc89569df1ddf8a5688a630810eb57b4a9cc83bcc39e7c4ea8069afc2c5f97cf41bf4894d7cd3ad9fee9dcb630cad06176bcb8d5cbed6a09a16451ea8ed80fd21e16aa136a720b76b8a6b03ff020f92ec55b78668ac41c5423de75eb4bfbc6c2c20abb74e7a82ed9d2d53510003bdc18588724e2ecc6afcbfb3d8765f15b00a006306778c04d305dd99b90ac10e92ac7473a379ba999316ba893d0ff320788ecc754632bc2bd565e0838ffc9a01adab99709eb702deb1c350954857856de88d5b5d007919c225f6d70e1b89a9067d40bb9918e657d8dd8741453877ce22b13a5442ebf963757bd48488f15b7b0e0c96c3fa4118c6beaf72693c64308b9343472784d965e04f8b304ee3ebdb4405d04d70208389b5200b771cae428b88be6fe91970e7ec0c2b9409f16a582c6a0b9b9278f65bb6401b18e5fe1031f52c8b036bd5afd0e984cfd3cf4c650a9940040331067730c9e58688286fa2533a4815eb5289f8860664d47b55f4507a7e5b0540d8b2d3a98a02caf4109de34289f04ca6b316f7a682328f3cef057bab3b7705e25fdbf9b361023602c9fe45c9d47615cd388b251d4cc13c667e2c8235e1bb0106374aa1ff0e61f1121d47abeaf0ac767d1bb1a0de2afbe706c1014ec8d146001d727248b4db192c5da6699d3a32b09652922908900267fd2e1982e1f05a02087c6ade39bab5b1bcdc2f6cf03d455f8221f9d937fadcb87c4070262bde23ca0edf3422c0787a2135b741d68f5b2dedf689f455f200378e57603ab0704c9ffc0c6f0c0d13b284111698cf0bae7b0faa9ae341a25eacbd9adc502df4a12705c50bbb3d708c72e496bb9913852765c676ae683a04479b93e117e9cc0cacb403720e8e685a2a6a8cba27590a0c1dd0f1c08022ea3590acf423121a3f1c8abfcb7302bc40bcff2cbceb474f0bbb400d74f17e76e9e665a7a0ad2aff650fb8ee41c605d45ab263f78d60a67567e2bdd2085cb321f348b27f92c2e570e0396a2595090832aa144fa375474f3ef8d95295f46f0c7cfa8058a4ff11bb0dfdec1c6ba9f60873f1a08aa38b6417ecd09cf92b1547eb6a49a694f0f6ed57fa157b6eedca46099248eccd40954c5bebc6f2bbd5de96ae3e2efe842a251a8651231bac54bc12052a1962e4dbdc3ff1797e1ddf401d070a9b9dd2ea2ff3ae2e207d10c02c13e50ca90ae0d17418051108f06a77fcbafa80fbbf563e334da38624525e1d203bd10018ce2730e92157810ef2c092a51dce81cdcab79dc271a47749b8dc1fd6d84d0e4e0e97ee23d09c70c2d2882858bc7a318ab516a1c9f3291824c5ed79baff2c05d9d93f18348c5fbffd2337c3ae6973eacbf67aeeda71ea745fd635da419fac03296b62aa4b3f4c554b7452db9ca1887250bc8c507d042db2b9b7c81d1018410863d23ad52e74b68b13a1d0b17fec9ac5043cf22452b98f2c05328707d3b51508645918528f4712d553db4e03325e11ac0a3b3794e74884ebc3e2d63a8c2a2a0af1dab421831e8336538d8441a5145eac4830ac0a7fe5cfab808e6fc23c7c4300a33fd2bcbfe92dc9c47d173af9be7f1e50da97ce5d88c36724e4dff79f6d9b059b47fbf1fa33e259b339f1f74c9831473b86e7767f4cef199c7b53d260f00001fe22ce255569940f6b460c6aa9eccb1afc191f786f00256aca97d7b465ad8e0ce21b01b36099a8326edd3d509bcc49b37c69fdd04d8716df564c42d01f589804824fab7c5a13b31e41ec62321f42ace7e7c301abd04a29c4692eb526a9f5bc0cfc47845963028fe9bcac2619255251235ff7be950066e41933d86114c4799c0f94394c9b48e34a39908cd2c676fe6ee42d21a09b2c148506e9cf72bba00aa7020850b7d693ef97ce9074adc966a258c87841aa76468e10cc2474f58690b08601a7d7620a3f6c826422ebd339b47e0566a4ef9d74c91f8733e68da260ff052700dd03dd70ff28bb938ac6c420eb8942d31e0222a2c4c5e07339503f33baf165074813beabf4ae379d0d093c16438e5c9e08c06d2909221278c398698a2a45710c1ac2954ffb0064229d43b1686dff63189c9def06fbc84d52c6dd1b45785bee01c43ba993220032fb7530872ebb42cd1b8d1b56cc98784ac01b242a0859ad5f0556be67160b9b6a676b3f5b36040947b4c8d97cb2125ee429a785f1798052780fa7acb882cfff3842d52df35bd957f462c1b493cb64852f8d3479b1d930e6cb042dacc0b2112af2fa9be170d164bef97f69cf0c7bb428f3677f8431aeb300b10d0b89d7dc92f2ee2f1f60a7e93cd7ac88b6f71b4c83f2df1c4f1df217795af501e274a6b4ba98db41785a1268a5c26093cd7799e2fc97fca3832465aa0ca8a80b241240171cc310cbe3835eb71edab3ca918b62a883f27be003be1984c9fced068d3cfa28d895556eb48d6f6f21fea79f6818297df8a030202ddf87c4b38d5202270555a24fa46c4b124af481e0a791336efc5d87d16191f7a73686dc330cd00a72431571eaa9a6866d114c2abf5a9599dbd8c6b8c6ab41ad155329b20bd9d302b896f7ef3859598c1f7312149d49c85fc32734aec69311a1f7241713c902ca0a578f78aaecc9f9783095216d583312116983c97da812058ba197a73b7b00a403823f17e7d12a20d8d2af791623984d8a5d42e8b81cc33d2075706f188b96240e0c70da64295b99aba8a1ca9a692f908224766dd4ab85b21c2e4a3561d92da70bb3fcb96c7067379745497875654d4135f2a367f9760e1101e1b7c8e45bd78801e7166d6480bcd5628467993ee7c11b651a3e97afffcb302fe91d6cf55036bc0acc531d9d6c3dc509c2878ed7378a82ecb182da633ab8d7e08241da069afb8b06e63c93d5e36e33e4ba022fe2352061f55f4e5115d048e4c36ab56d9d558daf0015a8ec96436dd72e4ee610e0806adbb382a7ef9d3dd9774ed070abeec25d460e304ebc4b2d097bfa0f6982abe3f7490f0374a278f1464505e0233a1195e7af01ae7e06f814cb7266ca60722c53e2d3eb66a5a1f8cff91bad0da26b5716ab7106fceb4b4a1a613014804c5284e7c8d785512730c7f109d4c225f9fa00cff4d10cf7f120e75d1c37c5e5b4e9117f30fb30c3cbcf86a33225fb4f63f2a2071682045dfb733111c50730556e62bd49a471b71c9421b312e8805358ba53af071b610399e917fa1b4687093313e330b453dd98b3f7866bcbe737f5e11f4bd1c0043b0975b84a92c4cfef856688345ebf4ba84b95aaad4ef573774e448d7ea93f6ba10937ec41a174bc0965d667918f608b6358d424628859d0933cccdf51ec7d55f906438342b45be0e9796bb57225e7bb184602fb97dfb48260bcfa427f787a759c0f5a2a8929697f47c854bebca692175efb36276a5f9fb92437836e65d87bb3b70a09663579a35c801c1aa654eb28517013b5e877a003be057b875b35df2fe227090c58358156115c37d72c290912ee288600cdabb492312227fe659dc238df6e03def5668fd22573e63b245586cd10bc66d68942e01bc9dfb9d152a1a7898ce002daef4fbe574991fe37e0b8b69ff269e098b7b1640a47b370fb38f410eb0d5703536bbe4bcc900df2a526d80a413b9c9ed3ab75124ff7198d3a44e2ca0cce410fedf5f2ef7b33f93b43a26f4d30751f2bfb4b13ca42a3105196bb17e5c2653e0c00adac72a44baa60b2d2411fb239d0d0dfd68251d362bb04ab0a3cc169cf61049bdc73cf11aee0b90f7b26cbcdab987873ab69542cdef0bb8c125da7034e320dc9bc4f84a9827cd927165c3455ae9fd8f5325e606b58e3cb6db67bc46b434c0bf78b1ef5530c0996205b419dbde1a8b1a9ed21abf38e3c5a16d34d6091a1e4065c79fe8d78b95184e779434993a52c23e6e9c6db90e9db5b90e97a29def79302922934dd5413a93d183cd83ad88ab3d439ef39753c84588a8269a74f0d1972080fbb35971f624d31041842ed430c70d09171c20f0f00e0d0c4e8ef8149b9af038846db4d5b2d8a586e5f1b34803e52cab395311ac5bac25f2ce3cb2203d8070354da89b48c24c0da18b4db9f87d210388ec2655f48fcc42bcb78c367bef2aa0c543f7e562adc23a6b22445ca1389156587c0af0fa16aeaa1aa5e6508228e9c04620aafcb6408e66ad86f0258cdd04a376ca1f41a623e7cf36a800d5da91583029bb46e73b038d27c370d948571c442f65302d09c1562ba3ebde7e7e803f32602799726043b23a4eca27e62986009cc9e4419fe95cece9504cabdf6d92f01f105beee7e9ece3fb49b495922bfc23337e53baafebcad53a250b66d168a87b3a4064440985fe7becf778fadbbf8eb4d01c024039a4cdfd5f27edab16f4702b4c20590dce3157b8c727316ba8c25de3cab3112c385f8ca0895e5c775e5f34c17520cc8cf80d28be593f186ad7f6f4666f702df2566c16a5d5e1aad21694774f41f0003fbcb4502fcb5e1deff0fa2a458f6ff88bbc91cfa2e21cb42b0077da5d43007d68610e05172ac51036ab6a52140e14e1707dc325fe8efcc39c74883780e9404bcb78fdf38b2763ddd7458db79ffaf28305aef1534d7ec4112316a8acfbe3504277ef2e7dd36cc72869f1d53689d904cabd23085165d2933ea15fa491da5ed01f00468c47ba2185b241405d89952922a74c02ceb8617543b70f353a62f017b023dc53beb7b858019493aa1d2bdcf2d6eb7683d453d62dfd391b189c296bee006d7c85bd22dd02bf6448cb7af0460cc319913f9fbc357875bf5396f9e33fab5050aeb928f76e42175bb300f1e254f981d8bf22079c66b03169e4d04879495e80a395187afeb4d7e73edf5d4320ce49cc4774560697ed4c58e8b4214fd03d3e808d666a22ab6104dbafa95c6c0db8af94ea49f24fbd5c36d00fc009c451ce097083f84b72f2e5b9b1dec327c4f14d43da764ad36c5e8e8389453755fb77157dd03f9e97ed405828144904fb6a2f578e1d39f7f30621d9a483291ce50391c53690a76b217cdc7d7ac7a1ca3add04d303a920713db149ca022ad49a83e72ea7fba0c6c29a44633d1c056ddbffcfd43cd1ad4b810cddf796ba994ddda1d63cebf8705e743ff022e0ecd99912346a9c2b892e5e4ebc3e52b98e60262d96f336771770349b69d7560d297a73658c22441c5ce1b294065ff4f3da35227f7684e3a445001910975c311f461abd40ee2b56ab71f105770a4bc1a581b1221495023353aaa01d4bd115f5a8d9ba37b3b41dab0cb181f07e60624b755e79e1abfc7da826a6d08cab5edf981c43ec78bdcbf74313af449bbfe3b4806a205de6adbd2249015aa0b0efe4e92b667f66f2b8899084941fce413edc2e4a5ab44c066236c6b2a527707fe52311427d13f55a87cb3a361b196e0faecc106ca6bf40e968b057f64cde106b2b446d99da75974b26266573f1a641b4c7b477c61b370116d63267471132806f9bdb81ab2a81f03165e2a5b2d32f1f2b2cbe86d95e3c6f8bf8ac6a2296ccd0c2e6412ed8f74082f073b6bc2dd63e59ead35112c5c9362610a6561531b931b09f21e5bef71a01433b29402e008ecebaadda2bd8bed28d0cdf910ffd573434c0ac5781c5eee63cd99e97b5b319cd3925607dbb371a360e2d5e070f161a063f10946793313125f55f30bbabb52e61db65e5ae507cef64f08f501417edef3da5e0202836fed5f531a64dd41aad00a1dc39ec13b49d1ebd7a2a6b5867d7d71db0e50a393ec33f28531624bdfa3ddab00ab9239b2bd54ea858920f34c26430045898f52b419f7f04f7b3b8e604bf11b20ed058e8ecdeae5be4845aa5f14f16c1f953d8e653029b087bcd4ab9953c1937cee84f81c393285c8bc3341a5eb5bae580b5d55cf6f0ee2afbea795764bf5d4ed0735367a0bf65b6f69732449e3dfec547d93d22056ada1eb1c35c2ca1924e07c0e6120b818d6941755efc4dae89d0cc039762bf5e003763a01910d434d3f0c3e71af286c55d30c9aadb2a480b8d0938ce43c202968df06659dc14e713dd6b2899be1501718e3003be5220b7e90034244ab3e9db020600bdb9b2a17b9d87e150356cb3f3a7150bf305d03efb0aab95cf43e5cf124a8f5db6c37b81f7841b81a7cc5c2ed6d3b8124ff95f9981fc93d732dbf470403a44e0f46ffff9bbef09e2fedbf9b42600918567617ea28d8f42e6b1d764a340ea317a4462b1083062b29560d25f2b35a82bfdf000033f8a0db28e7314d4d784f6b9d0df70928cc30b94119cf02085884a237b2ff7470e299726d06f430f72c73a1727446f060a3d98ee0c867ea162c197bacce848ebc423f3dc6c4835e8bbce299084ecf73139b024c33f215bc132fe77cf326e2da2cd18b3008bf16447ff4b2a4a5679987564d369e8936270b4adb3128e6e88eb75205f9de36088fde3a7d294c7c38cf0d0bb02c1160a6695550423737668f1646ca65b93d95547097a2fa8bd70f74a623e5a3c9c3c6b70097bde54853b7517c76c0d36e59cabce4bb691bff14524d2a0eac2155a9af73a4e87f0d6ec67d4a27840ed0b7134fa93d751588a77416daca2b68d054a7e74dc752a9506090e79596c9b558f085853055d87852c251baa10a9607b86c009ba3ecba6ed2457d499128f26fe632c39e5ccc46fb4b7e225ee282d9883abaed6e531b4f844df3d5cb547152742d0a0744e40fb9284637a34dbd3a4ff4dae9a07904b36e539f56eec6ce7baf51ed8e8189b242effc3bc60908d6b62739b6d6e8206e4223160eb293eeea2992c0dcaf60c73632ce196441692c1f5131526818f5bff1cd83a75e414e595e13a082a47985bd505cf256f5ec516ae79c1117f9f7820594d696a7714d2151450127dee286f8d18de13517b24eda31f18547032200489505d2f53101c962641b7275b8a48296f339402f7f48ac2674f669841615752eaf495a6dac6bd4d6cae58be8c779890d201904221ef4a1a0f87c523b027fa43f52f37ea06f8a19070fe04f7533dfb529d802a22d7d299f3abb6974611456060915173bcca396febe1bc943150131d94a2406565957950fea0f11421c7ae2b7e900f46a8c947b7e5e724bd981c00a1c93070a6dd51bf406b876c5141f7fb5bd84cb788dda0b822bdc4263e500c85c079aee409cc00e411628ef38f9c948a3ca7989efe4592d7702aa846b3a1cfbfc5e4a1801ad6a63386bdaa004af3bd3182a082ae5f17d30862c5a0de12078aba63b51ad2ad9c695257e90cf997464da236ace2f90020547003d30e8f63998ab705aa56fd4031397d36526e09faa72c4789380a4df227104cba21542a16f64b19ba96a84103513d94336e5077c76eb3a301e3bcdf507c9a0ab6b7f98d6533d6cdfe04f72d24042472cd55b68ff9d9e1d8155c21e7df17066751a99ccc5147d0a828053f77b059879160522a71a95f14e058a51bf443264700870541f074c182b1ca3d1250b6e03377dea966eeba2bd820d680f35dc7226e4a8faebc88da83b49da2b5e7dc897dfc7998961a5fb82372fc03ddcbb6043284c80358ec0f2bf9cba7e3bbce8696dd16bc32819e8182b5528b3ac82df464b286b944a13704dc17befb80ed61d24a2b2407fd2bbf0fd1e547b1437db807e113c9e514048342558691bb18c8e2fb7318a5dd730f570722b73446476e28fe2c85f1fb908cf4898025d519f075bc16c0db7c580f8d5eab655c7fb355eff1463026b61b092594a971e573cbc71f2c6fd23782171dd7c5a41d980ac1830093a2ba8be6de21cfe8a07463deb4428bcf1382326c02edf5e1cc806779da45ac34d5c0db4f2382f5e08be2c31fcdfd93be10f4f6c0842938f9080674ac1ce67acacb5727ac6bfc7995b29f02e324e59f291a5bb4751f66dd6200ca2c9c8af94a1d6c5531e6d332d8a91c4e00c21fc1248cede72d88c8fa61f98513bddd06b910456ee60635081b1627a0e01c67e8312f0e0fed42145e9c514547cf5b5bc53634a11729b9eb6ddd217568a49772e18e020d0d5ddd2220d938c753a044fb4bd49d3b2002e951849e32ad1f37a7058750c627f4a94b03d048a524eee29bc8de8ea1f76255b402b63f9b4ff050aec4af6c6ff1ed7cdd62d5f9e38e5deb73068b2a49a460adc7b98f64e72729ffe7d63662e25ea421d73995c22633e12ba5892f6c2b1dfdbe1f85bf5c22208bf1399bcefb5f9357db7c84bca879fbddc966d7adad62121490505b5b95cbd545753fb900ddf50a6a6588828f8f95252b9c7aaf321303f19c496bfa2af90104416a10771ca02d7daf9204daa3c0d9c8eccc7fa80234995db0869947e7412e5443851495e89259f92bee8e5b33c51e08f3dcfdcfb2a3b989ba51c16e820ca2f3bbd34c913d8ffd75d36447bb2539e2f424297ac39ac5090463289f332a4555c6243315c3e2c241c5a2534c08c6397ee82dffd9b8b2dca1d64ca3cc29cbd07e67bf095ab47273a7a70b59cbe936cd018be9d0c3f8a224c405de0a58cc5bc2cac7b6e6f514089b036eee6b0904b1979e6077b6132d9fd669788f5031d3f88107a857e35117054f9196a197e97ce89507701e23077c93b98401ad75a8a30a77317952fca30ec28eef2179699f93dd1d07b998118930c481260cffe5d098a81048b0853bc957f61c376a392bc740d4bc083e9bc4b62ee04daee640c5339121174af2f2c3c78d716d97497c0518d0a7468d5ab25b59fab995c815f65550d52567ab22ce7215eadb2a5bf07ff8991007d381ad3b18f9af8307e97282b681c37a74cc1083bc079e49b79d08e8d15d602b65d2f153c27e54407d80a4dbda35acc29220996375f7819919ebd9ec6999a0aa1ee2b5a2f8cd95c1ed40ed310ec102b872a2f0ae40a383036398a467a8450032b8dc50fb3aead263d9004dee7f10e12aea154ab97f04f4a016d39b97742d00c837350002903c83f056c5d64316c7aafd83fe355a64802fbef14516b197dfa001695103d494c48359e58bbcd358b0eb20d1ef42e3184594fcf51443ad3cfb4047ced01f064c638698c690b9179b61aa68d369e35d5ac8bc65dae702cbc74890dcbedb029dd98055213866576a3d5e0617d063dc2470e8d8e5b83574b5687b20990a81ccea7787be7d38ba9f21b5ba3443049ddd82328d4e8fbe6b2817bf9380b46f9e643f6a593081f7f1d5d97c69b0fe58c9cc6074c373f8a71c90a12604b0884716acb458e7dd490b5f0057d917e586c69cafe9fbe47f69bd64c7e61b5b80a6825520c5327772c2ea8d7450c3d764c4d08cdaa1562a1d990eb0878134d35012482c2f5c4ec38fe5161e00eaf669187f2ec98584a6735a9341cf66b4827dc0bf512a1d4ead70ad08445b1a9af017ac6b375f9c5f0103d0755e1c13046d2c102a041f2c79577ed00c330e1a7c8e3a6d474dbf6d9ef9c4e3217cc3fe611967a0bc5b96d0815ad46b04d043dd39ade61805d12aff54f4f60ca89f2ad77433c390822c633e75cba3a385958fe1015c502315ce63e440baab882dbeb4ba118994103676b9e83e6f6d2d53db075f662469e733778dd8082c36cb246b03f9701c580015d55bf2ca24a02bd3888eb95597f654d04e84fcd79aefa00125091db32898f0545a6b7695838c01d0be144395d40af9d8e69b0ce2f1326bcc3d57a244be2400031cbf5ff387e2439cc3b492466343d35a6a7ae244c0abdcada6cd0e37fb8170ad2c7582aafefc50fe74f4f05bd1b1aa0a0391c18b2e54f7b8078517ce5072305e4a509c5ceb5165a148a78c67fc1de9d97d4a7c0a9e6d152b47e2320721c200c830a015279a3d84acd32f7070af60f82db8fe6f035973f4250dba240999a5005f96f27a773fc9e7b68558f5f98699aeee07c645097111ec1aaabdefea774690482af54c967848b2cd65ebd7f9bd4657292864f46e4620210ae41c1642357eb0985f8b1dd6e9d87fcca833964873224c9128e899acd85f74ee625809e9f045907e717b4e15183bf2fc55a1cae983526e833255b1a38a532c3319ae67428e6b604f1aa90a29afe47935795d9b629abe0654a9375268d11f91a7f0e13a74c9a380ae66988a69672f086bb939df9590f0e495364a72c8cb298e98bf04a0709722f00449422874e793b6029a95e26724609dc06fe9086f6ae690b34dc1b29427b430350a43678dfb0ddde1db147da17c2e6b3fd44dbe186dabc204f47ab241c2f560d4c103099635d550200103c05232988f48838d4b56b401242852c4be6ef991203fa32db1bac264e7663b866f21314c05d3eb006a4fc2e00fabf30b4197b5edc06b8777c5560b10549622a28ef19132a9e3960f54f3c449c3039985b24be69e80f17df718ef65afb8fbd4b1d6eb3fa982590962d8863720a6f7080f6d73251360a62b8674d157eb1a2d313631aa446635ec453090c75cb7470cbe60044f329130b829cf76313e13a5a58dd5378ba3d1bd7c7a6b931e63f233204c7caf9d4fb8d0b036910d5fd088dff93e7f6a43888096b38b5e0d39bb94b2c570e94258ba8f60143f516aef8442b816bcf88ce21eb62bf58874644dfed54d13ecaec398846950a01b76ab69ed1324cdda07cf14831bf6554f1ad9952defc2186c66da67a6265049e01bb01a69c67b81bc0977ce81b13cd3058ca82bc212d17514ac19cc40c9703cd8a3c8253d0163d494a9bce334b798aaeddece4c546509ed61953f8ed357c0e861c7bd6ec632e7831b57a9d796b0513a715ab7348db38f3dcfc8474a82b81076e69287de84cc3bb1e8326f467b961f0dddcd70a9d104cfad472d3661bada40b1aafdc9b6fcaf21ba61e8e5367aba0374fb927c375f90b87350bb0e0dbd8e105bbc62c5fc32f99f4bed49baa2f3db5a4705c4d2feaf3cd244d4d28d6f6a1400b5384e8f695e1449db4ef23f7b77618ed7a366eef3ae38d0b36aa19541a5f1d0b26c006bdaca798027322f8294933ad9d877fb79cce3cd756b94ed2cd311cb40db8cd46406725c9e7fd92893cdba0d1001796a286d9b18707f4ee433f90665607f60f5caadfa08817e1ab0f440b26dfb5163469c871bd7e0bfda5597597a2d40265d270c8303c48231c97b1807ddd444105aa6e195df4c208a0d07ab7721e49096e2b8edcaab76fc8729fd3bab94895518e3549dfcfe5238ef3f92e912e23bc0f032af4ad5abedd0c10613e50c26c9522902074202bd2c93ca60a7fdf4c5dff06e27594188bd5c6fed2874df4acf8b6d2f412959c2f274ffba18d040909985f09038124541070db29455f8d58c33323798cb03cfa2dc0298a856e64d4d7eb190f9c47bbbf7c075e8cc3ac5f980cad86287d1940dd8dc0ace92241ddb49d454605cd9229fe4080ca8e4b34bf3c35c3b95436d64f61d321a30f402f2695b6452d04f4aa87a45504333b39c487f05fab5ac541893bcccaccc27313293c20da1df10a1fe1edcb7962bd4c9b211472947196e01ee99d2d3f518710241dfc5f06a3ea0f53b36bc3696808aeba745a58c8c8b27f5bfeda5676a95596cfc24e7aebec110f851e464baf54513501af9262618d44cb81eff6d451d2ef90868a0b76fee1ae0f2392eca80014d625cf976c763f86186cf984943a0324bb38b8d23d97a2dae7002590aa56836825bee4770d74ded76b9187ed26ce8795d96e4c211056b7d8380f3e2ee2b7f4b37342968ebb1706e85233ad8fa15a39cda0417f86dacd35829a0053eec03966ac62876503efabf4407da1fd5eb13b542cbfa9ee755725cfe9c30f1235645468cbe11d875c5ebf96363b25abefa920867af543bcbc4148d5f1e50ac520452ced483e258ffb7ace14531c5c7de112f4827c594cbdaa373e52f54506833713e2d2fab34ecb5228c7c1c1cf402be69654c5ace0376d34ef7339383f0a2243fba2d420eb964593f871e18f5764824976f2f4c28f8624d9921237d451068ed944427dd822c8789064dbcdadca5c14b766706c7c3ac034875d44e94bf6047e1e989bc3506445c88c08b7805bf5a9ff3bf1636b93e98f039cb762d667ae02eca0fa2e30835f037dc359ce12edaafd46f064e0aa513d1e3239e0b63af879090a56c3d72748837b84bbd59a55a15c15b4e44d110532a020b9da108bfcf9530c3e1a49bf0c90143ecdd1324ad2beb6a4e1d033f46ceb918f4259d5b17bad3304e40fa0c77edf3afee63799c94b470a4dc3c89d18a43dc8dcd31aac6d33a23801a3548cd48aae4fcc39d0c2775ea1a26290f6d7c6d49506ad8772b1a356a8170bd0be3d0d451aa618d6312d6be5a86a2a398ad051a7e5660a454a4c45da41630fc9e3af071abd46b85a07749d1ee6287a4afb898f7c49655763e34a9ab47b9d0d750e5c0b8526960a576d55862c3e75f97716f211903d808ec860f41361129c0fc8cf440475e64a92338a37ccdef482ad7d9ad75a0a94bfb9007db701548dad0097312ecdb25219b47f5f44a707846ff2c180198747102dad0e37f3bdda0cba060aa8fafcabd48672879641167142c0024458aa118c2b6189ff95812c56515c06adf80b43718fe494419e883abe0f33e36d4e18f6cdfeb8edb121b81d0db1770f2705face099e6bbdae0cd485d486a8c2018d49bfcd956704f669edb352807c086c9edd1d0fac0eefed63077d6022aff763c8d34e51d69b26016740638b477606f68a07a3089aca2204035f772b06945823375fff78fd21fa554b16688ecb210b7291f1617493c6913bae6c90eda5935cc53a50c933e7b3b453b68f3871885b073c7a25563a66104e4dc28007990565ec9e2a3382f9924c6a63953af4929d4d0cd3282c0bb4c5cb3f2aa4c1d88751f15a5cb7ae8687034b6ab38d46b76b0bcf0967ba42db29eb1d7c458e923e6c6474e79ef469e92805b30a3a6e6e9101c53e0ce747701d014c338171df6ba79f8aa6234a49e78a8039e2ea9b671feddffa7d03cba531d055be1808d6fc2a6e229087b77a3be35e40ab4a6f378c216ea05d1900cd1aa21bf3bf0afe40e95c406000b49a429bdeeb563f32b87bedc1adc6e5e501c051a69901925053f064ae1e8054e34500b97eff9f974292eafbbd85715c2500e45a20f16556e09b7bb530764582a59ec40a4af28d95e6fdfea3f10ce35e2b0416e209c92a5045b8d7af4762cebf2d6d97b8960d9e9565e0549a2488a9e8b50c985f5395774fb807d49a0f84e0983fec0e32fb5342a3704c5d198dddc945d806c3262dc93f96a1b1a09c2bd461f324c068aae59d5473deba8ba6036ad96c670f42667ada96da901567e544157b12f56159bb7c3f83b90abd1b9e7eb948f2e702c9eca93744830f0dff8b7bc73a1458b0f8d40e679afd1e22b09c93f2c9729602c6c81026f8f02991c094a38ed0e6062508974a8bc69e8d6e6741a45eabc7db0665f8a2d879de17fdaf7ad1b210f337b90aec1c1c3676f244aa3852a77c05db0c05b9936bc10f0a327602aa2109bdd115f8a3e1081c1b0c56649638207d2dd9086c6ab885512fec9650ba288df5133c9dc87cb7fe81919fafca783f1252c2930332559bbafcdc830223837a7b78a7ce137e9f4a4e3e7d1195a494fa6d0ca9a10659c0df4ee0c9db91dba17acad08e5f6ccae71012e2c6e4b37894520fffe9620d66a72a1e4f04e5fa2abe93ef7f4b2e9e837c9e0f9d962b5c80f5e8340874610358576c23403b2d742c587e3827d17c6f41b31f9da4030207aaa2f4c32059f4017bfa4c324be2ac00a0aeabc7756ff2e16bdf39ed6b0c313f01b1acce8bc2b408a0cb3eaf9af9b68dd17f258c0bf0c536b19040d6bebc0a9dac7fbc59398b520891a741c4183a851c1cbcf3f78f30924d85ea2d907a6ac8db9a1455e4c672800a88629853fefb86800fec6631d77edf591d56199d5980bf3c86d9254773e02508be6a488b032686134049415bb194ad11ab8e4b1e6304274091beccc795e7b604649f106b395bef6f30a1dca330561709cdf7295557410d040543c279e169a908dc3d94c4809f0a7dd92ad8cefb014f5d47681b21d6b955fa894eff5b57cc5d0d1b08c01c4e892af363e6560a02212edc1fa645cdbce380b3e010d3c7f473960ec86e8ab8d2c235f594cbb2c2fa62f27b4ea4ab9796b8534688fd9819ea529c023ab370c6e482633801e3d588f3a4433cef5aaeb633547e922dbb35410af8a803156b64daf9633636063a37c4adc43e492023e09137099a715a690a480119f60d9c2f1a110a6590ff1b7774e0fe3e4a7419226448634c129aa1213e560814cf0b28d28fbd4029688f0d920a5f45c2a06bdea6c38f731a8bf741ba106bb8a3ac0e35fd8c015995d55c0733a18e66705dc84dab552aff7d2ae654b903f6fc9f1305da5a5bafbfbb22df241e211416fa902eaec589c7a22d6bf160a884e5534c5e0a685c312527c037f3196d4122f4d42c5a683d0e555af2a271d8d68faea80dfa04bc27bdbca4d4b3c14ae0745ea453174a6e6e3b3014dbd23ac3c8a413b5e29d08524227a74d9da9b38d2ddeb8649eb9d46bafca1453fb6ab89112544597bf2dd855511f88a12a3000d7410572e9705c11b7fcfbeb9b1e2317d274526555e8104840abf75d25f845b7262d8b97d359ec39019b7e79750d3924564a757f61d0739edc3b52509ccd4822e0a009ff01e33619090961ffee25f22fb6132f3273d4a2be46de6f8ef4bbb8105fd7ca8329d986901e77ddc8364f56a841c66eccd7c29d34893077d396aa37f39e5157e56029141ed64a21ef75be7509c4787df1ad05e3106117d83208dffda5818ad4b9214f8d98525b01b283ac7b1e10342f546147dfc1e7f3cf1e0108070c30f760d0fa2a9681bf480b329ec479a7b70c6d90e6dd8d3b0292dade0859548bf22cf4c808691e59acb8948f14a5eafcb81e15a3f0af341b165712b33dcfa798c4fdc96d576a144d6186911e1efa3b39cea48adb7e325479e0a1d07e78c8eef802096af323acfe1f0dc5f09eddb6fdc484dd73b09ef8be8e302758bdcddd89c671361d9b56857efc8486e4d1639b0b0a70546f8323db524b6b95350a947a9f2b929badd89da73ccb4c46e36eb9f994030a6eb36a6615f0471ba343eb5b8eb80b7e07523cb99431029b36db074d8e8643c417b80ef6e2cee094a39a04be94c9d08d4632548574d71e758440fb5bc26a628d19aec66d263c7272a551228be488c132df8c56b961e203ea465354fff1931763bca9609b9392e8b3290e60f8df264746081f9400ed506e5ac9b15da69625fd774d3f3450ba9b8ebec6b6cca9822ed85d7fc1cf6dbdada1cb34970cf87cc80b4d0ce3e9b973a125d761f03d35508d209a5c37b17c8b4d6d5064d4c8df72763285cadda3b56e6c6e8b5b75b719c41974164fd7af9e2478cf0f8a8ee7f663b3e60570779ca891a939f7f8cd30d7115a1da50b6099d1817986ec9e16459563c26e6714b3bb4d169462c6b8469680ac8a833d1f5a7a3fb4a3320ea262c3dd7d0ba79a7bbde853287e505e1979a376b2a2d925f9dca00fdf29d3b76484e5928433e716b186badab0a9efbe1562bd221f7275e46520c3a70f931a45cf22bcf455d9a45f65d0b15f44631c8e79f4c6ff9365027db2d4b322a62dd9eb3fcda81e30721970730ae5732216f25d311cf39877fcf0a6e258f786b5a617b9cc2b00eec15ba2d3ca12bc6a59189ac688d1a7e14cafb9060b516438ff68ff6f6084664ca56787d2be64104a20ae61459c8df34f473beb2f99efd5a16c3cb008aaa79902e9d7a48e91365d8e411f7698bad64eb84c061d98954d4b98bc1c6f2de13c9dcd0a1354fb25081bce08fae4030628f061af5d731bb3b4bc9ff12e311daebb60005413b27dca8eec97eddda88b55fa193767f679fc012e6404f97ab5b7efc52dff2c0e523b8a8ab0f4ca0deefbadb9fb79bd57d49bf07d1f9f9d12b09931860df0444cadbfcce259f6e505eabaa4b7625ab3ae7d72c60b06ec95c07981b7552483d385a17d6179f6106e86c8609cbf2d948e16fb41e195b0a93f250b296ddcfe20a5bd50a46c5d7f5a0f284bdf8dae11f26821fdaaaeaa3a11fdce58240685ab2709caed3feab3cd5b38d150d2cf728a23339fdf6f5f501d08bac9ed7538a98610c933cada9b5307814fc8558fa44b2042edd8ea2777147a026260569cb893a9b302e4756ea008abdae8cc65a6962e9845d8ceba5e9f0e723d950f839c9975f10b60725d37361bd2ce557fa68a6e9a6069d9727a98fa47fda31deabecc5c6f48fa57884283166ab5d7b4918598a01bed5c3d178b07494d7ad6fd7fc2717f0703ea0ff1965e2c77c63f8e999dc40035a91123df458822509becbcc22386349dea118d70efd59843a4eb3dc9bec704626d93b8286d709c46a9171f02255ad28581d1a2e385f177e9ccf01b9f732b50ef827e812e09c819dc082beb168dfe07739f976023d208cbcc54b76e9f0ca7b7ec2ddf15fe23195241837d5bc0c690261b46f05b7020bbae7f59ef42c046a7aa498a245b11d04f6fea6c0bf565de3cc419d74f00ae2dc2c6311bab6c8f04100ab1f61412d7f8a5a615136ad7e8a4785d7717f34b32af22f0d677b019f6ce64bd0c94a6c6ef2f704df82d21c34f63ae6d6ff32a27e2205dfb077d2d8f5bbb6f2135f2e771f31dbb4e98be4d9fa86a204acc1bf7f8ad76c800f6ac2009916de7d3fe6a7201b2c0c260c9a24bc45516f2244f95e836f30e8ac041d46f0ed08c99de3d62779195c599a3ad158ff9a6efecacdb7924a2146c472f551b9160aebb256dce0236fc7d46be77cf93c9e80ab021bea750e0d9b4b4cda04ed408909ee449da45a63e06ffaf7e311b125f2fef63c2ba9b6d2f3bdb5b0d04c117908308cd37665cc6775ef4d49ce555d3286671bb1d150b16276e38233e425c3230a31a3bf7d5946e6e13327519880ab9ceca4259f966826dcdeb30eed9a993971d9961da3e98e0c60f7c3f415707ecabe41920b1a20723a7a99ce84a255ecf97d5b004ae0062f108a76c5df2cb9b4b5a548a8b82468bc1ddc3b62f0f98f60d8b5e933fda2f11ceae7340c8ca55ce5a0d0dcce3ed1a558456698350f818a035f4b9d43e7afd2dad4678a6029e68d99f1ec4bb68aba853cf408a84bac2d379b47a9a9fec4c3ed22207d08edc7feae97451b4257a876186bdb1095ff6a404b3dca3bf5170d0127b93b23a2b88651a6a546677536a6ff3cdddf5247c98290cdc09d5600c6693bd736f080cda67770a209827ebfecdf9c86ff131cd3a17b39188348db4ca043faba0bdf151be4bee2b0559ed43ea7d5c4bd5ca6287e5b82f896a2a561c915b7aaf0ab0c6a238fafa262aff20c9aaa6b47e25e7c811e12ac2e9ad879214804a9c7bbb5bcde50c7ed7fb094ae029f869b3a441e0750473f3faa1d614456adc475a02cdd7aaf9c48b45161eb2a105ea4b9f03d676fd34f69bac593777a74578ef0ade13db1564e4172576fe454edeae5bc523fd6048d4bcb5a5716f6da4bf01fd05738c182aed9dc6a0fe13584c520f2bde1ac54e3c10b34985fc9711f4b8dae5046b92929afb4c8ba3f7ca1c662b78abbfaf2febf4f4184c1f115d552ca0872c0af06b61516dbbe09c3b086712e3eef9a0a0f5c890417b4fcf656f1b4cdddeba0f5a63d3b2ce929f6c3032387b4d4bafef61fb5611f846da35d4e3e11fea2615070aa485e0828b38359cc30426796db346825191141a5b14211937b12924e0a5074932707d8f1e76c3c80d6a3c64518dd3691c5d5252ba28df354dbb231da4f40ab801a29f5769a3ad6866582966b40896d943f3b62f711988613b1ab59bc0cc0d6dd2ea50fd4afa1ffd250339cfc0484693d119961d7b4f17dc6116c62adc7d076ed6834db8886b1620aff0dcd5ddac2f0492f45a7c0b448a668688ad6cb1d101c451dd8cc0c762b544849ca04153936549ae3b39112dbe2ea96bf0b210ec840810b47be58637baa446509b07c58ffa1fb66585fd4cb01df05291fd4d7d25880483bb8ac4e0505a7cc5119ee45948309eae12ea41a8b83e0a612acc25338e4b008187156b0971c145f9d6214afd896b20a15edba78f7a087c23a30d894f688908\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "cookies": [],
+ "content": {
+ "size": 20,
+ "mimeType": "application/json",
+ "data": {
+ "status": "OK"
+ }
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 355
+ },
+ "cache": {},
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+
+ {
+ "startedDateTime": "2018-07-06T00:25:57.451Z",
+ "time": 165.87528899877725,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "cookies": [],
+ "content": {
+ "size": 969,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1305
+ },
+ "cache": {},
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:26:07.441Z",
+ "time": 172.03327899891883,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "cookies": [],
+ "content": {
+ "size": 969,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1305
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 0.830278999999864,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.09000000000000008,
+ "wait": 170.11299999871525,
+ "receive": 1.0000000002037268,
+ "_blocked_queueing": 0.2789999998640269
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ }
+ ]
+ }
+}
diff --git a/__test__/fixtures/requests.json b/__test__/fixtures/requests.json
new file mode 100644
index 0000000..2e7e9ad
--- /dev/null
+++ b/__test__/fixtures/requests.json
@@ -0,0 +1,4403 @@
+{
+ "log": {
+ "version": "1.2",
+ "creator": {
+ "name": "WebInspector",
+ "version": "537.36"
+ },
+ "pages": [],
+ "entries": [
+ {
+ "startedDateTime": "2018-07-06T00:25:10.474Z",
+ "time": 863.6693799996762,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/login",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/login"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:11 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.3603799999976764,
+ "dns": 178.158,
+ "ssl": 175.32299999999998,
+ "connect": 523.645,
+ "send": 0.18500000000005912,
+ "wait": 159.4730000000004,
+ "receive": 0.8479999996779952,
+ "_blocked_queueing": 0.3799999976763502
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:11.160Z",
+ "time": 159.51527599937253,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/login",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/login"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "232"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"withCredentials\":true,\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\",\"create_account\":true}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:11 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "26"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 26,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 361
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 0.9902759999988484,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.13400000000000012,
+ "wait": 157.55199999910454,
+ "receive": 0.8390000002691522,
+ "_blocked_queueing": 0.27599999884841964
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:11.334Z",
+ "time": 153.21448900175528,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_address_txs",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_txs"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:11 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.5474890000018349,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.18300000000000005,
+ "wait": 150.6389999994531,
+ "receive": 0.8450000023003668,
+ "_blocked_queueing": 0.4890000018349383
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:11.335Z",
+ "time": 159.11257000118712,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_address_txs",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_txs"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:11 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 0.8495700000001525,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.10899999999999999,
+ "wait": 157.00100000011898,
+ "receive": 1.1530000010679942,
+ "_blocked_queueing": 0.5700000001525041
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:11.487Z",
+ "time": 159.68929200007187,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_txs",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_txs"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "187"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:11 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "1218"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 1218,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1555
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.279292000000627,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.1100000000000001,
+ "wait": 157.4239999981902,
+ "receive": 0.8760000018810388,
+ "_blocked_queueing": 0.2920000006270129
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:11.495Z",
+ "time": 156.81636000243816,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_txs",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_txs"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "187"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:11 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "1218"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 1218,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1555
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.0663600000018196,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.14500000000000002,
+ "wait": 154.6599999990242,
+ "receive": 0.9450000034121331,
+ "_blocked_queueing": 0.3600000018195715
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:17.285Z",
+ "time": 164.4336129980984,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:17 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 0.9136130000001577,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.09399999999999986,
+ "wait": 162.2699999996526,
+ "receive": 1.1559999984456226,
+ "_blocked_queueing": 0.6130000001576263
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:17.450Z",
+ "time": 165.1763129986066,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "187"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:17 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "958"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 958,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1294
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.0823129999968224,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.10699999999999998,
+ "wait": 163.0099999989164,
+ "receive": 0.976999999693362,
+ "_blocked_queueing": 0.3129999968223274
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:18.319Z",
+ "time": 777.64030099907,
+ "request": {
+ "method": "GET",
+ "url": "https://mymonero.com/partials/send-coins.html?1",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/partials/send-coins.html?1"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "cookie",
+ "value":
+ "__cfduid=db9e89f80dfcf8d7ff1f8a18823c27ea31511991035"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "mymonero.com"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": ":method",
+ "value": "GET"
+ }
+ ],
+ "queryString": [
+ {
+ "name": "1",
+ "value": ""
+ }
+ ],
+ "cookies": [
+ {
+ "name": "__cfduid",
+ "value":
+ "db9e89f80dfcf8d7ff1f8a18823c27ea31511991035",
+ "expires": null,
+ "httpOnly": false,
+ "secure": false
+ }
+ ],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:18 GMT"
+ },
+ {
+ "name": "content-encoding",
+ "value": "gzip"
+ },
+ {
+ "name": "x-content-type-options",
+ "value": "nosniff"
+ },
+ {
+ "name": "last-modified",
+ "value": "Fri, 06 Apr 2018 05:57:24 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "x-frame-options",
+ "value": "Deny"
+ },
+ {
+ "name": "etag",
+ "value": "W/\"5ac70c44-18dd\""
+ },
+ {
+ "name": "vary",
+ "value": "Accept-Encoding"
+ },
+ {
+ "name": "content-type",
+ "value": "text/html"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "strict-transport-security",
+ "value": "max-age=31536000; includeSubdomains;"
+ },
+ {
+ "name": "x-xss-protection",
+ "value": "1"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 6365,
+ "mimeType": "text/html"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1672
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.544301000000036,
+ "dns": 118.545,
+ "ssl": 180.40699999999998,
+ "connect": 472.44,
+ "send": 0.27600000000001046,
+ "wait": 183.55399999832503,
+ "receive": 1.2810000007448252,
+ "_blocked_queueing": 0.3010000000358559
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56402"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:18.999Z",
+ "time": 166.6423920001398,
+ "request": {
+ "method": "GET",
+ "url": "https://mymonero.com/partials/account-nav.html",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/partials/account-nav.html"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "cookie",
+ "value":
+ "__cfduid=db9e89f80dfcf8d7ff1f8a18823c27ea31511991035"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "mymonero.com"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": ":method",
+ "value": "GET"
+ }
+ ],
+ "queryString": [],
+ "cookies": [
+ {
+ "name": "__cfduid",
+ "value":
+ "db9e89f80dfcf8d7ff1f8a18823c27ea31511991035",
+ "expires": null,
+ "httpOnly": false,
+ "secure": false
+ }
+ ],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:19 GMT"
+ },
+ {
+ "name": "x-content-type-options",
+ "value": "nosniff"
+ },
+ {
+ "name": "last-modified",
+ "value": "Thu, 21 Jun 2018 15:54:41 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "etag",
+ "value": "\"5b2bca41-474\""
+ },
+ {
+ "name": "x-frame-options",
+ "value": "Deny"
+ },
+ {
+ "name": "content-type",
+ "value": "text/html"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "strict-transport-security",
+ "value": "max-age=31536000; includeSubdomains;"
+ },
+ {
+ "name": "accept-ranges",
+ "value": "bytes"
+ },
+ {
+ "name": "content-length",
+ "value": "1140"
+ },
+ {
+ "name": "x-xss-protection",
+ "value": "1"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 1140,
+ "mimeType": "text/html"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1365
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.7473920000017389,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.1589999999999998,
+ "wait": 163.71500000010104,
+ "receive": 1.02100000003702,
+ "_blocked_queueing": 0.39200000173877925
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56402"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:19.000Z",
+ "time": 166.5593500011455,
+ "request": {
+ "method": "GET",
+ "url": "https://mymonero.com/modals/openalias-confirm.html",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/modals/openalias-confirm.html"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "cookie",
+ "value":
+ "__cfduid=db9e89f80dfcf8d7ff1f8a18823c27ea31511991035"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "mymonero.com"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": ":method",
+ "value": "GET"
+ }
+ ],
+ "queryString": [],
+ "cookies": [
+ {
+ "name": "__cfduid",
+ "value":
+ "db9e89f80dfcf8d7ff1f8a18823c27ea31511991035",
+ "expires": null,
+ "httpOnly": false,
+ "secure": false
+ }
+ ],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:19 GMT"
+ },
+ {
+ "name": "content-encoding",
+ "value": "gzip"
+ },
+ {
+ "name": "x-content-type-options",
+ "value": "nosniff"
+ },
+ {
+ "name": "last-modified",
+ "value": "Mon, 06 Feb 2017 19:19:53 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "x-frame-options",
+ "value": "Deny"
+ },
+ {
+ "name": "etag",
+ "value": "W/\"5898cc59-a21\""
+ },
+ {
+ "name": "vary",
+ "value": "Accept-Encoding"
+ },
+ {
+ "name": "content-type",
+ "value": "text/html"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "strict-transport-security",
+ "value": "max-age=31536000; includeSubdomains;"
+ },
+ {
+ "name": "x-xss-protection",
+ "value": "1"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 2593,
+ "mimeType": "text/html"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1016
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.1963500000020721,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.09000000000000008,
+ "wait": 164.43600000155135,
+ "receive": 0.8369999995920807,
+ "_blocked_queueing": 0.35000000207219273
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56402"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:19.002Z",
+ "time": 329.04037800053266,
+ "request": {
+ "method": "GET",
+ "url": "https://mymonero.com/images/aero.png",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/images/aero.png"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "cookie",
+ "value":
+ "__cfduid=db9e89f80dfcf8d7ff1f8a18823c27ea31511991035"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "image/webp,image/apng,image/*,*/*;q=0.8"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "mymonero.com"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": ":method",
+ "value": "GET"
+ }
+ ],
+ "queryString": [],
+ "cookies": [
+ {
+ "name": "__cfduid",
+ "value":
+ "db9e89f80dfcf8d7ff1f8a18823c27ea31511991035",
+ "expires": null,
+ "httpOnly": false,
+ "secure": false
+ }
+ ],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "pragma",
+ "value": "public"
+ },
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:19 GMT"
+ },
+ {
+ "name": "content-encoding",
+ "value": "gzip"
+ },
+ {
+ "name": "last-modified",
+ "value": "Mon, 06 Feb 2017 19:19:53 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "etag",
+ "value": "W/\"5898cc59-4098\""
+ },
+ {
+ "name": "vary",
+ "value": "Accept-Encoding"
+ },
+ {
+ "name": "content-type",
+ "value": "image/png"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "cache-control",
+ "value": "max-age=31536000"
+ },
+ {
+ "name": "cache-control",
+ "value": "max-age=31536000, public"
+ },
+ {
+ "name": "expires",
+ "value": "Sat, 06 Jul 2019 00:25:19 GMT"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 16536,
+ "mimeType": "image/png"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 15018
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.2853780000006372,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.25,
+ "wait": 325.6869999998924,
+ "receive": 1.8180000006395858,
+ "_blocked_queueing": 0.37800000063725747
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56402"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:19.002Z",
+ "time": 336.9524740014749,
+ "request": {
+ "method": "GET",
+ "url": "https://mymonero.com/images/check-icon.png",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/images/check-icon.png"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "cookie",
+ "value":
+ "__cfduid=db9e89f80dfcf8d7ff1f8a18823c27ea31511991035"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "image/webp,image/apng,image/*,*/*;q=0.8"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "mymonero.com"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": ":method",
+ "value": "GET"
+ }
+ ],
+ "queryString": [],
+ "cookies": [
+ {
+ "name": "__cfduid",
+ "value":
+ "db9e89f80dfcf8d7ff1f8a18823c27ea31511991035",
+ "expires": null,
+ "httpOnly": false,
+ "secure": false
+ }
+ ],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "pragma",
+ "value": "public"
+ },
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:19 GMT"
+ },
+ {
+ "name": "content-encoding",
+ "value": "gzip"
+ },
+ {
+ "name": "last-modified",
+ "value": "Mon, 06 Feb 2017 19:19:53 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "etag",
+ "value": "W/\"5898cc59-1c64\""
+ },
+ {
+ "name": "vary",
+ "value": "Accept-Encoding"
+ },
+ {
+ "name": "content-type",
+ "value": "image/png"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "cache-control",
+ "value": "max-age=31536000"
+ },
+ {
+ "name": "cache-control",
+ "value": "max-age=31536000, public"
+ },
+ {
+ "name": "expires",
+ "value": "Sat, 06 Jul 2019 00:25:19 GMT"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 7268,
+ "mimeType": "image/png"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 7293
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.342474000000395,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.125,
+ "wait": 333.75500000152226,
+ "receive": 1.7299999999522697,
+ "_blocked_queueing": 0.47400000039488077
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56402"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:27.285Z",
+ "time": 155.90357000258155,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:27 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.2885700000001525,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.14700000000000002,
+ "wait": 153.2940000016801,
+ "receive": 1.1740000009012874,
+ "_blocked_queueing": 0.5700000001525041
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:27.441Z",
+ "time": 162.4842840004021,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "187"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:27 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "958"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 958,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1294
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.0612839999979187,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.16300000000000003,
+ "wait": 160.59400000049408,
+ "receive": 0.6659999999101274,
+ "_blocked_queueing": 0.2839999979187269
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:37.285Z",
+ "time": 154.2695690023902,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:37 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.1425690000034519,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.1080000000000001,
+ "wait": 151.90199999895412,
+ "receive": 1.1170000034326222,
+ "_blocked_queueing": 0.5690000034519471
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:37.440Z",
+ "time": 175.2733090007423,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "187"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:37 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "958"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 958,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1294
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.007308999999106,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.14400000000000013,
+ "wait": 173.38100000090873,
+ "receive": 0.7409999998344574,
+ "_blocked_queueing": 0.30899999910616316
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:47.285Z",
+ "time": 161.12051199702546,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:47 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 0.9255119999987074,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.10799999999999987,
+ "wait": 159.08199999876834,
+ "receive": 1.0049999982584268,
+ "_blocked_queueing": 0.5119999987073243
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:47.447Z",
+ "time": 178.44339600069725,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "187"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:47 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "958"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 958,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1294
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 0.8653959999994549,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.134,
+ "wait": 176.48199999880651,
+ "receive": 0.9620000018912833,
+ "_blocked_queueing": 0.39599999945494346
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:48.370Z",
+ "time": 151.84338099964225,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_unspent_outs",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_unspent_outs"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:48 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.2163809999980149,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.125,
+ "wait": 149.6289999987788,
+ "receive": 0.8730000008654315,
+ "_blocked_queueing": 0.38099999801488593
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:48.523Z",
+ "time": 155.7823879979951,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_unspent_outs",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_unspent_outs"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "258"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\",\"amount\":\"0\",\"mixin\":6,\"use_dust\":false,\"dust_threshold\":\"10000000000\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:48 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "935"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 935,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1271
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.2723879999967467,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.11299999999999999,
+ "wait": 153.3750000012608,
+ "receive": 1.021999996737577,
+ "_blocked_queueing": 0.3879999967466574
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:48.684Z",
+ "time": 151.69544100047278,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_random_outs",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_random_outs"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:48 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.053441000000137,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.08600000000000008,
+ "wait": 149.6330000008703,
+ "receive": 0.9229999996023253,
+ "_blocked_queueing": 0.44100000013713725
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:48.837Z",
+ "time": 158.4292709991605,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_random_outs",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_random_outs"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "27"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text": "{\"amounts\":[\"0\"],\"count\":7}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:48 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "1723"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 1723,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 2069
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.1862710000007937,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.17300000000000004,
+ "wait": 156.43599999916879,
+ "receive": 0.6339999999909196,
+ "_blocked_queueing": 0.27100000079371966
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:51.117Z",
+ "time": 156.7322979994642,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/submit_raw_tx",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/submit_raw_tx"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:51 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.3272979999990202,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.14400000000000013,
+ "wait": 154.47100000123237,
+ "receive": 0.7899999982328154,
+ "_blocked_queueing": 0.29799999902024865
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:51.274Z",
+ "time": 401.30272299839635,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/submit_raw_tx",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/submit_raw_tx"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "26579"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\",\"tx\":\"020001020007d8fa7ef1f42ba3d85587de0499da67e8af019a940fc0ea7df439a9a9375151b7eb67df439be337bda577c711238c06e1284ad5e69b020002105f078f9332cf8fb2390be2f6be39abf40992dcd33daf0e3fbd9b02a23755b10002d4b94e2516134889b321d6f06792322debb0d311c761c6fc1fc4e699fbb87d312101c72f9ab780d28ac0c1d5df199c976792d5317fba9873694973b4e66565e54df301e0adcda708417e2c0f2fe4bfe629ad37a477089a98f91c98e59c54931dd53adc358a7f780864294cb7a3af9d977519ba3b9af993613e9c5f95c7ad3926fef81e603a1f3b0367826fabd56cd9596b110c32aff6a41a4eefc4309a43264609f1b69f52331c010db6b4fb75210d33417fc5700eaf5ae67f4f5930b9e4148824deafaf82cb8301200554164113b97acd5bb67b91ee69296bf15fff6001d7ce77374afffa68fb6b6464fb83555790fac27de59e3fd2cb8d27384ee923ad99de0dfe2e498104b31e094164e2b93cb85abd82ed7afc408fad29b1ae74fe2383a08cc4a154686e2e08b59d3a7a56337281c7de9edb43dc6cb3eb90afe399b23d65c9ca1c542e542b0c2b976ab45e2e11e27816623ce4349206c2a4c8b7540f394c868685267eefa60ae93f2fda2b1fd5835506acf9d7c77b0ca08e1cc89569df1ddf8a5688a630810eb57b4a9cc83bcc39e7c4ea8069afc2c5f97cf41bf4894d7cd3ad9fee9dcb630cad06176bcb8d5cbed6a09a16451ea8ed80fd21e16aa136a720b76b8a6b03ff020f92ec55b78668ac41c5423de75eb4bfbc6c2c20abb74e7a82ed9d2d53510003bdc18588724e2ecc6afcbfb3d8765f15b00a006306778c04d305dd99b90ac10e92ac7473a379ba999316ba893d0ff320788ecc754632bc2bd565e0838ffc9a01adab99709eb702deb1c350954857856de88d5b5d007919c225f6d70e1b89a9067d40bb9918e657d8dd8741453877ce22b13a5442ebf963757bd48488f15b7b0e0c96c3fa4118c6beaf72693c64308b9343472784d965e04f8b304ee3ebdb4405d04d70208389b5200b771cae428b88be6fe91970e7ec0c2b9409f16a582c6a0b9b9278f65bb6401b18e5fe1031f52c8b036bd5afd0e984cfd3cf4c650a9940040331067730c9e58688286fa2533a4815eb5289f8860664d47b55f4507a7e5b0540d8b2d3a98a02caf4109de34289f04ca6b316f7a682328f3cef057bab3b7705e25fdbf9b361023602c9fe45c9d47615cd388b251d4cc13c667e2c8235e1bb0106374aa1ff0e61f1121d47abeaf0ac767d1bb1a0de2afbe706c1014ec8d146001d727248b4db192c5da6699d3a32b09652922908900267fd2e1982e1f05a02087c6ade39bab5b1bcdc2f6cf03d455f8221f9d937fadcb87c4070262bde23ca0edf3422c0787a2135b741d68f5b2dedf689f455f200378e57603ab0704c9ffc0c6f0c0d13b284111698cf0bae7b0faa9ae341a25eacbd9adc502df4a12705c50bbb3d708c72e496bb9913852765c676ae683a04479b93e117e9cc0cacb403720e8e685a2a6a8cba27590a0c1dd0f1c08022ea3590acf423121a3f1c8abfcb7302bc40bcff2cbceb474f0bbb400d74f17e76e9e665a7a0ad2aff650fb8ee41c605d45ab263f78d60a67567e2bdd2085cb321f348b27f92c2e570e0396a2595090832aa144fa375474f3ef8d95295f46f0c7cfa8058a4ff11bb0dfdec1c6ba9f60873f1a08aa38b6417ecd09cf92b1547eb6a49a694f0f6ed57fa157b6eedca46099248eccd40954c5bebc6f2bbd5de96ae3e2efe842a251a8651231bac54bc12052a1962e4dbdc3ff1797e1ddf401d070a9b9dd2ea2ff3ae2e207d10c02c13e50ca90ae0d17418051108f06a77fcbafa80fbbf563e334da38624525e1d203bd10018ce2730e92157810ef2c092a51dce81cdcab79dc271a47749b8dc1fd6d84d0e4e0e97ee23d09c70c2d2882858bc7a318ab516a1c9f3291824c5ed79baff2c05d9d93f18348c5fbffd2337c3ae6973eacbf67aeeda71ea745fd635da419fac03296b62aa4b3f4c554b7452db9ca1887250bc8c507d042db2b9b7c81d1018410863d23ad52e74b68b13a1d0b17fec9ac5043cf22452b98f2c05328707d3b51508645918528f4712d553db4e03325e11ac0a3b3794e74884ebc3e2d63a8c2a2a0af1dab421831e8336538d8441a5145eac4830ac0a7fe5cfab808e6fc23c7c4300a33fd2bcbfe92dc9c47d173af9be7f1e50da97ce5d88c36724e4dff79f6d9b059b47fbf1fa33e259b339f1f74c9831473b86e7767f4cef199c7b53d260f00001fe22ce255569940f6b460c6aa9eccb1afc191f786f00256aca97d7b465ad8e0ce21b01b36099a8326edd3d509bcc49b37c69fdd04d8716df564c42d01f589804824fab7c5a13b31e41ec62321f42ace7e7c301abd04a29c4692eb526a9f5bc0cfc47845963028fe9bcac2619255251235ff7be950066e41933d86114c4799c0f94394c9b48e34a39908cd2c676fe6ee42d21a09b2c148506e9cf72bba00aa7020850b7d693ef97ce9074adc966a258c87841aa76468e10cc2474f58690b08601a7d7620a3f6c826422ebd339b47e0566a4ef9d74c91f8733e68da260ff052700dd03dd70ff28bb938ac6c420eb8942d31e0222a2c4c5e07339503f33baf165074813beabf4ae379d0d093c16438e5c9e08c06d2909221278c398698a2a45710c1ac2954ffb0064229d43b1686dff63189c9def06fbc84d52c6dd1b45785bee01c43ba993220032fb7530872ebb42cd1b8d1b56cc98784ac01b242a0859ad5f0556be67160b9b6a676b3f5b36040947b4c8d97cb2125ee429a785f1798052780fa7acb882cfff3842d52df35bd957f462c1b493cb64852f8d3479b1d930e6cb042dacc0b2112af2fa9be170d164bef97f69cf0c7bb428f3677f8431aeb300b10d0b89d7dc92f2ee2f1f60a7e93cd7ac88b6f71b4c83f2df1c4f1df217795af501e274a6b4ba98db41785a1268a5c26093cd7799e2fc97fca3832465aa0ca8a80b241240171cc310cbe3835eb71edab3ca918b62a883f27be003be1984c9fced068d3cfa28d895556eb48d6f6f21fea79f6818297df8a030202ddf87c4b38d5202270555a24fa46c4b124af481e0a791336efc5d87d16191f7a73686dc330cd00a72431571eaa9a6866d114c2abf5a9599dbd8c6b8c6ab41ad155329b20bd9d302b896f7ef3859598c1f7312149d49c85fc32734aec69311a1f7241713c902ca0a578f78aaecc9f9783095216d583312116983c97da812058ba197a73b7b00a403823f17e7d12a20d8d2af791623984d8a5d42e8b81cc33d2075706f188b96240e0c70da64295b99aba8a1ca9a692f908224766dd4ab85b21c2e4a3561d92da70bb3fcb96c7067379745497875654d4135f2a367f9760e1101e1b7c8e45bd78801e7166d6480bcd5628467993ee7c11b651a3e97afffcb302fe91d6cf55036bc0acc531d9d6c3dc509c2878ed7378a82ecb182da633ab8d7e08241da069afb8b06e63c93d5e36e33e4ba022fe2352061f55f4e5115d048e4c36ab56d9d558daf0015a8ec96436dd72e4ee610e0806adbb382a7ef9d3dd9774ed070abeec25d460e304ebc4b2d097bfa0f6982abe3f7490f0374a278f1464505e0233a1195e7af01ae7e06f814cb7266ca60722c53e2d3eb66a5a1f8cff91bad0da26b5716ab7106fceb4b4a1a613014804c5284e7c8d785512730c7f109d4c225f9fa00cff4d10cf7f120e75d1c37c5e5b4e9117f30fb30c3cbcf86a33225fb4f63f2a2071682045dfb733111c50730556e62bd49a471b71c9421b312e8805358ba53af071b610399e917fa1b4687093313e330b453dd98b3f7866bcbe737f5e11f4bd1c0043b0975b84a92c4cfef856688345ebf4ba84b95aaad4ef573774e448d7ea93f6ba10937ec41a174bc0965d667918f608b6358d424628859d0933cccdf51ec7d55f906438342b45be0e9796bb57225e7bb184602fb97dfb48260bcfa427f787a759c0f5a2a8929697f47c854bebca692175efb36276a5f9fb92437836e65d87bb3b70a09663579a35c801c1aa654eb28517013b5e877a003be057b875b35df2fe227090c58358156115c37d72c290912ee288600cdabb492312227fe659dc238df6e03def5668fd22573e63b245586cd10bc66d68942e01bc9dfb9d152a1a7898ce002daef4fbe574991fe37e0b8b69ff269e098b7b1640a47b370fb38f410eb0d5703536bbe4bcc900df2a526d80a413b9c9ed3ab75124ff7198d3a44e2ca0cce410fedf5f2ef7b33f93b43a26f4d30751f2bfb4b13ca42a3105196bb17e5c2653e0c00adac72a44baa60b2d2411fb239d0d0dfd68251d362bb04ab0a3cc169cf61049bdc73cf11aee0b90f7b26cbcdab987873ab69542cdef0bb8c125da7034e320dc9bc4f84a9827cd927165c3455ae9fd8f5325e606b58e3cb6db67bc46b434c0bf78b1ef5530c0996205b419dbde1a8b1a9ed21abf38e3c5a16d34d6091a1e4065c79fe8d78b95184e779434993a52c23e6e9c6db90e9db5b90e97a29def79302922934dd5413a93d183cd83ad88ab3d439ef39753c84588a8269a74f0d1972080fbb35971f624d31041842ed430c70d09171c20f0f00e0d0c4e8ef8149b9af038846db4d5b2d8a586e5f1b34803e52cab395311ac5bac25f2ce3cb2203d8070354da89b48c24c0da18b4db9f87d210388ec2655f48fcc42bcb78c367bef2aa0c543f7e562adc23a6b22445ca1389156587c0af0fa16aeaa1aa5e6508228e9c04620aafcb6408e66ad86f0258cdd04a376ca1f41a623e7cf36a800d5da91583029bb46e73b038d27c370d948571c442f65302d09c1562ba3ebde7e7e803f32602799726043b23a4eca27e62986009cc9e4419fe95cece9504cabdf6d92f01f105beee7e9ece3fb49b495922bfc23337e53baafebcad53a250b66d168a87b3a4064440985fe7becf778fadbbf8eb4d01c024039a4cdfd5f27edab16f4702b4c20590dce3157b8c727316ba8c25de3cab3112c385f8ca0895e5c775e5f34c17520cc8cf80d28be593f186ad7f6f4666f702df2566c16a5d5e1aad21694774f41f0003fbcb4502fcb5e1deff0fa2a458f6ff88bbc91cfa2e21cb42b0077da5d43007d68610e05172ac51036ab6a52140e14e1707dc325fe8efcc39c74883780e9404bcb78fdf38b2763ddd7458db79ffaf28305aef1534d7ec4112316a8acfbe3504277ef2e7dd36cc72869f1d53689d904cabd23085165d2933ea15fa491da5ed01f00468c47ba2185b241405d89952922a74c02ceb8617543b70f353a62f017b023dc53beb7b858019493aa1d2bdcf2d6eb7683d453d62dfd391b189c296bee006d7c85bd22dd02bf6448cb7af0460cc319913f9fbc357875bf5396f9e33fab5050aeb928f76e42175bb300f1e254f981d8bf22079c66b03169e4d04879495e80a395187afeb4d7e73edf5d4320ce49cc4774560697ed4c58e8b4214fd03d3e808d666a22ab6104dbafa95c6c0db8af94ea49f24fbd5c36d00fc009c451ce097083f84b72f2e5b9b1dec327c4f14d43da764ad36c5e8e8389453755fb77157dd03f9e97ed405828144904fb6a2f578e1d39f7f30621d9a483291ce50391c53690a76b217cdc7d7ac7a1ca3add04d303a920713db149ca022ad49a83e72ea7fba0c6c29a44633d1c056ddbffcfd43cd1ad4b810cddf796ba994ddda1d63cebf8705e743ff022e0ecd99912346a9c2b892e5e4ebc3e52b98e60262d96f336771770349b69d7560d297a73658c22441c5ce1b294065ff4f3da35227f7684e3a445001910975c311f461abd40ee2b56ab71f105770a4bc1a581b1221495023353aaa01d4bd115f5a8d9ba37b3b41dab0cb181f07e60624b755e79e1abfc7da826a6d08cab5edf981c43ec78bdcbf74313af449bbfe3b4806a205de6adbd2249015aa0b0efe4e92b667f66f2b8899084941fce413edc2e4a5ab44c066236c6b2a527707fe52311427d13f55a87cb3a361b196e0faecc106ca6bf40e968b057f64cde106b2b446d99da75974b26266573f1a641b4c7b477c61b370116d63267471132806f9bdb81ab2a81f03165e2a5b2d32f1f2b2cbe86d95e3c6f8bf8ac6a2296ccd0c2e6412ed8f74082f073b6bc2dd63e59ead35112c5c9362610a6561531b931b09f21e5bef71a01433b29402e008ecebaadda2bd8bed28d0cdf910ffd573434c0ac5781c5eee63cd99e97b5b319cd3925607dbb371a360e2d5e070f161a063f10946793313125f55f30bbabb52e61db65e5ae507cef64f08f501417edef3da5e0202836fed5f531a64dd41aad00a1dc39ec13b49d1ebd7a2a6b5867d7d71db0e50a393ec33f28531624bdfa3ddab00ab9239b2bd54ea858920f34c26430045898f52b419f7f04f7b3b8e604bf11b20ed058e8ecdeae5be4845aa5f14f16c1f953d8e653029b087bcd4ab9953c1937cee84f81c393285c8bc3341a5eb5bae580b5d55cf6f0ee2afbea795764bf5d4ed0735367a0bf65b6f69732449e3dfec547d93d22056ada1eb1c35c2ca1924e07c0e6120b818d6941755efc4dae89d0cc039762bf5e003763a01910d434d3f0c3e71af286c55d30c9aadb2a480b8d0938ce43c202968df06659dc14e713dd6b2899be1501718e3003be5220b7e90034244ab3e9db020600bdb9b2a17b9d87e150356cb3f3a7150bf305d03efb0aab95cf43e5cf124a8f5db6c37b81f7841b81a7cc5c2ed6d3b8124ff95f9981fc93d732dbf470403a44e0f46ffff9bbef09e2fedbf9b42600918567617ea28d8f42e6b1d764a340ea317a4462b1083062b29560d25f2b35a82bfdf000033f8a0db28e7314d4d784f6b9d0df70928cc30b94119cf02085884a237b2ff7470e299726d06f430f72c73a1727446f060a3d98ee0c867ea162c197bacce848ebc423f3dc6c4835e8bbce299084ecf73139b024c33f215bc132fe77cf326e2da2cd18b3008bf16447ff4b2a4a5679987564d369e8936270b4adb3128e6e88eb75205f9de36088fde3a7d294c7c38cf0d0bb02c1160a6695550423737668f1646ca65b93d95547097a2fa8bd70f74a623e5a3c9c3c6b70097bde54853b7517c76c0d36e59cabce4bb691bff14524d2a0eac2155a9af73a4e87f0d6ec67d4a27840ed0b7134fa93d751588a77416daca2b68d054a7e74dc752a9506090e79596c9b558f085853055d87852c251baa10a9607b86c009ba3ecba6ed2457d499128f26fe632c39e5ccc46fb4b7e225ee282d9883abaed6e531b4f844df3d5cb547152742d0a0744e40fb9284637a34dbd3a4ff4dae9a07904b36e539f56eec6ce7baf51ed8e8189b242effc3bc60908d6b62739b6d6e8206e4223160eb293eeea2992c0dcaf60c73632ce196441692c1f5131526818f5bff1cd83a75e414e595e13a082a47985bd505cf256f5ec516ae79c1117f9f7820594d696a7714d2151450127dee286f8d18de13517b24eda31f18547032200489505d2f53101c962641b7275b8a48296f339402f7f48ac2674f669841615752eaf495a6dac6bd4d6cae58be8c779890d201904221ef4a1a0f87c523b027fa43f52f37ea06f8a19070fe04f7533dfb529d802a22d7d299f3abb6974611456060915173bcca396febe1bc943150131d94a2406565957950fea0f11421c7ae2b7e900f46a8c947b7e5e724bd981c00a1c93070a6dd51bf406b876c5141f7fb5bd84cb788dda0b822bdc4263e500c85c079aee409cc00e411628ef38f9c948a3ca7989efe4592d7702aa846b3a1cfbfc5e4a1801ad6a63386bdaa004af3bd3182a082ae5f17d30862c5a0de12078aba63b51ad2ad9c695257e90cf997464da236ace2f90020547003d30e8f63998ab705aa56fd4031397d36526e09faa72c4789380a4df227104cba21542a16f64b19ba96a84103513d94336e5077c76eb3a301e3bcdf507c9a0ab6b7f98d6533d6cdfe04f72d24042472cd55b68ff9d9e1d8155c21e7df17066751a99ccc5147d0a828053f77b059879160522a71a95f14e058a51bf443264700870541f074c182b1ca3d1250b6e03377dea966eeba2bd820d680f35dc7226e4a8faebc88da83b49da2b5e7dc897dfc7998961a5fb82372fc03ddcbb6043284c80358ec0f2bf9cba7e3bbce8696dd16bc32819e8182b5528b3ac82df464b286b944a13704dc17befb80ed61d24a2b2407fd2bbf0fd1e547b1437db807e113c9e514048342558691bb18c8e2fb7318a5dd730f570722b73446476e28fe2c85f1fb908cf4898025d519f075bc16c0db7c580f8d5eab655c7fb355eff1463026b61b092594a971e573cbc71f2c6fd23782171dd7c5a41d980ac1830093a2ba8be6de21cfe8a07463deb4428bcf1382326c02edf5e1cc806779da45ac34d5c0db4f2382f5e08be2c31fcdfd93be10f4f6c0842938f9080674ac1ce67acacb5727ac6bfc7995b29f02e324e59f291a5bb4751f66dd6200ca2c9c8af94a1d6c5531e6d332d8a91c4e00c21fc1248cede72d88c8fa61f98513bddd06b910456ee60635081b1627a0e01c67e8312f0e0fed42145e9c514547cf5b5bc53634a11729b9eb6ddd217568a49772e18e020d0d5ddd2220d938c753a044fb4bd49d3b2002e951849e32ad1f37a7058750c627f4a94b03d048a524eee29bc8de8ea1f76255b402b63f9b4ff050aec4af6c6ff1ed7cdd62d5f9e38e5deb73068b2a49a460adc7b98f64e72729ffe7d63662e25ea421d73995c22633e12ba5892f6c2b1dfdbe1f85bf5c22208bf1399bcefb5f9357db7c84bca879fbddc966d7adad62121490505b5b95cbd545753fb900ddf50a6a6588828f8f95252b9c7aaf321303f19c496bfa2af90104416a10771ca02d7daf9204daa3c0d9c8eccc7fa80234995db0869947e7412e5443851495e89259f92bee8e5b33c51e08f3dcfdcfb2a3b989ba51c16e820ca2f3bbd34c913d8ffd75d36447bb2539e2f424297ac39ac5090463289f332a4555c6243315c3e2c241c5a2534c08c6397ee82dffd9b8b2dca1d64ca3cc29cbd07e67bf095ab47273a7a70b59cbe936cd018be9d0c3f8a224c405de0a58cc5bc2cac7b6e6f514089b036eee6b0904b1979e6077b6132d9fd669788f5031d3f88107a857e35117054f9196a197e97ce89507701e23077c93b98401ad75a8a30a77317952fca30ec28eef2179699f93dd1d07b998118930c481260cffe5d098a81048b0853bc957f61c376a392bc740d4bc083e9bc4b62ee04daee640c5339121174af2f2c3c78d716d97497c0518d0a7468d5ab25b59fab995c815f65550d52567ab22ce7215eadb2a5bf07ff8991007d381ad3b18f9af8307e97282b681c37a74cc1083bc079e49b79d08e8d15d602b65d2f153c27e54407d80a4dbda35acc29220996375f7819919ebd9ec6999a0aa1ee2b5a2f8cd95c1ed40ed310ec102b872a2f0ae40a383036398a467a8450032b8dc50fb3aead263d9004dee7f10e12aea154ab97f04f4a016d39b97742d00c837350002903c83f056c5d64316c7aafd83fe355a64802fbef14516b197dfa001695103d494c48359e58bbcd358b0eb20d1ef42e3184594fcf51443ad3cfb4047ced01f064c638698c690b9179b61aa68d369e35d5ac8bc65dae702cbc74890dcbedb029dd98055213866576a3d5e0617d063dc2470e8d8e5b83574b5687b20990a81ccea7787be7d38ba9f21b5ba3443049ddd82328d4e8fbe6b2817bf9380b46f9e643f6a593081f7f1d5d97c69b0fe58c9cc6074c373f8a71c90a12604b0884716acb458e7dd490b5f0057d917e586c69cafe9fbe47f69bd64c7e61b5b80a6825520c5327772c2ea8d7450c3d764c4d08cdaa1562a1d990eb0878134d35012482c2f5c4ec38fe5161e00eaf669187f2ec98584a6735a9341cf66b4827dc0bf512a1d4ead70ad08445b1a9af017ac6b375f9c5f0103d0755e1c13046d2c102a041f2c79577ed00c330e1a7c8e3a6d474dbf6d9ef9c4e3217cc3fe611967a0bc5b96d0815ad46b04d043dd39ade61805d12aff54f4f60ca89f2ad77433c390822c633e75cba3a385958fe1015c502315ce63e440baab882dbeb4ba118994103676b9e83e6f6d2d53db075f662469e733778dd8082c36cb246b03f9701c580015d55bf2ca24a02bd3888eb95597f654d04e84fcd79aefa00125091db32898f0545a6b7695838c01d0be144395d40af9d8e69b0ce2f1326bcc3d57a244be2400031cbf5ff387e2439cc3b492466343d35a6a7ae244c0abdcada6cd0e37fb8170ad2c7582aafefc50fe74f4f05bd1b1aa0a0391c18b2e54f7b8078517ce5072305e4a509c5ceb5165a148a78c67fc1de9d97d4a7c0a9e6d152b47e2320721c200c830a015279a3d84acd32f7070af60f82db8fe6f035973f4250dba240999a5005f96f27a773fc9e7b68558f5f98699aeee07c645097111ec1aaabdefea774690482af54c967848b2cd65ebd7f9bd4657292864f46e4620210ae41c1642357eb0985f8b1dd6e9d87fcca833964873224c9128e899acd85f74ee625809e9f045907e717b4e15183bf2fc55a1cae983526e833255b1a38a532c3319ae67428e6b604f1aa90a29afe47935795d9b629abe0654a9375268d11f91a7f0e13a74c9a380ae66988a69672f086bb939df9590f0e495364a72c8cb298e98bf04a0709722f00449422874e793b6029a95e26724609dc06fe9086f6ae690b34dc1b29427b430350a43678dfb0ddde1db147da17c2e6b3fd44dbe186dabc204f47ab241c2f560d4c103099635d550200103c05232988f48838d4b56b401242852c4be6ef991203fa32db1bac264e7663b866f21314c05d3eb006a4fc2e00fabf30b4197b5edc06b8777c5560b10549622a28ef19132a9e3960f54f3c449c3039985b24be69e80f17df718ef65afb8fbd4b1d6eb3fa982590962d8863720a6f7080f6d73251360a62b8674d157eb1a2d313631aa446635ec453090c75cb7470cbe60044f329130b829cf76313e13a5a58dd5378ba3d1bd7c7a6b931e63f233204c7caf9d4fb8d0b036910d5fd088dff93e7f6a43888096b38b5e0d39bb94b2c570e94258ba8f60143f516aef8442b816bcf88ce21eb62bf58874644dfed54d13ecaec398846950a01b76ab69ed1324cdda07cf14831bf6554f1ad9952defc2186c66da67a6265049e01bb01a69c67b81bc0977ce81b13cd3058ca82bc212d17514ac19cc40c9703cd8a3c8253d0163d494a9bce334b798aaeddece4c546509ed61953f8ed357c0e861c7bd6ec632e7831b57a9d796b0513a715ab7348db38f3dcfc8474a82b81076e69287de84cc3bb1e8326f467b961f0dddcd70a9d104cfad472d3661bada40b1aafdc9b6fcaf21ba61e8e5367aba0374fb927c375f90b87350bb0e0dbd8e105bbc62c5fc32f99f4bed49baa2f3db5a4705c4d2feaf3cd244d4d28d6f6a1400b5384e8f695e1449db4ef23f7b77618ed7a366eef3ae38d0b36aa19541a5f1d0b26c006bdaca798027322f8294933ad9d877fb79cce3cd756b94ed2cd311cb40db8cd46406725c9e7fd92893cdba0d1001796a286d9b18707f4ee433f90665607f60f5caadfa08817e1ab0f440b26dfb5163469c871bd7e0bfda5597597a2d40265d270c8303c48231c97b1807ddd444105aa6e195df4c208a0d07ab7721e49096e2b8edcaab76fc8729fd3bab94895518e3549dfcfe5238ef3f92e912e23bc0f032af4ad5abedd0c10613e50c26c9522902074202bd2c93ca60a7fdf4c5dff06e27594188bd5c6fed2874df4acf8b6d2f412959c2f274ffba18d040909985f09038124541070db29455f8d58c33323798cb03cfa2dc0298a856e64d4d7eb190f9c47bbbf7c075e8cc3ac5f980cad86287d1940dd8dc0ace92241ddb49d454605cd9229fe4080ca8e4b34bf3c35c3b95436d64f61d321a30f402f2695b6452d04f4aa87a45504333b39c487f05fab5ac541893bcccaccc27313293c20da1df10a1fe1edcb7962bd4c9b211472947196e01ee99d2d3f518710241dfc5f06a3ea0f53b36bc3696808aeba745a58c8c8b27f5bfeda5676a95596cfc24e7aebec110f851e464baf54513501af9262618d44cb81eff6d451d2ef90868a0b76fee1ae0f2392eca80014d625cf976c763f86186cf984943a0324bb38b8d23d97a2dae7002590aa56836825bee4770d74ded76b9187ed26ce8795d96e4c211056b7d8380f3e2ee2b7f4b37342968ebb1706e85233ad8fa15a39cda0417f86dacd35829a0053eec03966ac62876503efabf4407da1fd5eb13b542cbfa9ee755725cfe9c30f1235645468cbe11d875c5ebf96363b25abefa920867af543bcbc4148d5f1e50ac520452ced483e258ffb7ace14531c5c7de112f4827c594cbdaa373e52f54506833713e2d2fab34ecb5228c7c1c1cf402be69654c5ace0376d34ef7339383f0a2243fba2d420eb964593f871e18f5764824976f2f4c28f8624d9921237d451068ed944427dd822c8789064dbcdadca5c14b766706c7c3ac034875d44e94bf6047e1e989bc3506445c88c08b7805bf5a9ff3bf1636b93e98f039cb762d667ae02eca0fa2e30835f037dc359ce12edaafd46f064e0aa513d1e3239e0b63af879090a56c3d72748837b84bbd59a55a15c15b4e44d110532a020b9da108bfcf9530c3e1a49bf0c90143ecdd1324ad2beb6a4e1d033f46ceb918f4259d5b17bad3304e40fa0c77edf3afee63799c94b470a4dc3c89d18a43dc8dcd31aac6d33a23801a3548cd48aae4fcc39d0c2775ea1a26290f6d7c6d49506ad8772b1a356a8170bd0be3d0d451aa618d6312d6be5a86a2a398ad051a7e5660a454a4c45da41630fc9e3af071abd46b85a07749d1ee6287a4afb898f7c49655763e34a9ab47b9d0d750e5c0b8526960a576d55862c3e75f97716f211903d808ec860f41361129c0fc8cf440475e64a92338a37ccdef482ad7d9ad75a0a94bfb9007db701548dad0097312ecdb25219b47f5f44a707846ff2c180198747102dad0e37f3bdda0cba060aa8fafcabd48672879641167142c0024458aa118c2b6189ff95812c56515c06adf80b43718fe494419e883abe0f33e36d4e18f6cdfeb8edb121b81d0db1770f2705face099e6bbdae0cd485d486a8c2018d49bfcd956704f669edb352807c086c9edd1d0fac0eefed63077d6022aff763c8d34e51d69b26016740638b477606f68a07a3089aca2204035f772b06945823375fff78fd21fa554b16688ecb210b7291f1617493c6913bae6c90eda5935cc53a50c933e7b3b453b68f3871885b073c7a25563a66104e4dc28007990565ec9e2a3382f9924c6a63953af4929d4d0cd3282c0bb4c5cb3f2aa4c1d88751f15a5cb7ae8687034b6ab38d46b76b0bcf0967ba42db29eb1d7c458e923e6c6474e79ef469e92805b30a3a6e6e9101c53e0ce747701d014c338171df6ba79f8aa6234a49e78a8039e2ea9b671feddffa7d03cba531d055be1808d6fc2a6e229087b77a3be35e40ab4a6f378c216ea05d1900cd1aa21bf3bf0afe40e95c406000b49a429bdeeb563f32b87bedc1adc6e5e501c051a69901925053f064ae1e8054e34500b97eff9f974292eafbbd85715c2500e45a20f16556e09b7bb530764582a59ec40a4af28d95e6fdfea3f10ce35e2b0416e209c92a5045b8d7af4762cebf2d6d97b8960d9e9565e0549a2488a9e8b50c985f5395774fb807d49a0f84e0983fec0e32fb5342a3704c5d198dddc945d806c3262dc93f96a1b1a09c2bd461f324c068aae59d5473deba8ba6036ad96c670f42667ada96da901567e544157b12f56159bb7c3f83b90abd1b9e7eb948f2e702c9eca93744830f0dff8b7bc73a1458b0f8d40e679afd1e22b09c93f2c9729602c6c81026f8f02991c094a38ed0e6062508974a8bc69e8d6e6741a45eabc7db0665f8a2d879de17fdaf7ad1b210f337b90aec1c1c3676f244aa3852a77c05db0c05b9936bc10f0a327602aa2109bdd115f8a3e1081c1b0c56649638207d2dd9086c6ab885512fec9650ba288df5133c9dc87cb7fe81919fafca783f1252c2930332559bbafcdc830223837a7b78a7ce137e9f4a4e3e7d1195a494fa6d0ca9a10659c0df4ee0c9db91dba17acad08e5f6ccae71012e2c6e4b37894520fffe9620d66a72a1e4f04e5fa2abe93ef7f4b2e9e837c9e0f9d962b5c80f5e8340874610358576c23403b2d742c587e3827d17c6f41b31f9da4030207aaa2f4c32059f4017bfa4c324be2ac00a0aeabc7756ff2e16bdf39ed6b0c313f01b1acce8bc2b408a0cb3eaf9af9b68dd17f258c0bf0c536b19040d6bebc0a9dac7fbc59398b520891a741c4183a851c1cbcf3f78f30924d85ea2d907a6ac8db9a1455e4c672800a88629853fefb86800fec6631d77edf591d56199d5980bf3c86d9254773e02508be6a488b032686134049415bb194ad11ab8e4b1e6304274091beccc795e7b604649f106b395bef6f30a1dca330561709cdf7295557410d040543c279e169a908dc3d94c4809f0a7dd92ad8cefb014f5d47681b21d6b955fa894eff5b57cc5d0d1b08c01c4e892af363e6560a02212edc1fa645cdbce380b3e010d3c7f473960ec86e8ab8d2c235f594cbb2c2fa62f27b4ea4ab9796b8534688fd9819ea529c023ab370c6e482633801e3d588f3a4433cef5aaeb633547e922dbb35410af8a803156b64daf9633636063a37c4adc43e492023e09137099a715a690a480119f60d9c2f1a110a6590ff1b7774e0fe3e4a7419226448634c129aa1213e560814cf0b28d28fbd4029688f0d920a5f45c2a06bdea6c38f731a8bf741ba106bb8a3ac0e35fd8c015995d55c0733a18e66705dc84dab552aff7d2ae654b903f6fc9f1305da5a5bafbfbb22df241e211416fa902eaec589c7a22d6bf160a884e5534c5e0a685c312527c037f3196d4122f4d42c5a683d0e555af2a271d8d68faea80dfa04bc27bdbca4d4b3c14ae0745ea453174a6e6e3b3014dbd23ac3c8a413b5e29d08524227a74d9da9b38d2ddeb8649eb9d46bafca1453fb6ab89112544597bf2dd855511f88a12a3000d7410572e9705c11b7fcfbeb9b1e2317d274526555e8104840abf75d25f845b7262d8b97d359ec39019b7e79750d3924564a757f61d0739edc3b52509ccd4822e0a009ff01e33619090961ffee25f22fb6132f3273d4a2be46de6f8ef4bbb8105fd7ca8329d986901e77ddc8364f56a841c66eccd7c29d34893077d396aa37f39e5157e56029141ed64a21ef75be7509c4787df1ad05e3106117d83208dffda5818ad4b9214f8d98525b01b283ac7b1e10342f546147dfc1e7f3cf1e0108070c30f760d0fa2a9681bf480b329ec479a7b70c6d90e6dd8d3b0292dade0859548bf22cf4c808691e59acb8948f14a5eafcb81e15a3f0af341b165712b33dcfa798c4fdc96d576a144d6186911e1efa3b39cea48adb7e325479e0a1d07e78c8eef802096af323acfe1f0dc5f09eddb6fdc484dd73b09ef8be8e302758bdcddd89c671361d9b56857efc8486e4d1639b0b0a70546f8323db524b6b95350a947a9f2b929badd89da73ccb4c46e36eb9f994030a6eb36a6615f0471ba343eb5b8eb80b7e07523cb99431029b36db074d8e8643c417b80ef6e2cee094a39a04be94c9d08d4632548574d71e758440fb5bc26a628d19aec66d263c7272a551228be488c132df8c56b961e203ea465354fff1931763bca9609b9392e8b3290e60f8df264746081f9400ed506e5ac9b15da69625fd774d3f3450ba9b8ebec6b6cca9822ed85d7fc1cf6dbdada1cb34970cf87cc80b4d0ce3e9b973a125d761f03d35508d209a5c37b17c8b4d6d5064d4c8df72763285cadda3b56e6c6e8b5b75b719c41974164fd7af9e2478cf0f8a8ee7f663b3e60570779ca891a939f7f8cd30d7115a1da50b6099d1817986ec9e16459563c26e6714b3bb4d169462c6b8469680ac8a833d1f5a7a3fb4a3320ea262c3dd7d0ba79a7bbde853287e505e1979a376b2a2d925f9dca00fdf29d3b76484e5928433e716b186badab0a9efbe1562bd221f7275e46520c3a70f931a45cf22bcf455d9a45f65d0b15f44631c8e79f4c6ff9365027db2d4b322a62dd9eb3fcda81e30721970730ae5732216f25d311cf39877fcf0a6e258f786b5a617b9cc2b00eec15ba2d3ca12bc6a59189ac688d1a7e14cafb9060b516438ff68ff6f6084664ca56787d2be64104a20ae61459c8df34f473beb2f99efd5a16c3cb008aaa79902e9d7a48e91365d8e411f7698bad64eb84c061d98954d4b98bc1c6f2de13c9dcd0a1354fb25081bce08fae4030628f061af5d731bb3b4bc9ff12e311daebb60005413b27dca8eec97eddda88b55fa193767f679fc012e6404f97ab5b7efc52dff2c0e523b8a8ab0f4ca0deefbadb9fb79bd57d49bf07d1f9f9d12b09931860df0444cadbfcce259f6e505eabaa4b7625ab3ae7d72c60b06ec95c07981b7552483d385a17d6179f6106e86c8609cbf2d948e16fb41e195b0a93f250b296ddcfe20a5bd50a46c5d7f5a0f284bdf8dae11f26821fdaaaeaa3a11fdce58240685ab2709caed3feab3cd5b38d150d2cf728a23339fdf6f5f501d08bac9ed7538a98610c933cada9b5307814fc8558fa44b2042edd8ea2777147a026260569cb893a9b302e4756ea008abdae8cc65a6962e9845d8ceba5e9f0e723d950f839c9975f10b60725d37361bd2ce557fa68a6e9a6069d9727a98fa47fda31deabecc5c6f48fa57884283166ab5d7b4918598a01bed5c3d178b07494d7ad6fd7fc2717f0703ea0ff1965e2c77c63f8e999dc40035a91123df458822509becbcc22386349dea118d70efd59843a4eb3dc9bec704626d93b8286d709c46a9171f02255ad28581d1a2e385f177e9ccf01b9f732b50ef827e812e09c819dc082beb168dfe07739f976023d208cbcc54b76e9f0ca7b7ec2ddf15fe23195241837d5bc0c690261b46f05b7020bbae7f59ef42c046a7aa498a245b11d04f6fea6c0bf565de3cc419d74f00ae2dc2c6311bab6c8f04100ab1f61412d7f8a5a615136ad7e8a4785d7717f34b32af22f0d677b019f6ce64bd0c94a6c6ef2f704df82d21c34f63ae6d6ff32a27e2205dfb077d2d8f5bbb6f2135f2e771f31dbb4e98be4d9fa86a204acc1bf7f8ad76c800f6ac2009916de7d3fe6a7201b2c0c260c9a24bc45516f2244f95e836f30e8ac041d46f0ed08c99de3d62779195c599a3ad158ff9a6efecacdb7924a2146c472f551b9160aebb256dce0236fc7d46be77cf93c9e80ab021bea750e0d9b4b4cda04ed408909ee449da45a63e06ffaf7e311b125f2fef63c2ba9b6d2f3bdb5b0d04c117908308cd37665cc6775ef4d49ce555d3286671bb1d150b16276e38233e425c3230a31a3bf7d5946e6e13327519880ab9ceca4259f966826dcdeb30eed9a993971d9961da3e98e0c60f7c3f415707ecabe41920b1a20723a7a99ce84a255ecf97d5b004ae0062f108a76c5df2cb9b4b5a548a8b82468bc1ddc3b62f0f98f60d8b5e933fda2f11ceae7340c8ca55ce5a0d0dcce3ed1a558456698350f818a035f4b9d43e7afd2dad4678a6029e68d99f1ec4bb68aba853cf408a84bac2d379b47a9a9fec4c3ed22207d08edc7feae97451b4257a876186bdb1095ff6a404b3dca3bf5170d0127b93b23a2b88651a6a546677536a6ff3cdddf5247c98290cdc09d5600c6693bd736f080cda67770a209827ebfecdf9c86ff131cd3a17b39188348db4ca043faba0bdf151be4bee2b0559ed43ea7d5c4bd5ca6287e5b82f896a2a561c915b7aaf0ab0c6a238fafa262aff20c9aaa6b47e25e7c811e12ac2e9ad879214804a9c7bbb5bcde50c7ed7fb094ae029f869b3a441e0750473f3faa1d614456adc475a02cdd7aaf9c48b45161eb2a105ea4b9f03d676fd34f69bac593777a74578ef0ade13db1564e4172576fe454edeae5bc523fd6048d4bcb5a5716f6da4bf01fd05738c182aed9dc6a0fe13584c520f2bde1ac54e3c10b34985fc9711f4b8dae5046b92929afb4c8ba3f7ca1c662b78abbfaf2febf4f4184c1f115d552ca0872c0af06b61516dbbe09c3b086712e3eef9a0a0f5c890417b4fcf656f1b4cdddeba0f5a63d3b2ce929f6c3032387b4d4bafef61fb5611f846da35d4e3e11fea2615070aa485e0828b38359cc30426796db346825191141a5b14211937b12924e0a5074932707d8f1e76c3c80d6a3c64518dd3691c5d5252ba28df354dbb231da4f40ab801a29f5769a3ad6866582966b40896d943f3b62f711988613b1ab59bc0cc0d6dd2ea50fd4afa1ffd250339cfc0484693d119961d7b4f17dc6116c62adc7d076ed6834db8886b1620aff0dcd5ddac2f0492f45a7c0b448a668688ad6cb1d101c451dd8cc0c762b544849ca04153936549ae3b39112dbe2ea96bf0b210ec840810b47be58637baa446509b07c58ffa1fb66585fd4cb01df05291fd4d7d25880483bb8ac4e0505a7cc5119ee45948309eae12ea41a8b83e0a612acc25338e4b008187156b0971c145f9d6214afd896b20a15edba78f7a087c23a30d894f688908\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:51 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "20"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 20,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 355
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.2457230000010169,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.31499999999999995,
+ "wait": 398.4800000004447,
+ "receive": 1.2619999979506247,
+ "_blocked_queueing": 0.7230000010167714
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:57.285Z",
+ "time": 165.00839099994118,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:57 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.6373909999977623,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.123,
+ "wait": 162.38599999916391,
+ "receive": 0.862000000779517,
+ "_blocked_queueing": 0.3909999977622647
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:25:57.451Z",
+ "time": 165.87528899877725,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "187"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:25:57 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "969"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 969,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1305
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.0402889999996114,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.137,
+ "wait": 163.8619999995241,
+ "receive": 0.835999999253545,
+ "_blocked_queueing": 0.28899999961140566
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:26:07.285Z",
+ "time": 155.2864420010701,
+ "request": {
+ "method": "OPTIONS",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "access-control-request-headers",
+ "value": "content-type"
+ },
+ {
+ "name": "access-control-request-method",
+ "value": "POST"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "accept",
+ "value": "*/*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "OPTIONS"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0
+ },
+ "response": {
+ "status": 204,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:26:07 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "204"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "1728000"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "text/plain charset=UTF-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "0"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 0,
+ "mimeType": "text/plain"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 344
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 1.4484420000004756,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.133,
+ "wait": 152.7629999986731,
+ "receive": 0.9420000023965258,
+ "_blocked_queueing": 0.442000000475673
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ },
+ {
+ "startedDateTime": "2018-07-06T00:26:07.441Z",
+ "time": 172.03327899891883,
+ "request": {
+ "method": "POST",
+ "url": "https://api.mymonero.com:8443/get_address_info",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": ":path",
+ "value": "/get_address_info"
+ },
+ {
+ "name": "pragma",
+ "value": "no-cache"
+ },
+ {
+ "name": "origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, deflate, br"
+ },
+ {
+ "name": "accept-language",
+ "value": "en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7"
+ },
+ {
+ "name": "user-agent",
+ "value":
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.33 Safari/537.36"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json;charset=UTF-8"
+ },
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "cache-control",
+ "value": "no-cache"
+ },
+ {
+ "name": ":authority",
+ "value": "api.mymonero.com:8443"
+ },
+ {
+ "name": "referer",
+ "value": "https://mymonero.com/"
+ },
+ {
+ "name": ":scheme",
+ "value": "https"
+ },
+ {
+ "name": "content-length",
+ "value": "187"
+ },
+ {
+ "name": "dnt",
+ "value": "1"
+ },
+ {
+ "name": ":method",
+ "value": "POST"
+ }
+ ],
+ "queryString": [],
+ "cookies": [],
+ "headersSize": -1,
+ "bodySize": 0,
+ "postData": {
+ "mimeType": "application/json;charset=UTF-8",
+ "text":
+ "{\"address\":\"47VnPqnYvct5bDGSU1gsE35GiTtUQSAPmSGZ28wDU5EwDJCp9BVhtNb7H56CHmnvaGTZuAav89MyFLbDJE6z6oRp9ecnbTX\",\"view_key\":\"550eae3eb2cec618f8f726020276943dba03c189adf7ad2d8edb4de54482650c\"}"
+ }
+ },
+ "response": {
+ "status": 200,
+ "statusText": "",
+ "httpVersion": "http/2.0",
+ "headers": [
+ {
+ "name": "date",
+ "value": "Fri, 06 Jul 2018 00:26:07 GMT"
+ },
+ {
+ "name": "server",
+ "value": "nginx"
+ },
+ {
+ "name": "status",
+ "value": "200"
+ },
+ {
+ "name": "x-powered-by",
+ "value": "go-json-rest"
+ },
+ {
+ "name": "access-control-max-age",
+ "value": "86400"
+ },
+ {
+ "name": "access-control-allow-methods",
+ "value": "GET, POST, OPTIONS"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json; charset=utf-8"
+ },
+ {
+ "name": "access-control-allow-origin",
+ "value": "https://mymonero.com"
+ },
+ {
+ "name": "access-control-allow-credentials",
+ "value": "true"
+ },
+ {
+ "name": "access-control-allow-headers",
+ "value":
+ "*, DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Set-Cookie"
+ },
+ {
+ "name": "content-length",
+ "value": "969"
+ }
+ ],
+ "cookies": [],
+ "content": {
+ "size": 969,
+ "mimeType": "application/json"
+ },
+ "redirectURL": "",
+ "headersSize": -1,
+ "bodySize": -1,
+ "_transferSize": 1305
+ },
+ "cache": {},
+ "timings": {
+ "blocked": 0.830278999999864,
+ "dns": -1,
+ "ssl": -1,
+ "connect": -1,
+ "send": 0.09000000000000008,
+ "wait": 170.11299999871525,
+ "receive": 1.0000000002037268,
+ "_blocked_queueing": 0.2789999998640269
+ },
+ "serverIPAddress": "185.152.64.175",
+ "connection": "56354"
+ }
+ ]
+ }
+}
diff --git a/tests/range_proofs/range_proofs.spec.js b/__test__/range_proofs/range_proofs.spec.ts
similarity index 67%
rename from tests/range_proofs/range_proofs.spec.js
rename to __test__/range_proofs/range_proofs.spec.ts
index 4b11289..2c75ad3 100644
--- a/tests/range_proofs/range_proofs.spec.js
+++ b/__test__/range_proofs/range_proofs.spec.ts
@@ -1,3 +1,14 @@
+import { BigInt } from "biginteger";
+import {
+ hash_to_scalar,
+ Z,
+ generate_key_image_2,
+ genRct,
+ verRct,
+ decodeRct,
+} from "cryptonote_utils";
+import { ctskpkGen, populateFromBlockchain } from "./test_utils";
+
// Copyright (c) 2014-2018, MyMonero.com
//
// All rights reserved.
@@ -25,12 +36,6 @@
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const {
- ctskpkGen,
- populateFromBlockchain,
- JSBigInt,
- monero_utils,
-} = require("./test_utils");
it("range_proofs", () => {
//Ring CT Stuff
@@ -55,29 +60,29 @@ it("range_proofs", () => {
// key vector
let amount_keys = [];
- amounts.push(new JSBigInt(500));
- amount_keys.push(monero_utils.hash_to_scalar(monero_utils.Z));
+ amounts.push(new BigInt(500));
+ amount_keys.push(hash_to_scalar(Z));
- amounts.push(new JSBigInt(4500));
- amount_keys.push(monero_utils.hash_to_scalar(monero_utils.Z));
+ amounts.push(new BigInt(4500));
+ amount_keys.push(hash_to_scalar(Z));
- amounts.push(new JSBigInt(500));
- amount_keys.push(monero_utils.hash_to_scalar(monero_utils.Z));
+ amounts.push(new BigInt(500));
+ amount_keys.push(hash_to_scalar(Z));
- amounts.push(new JSBigInt(500));
- amount_keys.push(monero_utils.hash_to_scalar(monero_utils.Z));
+ amounts.push(new BigInt(500));
+ amount_keys.push(hash_to_scalar(Z));
//compute rct data with mixin 500
const { index, mixRing } = populateFromBlockchain(inPk, 3);
// generate kimg
- const kimg = [monero_utils.generate_key_image_2(inPk[0].dest, inSk[0].x)];
+ const kimg = [generate_key_image_2(inPk[0].dest, inSk[0].x)];
- let s = monero_utils.genRct(
- monero_utils.Z,
+ let s = genRct(
+ Z,
inSk,
kimg,
- [[]],
+ [],
amounts,
mixRing,
amount_keys,
@@ -85,32 +90,22 @@ it("range_proofs", () => {
"0",
);
- expect(monero_utils.verRct(s, true, mixRing, kimg[0])).toEqual(true);
- expect(monero_utils.verRct(s, false, mixRing, kimg[0])).toEqual(true);
+ expect(verRct(s, true, mixRing, kimg[0])).toEqual(true);
+ expect(verRct(s, false, mixRing, kimg[0])).toEqual(true);
//decode received amount
- monero_utils.decodeRct(s, amount_keys[1], 1);
+ decodeRct(s, amount_keys[1], 1);
// Ring CT with failing MG sig part should not verify!
// Since sum of inputs != outputs
- amounts[1] = new JSBigInt(12501);
+ amounts[1] = new BigInt(12501);
- s = monero_utils.genRct(
- monero_utils.Z,
- inSk,
- kimg,
- [[]],
- amounts,
- mixRing,
- amount_keys,
- [index],
- "0",
- );
+ s = genRct(Z, inSk, kimg, [], amounts, mixRing, amount_keys, [index], "0");
- expect(monero_utils.verRct(s, true, mixRing, kimg[0])).toEqual(true);
- expect(monero_utils.verRct(s, false, mixRing, kimg[0])).toEqual(false);
+ expect(verRct(s, true, mixRing, kimg[0])).toEqual(true);
+ expect(verRct(s, false, mixRing, kimg[0])).toEqual(false);
//decode received amount
- monero_utils.decodeRct(s, amount_keys[1], 1);
+ decodeRct(s, amount_keys[1], 1);
});
diff --git a/tests/range_proofs/range_proofs_with_fee.spec.js b/__test__/range_proofs/range_proofs_with_fee.spec.ts
similarity index 69%
rename from tests/range_proofs/range_proofs_with_fee.spec.js
rename to __test__/range_proofs/range_proofs_with_fee.spec.ts
index a9bbe60..7f46ba9 100644
--- a/tests/range_proofs/range_proofs_with_fee.spec.js
+++ b/__test__/range_proofs/range_proofs_with_fee.spec.ts
@@ -1,3 +1,15 @@
+import { BigInt } from "biginteger";
+import {
+ hash_to_scalar,
+ Z,
+ generate_key_image_2,
+ genRct,
+ verRct,
+ decodeRct,
+} from "cryptonote_utils";
+import { ctskpkGen, populateFromBlockchain } from "./test_utils";
+import { SecretCommitment, MixCommitment } from "types";
+
// Copyright (c) 2014-2018, MyMonero.com
//
// All rights reserved.
@@ -25,19 +37,13 @@
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const {
- ctskpkGen,
- populateFromBlockchain,
- JSBigInt,
- monero_utils,
-} = require("./test_utils");
it("range_proofs", () => {
//Ring CT Stuff
//ct range proofs
// ctkey vectors
- let inSk = [],
- inPk = [];
+ let inSk: SecretCommitment[] = [],
+ inPk: MixCommitment[] = [];
// ctkeys
// we test only a single input here since the current impl of
@@ -55,29 +61,29 @@ it("range_proofs", () => {
// key vector
let amount_keys = [];
- amounts.push(new JSBigInt(1000));
- amount_keys.push(monero_utils.hash_to_scalar(monero_utils.Z));
+ amounts.push(new BigInt(1000));
+ amount_keys.push(hash_to_scalar(Z));
- amounts.push(new JSBigInt(4000));
- amount_keys.push(monero_utils.hash_to_scalar(monero_utils.Z));
+ amounts.push(new BigInt(4000));
+ amount_keys.push(hash_to_scalar(Z));
- amounts.push(new JSBigInt(1000));
- amount_keys.push(monero_utils.hash_to_scalar(monero_utils.Z));
+ amounts.push(new BigInt(1000));
+ amount_keys.push(hash_to_scalar(Z));
//compute rct data with mixin 500
const { index, mixRing } = populateFromBlockchain(inPk, 2);
// generate kimg
- const kimg = [monero_utils.generate_key_image_2(inPk[0].dest, inSk[0].x)];
+ const kimg = [generate_key_image_2(inPk[0].dest, inSk[0].x)];
// add fee of 1 NOTE: fee is passed in with its endian not swapped, hence no usage of d2s
const fee = "1";
- let s = monero_utils.genRct(
- monero_utils.Z,
+ let s = genRct(
+ Z,
inSk,
kimg,
- [[]],
+ [],
amounts,
mixRing,
amount_keys,
@@ -85,32 +91,22 @@ it("range_proofs", () => {
fee,
);
- expect(monero_utils.verRct(s, true, mixRing, kimg[0])).toEqual(true);
- expect(monero_utils.verRct(s, false, mixRing, kimg[0])).toEqual(true);
+ expect(verRct(s, true, mixRing, kimg[0])).toEqual(true);
+ expect(verRct(s, false, mixRing, kimg[0])).toEqual(true);
//decode received amount
- monero_utils.decodeRct(s, amount_keys[1], 1);
+ decodeRct(s, amount_keys[1], 1);
// Ring CT with failing MG sig part should not verify!
// Since sum of inputs != outputs
- amounts[1] = new JSBigInt(4501);
+ amounts[1] = new BigInt(4501);
- s = monero_utils.genRct(
- monero_utils.Z,
- inSk,
- kimg,
- [[]],
- amounts,
- mixRing,
- amount_keys,
- [index],
- fee,
- );
+ s = genRct(Z, inSk, kimg, [], amounts, mixRing, amount_keys, [index], fee);
- expect(monero_utils.verRct(s, true, mixRing, kimg[0])).toEqual(true);
- expect(monero_utils.verRct(s, false, mixRing, kimg[0])).toEqual(false);
+ expect(verRct(s, true, mixRing, kimg[0])).toEqual(true);
+ expect(verRct(s, false, mixRing, kimg[0])).toEqual(false);
//decode received amount
- monero_utils.decodeRct(s, amount_keys[1], 1);
+ decodeRct(s, amount_keys[1], 1);
});
diff --git a/tests/range_proofs/simple.spec.js b/__test__/range_proofs/simple.spec.ts
similarity index 76%
rename from tests/range_proofs/simple.spec.js
rename to __test__/range_proofs/simple.spec.ts
index a92aa08..76aba3e 100644
--- a/tests/range_proofs/simple.spec.js
+++ b/__test__/range_proofs/simple.spec.ts
@@ -26,12 +26,17 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const {
- ctskpkGen,
- populateFromBlockchainSimple,
- JSBigInt,
- monero_utils,
-} = require("./test_utils");
+import { ctskpkGen, populateFromBlockchainSimple } from "./test_utils";
+import {
+ hash_to_scalar,
+ Z,
+ random_scalar,
+ generate_key_image_2,
+ genRct,
+ verRctSimple,
+ decodeRctSimple,
+} from "cryptonote_utils";
+import { BigInt } from "index";
it("should test ringct simple transactions", () => {
//Ring CT Stuff
@@ -50,7 +55,7 @@ it("should test ringct simple transactions", () => {
let [sctmp, pctmp] = ctskpkGen(3000);
inSk.push(sctmp);
inPk.push(pctmp);
- inamounts.push(3000);
+ inamounts.push(new BigInt(3000));
}
//add fake input 3000
@@ -60,16 +65,16 @@ it("should test ringct simple transactions", () => {
let [sctmp, pctmp] = ctskpkGen(3000);
inSk.push(sctmp);
inPk.push(pctmp);
- inamounts.push(3000);
+ inamounts.push(new BigInt(3000));
}
- outamounts.push(5000);
- amount_keys.push(monero_utils.hash_to_scalar(monero_utils.Z));
+ outamounts.push(new BigInt(5000));
+ amount_keys.push(hash_to_scalar(Z));
- outamounts.push(999);
- amount_keys.push(monero_utils.hash_to_scalar(monero_utils.Z));
+ outamounts.push(new BigInt(999));
+ amount_keys.push(hash_to_scalar(Z));
- const message = monero_utils.random_scalar();
+ const message = random_scalar();
const txnFee = "1";
// generate mixin and indices
@@ -84,11 +89,11 @@ it("should test ringct simple transactions", () => {
// generate kimg
const kimg = [
- monero_utils.generate_key_image_2(inPk[0].dest, inSk[0].x),
- monero_utils.generate_key_image_2(inPk[1].dest, inSk[1].x),
+ generate_key_image_2(inPk[0].dest, inSk[0].x),
+ generate_key_image_2(inPk[1].dest, inSk[1].x),
];
- const s = monero_utils.genRct(
+ const s = genRct(
message,
inSk,
kimg,
@@ -100,8 +105,8 @@ it("should test ringct simple transactions", () => {
txnFee,
);
- expect(monero_utils.verRctSimple(s, true, mixRings, kimg)).toEqual(true);
- expect(monero_utils.verRctSimple(s, false, mixRings, kimg)).toEqual(true);
+ expect(verRctSimple(s, true, mixRings, kimg)).toEqual(true);
+ expect(verRctSimple(s, false, mixRings, kimg)).toEqual(true);
- monero_utils.decodeRctSimple(s, amount_keys[1], 1);
+ decodeRctSimple(s, amount_keys[1], 1);
});
diff --git a/tests/range_proofs/test_utils.js b/__test__/range_proofs/test_utils.ts
similarity index 66%
rename from tests/range_proofs/test_utils.js
rename to __test__/range_proofs/test_utils.ts
index 542cdd8..bb21ee2 100644
--- a/tests/range_proofs/test_utils.js
+++ b/__test__/range_proofs/test_utils.ts
@@ -25,60 +25,67 @@
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const monero_utils = require("../../").monero_utils;
-const JSBigInt = require("../../cryptonote_utils/biginteger").BigInteger;
-const { randomBytes } = require("crypto");
+import { randomBytes } from "crypto";
+import {
+ random_keypair,
+ d2s,
+ ge_scalarmult,
+ ge_add,
+ H,
+} from "cryptonote_utils";
+import { SecretCommitment, MixCommitment } from "types";
//generates a / Pedersen commitment to the amount
-function ctskpkGen(amount) {
- let sk = {},
- pk = {};
- const key_pair1 = monero_utils.random_keypair();
- const key_pair2 = monero_utils.random_keypair();
+
+export function ctskpkGen(amount: number): [SecretCommitment, MixCommitment] {
+ let sk = { x: "", a: "" },
+ pk = { dest: "", mask: "" };
+ const key_pair1 = random_keypair();
+ const key_pair2 = random_keypair();
sk.x = key_pair1.sec;
pk.dest = key_pair1.pub;
sk.a = key_pair2.sec;
pk.mask = key_pair2.pub;
- const am = monero_utils.d2s(amount.toString());
- const bH = monero_utils.ge_scalarmult(monero_utils.H, am);
+ const am = d2s(amount.toString());
+ const bH = ge_scalarmult(H, am);
- pk.mask = monero_utils.ge_add(pk.mask, bH);
+ pk.mask = ge_add(pk.mask, bH);
return [sk, pk];
}
-function randomNum(upperLimit) {
+export function randomNum(upperLimit: number) {
return parseInt(randomBytes(1).toString("hex"), 16) % upperLimit;
}
-//These functions get keys from blockchain
-//replace these when connecting blockchain
-//getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with
-function getKeyFromBlockchain(reference_index) {
- let a = {};
- a.dest = monero_utils.random_keypair().pub;
- a.mask = monero_utils.random_keypair().pub;
+// These functions get keys from blockchain
+// replace these when connecting blockchain
+// getKeyFromBlockchain grabs a key from the blockchain at "reference_index" (unused param) to mix with
+export function getKeyFromBlockchain() {
+ let a = { dest: "", mask: "" };
+ a.dest = random_keypair().pub;
+ a.mask = random_keypair().pub;
return a;
}
// populateFromBlockchain creates a keymatrix with "mixin" + 1 columns and one of the columns is inPk
// the return values are the key matrix, and the index where inPk was put (random).
-function populateFromBlockchain(inPk, mixin) {
+export function populateFromBlockchain(inPk: MixCommitment[], mixin: number) {
const rows = inPk.length;
const inPkCpy = [...inPk];
// ctkeyMatrix
- const mixRing = [];
+ const mixRing: MixCommitment[][] = [];
const index = randomNum(mixin);
for (let i = 0; i < rows; i++) {
mixRing[i] = [];
for (let j = 0; j <= mixin; j++) {
if (j !== index) {
- mixRing[i][j] = getKeyFromBlockchain(index); /*?*/
+ mixRing[i][j] = getKeyFromBlockchain(); /*?*/
} else {
- mixRing[i][j] = inPkCpy.pop();
+ mixRing[i][j] = inPkCpy.pop() as MixCommitment;
}
}
}
@@ -86,13 +93,16 @@ function populateFromBlockchain(inPk, mixin) {
return { mixRing, index };
}
-function populateFromBlockchainSimple(inPk, mixin) {
+export function populateFromBlockchainSimple(
+ inPk: MixCommitment,
+ mixin: number,
+) {
const index = randomNum(mixin);
const mixRing = [];
for (let i = 0; i <= mixin; i++) {
if (i !== index) {
- mixRing[i] = getKeyFromBlockchain(index);
+ mixRing[i] = getKeyFromBlockchain();
} else {
mixRing[i] = inPk;
}
@@ -100,12 +110,3 @@ function populateFromBlockchainSimple(inPk, mixin) {
return { mixRing, index };
}
-
-module.exports = {
- ctskpkGen,
- populateFromBlockchain,
- populateFromBlockchainSimple,
- getKeyFromBlockchain,
- monero_utils,
- JSBigInt,
-};
diff --git a/cryptonote_utils/cryptonote_utils.js b/cryptonote_utils/cryptonote_utils.js
deleted file mode 100644
index e18bc1b..0000000
--- a/cryptonote_utils/cryptonote_utils.js
+++ /dev/null
@@ -1,2890 +0,0 @@
-// Copyright (c) 2014-2018, MyMonero.com
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Original Author: Lucas Jones
-// Modified to remove jQuery dep and support modular inclusion of deps by Paul Shapiro (2016)
-// Modified to add RingCT support by luigi1111 (2017)
-//
-// v--- These should maybe be injected into a context and supplied to currencyConfig for future platforms
-const JSBigInt = require("./biginteger").BigInteger;
-const cnBase58 = require("./internal_libs/bs58");
-const CNCrypto = require("./internal_libs/cn_crypto");
-const nacl = require("./internal_libs/fast_cn");
-const SHA3 = require("keccakjs");
-const nettype_utils = require("./nettype");
-const { randomBytes } = require("crypto");
-
-var cnUtil = function(currencyConfig) {
- var config = {}; // shallow copy of initConfig
- for (var key in currencyConfig) {
- config[key] = currencyConfig[key];
- }
-
- config.coinUnits = new JSBigInt(10).pow(config.coinUnitPlaces);
-
- var HASH_SIZE = 32;
- var ADDRESS_CHECKSUM_SIZE = 4;
- var INTEGRATED_ID_SIZE = 8;
- var ENCRYPTED_PAYMENT_ID_TAIL = 141;
- //
- var UINT64_MAX = new JSBigInt(2).pow(64);
- var CURRENT_TX_VERSION = 2;
- var OLD_TX_VERSION = 1;
- var RCTTypeFull = 1;
- var RCTTypeSimple = 2;
- var TX_EXTRA_NONCE_MAX_COUNT = 255;
- var TX_EXTRA_TAGS = {
- PADDING: "00",
- PUBKEY: "01",
- NONCE: "02",
- MERGE_MINING: "03",
- };
- var TX_EXTRA_NONCE_TAGS = {
- PAYMENT_ID: "00",
- ENCRYPTED_PAYMENT_ID: "01",
- };
- var KEY_SIZE = 32;
- var STRUCT_SIZES = {
- GE_P3: 160,
- GE_P2: 120,
- GE_P1P1: 160,
- GE_CACHED: 160,
- EC_SCALAR: 32,
- EC_POINT: 32,
- KEY_IMAGE: 32,
- GE_DSMP: 160 * 8, // ge_cached * 8
- SIGNATURE: 64, // ec_scalar * 2
- };
-
- //RCT vars
- var H = "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94"; //base H for amounts
- this.H = H;
- var l = JSBigInt(
- "7237005577332262213973186563042994240857116359379907606001950938285454250989",
- ); //curve order (not RCT specific)
-
- var I = "0100000000000000000000000000000000000000000000000000000000000000"; //identity element
- this.I = I;
- this.identity = function() {
- return I;
- };
-
- var Z = "0000000000000000000000000000000000000000000000000000000000000000"; //zero scalar
- this.Z = Z;
-
- //H2 object to speed up some operations
- var H2 = [
- "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94",
- "8faa448ae4b3e2bb3d4d130909f55fcd79711c1c83cdbccadd42cbe1515e8712",
- "12a7d62c7791654a57f3e67694ed50b49a7d9e3fc1e4c7a0bde29d187e9cc71d",
- "789ab9934b49c4f9e6785c6d57a498b3ead443f04f13df110c5427b4f214c739",
- "771e9299d94f02ac72e38e44de568ac1dcb2edc6edb61f83ca418e1077ce3de8",
- "73b96db43039819bdaf5680e5c32d741488884d18d93866d4074a849182a8a64",
- "8d458e1c2f68ebebccd2fd5d379f5e58f8134df3e0e88cad3d46701063a8d412",
- "09551edbe494418e81284455d64b35ee8ac093068a5f161fa6637559177ef404",
- "d05a8866f4df8cee1e268b1d23a4c58c92e760309786cdac0feda1d247a9c9a7",
- "55cdaad518bd871dd1eb7bc7023e1dc0fdf3339864f88fdd2de269fe9ee1832d",
- "e7697e951a98cfd5712b84bbe5f34ed733e9473fcb68eda66e3788df1958c306",
- "f92a970bae72782989bfc83adfaa92a4f49c7e95918b3bba3cdc7fe88acc8d47",
- "1f66c2d491d75af915c8db6a6d1cb0cd4f7ddcd5e63d3ba9b83c866c39ef3a2b",
- "3eec9884b43f58e93ef8deea260004efea2a46344fc5965b1a7dd5d18997efa7",
- "b29f8f0ccb96977fe777d489d6be9e7ebc19c409b5103568f277611d7ea84894",
- "56b1f51265b9559876d58d249d0c146d69a103636699874d3f90473550fe3f2c",
- "1d7a36575e22f5d139ff9cc510fa138505576b63815a94e4b012bfd457caaada",
- "d0ac507a864ecd0593fa67be7d23134392d00e4007e2534878d9b242e10d7620",
- "f6c6840b9cf145bb2dccf86e940be0fc098e32e31099d56f7fe087bd5deb5094",
- "28831a3340070eb1db87c12e05980d5f33e9ef90f83a4817c9f4a0a33227e197",
- "87632273d629ccb7e1ed1a768fa2ebd51760f32e1c0b867a5d368d5271055c6e",
- "5c7b29424347964d04275517c5ae14b6b5ea2798b573fc94e6e44a5321600cfb",
- "e6945042d78bc2c3bd6ec58c511a9fe859c0ad63fde494f5039e0e8232612bd5",
- "36d56907e2ec745db6e54f0b2e1b2300abcb422e712da588a40d3f1ebbbe02f6",
- "34db6ee4d0608e5f783650495a3b2f5273c5134e5284e4fdf96627bb16e31e6b",
- "8e7659fb45a3787d674ae86731faa2538ec0fdf442ab26e9c791fada089467e9",
- "3006cf198b24f31bb4c7e6346000abc701e827cfbb5df52dcfa42e9ca9ff0802",
- "f5fd403cb6e8be21472e377ffd805a8c6083ea4803b8485389cc3ebc215f002a",
- "3731b260eb3f9482e45f1c3f3b9dcf834b75e6eef8c40f461ea27e8b6ed9473d",
- "9f9dab09c3f5e42855c2de971b659328a2dbc454845f396ffc053f0bb192f8c3",
- "5e055d25f85fdb98f273e4afe08464c003b70f1ef0677bb5e25706400be620a5",
- "868bcf3679cb6b500b94418c0b8925f9865530303ae4e4b262591865666a4590",
- "b3db6bd3897afbd1df3f9644ab21c8050e1f0038a52f7ca95ac0c3de7558cb7a",
- "8119b3a059ff2cac483e69bcd41d6d27149447914288bbeaee3413e6dcc6d1eb",
- "10fc58f35fc7fe7ae875524bb5850003005b7f978c0c65e2a965464b6d00819c",
- "5acd94eb3c578379c1ea58a343ec4fcff962776fe35521e475a0e06d887b2db9",
- "33daf3a214d6e0d42d2300a7b44b39290db8989b427974cd865db011055a2901",
- "cfc6572f29afd164a494e64e6f1aeb820c3e7da355144e5124a391d06e9f95ea",
- "d5312a4b0ef615a331f6352c2ed21dac9e7c36398b939aec901c257f6cbc9e8e",
- "551d67fefc7b5b9f9fdbf6af57c96c8a74d7e45a002078a7b5ba45c6fde93e33",
- "d50ac7bd5ca593c656928f38428017fc7ba502854c43d8414950e96ecb405dc3",
- "0773e18ea1be44fe1a97e239573cfae3e4e95ef9aa9faabeac1274d3ad261604",
- "e9af0e7ca89330d2b8615d1b4137ca617e21297f2f0ded8e31b7d2ead8714660",
- "7b124583097f1029a0c74191fe7378c9105acc706695ed1493bb76034226a57b",
- "ec40057b995476650b3db98e9db75738a8cd2f94d863b906150c56aac19caa6b",
- "01d9ff729efd39d83784c0fe59c4ae81a67034cb53c943fb818b9d8ae7fc33e5",
- "00dfb3c696328c76424519a7befe8e0f6c76f947b52767916d24823f735baf2e",
- "461b799b4d9ceea8d580dcb76d11150d535e1639d16003c3fb7e9d1fd13083a8",
- "ee03039479e5228fdc551cbde7079d3412ea186a517ccc63e46e9fcce4fe3a6c",
- "a8cfb543524e7f02b9f045acd543c21c373b4c9b98ac20cec417a6ddb5744e94",
- "932b794bf89c6edaf5d0650c7c4bad9242b25626e37ead5aa75ec8c64e09dd4f",
- "16b10c779ce5cfef59c7710d2e68441ea6facb68e9b5f7d533ae0bb78e28bf57",
- "0f77c76743e7396f9910139f4937d837ae54e21038ac5c0b3fd6ef171a28a7e4",
- "d7e574b7b952f293e80dde905eb509373f3f6cd109a02208b3c1e924080a20ca",
- "45666f8c381e3da675563ff8ba23f83bfac30c34abdde6e5c0975ef9fd700cb9",
- "b24612e454607eb1aba447f816d1a4551ef95fa7247fb7c1f503020a7177f0dd",
- "7e208861856da42c8bb46a7567f8121362d9fb2496f131a4aa9017cf366cdfce",
- "5b646bff6ad1100165037a055601ea02358c0f41050f9dfe3c95dccbd3087be0",
- "746d1dccfed2f0ff1e13c51e2d50d5324375fbd5bf7ca82a8931828d801d43ab",
- "cb98110d4a6bb97d22feadbc6c0d8930c5f8fc508b2fc5b35328d26b88db19ae",
- "60b626a033b55f27d7676c4095eababc7a2c7ede2624b472e97f64f96b8cfc0e",
- "e5b52bc927468df71893eb8197ef820cf76cb0aaf6e8e4fe93ad62d803983104",
- "056541ae5da9961be2b0a5e895e5c5ba153cbb62dd561a427bad0ffd41923199",
- "f8fef05a3fa5c9f3eba41638b247b711a99f960fe73aa2f90136aeb20329b888",
- ];
-
- this.H2 = H2;
-
- //begin rct new functions
- //creates a Pedersen commitment from an amount (in scalar form) and a mask
- //C = bG + aH where b = mask, a = amount
- function commit(amount, mask) {
- if (
- !valid_hex(mask) ||
- mask.length !== 64 ||
- !valid_hex(amount) ||
- amount.length !== 64
- ) {
- throw "invalid amount or mask!";
- }
- var C = this.ge_double_scalarmult_base_vartime(amount, H, mask);
- return C;
- }
-
- function zeroCommit(amount) {
- if (!valid_hex(amount) || amount.length !== 64) {
- throw "invalid amount!";
- }
- var C = this.ge_double_scalarmult_base_vartime(amount, H, I);
- return C;
- }
-
- this.decode_rct_ecdh = function(ecdh, key) {
- var first = this.hash_to_scalar(key);
- var second = this.hash_to_scalar(first);
- return {
- mask: this.sc_sub(ecdh.mask, first),
- amount: this.sc_sub(ecdh.amount, second),
- };
- };
-
- this.encode_rct_ecdh = function(ecdh, key) {
- var first = this.hash_to_scalar(key);
- var second = this.hash_to_scalar(first);
- return {
- mask: this.sc_add(ecdh.mask, first),
- amount: this.sc_add(ecdh.amount, second),
- };
- };
-
- //switch byte order for hex string
- function swapEndian(hex) {
- if (hex.length % 2 !== 0) {
- return "length must be a multiple of 2!";
- }
- var data = "";
- for (var i = 1; i <= hex.length / 2; i++) {
- data += hex.substr(0 - 2 * i, 2);
- }
- return data;
- }
-
- //switch byte order charwise
- function swapEndianC(string) {
- var data = "";
- for (var i = 1; i <= string.length; i++) {
- data += string.substr(0 - i, 1);
- }
- return data;
- }
-
- //for most uses you'll also want to swapEndian after conversion
- //mainly to convert integer "scalars" to usable hexadecimal strings
- //uint long long to 32 byte key
- function d2h(integer) {
- if (typeof integer !== "string" && integer.toString().length > 15) {
- throw "integer should be entered as a string for precision";
- }
- var padding = "";
- for (var i = 0; i < 63; i++) {
- padding += "0";
- }
- return (
- padding +
- JSBigInt(integer)
- .toString(16)
- .toLowerCase()
- ).slice(-64);
- }
- this.d2h = d2h;
-
- //integer (string) to scalar
- function d2s(integer) {
- return swapEndian(d2h(integer));
- }
-
- this.d2s = d2s;
- //scalar to integer (string)
- function s2d(scalar) {
- return JSBigInt.parse(swapEndian(scalar), 16).toString();
- }
-
- //convert integer string to 64bit "binary" little-endian string
- function d2b(integer) {
- if (typeof integer !== "string" && integer.toString().length > 15) {
- throw "integer should be entered as a string for precision";
- }
- var padding = "";
- for (var i = 0; i < 63; i++) {
- padding += "0";
- }
- var a = new JSBigInt(integer);
- if (a.toString(2).length > 64) {
- throw "amount overflows uint64!";
- }
- return swapEndianC((padding + a.toString(2)).slice(-64));
- }
-
- //end rct new functions
-
- this.valid_hex = function(hex) {
- var exp = new RegExp("[0-9a-fA-F]{" + hex.length + "}");
- return exp.test(hex);
- };
-
- //simple exclusive or function for two hex inputs
- this.hex_xor = function(hex1, hex2) {
- if (
- !hex1 ||
- !hex2 ||
- hex1.length !== hex2.length ||
- hex1.length % 2 !== 0 ||
- hex2.length % 2 !== 0
- ) {
- throw "Hex string(s) is/are invalid!";
- }
- var bin1 = hextobin(hex1);
- var bin2 = hextobin(hex2);
- var xor = new Uint8Array(bin1.length);
- for (var i = 0; i < xor.length; i++) {
- xor[i] = bin1[i] ^ bin2[i];
- }
- return bintohex(xor);
- };
-
- function hextobin(hex) {
- if (hex.length % 2 !== 0) throw "Hex string has invalid length!";
- var res = new Uint8Array(hex.length / 2);
- for (var i = 0; i < hex.length / 2; ++i) {
- res[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
- }
- return res;
- }
- this.hextobin = hextobin;
-
- function bintohex(bin) {
- var out = [];
- for (var i = 0; i < bin.length; ++i) {
- out.push(("0" + bin[i].toString(16)).slice(-2));
- }
- return out.join("");
- }
-
- // Generate a 256-bit / 64-char / 32-byte crypto random
- this.rand_32 = function() {
- return randomBytes(32).toString("hex");
- };
-
- // Generate a 64-bit / 16-char / 8-byte crypto random
- this.rand_8 = function() {
- return randomBytes(8).toString("hex");
- };
-
- this.encode_varint = function(i) {
- i = new JSBigInt(i);
- var out = "";
- // While i >= b10000000
- while (i.compare(0x80) >= 0) {
- // out.append i & b01111111 | b10000000
- out += ("0" + ((i.lowVal() & 0x7f) | 0x80).toString(16)).slice(-2);
- i = i.divide(new JSBigInt(2).pow(7));
- }
- out += ("0" + i.toJSValue().toString(16)).slice(-2);
- return out;
- };
-
- this.sc_reduce = function(hex) {
- var input = hextobin(hex);
- if (input.length !== 64) {
- throw "Invalid input length";
- }
- var mem = CNCrypto._malloc(64);
- CNCrypto.HEAPU8.set(input, mem);
- CNCrypto.ccall("sc_reduce", "void", ["number"], [mem]);
- var output = CNCrypto.HEAPU8.subarray(mem, mem + 64);
- CNCrypto._free(mem);
- return bintohex(output);
- };
-
- this.sc_reduce32 = function(hex) {
- var input = hextobin(hex);
- if (input.length !== 32) {
- throw "Invalid input length";
- }
- var mem = CNCrypto._malloc(32);
- CNCrypto.HEAPU8.set(input, mem);
- CNCrypto.ccall("sc_reduce32", "void", ["number"], [mem]);
- var output = CNCrypto.HEAPU8.subarray(mem, mem + 32);
- CNCrypto._free(mem);
- return bintohex(output);
- };
-
- this.cn_fast_hash = function(input) {
- if (input.length % 2 !== 0 || !this.valid_hex(input)) {
- throw "Input invalid";
- }
-
- const hasher = new SHA3(256);
- hasher.update(hextobin(input));
- return hasher.digest("hex");
- };
-
- this.sec_key_to_pub = function(sec) {
- if (sec.length !== 64) {
- throw "Invalid sec length";
- }
- return bintohex(nacl.ge_scalarmult_base(hextobin(sec)));
- };
-
- //alias
- this.ge_scalarmult_base = function(sec) {
- return this.sec_key_to_pub(sec);
- };
-
- this.ge_scalarmult = function(pub, sec) {
- if (pub.length !== 64 || sec.length !== 64) {
- throw "Invalid input length";
- }
- return bintohex(nacl.ge_scalarmult(hextobin(pub), hextobin(sec)));
- };
-
- this.pubkeys_to_string = function(spend, view, nettype) {
- var prefix = this.encode_varint(
- nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype),
- );
- var data = prefix + spend + view;
- var checksum = this.cn_fast_hash(data);
- return cnBase58.encode(
- data + checksum.slice(0, ADDRESS_CHECKSUM_SIZE * 2),
- );
- };
-
- this.new__int_addr_from_addr_and_short_pid = function(
- address,
- short_pid,
- nettype,
- ) {
- // throws
- let decoded_address = this.decode_address(
- address, // TODO/FIXME: not super happy about having to decode just to re-encode… this was a quick hack
- nettype,
- ); // throws
- if (!short_pid || short_pid.length != 16) {
- throw "expected valid short_pid";
- }
- var prefix = this.encode_varint(
- nettype_utils.cryptonoteBase58PrefixForIntegratedAddressOn(nettype),
- );
- var data =
- prefix + decoded_address.spend + decoded_address.view + short_pid;
- var checksum = this.cn_fast_hash(data);
- var encodable__data =
- data + checksum.slice(0, ADDRESS_CHECKSUM_SIZE * 2);
- //
- return cnBase58.encode(encodable__data);
- };
-
- // Generate keypair from seed
- this.generate_keys = function(seed) {
- if (seed.length !== 64) throw "Invalid input length!";
- var sec = this.sc_reduce32(seed);
- var pub = this.sec_key_to_pub(sec);
- return {
- sec: sec,
- pub: pub,
- };
- };
-
- this.random_keypair = function() {
- return this.generate_keys(this.rand_32());
- };
-
- // Random 32-byte ec scalar
- this.random_scalar = function() {
- return this.sc_reduce32(this.rand_32());
- };
-
- // alias
- this.skGen = random_scalar;
-
- this.create_address = function(seed, nettype) {
- var keys = {};
- // updated by Luigi and PS to support reduced and non-reduced seeds
- var first;
- if (seed.length !== 64) {
- first = this.cn_fast_hash(seed);
- } else {
- first = this.sc_reduce32(seed);
- }
- keys.spend = this.generate_keys(first);
- var second = this.cn_fast_hash(first);
- keys.view = this.generate_keys(second);
- keys.public_addr = this.pubkeys_to_string(
- keys.spend.pub,
- keys.view.pub,
- nettype,
- );
- return keys;
- };
-
- this.create_addr_prefix = function(seed, nettype) {
- var first;
- if (seed.length !== 64) {
- first = this.cn_fast_hash(seed);
- } else {
- first = seed;
- }
- var spend = this.generate_keys(first);
- var prefix = this.encode_varint(
- nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype),
- );
- return cnBase58.encode(prefix + spend.pub).slice(0, 44);
- };
-
- this.decode_address = function(address, nettype) {
- var dec = cnBase58.decode(address);
- var expectedPrefix = this.encode_varint(
- nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype),
- );
- var expectedPrefixInt = this.encode_varint(
- nettype_utils.cryptonoteBase58PrefixForIntegratedAddressOn(nettype),
- );
- var expectedPrefixSub = this.encode_varint(
- nettype_utils.cryptonoteBase58PrefixForSubAddressOn(nettype),
- );
- var prefix = dec.slice(0, expectedPrefix.length);
- if (
- prefix !== expectedPrefix &&
- prefix !== expectedPrefixInt &&
- prefix !== expectedPrefixSub
- ) {
- throw "Invalid address prefix";
- }
- dec = dec.slice(expectedPrefix.length);
- var spend = dec.slice(0, 64);
- var view = dec.slice(64, 128);
- if (prefix === expectedPrefixInt) {
- var intPaymentId = dec.slice(128, 128 + INTEGRATED_ID_SIZE * 2);
- var checksum = dec.slice(
- 128 + INTEGRATED_ID_SIZE * 2,
- 128 + INTEGRATED_ID_SIZE * 2 + ADDRESS_CHECKSUM_SIZE * 2,
- );
- var expectedChecksum = this.cn_fast_hash(
- prefix + spend + view + intPaymentId,
- ).slice(0, ADDRESS_CHECKSUM_SIZE * 2);
- } else {
- var checksum = dec.slice(128, 128 + ADDRESS_CHECKSUM_SIZE * 2);
- var expectedChecksum = this.cn_fast_hash(
- prefix + spend + view,
- ).slice(0, ADDRESS_CHECKSUM_SIZE * 2);
- }
- if (checksum !== expectedChecksum) {
- throw "Invalid checksum";
- }
- if (intPaymentId) {
- return {
- spend: spend,
- view: view,
- intPaymentId: intPaymentId,
- };
- } else {
- return {
- spend: spend,
- view: view,
- };
- }
- };
-
- this.is_subaddress = function(addr, nettype) {
- var decoded = cnBase58.decode(addr);
- var subaddressPrefix = this.encode_varint(
- nettype_utils.cryptonoteBase58PrefixForSubAddressOn(nettype),
- );
- var prefix = decoded.slice(0, subaddressPrefix.length);
- return prefix === subaddressPrefix;
- };
-
- this.valid_keys = function(view_pub, view_sec, spend_pub, spend_sec) {
- var expected_view_pub = this.sec_key_to_pub(view_sec);
- var expected_spend_pub = this.sec_key_to_pub(spend_sec);
- return (
- expected_spend_pub === spend_pub && expected_view_pub === view_pub
- );
- };
-
- this.hash_to_scalar = function(buf) {
- var hash = this.cn_fast_hash(buf);
- var scalar = this.sc_reduce32(hash);
- return scalar;
- };
-
- this.generate_key_derivation = function(pub, sec) {
- if (pub.length !== 64 || sec.length !== 64) {
- throw "Invalid input length";
- }
- var P = this.ge_scalarmult(pub, sec);
- return this.ge_scalarmult(P, d2s(8)); //mul8 to ensure group
- };
-
- this.derivation_to_scalar = function(derivation, output_index) {
- var buf = "";
- if (derivation.length !== STRUCT_SIZES.EC_POINT * 2) {
- throw "Invalid derivation length!";
- }
- buf += derivation;
- var enc = encode_varint(output_index);
- if (enc.length > 10 * 2) {
- throw "output_index didn't fit in 64-bit varint";
- }
- buf += enc;
- return this.hash_to_scalar(buf);
- };
-
- this.derive_secret_key = function(derivation, out_index, sec) {
- if (derivation.length !== 64 || sec.length !== 64) {
- throw "Invalid input length!";
- }
- var scalar_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- var scalar_b = hextobin(
- this.derivation_to_scalar(derivation, out_index),
- );
- CNCrypto.HEAPU8.set(scalar_b, scalar_m);
- var base_m = CNCrypto._malloc(KEY_SIZE);
- CNCrypto.HEAPU8.set(hextobin(sec), base_m);
- var derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- CNCrypto.ccall(
- "sc_add",
- "void",
- ["number", "number", "number"],
- [derived_m, base_m, scalar_m],
- );
- var res = CNCrypto.HEAPU8.subarray(
- derived_m,
- derived_m + STRUCT_SIZES.EC_SCALAR,
- );
- CNCrypto._free(scalar_m);
- CNCrypto._free(base_m);
- CNCrypto._free(derived_m);
- return bintohex(res);
- };
-
- this.derive_public_key = function(derivation, out_index, pub) {
- if (derivation.length !== 64 || pub.length !== 64) {
- throw "Invalid input length!";
- }
- var s = this.derivation_to_scalar(derivation, out_index);
- return bintohex(
- nacl.ge_add(hextobin(pub), hextobin(this.ge_scalarmult_base(s))),
- );
- };
-
- // D' = P - Hs(aR|i)G
- this.derive_subaddress_public_key = function(
- output_key,
- derivation,
- out_index,
- ) {
- if (output_key.length !== 64 || derivation.length !== 64) {
- throw "Invalid input length!";
- }
- var scalar = this.derivation_to_scalar(derivation, out_index);
- var point = this.ge_scalarmult_base(scalar);
- return this.ge_sub(output_key, point);
- };
-
- this.hash_to_ec = function(key) {
- if (key.length !== KEY_SIZE * 2) {
- throw "Invalid input length";
- }
- var h_m = CNCrypto._malloc(HASH_SIZE);
- var point_m = CNCrypto._malloc(STRUCT_SIZES.GE_P2);
- var point2_m = CNCrypto._malloc(STRUCT_SIZES.GE_P1P1);
- var res_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
- var hash = hextobin(this.cn_fast_hash(key, KEY_SIZE));
- CNCrypto.HEAPU8.set(hash, h_m);
- CNCrypto.ccall(
- "ge_fromfe_frombytes_vartime",
- "void",
- ["number", "number"],
- [point_m, h_m],
- );
- CNCrypto.ccall(
- "ge_mul8",
- "void",
- ["number", "number"],
- [point2_m, point_m],
- );
- CNCrypto.ccall(
- "ge_p1p1_to_p3",
- "void",
- ["number", "number"],
- [res_m, point2_m],
- );
- var res = CNCrypto.HEAPU8.subarray(res_m, res_m + STRUCT_SIZES.GE_P3);
- CNCrypto._free(h_m);
- CNCrypto._free(point_m);
- CNCrypto._free(point2_m);
- CNCrypto._free(res_m);
- return bintohex(res);
- };
-
- //returns a 32 byte point via "ge_p3_tobytes" rather than a 160 byte "p3", otherwise same as above;
- this.hash_to_ec_2 = function(key) {
- if (key.length !== KEY_SIZE * 2) {
- throw "Invalid input length";
- }
- var h_m = CNCrypto._malloc(HASH_SIZE);
- var point_m = CNCrypto._malloc(STRUCT_SIZES.GE_P2);
- var point2_m = CNCrypto._malloc(STRUCT_SIZES.GE_P1P1);
- var res_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
- var hash = hextobin(this.cn_fast_hash(key, KEY_SIZE));
- var res2_m = CNCrypto._malloc(KEY_SIZE);
- CNCrypto.HEAPU8.set(hash, h_m);
- CNCrypto.ccall(
- "ge_fromfe_frombytes_vartime",
- "void",
- ["number", "number"],
- [point_m, h_m],
- );
- CNCrypto.ccall(
- "ge_mul8",
- "void",
- ["number", "number"],
- [point2_m, point_m],
- );
- CNCrypto.ccall(
- "ge_p1p1_to_p3",
- "void",
- ["number", "number"],
- [res_m, point2_m],
- );
- CNCrypto.ccall(
- "ge_p3_tobytes",
- "void",
- ["number", "number"],
- [res2_m, res_m],
- );
- var res = CNCrypto.HEAPU8.subarray(res2_m, res2_m + KEY_SIZE);
- CNCrypto._free(h_m);
- CNCrypto._free(point_m);
- CNCrypto._free(point2_m);
- CNCrypto._free(res_m);
- CNCrypto._free(res2_m);
- return bintohex(res);
- };
- this.hashToPoint = hash_to_ec_2;
-
- this.generate_key_image_2 = function(pub, sec) {
- if (!pub || !sec || pub.length !== 64 || sec.length !== 64) {
- throw "Invalid input length";
- }
- var pub_m = CNCrypto._malloc(KEY_SIZE);
- var sec_m = CNCrypto._malloc(KEY_SIZE);
- CNCrypto.HEAPU8.set(hextobin(pub), pub_m);
- CNCrypto.HEAPU8.set(hextobin(sec), sec_m);
- if (CNCrypto.ccall("sc_check", "number", ["number"], [sec_m]) !== 0) {
- throw "sc_check(sec) != 0";
- }
- var point_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
- var point2_m = CNCrypto._malloc(STRUCT_SIZES.GE_P2);
- var point_b = hextobin(this.hash_to_ec(pub));
- CNCrypto.HEAPU8.set(point_b, point_m);
- var image_m = CNCrypto._malloc(STRUCT_SIZES.KEY_IMAGE);
- CNCrypto.ccall(
- "ge_scalarmult",
- "void",
- ["number", "number", "number"],
- [point2_m, sec_m, point_m],
- );
- CNCrypto.ccall(
- "ge_tobytes",
- "void",
- ["number", "number"],
- [image_m, point2_m],
- );
- var res = CNCrypto.HEAPU8.subarray(
- image_m,
- image_m + STRUCT_SIZES.KEY_IMAGE,
- );
- CNCrypto._free(pub_m);
- CNCrypto._free(sec_m);
- CNCrypto._free(point_m);
- CNCrypto._free(point2_m);
- CNCrypto._free(image_m);
- return bintohex(res);
- };
-
- this.generate_key_image = function(
- tx_pub,
- view_sec,
- spend_pub,
- spend_sec,
- output_index,
- ) {
- if (tx_pub.length !== 64) {
- throw "Invalid tx_pub length";
- }
- if (view_sec.length !== 64) {
- throw "Invalid view_sec length";
- }
- if (spend_pub.length !== 64) {
- throw "Invalid spend_pub length";
- }
- if (spend_sec.length !== 64) {
- throw "Invalid spend_sec length";
- }
- var recv_derivation = this.generate_key_derivation(tx_pub, view_sec);
- var ephemeral_pub = this.derive_public_key(
- recv_derivation,
- output_index,
- spend_pub,
- );
- var ephemeral_sec = this.derive_secret_key(
- recv_derivation,
- output_index,
- spend_sec,
- );
- var k_image = this.generate_key_image_2(ephemeral_pub, ephemeral_sec);
- return {
- ephemeral_pub: ephemeral_pub,
- key_image: k_image,
- };
- };
-
- this.generate_key_image_helper_rct = function(
- keys,
- tx_pub_key,
- out_index,
- enc_mask,
- ) {
- var recv_derivation = this.generate_key_derivation(
- tx_pub_key,
- keys.view.sec,
- );
- if (!recv_derivation) throw "Failed to generate key image";
- var mask = enc_mask
- ? sc_sub(
- enc_mask,
- hash_to_scalar(
- derivation_to_scalar(recv_derivation, out_index),
- ),
- )
- : I; //decode mask, or d2s(1) if no mask
- var ephemeral_pub = this.derive_public_key(
- recv_derivation,
- out_index,
- keys.spend.pub,
- );
- if (!ephemeral_pub) throw "Failed to generate key image";
- var ephemeral_sec = this.derive_secret_key(
- recv_derivation,
- out_index,
- keys.spend.sec,
- );
- var image = this.generate_key_image_2(ephemeral_pub, ephemeral_sec);
- return {
- in_ephemeral: {
- pub: ephemeral_pub,
- sec: ephemeral_sec,
- mask: mask,
- },
- image: image,
- };
- };
-
- //curve and scalar functions; split out to make their host functions cleaner and more readable
- //inverts X coordinate -- this seems correct ^_^ -luigi1111
- this.ge_neg = function(point) {
- if (point.length !== 64) {
- throw "expected 64 char hex string";
- }
- return (
- point.slice(0, 62) +
- ((parseInt(point.slice(62, 63), 16) + 8) % 16).toString(16) +
- point.slice(63, 64)
- );
- };
-
- this.ge_add = function(p1, p2) {
- if (p1.length !== 64 || p2.length !== 64) {
- throw "Invalid input length!";
- }
- return bintohex(nacl.ge_add(hextobin(p1), hextobin(p2)));
- };
-
- //order matters
- this.ge_sub = function(point1, point2) {
- point2n = ge_neg(point2);
- return ge_add(point1, point2n);
- };
-
- //adds two scalars together
- this.sc_add = function(scalar1, scalar2) {
- if (scalar1.length !== 64 || scalar2.length !== 64) {
- throw "Invalid input length!";
- }
- var scalar1_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- var scalar2_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- CNCrypto.HEAPU8.set(hextobin(scalar1), scalar1_m);
- CNCrypto.HEAPU8.set(hextobin(scalar2), scalar2_m);
- var derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- CNCrypto.ccall(
- "sc_add",
- "void",
- ["number", "number", "number"],
- [derived_m, scalar1_m, scalar2_m],
- );
- var res = CNCrypto.HEAPU8.subarray(
- derived_m,
- derived_m + STRUCT_SIZES.EC_SCALAR,
- );
- CNCrypto._free(scalar1_m);
- CNCrypto._free(scalar2_m);
- CNCrypto._free(derived_m);
- return bintohex(res);
- };
-
- //subtracts one scalar from another
- this.sc_sub = function(scalar1, scalar2) {
- if (scalar1.length !== 64 || scalar2.length !== 64) {
- throw "Invalid input length!";
- }
- var scalar1_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- var scalar2_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- CNCrypto.HEAPU8.set(hextobin(scalar1), scalar1_m);
- CNCrypto.HEAPU8.set(hextobin(scalar2), scalar2_m);
- var derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- CNCrypto.ccall(
- "sc_sub",
- "void",
- ["number", "number", "number"],
- [derived_m, scalar1_m, scalar2_m],
- );
- var res = CNCrypto.HEAPU8.subarray(
- derived_m,
- derived_m + STRUCT_SIZES.EC_SCALAR,
- );
- CNCrypto._free(scalar1_m);
- CNCrypto._free(scalar2_m);
- CNCrypto._free(derived_m);
- return bintohex(res);
- };
-
- //fun mul function
- this.sc_mul = function(scalar1, scalar2) {
- if (scalar1.length !== 64 || scalar2.length !== 64) {
- throw "Invalid input length!";
- }
- return d2s(
- JSBigInt(s2d(scalar1))
- .multiply(JSBigInt(s2d(scalar2)))
- .remainder(l)
- .toString(),
- );
- };
-
- //res = c - (ab) mod l; argument names copied from the signature implementation
- this.sc_mulsub = function(sigc, sec, k) {
- if (
- k.length !== KEY_SIZE * 2 ||
- sigc.length !== KEY_SIZE * 2 ||
- sec.length !== KEY_SIZE * 2 ||
- !this.valid_hex(k) ||
- !this.valid_hex(sigc) ||
- !this.valid_hex(sec)
- ) {
- throw "bad scalar";
- }
- var sec_m = CNCrypto._malloc(KEY_SIZE);
- CNCrypto.HEAPU8.set(hextobin(sec), sec_m);
- var sigc_m = CNCrypto._malloc(KEY_SIZE);
- CNCrypto.HEAPU8.set(hextobin(sigc), sigc_m);
- var k_m = CNCrypto._malloc(KEY_SIZE);
- CNCrypto.HEAPU8.set(hextobin(k), k_m);
- var res_m = CNCrypto._malloc(KEY_SIZE);
-
- CNCrypto.ccall(
- "sc_mulsub",
- "void",
- ["number", "number", "number", "number"],
- [res_m, sigc_m, sec_m, k_m],
- );
- res = CNCrypto.HEAPU8.subarray(res_m, res_m + KEY_SIZE);
- CNCrypto._free(k_m);
- CNCrypto._free(sec_m);
- CNCrypto._free(sigc_m);
- CNCrypto._free(res_m);
- return bintohex(res);
- };
-
- this.ge_double_scalarmult_base_vartime = function(c, P, r) {
- if (c.length !== 64 || P.length !== 64 || r.length !== 64) {
- throw "Invalid input length!";
- }
- return bintohex(
- nacl.ge_double_scalarmult_base_vartime(
- hextobin(c),
- hextobin(P),
- hextobin(r),
- ),
- );
- };
-
- this.ge_double_scalarmult_postcomp_vartime = function(r, P, c, I) {
- if (
- c.length !== 64 ||
- P.length !== 64 ||
- r.length !== 64 ||
- I.length !== 64
- ) {
- throw "Invalid input length!";
- }
- var Pb = this.hash_to_ec_2(P);
- return bintohex(
- nacl.ge_double_scalarmult_postcomp_vartime(
- hextobin(r),
- hextobin(Pb),
- hextobin(c),
- hextobin(I),
- ),
- );
- };
-
- //begin RCT functions
-
- //xv: vector of secret keys, 1 per ring (nrings)
- //pm: matrix of pubkeys, indexed by size first
- //iv: vector of indexes, 1 per ring (nrings), can be a string
- //size: ring size, default 2
- //nrings: number of rings, default 64
- //extensible borromean signatures
- this.genBorromean = function(xv, pm, iv, size, nrings) {
- if (xv.length !== nrings) {
- throw "wrong xv length " + xv.length;
- }
- if (pm.length !== size) {
- throw "wrong pm size " + pm.length;
- }
- for (var i = 0; i < pm.length; i++) {
- if (pm[i].length !== nrings) {
- throw "wrong pm[" + i + "] length " + pm[i].length;
- }
- }
- if (iv.length !== nrings) {
- throw "wrong iv length " + iv.length;
- }
- for (var i = 0; i < iv.length; i++) {
- if (iv[i] >= size) {
- throw "bad indices value at: " + i + ": " + iv[i];
- }
- }
- //signature struct
- // in the case of size 2 and nrings 64
- // bb.s = [[64], [64]]
- var bb = {
- s: [],
- ee: "",
- };
- //signature pubkey matrix
- var L = [];
- //add needed sub vectors (1 per ring size)
- for (var i = 0; i < size; i++) {
- bb.s[i] = [];
- L[i] = [];
- }
- //compute starting at the secret index to the last row
- var index;
- var alpha = [];
- for (var i = 0; i < nrings; i++) {
- index = parseInt(iv[i]);
- alpha[i] = random_scalar();
- L[index][i] = ge_scalarmult_base(alpha[i]);
- for (var j = index + 1; j < size; j++) {
- bb.s[j][i] = random_scalar();
- var c = hash_to_scalar(L[j - 1][i]);
- L[j][i] = ge_double_scalarmult_base_vartime(
- c,
- pm[j][i],
- bb.s[j][i],
- );
- }
- }
- //hash last row to create ee
- var ltemp = "";
- for (var i = 0; i < nrings; i++) {
- ltemp += L[size - 1][i];
- }
- bb.ee = hash_to_scalar(ltemp);
- //compute the rest from 0 to secret index
- for (var i = 0; i < nrings; i++) {
- var cc = bb.ee;
- for (var j = 0; j < iv[i]; j++) {
- bb.s[j][i] = random_scalar();
- var LL = ge_double_scalarmult_base_vartime(
- cc,
- pm[j][i],
- bb.s[j][i],
- );
- cc = hash_to_scalar(LL);
- }
- bb.s[j][i] = sc_mulsub(xv[i], cc, alpha[i]);
- }
- return bb;
- };
-
- this.verifyBorromean = function(bb, P1, P2) {
- let Lv1 = [];
- let chash;
- let LL;
-
- let p2 = "";
- for (let ii = 0; ii < 64; ii++) {
- p2 = this.ge_double_scalarmult_base_vartime(
- bb.ee,
- P1[ii],
- bb.s[0][ii],
- );
- LL = p2;
- chash = this.hash_to_scalar(LL);
-
- p2 = this.ge_double_scalarmult_base_vartime(
- chash,
- P2[ii],
- bb.s[1][ii],
- );
- Lv1[ii] = p2;
- }
- const eeComputed = this.array_hash_to_scalar(Lv1);
- const equalKeys = eeComputed === bb.ee;
- console.log(`[verifyBorromean] Keys equal? ${equalKeys}
- ${eeComputed}
- ${bb.ee}`);
-
- return equalKeys;
- };
-
- //proveRange
- //proveRange gives C, and mask such that \sumCi = C
- // c.f. http://eprint.iacr.org/2015/1098 section 5.1
- // and Ci is a commitment to either 0 or s^i, i=0,...,n
- // thus this proves that "amount" is in [0, s^n] (we assume s to be 4) (2 for now with v2 txes)
- // mask is a such that C = aG + bH, and b = amount
- //commitMaskObj = {C: commit, mask: mask}
- this.proveRange = function(commitMaskObj, amount, nrings) {
- var size = 2;
- var C = I; //identity
- var mask = Z; //zero scalar
- var indices = d2b(amount); //base 2 for now
- var sig = {
- Ci: [],
- //exp: exponent //doesn't exist for now
- };
-
- var ai = [];
- var PM = [];
- for (var i = 0; i < size; i++) {
- PM[i] = [];
- }
- var j;
- //start at index and fill PM left and right -- PM[0] holds Ci
- for (var i = 0; i < nrings; i++) {
- ai[i] = random_scalar();
- j = indices[i];
- PM[j][i] = ge_scalarmult_base(ai[i]);
- while (j > 0) {
- j--;
- PM[j][i] = ge_add(PM[j + 1][i], H2[i]); //will need to use i*2 for base 4 (or different object)
- }
- j = indices[i];
- while (j < size - 1) {
- j++;
- PM[j][i] = ge_sub(PM[j - 1][i], H2[i]); //will need to use i*2 for base 4 (or different object)
- }
- mask = sc_add(mask, ai[i]);
- }
- /*
- * some more payload stuff here
- */
- //copy commitments to sig and sum them to commitment
- for (var i = 0; i < nrings; i++) {
- //if (i < nrings - 1) //for later version
- sig.Ci[i] = PM[0][i];
- C = ge_add(C, PM[0][i]);
- }
-
- sig.bsig = this.genBorromean(ai, PM, indices, size, nrings);
- commitMaskObj.C = C;
- commitMaskObj.mask = mask;
- return sig;
- };
-
- //proveRange and verRange
- //proveRange gives C, and mask such that \sumCi = C
- // c.f. http://eprint.iacr.org/2015/1098 section 5.1
- // and Ci is a commitment to either 0 or 2^i, i=0,...,63
- // thus this proves that "amount" is in [0, 2^64]
- // mask is a such that C = aG + bH, and b = amount
- //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
-
- this.verRange = function(C, as, nrings = 64) {
- try {
- let CiH = []; // len 64
- let asCi = []; // len 64
- let Ctmp = this.identity();
- for (let i = 0; i < nrings; i++) {
- CiH[i] = this.ge_sub(as.Ci[i], this.H2[i]);
- asCi[i] = as.Ci[i];
- Ctmp = this.ge_add(Ctmp, as.Ci[i]);
- }
- const equalKeys = Ctmp === C;
- console.log(`[verRange] Equal keys? ${equalKeys}
- C: ${C}
- Ctmp: ${Ctmp}`);
- if (!equalKeys) {
- return false;
- }
-
- if (!this.verifyBorromean(as.bsig, asCi, CiH)) {
- return false;
- }
-
- return true;
- } catch (e) {
- console.error(`[verRange]`, e);
- return false;
- }
- };
-
- function array_hash_to_scalar(array) {
- var buf = "";
- for (var i = 0; i < array.length; i++) {
- if (typeof array[i] !== "string") {
- throw "unexpected array element";
- }
- buf += array[i];
- }
- return hash_to_scalar(buf);
- }
- this.array_hash_to_scalar = array_hash_to_scalar;
-
- // Gen creates a signature which proves that for some column in the keymatrix "pk"
- // the signer knows a secret key for each row in that column
- // we presently only support matrices of 2 rows (pubkey, commitment)
- // this is a simplied MLSAG_Gen function to reflect that
- // because we don't want to force same secret column for all inputs
- this.MLSAG_Gen = function(message, pk, xx, kimg, index) {
- var cols = pk.length; //ring size
- var i;
-
- // secret index
- if (index >= cols) {
- throw "index out of range";
- }
- var rows = pk[0].length; //number of signature rows (always 2)
- // [pub, com] = 2
- if (rows !== 2) {
- throw "wrong row count";
- }
- // check all are len 2
- for (i = 0; i < cols; i++) {
- if (pk[i].length !== rows) {
- throw "pk is not rectangular";
- }
- }
- if (xx.length !== rows) {
- throw "bad xx size";
- }
-
- var c_old = "";
- var alpha = [];
-
- var rv = {
- ss: [],
- cc: null,
- };
- for (i = 0; i < cols; i++) {
- rv.ss[i] = [];
- }
- var toHash = []; //holds 6 elements: message, pubkey, dsRow L, dsRow R, commitment, ndsRow L
- toHash[0] = message;
-
- //secret index (pubkey section)
-
- alpha[0] = random_scalar(); //need to save alphas for later
- toHash[1] = pk[index][0]; //secret index pubkey
-
- // this is the keyimg anyway const H1 = this.hashToPoint(pk[index][0]) // Hp(K_in)
- // rv.II[0] = this.ge_scalarmult(H1, xx[0]) // k_in.Hp(K_in)
-
- toHash[2] = ge_scalarmult_base(alpha[0]); //dsRow L, a.G
- toHash[3] = generate_key_image_2(pk[index][0], alpha[0]); //dsRow R (key image check)
- //secret index (commitment section)
- alpha[1] = random_scalar();
- toHash[4] = pk[index][1]; //secret index commitment
- toHash[5] = ge_scalarmult_base(alpha[1]); //ndsRow L
-
- c_old = array_hash_to_scalar(toHash);
-
- i = (index + 1) % cols;
- if (i === 0) {
- rv.cc = c_old;
- }
- while (i != index) {
- rv.ss[i][0] = random_scalar(); //dsRow ss
- rv.ss[i][1] = random_scalar(); //ndsRow ss
-
- //!secret index (pubkey section)
- toHash[1] = pk[i][0];
- toHash[2] = ge_double_scalarmult_base_vartime(
- c_old,
- pk[i][0],
- rv.ss[i][0],
- );
- toHash[3] = ge_double_scalarmult_postcomp_vartime(
- rv.ss[i][0],
- pk[i][0],
- c_old,
- kimg,
- );
- //!secret index (commitment section)
- toHash[4] = pk[i][1];
- toHash[5] = ge_double_scalarmult_base_vartime(
- c_old,
- pk[i][1],
- rv.ss[i][1],
- );
- c_old = array_hash_to_scalar(toHash); //hash to get next column c
- i = (i + 1) % cols;
- if (i === 0) {
- rv.cc = c_old;
- }
- }
- for (i = 0; i < rows; i++) {
- rv.ss[index][i] = sc_mulsub(c_old, xx[i], alpha[i]);
- }
- return rv;
- };
-
- this.MLSAG_ver = function(message, pk, rv, kimg) {
- // we assume that col, row, rectangular checks are already done correctly
- // in MLSAG_gen
- const cols = pk.length;
- let c_old = rv.cc;
- let i = 0;
- let toHash = [];
- toHash[0] = message;
- while (i < cols) {
- //!secret index (pubkey section)
- toHash[1] = pk[i][0];
- toHash[2] = ge_double_scalarmult_base_vartime(
- c_old,
- pk[i][0],
- rv.ss[i][0],
- );
- toHash[3] = ge_double_scalarmult_postcomp_vartime(
- rv.ss[i][0],
- pk[i][0],
- c_old,
- kimg,
- );
-
- //!secret index (commitment section)
- toHash[4] = pk[i][1];
- toHash[5] = ge_double_scalarmult_base_vartime(
- c_old,
- pk[i][1],
- rv.ss[i][1],
- );
-
- c_old = array_hash_to_scalar(toHash);
-
- i = i + 1;
- }
-
- const c = this.sc_sub(c_old, rv.cc);
- console.log(`[MLSAG_ver]
- c_old: ${c_old}
- rc.cc: ${rv.cc}
- c: ${c}`);
-
- return Number(c) === 0;
- };
-
- //Ring-ct MG sigs
- //Prove:
- // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
- // This does the MG sig on the "dest" part of the given key matrix, and
- // the last row is the sum of input commitments from that column - sum output commitments
- // this shows that sum inputs = sum outputs
- //Ver:
- // verifies the above sig is created corretly
- this.proveRctMG = function(message, pubs, inSk, kimg, mask, Cout, index) {
- var cols = pubs.length;
- if (cols < 3) {
- throw "cols must be > 2 (mixin)";
- }
- var xx = [];
- var PK = [];
- //fill pubkey matrix (copy destination, subtract commitments)
- for (var i = 0; i < cols; i++) {
- PK[i] = [];
- PK[i][0] = pubs[i].dest;
- PK[i][1] = ge_sub(pubs[i].mask, Cout);
- }
- xx[0] = inSk.x;
- xx[1] = sc_sub(inSk.a, mask);
- return this.MLSAG_Gen(message, PK, xx, kimg, index);
- };
-
- //Ring-ct MG sigs
- //Prove:
- // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
- // This does the MG sig on the "dest" part of the given key matrix, and
- // the last row is the sum of input commitments from that column - sum output commitments
- // this shows that sum inputs = sum outputs
- //Ver:
- // verifies the above sig is created corretly
-
- this.verRctMG = function(mg, pubs, outPk, txnFeeKey, message, kimg) {
- const cols = pubs.length;
- if (cols < 1) {
- throw Error("Empty pubs");
- }
- const rows = pubs[0].length;
- if (rows < 1) {
- throw Error("Empty pubs");
- }
- for (let i = 0; i < cols.length; ++i) {
- if (pubs[i].length !== rows) {
- throw Error("Pubs is not rectangular");
- }
- }
-
- // key matrix of (cols, tmp)
-
- let M = [];
- console.log(pubs);
- //create the matrix to mg sig
- for (let i = 0; i < rows; i++) {
- M[i] = [];
- M[i][0] = pubs[0][i].dest;
- M[i][1] = this.ge_add(M[i][1] || this.identity(), pubs[0][i].mask); // start with input commitment
- for (let j = 0; j < outPk.length; j++) {
- M[i][1] = this.ge_sub(M[i][1], outPk[j]); // subtract all output commitments
- }
- M[i][1] = this.ge_sub(M[i][1], txnFeeKey); // subtract txnfee
- }
-
- console.log(
- `[MLSAG_ver input]`,
- JSON.stringify({ message, M, mg, kimg }, null, 1),
- );
- return this.MLSAG_ver(message, M, mg, kimg);
- };
-
- // simple version, assuming only post Rct
-
- this.verRctMGSimple = function(message, mg, pubs, C, kimg) {
- try {
- const rows = 1;
- const cols = pubs.len;
- const M = [];
-
- for (let i = 0; i < cols; i++) {
- M[i][0] = pubs[i].dest;
- M[i][1] = this.ge_sub(pubs[i].mask, C);
- }
-
- return MLSAG_ver(message, M, mg, kimg);
- } catch (error) {
- console.error("[verRctSimple]", error);
- return false;
- }
- };
-
- this.verBulletProof = function() {
- throw Error("verBulletProof is not implemented");
- };
-
- this.get_pre_mlsag_hash = function(rv) {
- var hashes = "";
- hashes += rv.message;
- hashes += this.cn_fast_hash(this.serialize_rct_base(rv));
- var buf = serialize_range_proofs(rv);
- hashes += this.cn_fast_hash(buf);
- return this.cn_fast_hash(hashes);
- };
-
- function serialize_range_proofs(rv) {
- var buf = "";
- for (var i = 0; i < rv.p.rangeSigs.length; i++) {
- for (var j = 0; j < rv.p.rangeSigs[i].bsig.s.length; j++) {
- for (var l = 0; l < rv.p.rangeSigs[i].bsig.s[j].length; l++) {
- buf += rv.p.rangeSigs[i].bsig.s[j][l];
- }
- }
- buf += rv.p.rangeSigs[i].bsig.ee;
- for (j = 0; j < rv.p.rangeSigs[i].Ci.length; j++) {
- buf += rv.p.rangeSigs[i].Ci[j];
- }
- }
- return buf;
- }
-
- //message is normal prefix hash
- //inSk is vector of x,a
- //kimg is vector of kimg
- //destinations is vector of pubkeys (we skip and proxy outAmounts instead)
- //inAmounts is vector of strings
- //outAmounts is vector of strings
- //mixRing is matrix of pubkey, commit (dest, mask)
- //amountKeys is vector of scalars
- //indices is vector
- //txnFee is string, with its endian not swapped (e.g d2s is not called before passing it in as an argument)
- //to this function
- this.genRct = function(
- message,
- inSk,
- kimg,
- inAmounts,
- outAmounts,
- mixRing,
- amountKeys,
- indices,
- txnFee,
- ) {
- if (outAmounts.length !== amountKeys.length) {
- throw "different number of amounts/amount_keys";
- }
- for (var i = 0; i < mixRing.length; i++) {
- if (mixRing[i].length <= indices[i]) {
- throw "bad mixRing/index size";
- }
- }
- if (mixRing.length !== inSk.length) {
- throw "mismatched mixRing/inSk";
- }
- if (inAmounts.length !== inSk.length) {
- throw "mismatched inAmounts/inSk";
- }
- if (indices.length !== inSk.length) {
- throw "mismatched indices/inSk";
- }
-
- rv = {
- type: inSk.length === 1 ? RCTTypeFull : RCTTypeSimple,
- message: message,
- outPk: [],
- p: {
- rangeSigs: [],
- MGs: [],
- },
- ecdhInfo: [],
- txnFee: txnFee.toString(),
- pseudoOuts: [],
- };
-
- var sumout = Z;
- var cmObj = {
- C: null,
- mask: null,
- };
- var nrings = 64; //for base 2/current
- var i;
- //compute range proofs, etc
- for (i = 0; i < outAmounts.length; i++) {
- var teststart = new Date().getTime();
- rv.p.rangeSigs[i] = this.proveRange(
- cmObj,
- outAmounts[i],
- nrings,
- 0,
- 0,
- );
- var testfinish = new Date().getTime() - teststart;
- console.log("Time take for range proof " + i + ": " + testfinish);
- rv.outPk[i] = cmObj.C;
- sumout = sc_add(sumout, cmObj.mask);
- rv.ecdhInfo[i] = this.encode_rct_ecdh(
- { mask: cmObj.mask, amount: d2s(outAmounts[i]) },
- amountKeys[i],
- );
- }
-
- //simple
- if (rv.type === 2) {
- var ai = [];
- var sumpouts = Z;
- //create pseudoOuts
- for (i = 0; i < inAmounts.length - 1; i++) {
- ai[i] = random_scalar();
- sumpouts = sc_add(sumpouts, ai[i]);
- rv.pseudoOuts[i] = commit(d2s(inAmounts[i]), ai[i]);
- }
- ai[i] = sc_sub(sumout, sumpouts);
- rv.pseudoOuts[i] = commit(d2s(inAmounts[i]), ai[i]);
- var full_message = this.get_pre_mlsag_hash(rv);
- for (i = 0; i < inAmounts.length; i++) {
- rv.p.MGs.push(
- this.proveRctMG(
- full_message,
- mixRing[i],
- inSk[i],
- kimg[i],
- ai[i],
- rv.pseudoOuts[i],
- indices[i],
- ),
- );
- }
- } else {
- var sumC = I;
- //get sum of output commitments to use in MLSAG
- for (i = 0; i < rv.outPk.length; i++) {
- sumC = ge_add(sumC, rv.outPk[i]);
- }
- sumC = ge_add(sumC, ge_scalarmult(H, d2s(rv.txnFee)));
- var full_message = this.get_pre_mlsag_hash(rv);
- rv.p.MGs.push(
- this.proveRctMG(
- full_message,
- mixRing[0],
- inSk[0],
- kimg[0],
- sumout,
- sumC,
- indices[0],
- ),
- );
- }
- return rv;
- };
-
- this.verRct = function(rv, semantics, mixRing, kimg) {
- if (rv.type === 0x03) {
- throw Error("Bulletproof validation not implemented");
- }
-
- // where RCTTypeFull is 0x01 and RCTTypeFullBulletproof is 0x03
- if (rv.type !== 0x01 && rv.type !== 0x03) {
- throw Error("verRct called on non-full rctSig");
- }
- if (semantics) {
- //RCTTypeFullBulletproof checks not implemented
- // RCTTypeFull checks
- if (rv.outPk.length !== rv.p.rangeSigs.length) {
- throw Error("Mismatched sizes of outPk and rv.p.rangeSigs");
- }
- if (rv.outPk.length !== rv.ecdhInfo.length) {
- throw Error("Mismatched sizes of outPk and rv.ecdhInfo");
- }
- if (rv.p.MGs.length !== 1) {
- throw Error("full rctSig has not one MG");
- }
- } else {
- // semantics check is early, we don't have the MGs resolved yet
- }
- try {
- if (semantics) {
- const results = [];
- for (let i = 0; i < rv.outPk.length; i++) {
- // might want to parallelize this like its done in the c++ codebase
- // via some abstraction library to support browser + node
- if (rv.p.rangeSigs.length === 0) {
- results[i] = this.verBulletproof(rv.p.bulletproofs[i]);
- } else {
- // mask -> C if public
- results[i] = this.verRange(
- rv.outPk[i],
- rv.p.rangeSigs[i],
- );
- }
- }
-
- for (let i = 0; i < rv.outPk.length; i++) {
- if (!results[i]) {
- console.error(
- "Range proof verification failed for output",
- i,
- );
- return false;
- }
- }
- } else {
- // compute txn fee
- const txnFeeKey = this.ge_scalarmult(H, this.d2s(rv.txnFee));
- const mgVerd = this.verRctMG(
- rv.p.MGs[0],
- mixRing,
- rv.outPk,
- txnFeeKey,
- this.get_pre_mlsag_hash(rv),
- kimg,
- );
- console.log("mg sig verified?", mgVerd);
- if (!mgVerd) {
- console.error("MG Signature verification failed");
- return false;
- }
- }
- return true;
- } catch (e) {
- console.error("Error in verRct: ", e);
- return false;
- }
- };
- //ver RingCT simple
- //assumes only post-rct style inputs (at least for max anonymity)
- this.verRctSimple = function(rv, semantics, mixRing, kimgs) {
- try {
- if (rv.type === 0x04) {
- throw Error("Simple Bulletproof validation not implemented");
- }
-
- if (rv.type !== 0x02 && rv.type !== 0x04) {
- throw Error("verRctSimple called on non simple rctSig");
- }
-
- if (semantics) {
- if (rv.type == 0x04) {
- throw Error(
- "Simple Bulletproof validation not implemented",
- );
- } else {
- if (rv.outPk.length !== rv.p.rangeSigs.length) {
- throw Error(
- "Mismatched sizes of outPk and rv.p.rangeSigs",
- );
- }
- if (rv.pseudoOuts.length !== rv.p.MGs.length) {
- throw Error(
- "Mismatched sizes of rv.pseudoOuts and rv.p.MGs",
- );
- }
- // originally the check is rv.p.pseudoOuts.length, but this'll throw
- // until p.pseudoOuts is added as a property to the rv object
- if (rv.p.pseudoOuts) {
- throw Error("rv.p.pseudoOuts must be empty");
- }
- }
- } else {
- if (rv.type === 0x04) {
- throw Error(
- "Simple Bulletproof validation not implemented",
- );
- } else {
- // semantics check is early, and mixRing/MGs aren't resolved yet
- if (rv.pseudoOuts.length !== mixRing.length) {
- throw Error(
- "Mismatched sizes of rv.pseudoOuts and mixRing",
- );
- }
- }
- }
-
- // if bulletproof, then use rv.p.pseudoOuts, otherwise use rv.pseudoOuts
- const pseudoOuts =
- rv.type === 0x04 ? rv.p.pseudoOuts : rv.pseudoOuts;
-
- if (semantics) {
- let sumOutpks = this.identity();
- for (let i = 0; i < rv.outPk.length; i++) {
- sumOutpks = this.ge_add(sumOutpks, rv.outPk[i]); // add all of the output commitments
- }
-
- const txnFeeKey = this.ge_scalarmult(
- this.H,
- this.d2s(rv.txnFee),
- );
- sumOutpks = this.ge_add(txnFeeKey, sumOutpks); // add txnfeekey
-
- let sumPseudoOuts = this.identity();
- for (let i = 0; i < pseudoOuts.length; i++) {
- sumPseudoOuts = this.ge_add(sumPseudoOuts, pseudoOuts[i]); // sum up all of the pseudoOuts
- }
-
- if (sumOutpks !== sumPseudoOuts) {
- console.error("Sum check failed");
- return false;
- }
-
- const results = [];
- for (let i = 0; i < rv.outPk.length; i++) {
- // might want to parallelize this like its done in the c++ codebase
- // via some abstraction library to support browser + node
- if (rv.p.rangeSigs.length === 0) {
- results[i] = this.verBulletproof(rv.p.bulletproofs[i]);
- } else {
- // mask -> C if public
- results[i] = this.verRange(
- rv.outPk[i],
- rv.p.rangeSigs[i],
- );
- }
- }
-
- for (let i = 0; i < results.length; i++) {
- if (!results[i]) {
- console.error(
- "Range proof verification failed for output",
- i,
- );
- return false;
- }
- }
- } else {
- const message = this.get_pre_mlsag_hash(rv);
- const results = [];
- for (let i = 0; i < mixRing.length; i++) {
- results[i] = this.verRctMGSimple(
- message,
- rv.p.MGs[i],
- mixRing[i],
- pseudoOuts[i],
- kimgs[i],
- );
- }
-
- for (let i = 0; i < results.length; i++) {
- if (!results[i]) {
- console.error(
- "Range proof verification failed for output",
- i,
- );
- return false;
- }
- }
- }
-
- return true;
- } catch (error) {
- console.log("[verRctSimple]", error);
- return false;
- }
- };
-
- //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1)
- // uses the attached ecdh info to find the amounts represented by each output commitment
- // must know the destination private key to find the correct amount, else will return a random number
-
- this.decodeRct = function(rv, sk, i) {
- // where RCTTypeFull is 0x01 and RCTTypeFullBulletproof is 0x03
- if (rv.type !== 0x01 && rv.type !== 0x03) {
- throw Error("verRct called on non-full rctSig");
- }
- if (i >= rv.ecdhInfo.length) {
- throw Error("Bad index");
- }
- if (rv.outPk.length !== rv.ecdhInfo.length) {
- throw Error("Mismatched sizes of rv.outPk and rv.ecdhInfo");
- }
-
- // mask amount and mask
- const ecdh_info = rv.ecdhInfo[i];
- const { mask, amount } = this.decode_rct_ecdh(ecdh_info, sk);
-
- const C = rv.outPk[i];
- const Ctmp = this.ge_double_scalarmult_base_vartime(
- amount,
- this.H,
- mask,
- );
-
- console.log("[decodeRct]", C, Ctmp);
- if (C !== Ctmp) {
- throw Error(
- "warning, amount decoded incorrectly, will be unable to spend",
- );
- }
- return { amount, mask };
- };
-
- this.decodeRctSimple = function(rv, sk, i) {
- if (rv.type !== 0x02 && rv.type !== 0x04) {
- throw Error("verRct called on full rctSig");
- }
- if (i >= rv.ecdhInfo.length) {
- throw Error("Bad index");
- }
- if (rv.outPk.length !== rv.ecdhInfo.length) {
- throw Error("Mismatched sizes of rv.outPk and rv.ecdhInfo");
- }
-
- // mask amount and mask
- const ecdh_info = rv.ecdhInfo[i];
- const { mask, amount } = this.decode_rct_ecdh(ecdh_info, sk);
-
- const C = rv.outPk[i];
- const Ctmp = this.ge_double_scalarmult_base_vartime(
- amount,
- this.H,
- mask,
- );
-
- console.log("[decodeRctSimple]", C, Ctmp);
- if (C !== Ctmp) {
- throw Error(
- "warning, amount decoded incorrectly, will be unable to spend",
- );
- }
- return { amount, mask };
- };
-
- this.verBulletProof = function() {
- throw Error("verBulletProof is not implemented");
- };
- //end RCT functions
-
- this.add_pub_key_to_extra = function(extra, pubkey) {
- if (pubkey.length !== 64) throw "Invalid pubkey length";
- // Append pubkey tag and pubkey
- extra += TX_EXTRA_TAGS.PUBKEY + pubkey;
- return extra;
- };
-
- this.add_nonce_to_extra = function(extra, nonce) {
- // Append extra nonce
- if (nonce.length % 2 !== 0) {
- throw "Invalid extra nonce";
- }
- if (nonce.length / 2 > TX_EXTRA_NONCE_MAX_COUNT) {
- throw "Extra nonce must be at most " +
- TX_EXTRA_NONCE_MAX_COUNT +
- " bytes";
- }
- // Add nonce tag
- extra += TX_EXTRA_TAGS.NONCE;
- // Encode length of nonce
- extra += ("0" + (nonce.length / 2).toString(16)).slice(-2);
- // Write nonce
- extra += nonce;
- return extra;
- };
-
- this.get_payment_id_nonce = function(payment_id, pid_encrypt) {
- if (payment_id.length !== 64 && payment_id.length !== 16) {
- throw "Invalid payment id";
- }
- var res = "";
- if (pid_encrypt) {
- res += TX_EXTRA_NONCE_TAGS.ENCRYPTED_PAYMENT_ID;
- } else {
- res += TX_EXTRA_NONCE_TAGS.PAYMENT_ID;
- }
- res += payment_id;
- return res;
- };
-
- this.abs_to_rel_offsets = function(offsets) {
- if (offsets.length === 0) return offsets;
- for (var i = offsets.length - 1; i >= 1; --i) {
- offsets[i] = new JSBigInt(offsets[i])
- .subtract(offsets[i - 1])
- .toString();
- }
- return offsets;
- };
-
- this.get_tx_prefix_hash = function(tx) {
- var prefix = this.serialize_tx(tx, true);
- return this.cn_fast_hash(prefix);
- };
-
- this.get_tx_hash = function(tx) {
- if (typeof tx === "string") {
- return this.cn_fast_hash(tx);
- } else {
- return this.cn_fast_hash(this.serialize_tx(tx));
- }
- };
-
- this.serialize_tx = function(tx, headeronly) {
- //tx: {
- // version: uint64,
- // unlock_time: uint64,
- // extra: hex,
- // vin: [{amount: uint64, k_image: hex, key_offsets: [uint64,..]},...],
- // vout: [{amount: uint64, target: {key: hex}},...],
- // signatures: [[s,s,...],...]
- //}
- if (headeronly === undefined) {
- headeronly = false;
- }
- var buf = "";
- buf += this.encode_varint(tx.version);
- buf += this.encode_varint(tx.unlock_time);
- buf += this.encode_varint(tx.vin.length);
- var i, j;
- for (i = 0; i < tx.vin.length; i++) {
- var vin = tx.vin[i];
- switch (vin.type) {
- case "input_to_key":
- buf += "02";
- buf += this.encode_varint(vin.amount);
- buf += this.encode_varint(vin.key_offsets.length);
- for (j = 0; j < vin.key_offsets.length; j++) {
- buf += this.encode_varint(vin.key_offsets[j]);
- }
- buf += vin.k_image;
- break;
- default:
- throw "Unhandled vin type: " + vin.type;
- }
- }
- buf += this.encode_varint(tx.vout.length);
- for (i = 0; i < tx.vout.length; i++) {
- var vout = tx.vout[i];
- buf += this.encode_varint(vout.amount);
- switch (vout.target.type) {
- case "txout_to_key":
- buf += "02";
- buf += vout.target.key;
- break;
- default:
- throw "Unhandled txout target type: " + vout.target.type;
- }
- }
- if (!this.valid_hex(tx.extra)) {
- throw "Tx extra has invalid hex";
- }
- buf += this.encode_varint(tx.extra.length / 2);
- buf += tx.extra;
- if (!headeronly) {
- if (tx.vin.length !== tx.signatures.length) {
- throw "Signatures length != vin length";
- }
- for (i = 0; i < tx.vin.length; i++) {
- for (j = 0; j < tx.signatures[i].length; j++) {
- buf += tx.signatures[i][j];
- }
- }
- }
- return buf;
- };
-
- this.serialize_rct_tx_with_hash = function(tx) {
- var hashes = "";
- var buf = "";
- buf += this.serialize_tx(tx, true);
- hashes += this.cn_fast_hash(buf);
- var buf2 = this.serialize_rct_base(tx.rct_signatures);
- hashes += this.cn_fast_hash(buf2);
- buf += buf2;
- var buf3 = serialize_range_proofs(tx.rct_signatures);
- //add MGs
- for (var i = 0; i < tx.rct_signatures.p.MGs.length; i++) {
- for (var j = 0; j < tx.rct_signatures.p.MGs[i].ss.length; j++) {
- buf3 += tx.rct_signatures.p.MGs[i].ss[j][0];
- buf3 += tx.rct_signatures.p.MGs[i].ss[j][1];
- }
- buf3 += tx.rct_signatures.p.MGs[i].cc;
- }
- hashes += this.cn_fast_hash(buf3);
- buf += buf3;
- var hash = this.cn_fast_hash(hashes);
- return {
- raw: buf,
- hash: hash,
- };
- };
-
- this.serialize_rct_base = function(rv) {
- var buf = "";
- buf += this.encode_varint(rv.type);
- buf += this.encode_varint(rv.txnFee);
- var i;
- if (rv.type === 2) {
- for (var i = 0; i < rv.pseudoOuts.length; i++) {
- buf += rv.pseudoOuts[i];
- }
- }
- if (rv.ecdhInfo.length !== rv.outPk.length) {
- throw "mismatched outPk/ecdhInfo!";
- }
- for (i = 0; i < rv.ecdhInfo.length; i++) {
- buf += rv.ecdhInfo[i].mask;
- buf += rv.ecdhInfo[i].amount;
- }
- for (i = 0; i < rv.outPk.length; i++) {
- buf += rv.outPk[i];
- }
- return buf;
- };
-
- this.generate_ring_signature = function(
- prefix_hash,
- k_image,
- keys,
- sec,
- real_index,
- ) {
- if (k_image.length !== STRUCT_SIZES.KEY_IMAGE * 2) {
- throw "invalid key image length";
- }
- if (sec.length !== KEY_SIZE * 2) {
- throw "Invalid secret key length";
- }
- if (
- prefix_hash.length !== HASH_SIZE * 2 ||
- !this.valid_hex(prefix_hash)
- ) {
- throw "Invalid prefix hash";
- }
- if (real_index >= keys.length || real_index < 0) {
- throw "real_index is invalid";
- }
- var _ge_tobytes = CNCrypto.cwrap("ge_tobytes", "void", [
- "number",
- "number",
- ]);
- var _ge_p3_tobytes = CNCrypto.cwrap("ge_p3_tobytes", "void", [
- "number",
- "number",
- ]);
- var _ge_scalarmult_base = CNCrypto.cwrap("ge_scalarmult_base", "void", [
- "number",
- "number",
- ]);
- var _ge_scalarmult = CNCrypto.cwrap("ge_scalarmult", "void", [
- "number",
- "number",
- "number",
- ]);
- var _sc_add = CNCrypto.cwrap("sc_add", "void", [
- "number",
- "number",
- "number",
- ]);
- var _sc_sub = CNCrypto.cwrap("sc_sub", "void", [
- "number",
- "number",
- "number",
- ]);
- var _sc_mulsub = CNCrypto.cwrap("sc_mulsub", "void", [
- "number",
- "number",
- "number",
- "number",
- ]);
- var _sc_0 = CNCrypto.cwrap("sc_0", "void", ["number"]);
- var _ge_double_scalarmult_base_vartime = CNCrypto.cwrap(
- "ge_double_scalarmult_base_vartime",
- "void",
- ["number", "number", "number", "number"],
- );
- var _ge_double_scalarmult_precomp_vartime = CNCrypto.cwrap(
- "ge_double_scalarmult_precomp_vartime",
- "void",
- ["number", "number", "number", "number", "number"],
- );
- var _ge_frombytes_vartime = CNCrypto.cwrap(
- "ge_frombytes_vartime",
- "number",
- ["number", "number"],
- );
- var _ge_dsm_precomp = CNCrypto.cwrap("ge_dsm_precomp", "void", [
- "number",
- "number",
- ]);
-
- var buf_size = STRUCT_SIZES.EC_POINT * 2 * keys.length;
- var buf_m = CNCrypto._malloc(buf_size);
- var sig_size = STRUCT_SIZES.SIGNATURE * keys.length;
- var sig_m = CNCrypto._malloc(sig_size);
-
- // Struct pointer helper functions
- function buf_a(i) {
- return buf_m + STRUCT_SIZES.EC_POINT * (2 * i);
- }
- function buf_b(i) {
- return buf_m + STRUCT_SIZES.EC_POINT * (2 * i + 1);
- }
- function sig_c(i) {
- return sig_m + STRUCT_SIZES.EC_SCALAR * (2 * i);
- }
- function sig_r(i) {
- return sig_m + STRUCT_SIZES.EC_SCALAR * (2 * i + 1);
- }
- var image_m = CNCrypto._malloc(STRUCT_SIZES.KEY_IMAGE);
- CNCrypto.HEAPU8.set(hextobin(k_image), image_m);
- var i;
- var image_unp_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
- var image_pre_m = CNCrypto._malloc(STRUCT_SIZES.GE_DSMP);
- var sum_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- var k_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- var h_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
- var tmp2_m = CNCrypto._malloc(STRUCT_SIZES.GE_P2);
- var tmp3_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
- var pub_m = CNCrypto._malloc(KEY_SIZE);
- var sec_m = CNCrypto._malloc(KEY_SIZE);
- CNCrypto.HEAPU8.set(hextobin(sec), sec_m);
- if (_ge_frombytes_vartime(image_unp_m, image_m) != 0) {
- throw "failed to call ge_frombytes_vartime";
- }
- _ge_dsm_precomp(image_pre_m, image_unp_m);
- _sc_0(sum_m);
- for (i = 0; i < keys.length; i++) {
- if (i === real_index) {
- // Real key
- var rand = this.random_scalar();
- CNCrypto.HEAPU8.set(hextobin(rand), k_m);
- _ge_scalarmult_base(tmp3_m, k_m);
- _ge_p3_tobytes(buf_a(i), tmp3_m);
- var ec = this.hash_to_ec(keys[i]);
- CNCrypto.HEAPU8.set(hextobin(ec), tmp3_m);
- _ge_scalarmult(tmp2_m, k_m, tmp3_m);
- _ge_tobytes(buf_b(i), tmp2_m);
- } else {
- CNCrypto.HEAPU8.set(hextobin(this.random_scalar()), sig_c(i));
- CNCrypto.HEAPU8.set(hextobin(this.random_scalar()), sig_r(i));
- CNCrypto.HEAPU8.set(hextobin(keys[i]), pub_m);
- if (
- CNCrypto.ccall(
- "ge_frombytes_vartime",
- "void",
- ["number", "number"],
- [tmp3_m, pub_m],
- ) !== 0
- ) {
- throw "Failed to call ge_frombytes_vartime";
- }
- _ge_double_scalarmult_base_vartime(
- tmp2_m,
- sig_c(i),
- tmp3_m,
- sig_r(i),
- );
- _ge_tobytes(buf_a(i), tmp2_m);
- var ec = this.hash_to_ec(keys[i]);
- CNCrypto.HEAPU8.set(hextobin(ec), tmp3_m);
- _ge_double_scalarmult_precomp_vartime(
- tmp2_m,
- sig_r(i),
- tmp3_m,
- sig_c(i),
- image_pre_m,
- );
- _ge_tobytes(buf_b(i), tmp2_m);
- _sc_add(sum_m, sum_m, sig_c(i));
- }
- }
- var buf_bin = CNCrypto.HEAPU8.subarray(buf_m, buf_m + buf_size);
- var scalar = this.hash_to_scalar(prefix_hash + bintohex(buf_bin));
- CNCrypto.HEAPU8.set(hextobin(scalar), h_m);
- _sc_sub(sig_c(real_index), h_m, sum_m);
- _sc_mulsub(sig_r(real_index), sig_c(real_index), sec_m, k_m);
- var sig_data = bintohex(
- CNCrypto.HEAPU8.subarray(sig_m, sig_m + sig_size),
- );
- var sigs = [];
- for (var k = 0; k < keys.length; k++) {
- sigs.push(
- sig_data.slice(
- STRUCT_SIZES.SIGNATURE * 2 * k,
- STRUCT_SIZES.SIGNATURE * 2 * (k + 1),
- ),
- );
- }
- CNCrypto._free(image_m);
- CNCrypto._free(image_unp_m);
- CNCrypto._free(image_pre_m);
- CNCrypto._free(sum_m);
- CNCrypto._free(k_m);
- CNCrypto._free(h_m);
- CNCrypto._free(tmp2_m);
- CNCrypto._free(tmp3_m);
- CNCrypto._free(buf_m);
- CNCrypto._free(sig_m);
- CNCrypto._free(pub_m);
- CNCrypto._free(sec_m);
- return sigs;
- };
-
- this.construct_tx = function(
- keys,
- sources,
- dsts,
- fee_amount,
- payment_id,
- pid_encrypt,
- realDestViewKey,
- unlock_time,
- rct,
- nettype,
- ) {
- //we move payment ID stuff here, because we need txkey to encrypt
- var txkey = this.random_keypair();
- console.log(txkey);
- var extra = "";
- if (payment_id) {
- if (pid_encrypt && payment_id.length !== INTEGRATED_ID_SIZE * 2) {
- throw "payment ID must be " +
- INTEGRATED_ID_SIZE +
- " bytes to be encrypted!";
- }
- console.log("Adding payment id: " + payment_id);
- if (pid_encrypt) {
- //get the derivation from our passed viewkey, then hash that + tail to get encryption key
- var pid_key = this.cn_fast_hash(
- this.generate_key_derivation(realDestViewKey, txkey.sec) +
- ENCRYPTED_PAYMENT_ID_TAIL.toString(16),
- ).slice(0, INTEGRATED_ID_SIZE * 2);
- console.log("Txkeys:", txkey, "Payment ID key:", pid_key);
- payment_id = this.hex_xor(payment_id, pid_key);
- }
- var nonce = this.get_payment_id_nonce(payment_id, pid_encrypt);
- console.log("Extra nonce: " + nonce);
- extra = this.add_nonce_to_extra(extra, nonce);
- }
- var tx = {
- unlock_time: unlock_time,
- version: rct ? CURRENT_TX_VERSION : OLD_TX_VERSION,
- extra: extra,
- vin: [],
- vout: [],
- };
- if (rct) {
- tx.rct_signatures = {};
- } else {
- tx.signatures = [];
- }
-
- var in_contexts = [];
- var inputs_money = JSBigInt.ZERO;
- var i, j;
- console.log("Sources: ");
- //run the for loop twice to sort ins by key image
- //first generate key image and other construction data to sort it all in one go
- for (i = 0; i < sources.length; i++) {
- console.log(i + ": " + this.formatMoneyFull(sources[i].amount));
- if (sources[i].real_out >= sources[i].outputs.length) {
- throw "real index >= outputs.length";
- }
- var res = this.generate_key_image_helper_rct(
- keys,
- sources[i].real_out_tx_key,
- sources[i].real_out_in_tx,
- sources[i].mask,
- ); //mask will be undefined for non-rct
- if (
- res.in_ephemeral.pub !==
- sources[i].outputs[sources[i].real_out].key
- ) {
- throw "in_ephemeral.pub != source.real_out.key";
- }
- sources[i].key_image = res.image;
- sources[i].in_ephemeral = res.in_ephemeral;
- }
- //sort ins
- sources.sort(function(a, b) {
- return (
- JSBigInt.parse(a.key_image, 16).compare(
- JSBigInt.parse(b.key_image, 16),
- ) * -1
- );
- });
- //copy the sorted sources data to tx
- for (i = 0; i < sources.length; i++) {
- inputs_money = inputs_money.add(sources[i].amount);
- in_contexts.push(sources[i].in_ephemeral);
- var input_to_key = {};
- input_to_key.type = "input_to_key";
- input_to_key.amount = sources[i].amount;
- input_to_key.k_image = sources[i].key_image;
- input_to_key.key_offsets = [];
- for (j = 0; j < sources[i].outputs.length; ++j) {
- input_to_key.key_offsets.push(sources[i].outputs[j].index);
- }
- input_to_key.key_offsets = this.abs_to_rel_offsets(
- input_to_key.key_offsets,
- );
- tx.vin.push(input_to_key);
- }
- var outputs_money = JSBigInt.ZERO;
- var out_index = 0;
- var amountKeys = []; //rct only
- for (i = 0; i < dsts.length; ++i) {
- if (new JSBigInt(dsts[i].amount).compare(0) < 0) {
- throw "dst.amount < 0"; //amount can be zero if no change
- }
- dsts[i].keys = this.decode_address(dsts[i].address, nettype);
-
- // R = rD for subaddresses
- if (this.is_subaddress(dsts[i].address, nettype)) {
- if (typeof payment_id !== "undefined" && payment_id) {
- // this could stand to be placed earlier in the function but we save repeating a little algo time this way
- throw "Payment ID must not be supplied when sending to a subaddress";
- }
- txkey.pub = this.ge_scalarmult(dsts[i].keys.spend, txkey.sec);
- }
-
- var out_derivation;
-
- // send change to ourselves
- if (dsts[i].keys.view == keys.view.pub) {
- out_derivation = this.generate_key_derivation(
- txkey.pub,
- keys.view.sec,
- );
- } else {
- out_derivation = this.generate_key_derivation(
- dsts[i].keys.view,
- txkey.sec,
- );
- }
-
- if (rct) {
- amountKeys.push(
- this.derivation_to_scalar(out_derivation, out_index),
- );
- }
- var out_ephemeral_pub = this.derive_public_key(
- out_derivation,
- out_index,
- dsts[i].keys.spend,
- );
- var out = {
- amount: dsts[i].amount.toString(),
- };
- // txout_to_key
- out.target = {
- type: "txout_to_key",
- key: out_ephemeral_pub,
- };
- tx.vout.push(out);
- ++out_index;
- outputs_money = outputs_money.add(dsts[i].amount);
- }
-
- // add pub key to extra after we know whether to use R = rG or R = rD
- tx.extra = this.add_pub_key_to_extra(tx.extra, txkey.pub);
-
- if (outputs_money.add(fee_amount).compare(inputs_money) > 0) {
- throw "outputs money (" +
- this.formatMoneyFull(outputs_money) +
- ") + fee (" +
- this.formatMoneyFull(fee_amount) +
- ") > inputs money (" +
- this.formatMoneyFull(inputs_money) +
- ")";
- }
- if (!rct) {
- for (i = 0; i < sources.length; ++i) {
- var src_keys = [];
- for (j = 0; j < sources[i].outputs.length; ++j) {
- src_keys.push(sources[i].outputs[j].key);
- }
- var sigs = this.generate_ring_signature(
- this.get_tx_prefix_hash(tx),
- tx.vin[i].k_image,
- src_keys,
- in_contexts[i].sec,
- sources[i].real_out,
- );
- tx.signatures.push(sigs);
- }
- } else {
- //rct
- var txnFee = fee_amount;
- var keyimages = [];
- var inSk = [];
- var inAmounts = [];
- var mixRing = [];
- var indices = [];
- for (i = 0; i < tx.vin.length; i++) {
- keyimages.push(tx.vin[i].k_image);
- inSk.push({
- x: in_contexts[i].sec,
- a: in_contexts[i].mask,
- });
- inAmounts.push(tx.vin[i].amount);
- if (in_contexts[i].mask !== I) {
- //if input is rct (has a valid mask), 0 out amount
- tx.vin[i].amount = "0";
- }
- mixRing[i] = [];
- for (j = 0; j < sources[i].outputs.length; j++) {
- mixRing[i].push({
- dest: sources[i].outputs[j].key,
- mask: sources[i].outputs[j].commit,
- });
- }
- indices.push(sources[i].real_out);
- }
- var outAmounts = [];
- for (i = 0; i < tx.vout.length; i++) {
- outAmounts.push(tx.vout[i].amount);
- tx.vout[i].amount = "0"; //zero out all rct outputs
- }
- var tx_prefix_hash = this.get_tx_prefix_hash(tx);
- tx.rct_signatures = genRct(
- tx_prefix_hash,
- inSk,
- keyimages,
- /*destinations, */ inAmounts,
- outAmounts,
- mixRing,
- amountKeys,
- indices,
- txnFee,
- );
- }
- console.log(tx);
- return tx;
- };
-
- this.create_transaction = function(
- pub_keys,
- sec_keys,
- dsts,
- outputs,
- mix_outs,
- fake_outputs_count,
- fee_amount,
- payment_id,
- pid_encrypt,
- realDestViewKey,
- unlock_time,
- rct,
- nettype,
- ) {
- unlock_time = unlock_time || 0;
- mix_outs = mix_outs || [];
- var i, j;
- if (dsts.length === 0) {
- throw "Destinations empty";
- }
- if (mix_outs.length !== outputs.length && fake_outputs_count !== 0) {
- throw "Wrong number of mix outs provided (" +
- outputs.length +
- " outputs, " +
- mix_outs.length +
- " mix outs)";
- }
- for (i = 0; i < mix_outs.length; i++) {
- if ((mix_outs[i].outputs || []).length < fake_outputs_count) {
- throw "Not enough outputs to mix with";
- }
- }
- var keys = {
- view: {
- pub: pub_keys.view,
- sec: sec_keys.view,
- },
- spend: {
- pub: pub_keys.spend,
- sec: sec_keys.spend,
- },
- };
- if (
- !this.valid_keys(
- keys.view.pub,
- keys.view.sec,
- keys.spend.pub,
- keys.spend.sec,
- )
- ) {
- throw "Invalid secret keys!";
- }
- var needed_money = JSBigInt.ZERO;
- for (i = 0; i < dsts.length; ++i) {
- needed_money = needed_money.add(dsts[i].amount);
- if (needed_money.compare(UINT64_MAX) !== -1) {
- throw "Output overflow!";
- }
- }
- var found_money = JSBigInt.ZERO;
- var sources = [];
- console.log("Selected transfers: ", outputs);
- for (i = 0; i < outputs.length; ++i) {
- found_money = found_money.add(outputs[i].amount);
- if (found_money.compare(UINT64_MAX) !== -1) {
- throw "Input overflow!";
- }
- var src = {
- outputs: [],
- };
- src.amount = new JSBigInt(outputs[i].amount).toString();
- if (mix_outs.length !== 0) {
- // Sort fake outputs by global index
- mix_outs[i].outputs.sort(function(a, b) {
- return new JSBigInt(a.global_index).compare(b.global_index);
- });
- j = 0;
- while (
- src.outputs.length < fake_outputs_count &&
- j < mix_outs[i].outputs.length
- ) {
- var out = mix_outs[i].outputs[j];
- if (out.global_index === outputs[i].global_index) {
- console.log("got mixin the same as output, skipping");
- j++;
- continue;
- }
- var oe = {};
- oe.index = out.global_index.toString();
- oe.key = out.public_key;
- if (rct) {
- if (out.rct) {
- oe.commit = out.rct.slice(0, 64); //add commitment from rct mix outs
- } else {
- if (outputs[i].rct) {
- throw "mix rct outs missing commit";
- }
- oe.commit = zeroCommit(d2s(src.amount)); //create identity-masked commitment for non-rct mix input
- }
- }
- src.outputs.push(oe);
- j++;
- }
- }
- var real_oe = {};
- real_oe.index = new JSBigInt(
- outputs[i].global_index || 0,
- ).toString();
- real_oe.key = outputs[i].public_key;
- if (rct) {
- if (outputs[i].rct) {
- real_oe.commit = outputs[i].rct.slice(0, 64); //add commitment for real input
- } else {
- real_oe.commit = zeroCommit(d2s(src.amount)); //create identity-masked commitment for non-rct input
- }
- }
- var real_index = src.outputs.length;
- for (j = 0; j < src.outputs.length; j++) {
- if (
- new JSBigInt(real_oe.index).compare(src.outputs[j].index) <
- 0
- ) {
- real_index = j;
- break;
- }
- }
- // Add real_oe to outputs
- src.outputs.splice(real_index, 0, real_oe);
- src.real_out_tx_key = outputs[i].tx_pub_key;
- // Real output entry index
- src.real_out = real_index;
- src.real_out_in_tx = outputs[i].index;
- if (rct) {
- if (outputs[i].rct) {
- src.mask = outputs[i].rct.slice(64, 128); //encrypted
- } else {
- src.mask = null; //will be set by generate_key_image_helper_rct
- }
- }
- sources.push(src);
- }
- console.log("sources: ", sources);
- var change = {
- amount: JSBigInt.ZERO,
- };
- var cmp = needed_money.compare(found_money);
- if (cmp < 0) {
- change.amount = found_money.subtract(needed_money);
- if (change.amount.compare(fee_amount) !== 0) {
- throw "early fee calculation != later";
- }
- } else if (cmp > 0) {
- throw "Need more money than found! (have: " +
- cnUtil.formatMoney(found_money) +
- " need: " +
- cnUtil.formatMoney(needed_money) +
- ")";
- }
- return this.construct_tx(
- keys,
- sources,
- dsts,
- fee_amount,
- payment_id,
- pid_encrypt,
- realDestViewKey,
- unlock_time,
- rct,
- nettype,
- );
- };
-
- this.estimateRctSize = function(inputs, mixin, outputs) {
- var size = 0;
- // tx prefix
- // first few bytes
- size += 1 + 6;
- size += inputs * (1 + 6 + (mixin + 1) * 3 + 32); // original C implementation is *2+32 but author advised to change 2 to 3 as key offsets are variable size and this constitutes a best guess
- // vout
- size += outputs * (6 + 32);
- // extra
- size += 40;
- // rct signatures
- // type
- size += 1;
- // rangeSigs
- size += (2 * 64 * 32 + 32 + 64 * 32) * outputs;
- // MGs
- size += inputs * (32 * (mixin + 1) + 32);
- // mixRing - not serialized, can be reconstructed
- /* size += 2 * 32 * (mixin+1) * inputs; */
- // pseudoOuts
- size += 32 * inputs;
- // ecdhInfo
- size += 2 * 32 * outputs;
- // outPk - only commitment is saved
- size += 32 * outputs;
- // txnFee
- size += 4;
- // const logStr = `estimated rct tx size for ${inputs} at mixin ${mixin} and ${outputs} : ${size} (${((32 * inputs/*+1*/) + 2 * 32 * (mixin+1) * inputs + 32 * outputs)}) saved)`
- // console.log(logStr)
-
- return size;
- };
-
- function trimRight(str, char) {
- while (str[str.length - 1] == char) str = str.slice(0, -1);
- return str;
- }
-
- function padLeft(str, len, char) {
- while (str.length < len) {
- str = char + str;
- }
- return str;
- }
-
- this.padLeft = padLeft;
-
- this.printDsts = function(dsts) {
- for (var i = 0; i < dsts.length; i++) {
- console.log(
- dsts[i].address + ": " + this.formatMoneyFull(dsts[i].amount),
- );
- }
- };
-
- this.formatMoneyFull = function(units) {
- units = units.toString();
- var symbol = units[0] === "-" ? "-" : "";
- if (symbol === "-") {
- units = units.slice(1);
- }
- var decimal;
- if (units.length >= config.coinUnitPlaces) {
- decimal = units.substr(
- units.length - config.coinUnitPlaces,
- config.coinUnitPlaces,
- );
- } else {
- decimal = padLeft(units, config.coinUnitPlaces, "0");
- }
- return (
- symbol +
- (units.substr(0, units.length - config.coinUnitPlaces) || "0") +
- "." +
- decimal
- );
- };
-
- this.formatMoneyFullSymbol = function(units) {
- return this.formatMoneyFull(units) + " " + config.coinSymbol;
- };
-
- this.formatMoney = function(units) {
- var f = trimRight(this.formatMoneyFull(units), "0");
- if (f[f.length - 1] === ".") {
- return f.slice(0, f.length - 1);
- }
- return f;
- };
-
- this.formatMoneySymbol = function(units) {
- return this.formatMoney(units) + " " + config.coinSymbol;
- };
-
- /**
- *
- * @param {string} str
- */
- this.parseMoney = function(str) {
- if (!str) return JSBigInt.ZERO;
- var negative = str[0] === "-";
- if (negative) {
- str = str.slice(1);
- }
- var decimalIndex = str.indexOf(".");
- if (decimalIndex == -1) {
- if (negative) {
- return JSBigInt.multiply(str, config.coinUnits).negate();
- }
- return JSBigInt.multiply(str, config.coinUnits);
- }
- if (decimalIndex + config.coinUnitPlaces + 1 < str.length) {
- str = str.substr(0, decimalIndex + config.coinUnitPlaces + 1);
- }
- if (negative) {
- return new JSBigInt(str.substr(0, decimalIndex))
- .exp10(config.coinUnitPlaces)
- .add(
- new JSBigInt(str.substr(decimalIndex + 1)).exp10(
- decimalIndex + config.coinUnitPlaces - str.length + 1,
- ),
- ).negate;
- }
- return new JSBigInt(str.substr(0, decimalIndex))
- .exp10(config.coinUnitPlaces)
- .add(
- new JSBigInt(str.substr(decimalIndex + 1)).exp10(
- decimalIndex + config.coinUnitPlaces - str.length + 1,
- ),
- );
- };
-
- this.decompose_amount_into_digits = function(amount) {
- amount = amount.toString();
- var ret = [];
- while (amount.length > 0) {
- //check so we don't create 0s
- if (amount[0] !== "0") {
- var digit = amount[0];
- while (digit.length < amount.length) {
- digit += "0";
- }
- ret.push(new JSBigInt(digit));
- }
- amount = amount.slice(1);
- }
- return ret;
- };
-
- this.decompose_tx_destinations = function(dsts, rct) {
- var out = [];
- if (rct) {
- for (var i = 0; i < dsts.length; i++) {
- out.push({
- address: dsts[i].address,
- amount: dsts[i].amount,
- });
- }
- } else {
- for (var i = 0; i < dsts.length; i++) {
- var digits = this.decompose_amount_into_digits(dsts[i].amount);
- for (var j = 0; j < digits.length; j++) {
- if (digits[j].compare(0) > 0) {
- out.push({
- address: dsts[i].address,
- amount: digits[j],
- });
- }
- }
- }
- }
- return out.sort(function(a, b) {
- return a["amount"] - b["amount"];
- });
- };
-
- this.is_tx_unlocked = function(unlock_time, blockchain_height) {
- if (!config.maxBlockNumber) {
- throw "Max block number is not set in config!";
- }
- if (unlock_time < config.maxBlockNumber) {
- // unlock time is block height
- return blockchain_height >= unlock_time;
- } else {
- // unlock time is timestamp
- var current_time = Math.round(new Date().getTime() / 1000);
- return current_time >= unlock_time;
- }
- };
-
- this.tx_locked_reason = function(unlock_time, blockchain_height) {
- if (unlock_time < config.maxBlockNumber) {
- // unlock time is block height
- var numBlocks = unlock_time - blockchain_height;
- if (numBlocks <= 0) {
- return "Transaction is unlocked";
- }
- var unlock_prediction = moment().add(
- numBlocks * config.avgBlockTime,
- "seconds",
- );
- return (
- "Will be unlocked in " +
- numBlocks +
- " blocks, ~" +
- unlock_prediction.fromNow(true) +
- ", " +
- unlock_prediction.calendar() +
- ""
- );
- } else {
- // unlock time is timestamp
- var current_time = Math.round(new Date().getTime() / 1000);
- var time_difference = unlock_time - current_time;
- if (time_difference <= 0) {
- return "Transaction is unlocked";
- }
- var unlock_moment = moment(unlock_time * 1000);
- return (
- "Will be unlocked " +
- unlock_moment.fromNow() +
- ", " +
- unlock_moment.calendar()
- );
- }
- };
-
- return this;
-};
-exports.cnUtil = cnUtil;
diff --git a/cryptonote_utils/internal_libs/bs58/index.js b/cryptonote_utils/internal_libs/bs58/index.js
deleted file mode 100644
index d898210..0000000
--- a/cryptonote_utils/internal_libs/bs58/index.js
+++ /dev/null
@@ -1,6 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-const cryptonote_base58_1 = require("./cryptonote_base58");
-const { decode, encode } = cryptonote_base58_1.cnBase58;
-exports.decode = decode;
-exports.encode = encode;
diff --git a/cryptonote_utils/internal_libs/cn_crypto/index.js b/cryptonote_utils/internal_libs/cn_crypto/index.js
deleted file mode 100644
index 4e247df..0000000
--- a/cryptonote_utils/internal_libs/cn_crypto/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-"use strict";
-const CNCrypto = require("./cryptonote_crypto_EMSCRIPTEN");
-module.exports = CNCrypto;
diff --git a/cryptonote_utils/internal_libs/fast_cn/index.js b/cryptonote_utils/internal_libs/fast_cn/index.js
deleted file mode 100644
index 5bb1179..0000000
--- a/cryptonote_utils/internal_libs/fast_cn/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-const nacl_fast_cn_1 = require("./nacl-fast-cn");
-const { ge_add, ge_double_scalarmult_base_vartime, ge_double_scalarmult_postcomp_vartime, ge_scalarmult, ge_scalarmult_base, } = nacl_fast_cn_1.ll;
-exports.ge_add = ge_add;
-exports.ge_double_scalarmult_base_vartime = ge_double_scalarmult_base_vartime;
-exports.ge_double_scalarmult_postcomp_vartime = ge_double_scalarmult_postcomp_vartime;
-exports.ge_scalarmult = ge_scalarmult;
-exports.ge_scalarmult_base = ge_scalarmult_base;
diff --git a/cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.js b/cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.js
deleted file mode 100644
index 536b667..0000000
--- a/cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.js
+++ /dev/null
@@ -1,571 +0,0 @@
-(function(nacl) {
-'use strict';
-
-// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri.
-// Public domain.
-//
-// Implementation derived from TweetNaCl version 20140427.
-// See for details: http://tweetnacl.cr.yp.to/
-
-// modified 2017 for some CN functions by luigi1111
-
-var gf = function(init) {
- var i, r = new Float64Array(16);
- if (init) for (i = 0; i < init.length; i++) r[i] = init[i];
- return r;
-};
-
-// Pluggable, initialized in high-level API below.
-var randombytes = function(/* x, n */) { throw new Error('no PRNG'); };
-
-var _0 = new Uint8Array(16);
-var _9 = new Uint8Array(32); _9[0] = 9;
-
-var gf0 = gf(),
- gf1 = gf([1]),
- _121665 = gf([0xdb41, 1]),
- D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]),
- D2 = gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]),
- X = gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]),
- Y = gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]),
- I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);
-
-function vn(x, xi, y, yi, n) {
- var i,d = 0;
- for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i];
- return (1 & ((d - 1) >>> 8)) - 1;
-}
-
-function crypto_verify_16(x, xi, y, yi) {
- return vn(x,xi,y,yi,16);
-}
-
-function crypto_verify_32(x, xi, y, yi) {
- return vn(x,xi,y,yi,32);
-}
-
-function set25519(r, a) {
- var i;
- for (i = 0; i < 16; i++) r[i] = a[i]|0;
-}
-
-function car25519(o) {
- var i, v, c = 1;
- for (i = 0; i < 16; i++) {
- v = o[i] + c + 65535;
- c = Math.floor(v / 65536);
- o[i] = v - c * 65536;
- }
- o[0] += c-1 + 37 * (c-1);
-}
-
-function sel25519(p, q, b) {
- var t, c = ~(b-1);
- for (var i = 0; i < 16; i++) {
- t = c & (p[i] ^ q[i]);
- p[i] ^= t;
- q[i] ^= t;
- }
-}
-
-function pack25519(o, n) {
- var i, j, b;
- var m = gf(), t = gf();
- for (i = 0; i < 16; i++) t[i] = n[i];
- car25519(t);
- car25519(t);
- car25519(t);
- for (j = 0; j < 2; j++) {
- m[0] = t[0] - 0xffed;
- for (i = 1; i < 15; i++) {
- m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1);
- m[i-1] &= 0xffff;
- }
- m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1);
- b = (m[15]>>16) & 1;
- m[14] &= 0xffff;
- sel25519(t, m, 1-b);
- }
- for (i = 0; i < 16; i++) {
- o[2*i] = t[i] & 0xff;
- o[2*i+1] = t[i]>>8;
- }
-}
-
-function neq25519(a, b) {
- var c = new Uint8Array(32), d = new Uint8Array(32);
- pack25519(c, a);
- pack25519(d, b);
- return crypto_verify_32(c, 0, d, 0);
-}
-
-function par25519(a) {
- var d = new Uint8Array(32);
- pack25519(d, a);
- return d[0] & 1;
-}
-
-function unpack25519(o, n) {
- var i;
- for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8);
- o[15] &= 0x7fff;
-}
-
-function A(o, a, b) {
- for (var i = 0; i < 16; i++) o[i] = a[i] + b[i];
-}
-
-function Z(o, a, b) {
- for (var i = 0; i < 16; i++) o[i] = a[i] - b[i];
-}
-
-function M(o, a, b) {
- var v, c,
- t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,
- t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,
- t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,
- t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0,
- b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5],
- b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10],
- b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];
-
- v = a[0];
- t0 += v * b0; t1 += v * b1; t2 += v * b2; t3 += v * b3;
- t4 += v * b4; t5 += v * b5; t6 += v * b6; t7 += v * b7;
- t8 += v * b8; t9 += v * b9; t10 += v * b10; t11 += v * b11;
- t12 += v * b12; t13 += v * b13; t14 += v * b14; t15 += v * b15;
- v = a[1];
- t1 += v * b0; t2 += v * b1; t3 += v * b2; t4 += v * b3;
- t5 += v * b4; t6 += v * b5; t7 += v * b6; t8 += v * b7;
- t9 += v * b8; t10 += v * b9; t11 += v * b10; t12 += v * b11;
- t13 += v * b12; t14 += v * b13; t15 += v * b14; t16 += v * b15;
- v = a[2];
- t2 += v * b0; t3 += v * b1; t4 += v * b2; t5 += v * b3;
- t6 += v * b4; t7 += v * b5; t8 += v * b6; t9 += v * b7;
- t10 += v * b8; t11 += v * b9; t12 += v * b10; t13 += v * b11;
- t14 += v * b12; t15 += v * b13; t16 += v * b14; t17 += v * b15;
- v = a[3];
- t3 += v * b0; t4 += v * b1; t5 += v * b2; t6 += v * b3;
- t7 += v * b4; t8 += v * b5; t9 += v * b6; t10 += v * b7;
- t11 += v * b8; t12 += v * b9; t13 += v * b10; t14 += v * b11;
- t15 += v * b12; t16 += v * b13; t17 += v * b14; t18 += v * b15;
- v = a[4];
- t4 += v * b0; t5 += v * b1; t6 += v * b2; t7 += v * b3;
- t8 += v * b4; t9 += v * b5; t10 += v * b6; t11 += v * b7;
- t12 += v * b8; t13 += v * b9; t14 += v * b10; t15 += v * b11;
- t16 += v * b12; t17 += v * b13; t18 += v * b14; t19 += v * b15;
- v = a[5];
- t5 += v * b0; t6 += v * b1; t7 += v * b2; t8 += v * b3;
- t9 += v * b4; t10 += v * b5; t11 += v * b6; t12 += v * b7;
- t13 += v * b8; t14 += v * b9; t15 += v * b10; t16 += v * b11;
- t17 += v * b12; t18 += v * b13; t19 += v * b14; t20 += v * b15;
- v = a[6];
- t6 += v * b0; t7 += v * b1; t8 += v * b2; t9 += v * b3;
- t10 += v * b4; t11 += v * b5; t12 += v * b6; t13 += v * b7;
- t14 += v * b8; t15 += v * b9; t16 += v * b10; t17 += v * b11;
- t18 += v * b12; t19 += v * b13; t20 += v * b14; t21 += v * b15;
- v = a[7];
- t7 += v * b0; t8 += v * b1; t9 += v * b2; t10 += v * b3;
- t11 += v * b4; t12 += v * b5; t13 += v * b6; t14 += v * b7;
- t15 += v * b8; t16 += v * b9; t17 += v * b10; t18 += v * b11;
- t19 += v * b12; t20 += v * b13; t21 += v * b14; t22 += v * b15;
- v = a[8];
- t8 += v * b0; t9 += v * b1; t10 += v * b2; t11 += v * b3;
- t12 += v * b4; t13 += v * b5; t14 += v * b6; t15 += v * b7;
- t16 += v * b8; t17 += v * b9; t18 += v * b10; t19 += v * b11;
- t20 += v * b12; t21 += v * b13; t22 += v * b14; t23 += v * b15;
- v = a[9];
- t9 += v * b0; t10 += v * b1; t11 += v * b2; t12 += v * b3;
- t13 += v * b4; t14 += v * b5; t15 += v * b6; t16 += v * b7;
- t17 += v * b8; t18 += v * b9; t19 += v * b10; t20 += v * b11;
- t21 += v * b12; t22 += v * b13; t23 += v * b14; t24 += v * b15;
- v = a[10];
- t10 += v * b0; t11 += v * b1; t12 += v * b2; t13 += v * b3;
- t14 += v * b4; t15 += v * b5; t16 += v * b6; t17 += v * b7;
- t18 += v * b8; t19 += v * b9; t20 += v * b10; t21 += v * b11;
- t22 += v * b12; t23 += v * b13; t24 += v * b14; t25 += v * b15;
- v = a[11];
- t11 += v * b0; t12 += v * b1; t13 += v * b2; t14 += v * b3;
- t15 += v * b4; t16 += v * b5; t17 += v * b6; t18 += v * b7;
- t19 += v * b8; t20 += v * b9; t21 += v * b10; t22 += v * b11;
- t23 += v * b12; t24 += v * b13; t25 += v * b14; t26 += v * b15;
- v = a[12];
- t12 += v * b0; t13 += v * b1; t14 += v * b2; t15 += v * b3;
- t16 += v * b4; t17 += v * b5; t18 += v * b6; t19 += v * b7;
- t20 += v * b8; t21 += v * b9; t22 += v * b10; t23 += v * b11;
- t24 += v * b12; t25 += v * b13; t26 += v * b14; t27 += v * b15;
- v = a[13];
- t13 += v * b0; t14 += v * b1; t15 += v * b2; t16 += v * b3;
- t17 += v * b4; t18 += v * b5; t19 += v * b6; t20 += v * b7;
- t21 += v * b8; t22 += v * b9; t23 += v * b10; t24 += v * b11;
- t25 += v * b12; t26 += v * b13; t27 += v * b14; t28 += v * b15;
- v = a[14];
- t14 += v * b0; t15 += v * b1; t16 += v * b2; t17 += v * b3;
- t18 += v * b4; t19 += v * b5; t20 += v * b6; t21 += v * b7;
- t22 += v * b8; t23 += v * b9; t24 += v * b10; t25 += v * b11;
- t26 += v * b12; t27 += v * b13; t28 += v * b14; t29 += v * b15;
- v = a[15];
- t15 += v * b0; t16 += v * b1; t17 += v * b2; t18 += v * b3;
- t19 += v * b4; t20 += v * b5; t21 += v * b6; t22 += v * b7;
- t23 += v * b8; t24 += v * b9; t25 += v * b10; t26 += v * b11;
- t27 += v * b12; t28 += v * b13; t29 += v * b14; t30 += v * b15;
-
- t0 += 38 * t16; t1 += 38 * t17; t2 += 38 * t18; t3 += 38 * t19;
- t4 += 38 * t20; t5 += 38 * t21; t6 += 38 * t22; t7 += 38 * t23;
- t8 += 38 * t24; t9 += 38 * t25; t10 += 38 * t26; t11 += 38 * t27;
- t12 += 38 * t28; t13 += 38 * t29; t14 += 38 * t30; // t15 left as is
-
- // first car
- c = 1;
- v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
- v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
- v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
- v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
- v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
- v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
- v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
- v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
- v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
- v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
- v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
- v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
- v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
- v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
- v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
- v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
- t0 += c-1 + 37 * (c-1);
-
- // second car
- c = 1;
- v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
- v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
- v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
- v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
- v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
- v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
- v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
- v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
- v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
- v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
- v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
- v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
- v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
- v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
- v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
- v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
- t0 += c-1 + 37 * (c-1);
-
- o[ 0] = t0;
- o[ 1] = t1;
- o[ 2] = t2;
- o[ 3] = t3;
- o[ 4] = t4;
- o[ 5] = t5;
- o[ 6] = t6;
- o[ 7] = t7;
- o[ 8] = t8;
- o[ 9] = t9;
- o[10] = t10;
- o[11] = t11;
- o[12] = t12;
- o[13] = t13;
- o[14] = t14;
- o[15] = t15;
-}
-
-function S(o, a) {
- M(o, a, a);
-}
-
-function inv25519(o, i) {
- var c = gf();
- var a;
- for (a = 0; a < 16; a++) c[a] = i[a];
- for (a = 253; a >= 0; a--) {
- S(c, c);
- if(a !== 2 && a !== 4) M(c, c, i);
- }
- for (a = 0; a < 16; a++) o[a] = c[a];
-}
-
-function pow2523(o, i) {
- var c = gf();
- var a;
- for (a = 0; a < 16; a++) c[a] = i[a];
- for (a = 250; a >= 0; a--) {
- S(c, c);
- if(a !== 1) M(c, c, i);
- }
- for (a = 0; a < 16; a++) o[a] = c[a];
-}
-
-function add(p, q) {
- var a = gf(), b = gf(), c = gf(),
- d = gf(), e = gf(), f = gf(),
- g = gf(), h = gf(), t = gf();
-
- Z(a, p[1], p[0]);
- Z(t, q[1], q[0]);
- M(a, a, t);
- A(b, p[0], p[1]);
- A(t, q[0], q[1]);
- M(b, b, t);
- M(c, p[3], q[3]);
- M(c, c, D2);
- M(d, p[2], q[2]);
- A(d, d, d);
- Z(e, b, a);
- Z(f, d, c);
- A(g, d, c);
- A(h, b, a);
-
- M(p[0], e, f);
- M(p[1], h, g);
- M(p[2], g, f);
- M(p[3], e, h);
-}
-
-function cswap(p, q, b) {
- var i;
- for (i = 0; i < 4; i++) {
- sel25519(p[i], q[i], b);
- }
-}
-
-function pack(r, p) {
- var tx = gf(), ty = gf(), zi = gf();
- inv25519(zi, p[2]);
- M(tx, p[0], zi);
- M(ty, p[1], zi);
- pack25519(r, ty);
- r[31] ^= par25519(tx) << 7;
-}
-
-function scalarmult(p, q, s) {
- var b, i;
- set25519(p[0], gf0);
- set25519(p[1], gf1);
- set25519(p[2], gf1);
- set25519(p[3], gf0);
- for (i = 255; i >= 0; --i) {
- b = (s[(i/8)|0] >> (i&7)) & 1;
- cswap(p, q, b);
- add(q, p);
- add(p, p);
- cswap(p, q, b);
- }
-}
-
-function scalarbase(p, s) {
- var q = [gf(), gf(), gf(), gf()];
- set25519(q[0], X);
- set25519(q[1], Y);
- set25519(q[2], gf1);
- M(q[3], X, Y);
- scalarmult(p, q, s);
-}
-
-//new functions for CN - scalar operations are handled externally
-// this only handles curve operations, except for Hp()
-
-//why do we negate points when unpacking them???
-function ge_neg(pub) {
- pub[31] ^= 0x80;
-}
-
-//res = s*G
-function ge_scalarmult_base(s) {
- var p = [gf(), gf(), gf(), gf()];
- scalarbase(p, s);
- var pk = new Uint8Array(32);
- pack(pk, p);
- return pk;
-}
-
-//res = s*P
-function ge_scalarmult(P, s) {
- var p = [gf(), gf(), gf(), gf()],
- upk = [gf(), gf(), gf(), gf()],
- res = new Uint8Array(32);
- ge_neg(P);
- if (unpackneg(upk, P) !== 0) throw "non-0 error on point decode";
- scalarmult(p, upk, s);
- pack(res, p);
- return res;
-}
-
-//res = c*P + r*G
-function ge_double_scalarmult_base_vartime(c, P, r) {
- var uP = [gf(), gf(), gf(), gf()],
- cP = [gf(), gf(), gf(), gf()],
- rG = [gf(), gf(), gf(), gf()],
- res = new Uint8Array(32);
- ge_neg(P);
- if (unpackneg(uP, P) !== 0) throw "non-0 error on point decode";
- scalarmult(cP, uP, c);
- scalarbase(rG, r);
- add(rG, cP);
- pack(res, rG);
- return res;
-}
-
-//name changed to reflect not using precomp; res = r*Pb + c*I
-function ge_double_scalarmult_postcomp_vartime(r, Pb, c, I) {
- var uPb = [gf(), gf(), gf(), gf()],
- uI = [gf(), gf(), gf(), gf()],
- cI = [gf(), gf(), gf(), gf()],
- rPb = [gf(), gf(), gf(), gf()],
- res = new Uint8Array(32);
- ge_neg(Pb);
- if (unpackneg(uPb, Pb) !== 0) throw "non-0 error on point decode";
- scalarmult(rPb, uPb, r);
- ge_neg(I);
- if (unpackneg(uI, I) !== 0) throw "non-0 error on point decode";
- scalarmult(cI, uI, c);
- add(rPb, cI);
- pack(res, rPb);
- return res;
-}
-
-//res = P + Q
-function ge_add(P, Q) {
- var uP = [gf(), gf(), gf(), gf()],
- uQ = [gf(), gf(), gf(), gf()],
- res = new Uint8Array(32);
- ge_neg(P);
- ge_neg(Q);
- if (unpackneg(uP, P) !== 0) throw "non-0 error on point decode";
- if (unpackneg(uQ, Q) !== 0) throw "non-0 error on point decode";
- add(uP, uQ);
- pack(res, uP);
- return res;
-}
-
-var L = new Float64Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]);
-
-function modL(r, x) {
- var carry, i, j, k;
- for (i = 63; i >= 32; --i) {
- carry = 0;
- for (j = i - 32, k = i - 12; j < k; ++j) {
- x[j] += carry - 16 * x[i] * L[j - (i - 32)];
- carry = (x[j] + 128) >> 8;
- x[j] -= carry * 256;
- }
- x[j] += carry;
- x[i] = 0;
- }
- carry = 0;
- for (j = 0; j < 32; j++) {
- x[j] += carry - (x[31] >> 4) * L[j];
- carry = x[j] >> 8;
- x[j] &= 255;
- }
- for (j = 0; j < 32; j++) x[j] -= carry * L[j];
- for (i = 0; i < 32; i++) {
- x[i+1] += x[i] >> 8;
- r[i] = x[i] & 255;
- }
-}
-
-function reduce(r) {
- var x = new Float64Array(64), i;
- for (i = 0; i < 64; i++) x[i] = r[i];
- for (i = 0; i < 64; i++) r[i] = 0;
- modL(r, x);
-}
-
-function unpackneg(r, p) {
- var t = gf(), chk = gf(), num = gf(),
- den = gf(), den2 = gf(), den4 = gf(),
- den6 = gf();
-
- set25519(r[2], gf1);
- unpack25519(r[1], p);
- S(num, r[1]);
- M(den, num, D);
- Z(num, num, r[2]);
- A(den, r[2], den);
-
- S(den2, den);
- S(den4, den2);
- M(den6, den4, den2);
- M(t, den6, num);
- M(t, t, den);
-
- pow2523(t, t);
- M(t, t, num);
- M(t, t, den);
- M(t, t, den);
- M(r[0], t, den);
-
- S(chk, r[0]);
- M(chk, chk, den);
- if (neq25519(chk, num)) M(r[0], r[0], I);
-
- S(chk, r[0]);
- M(chk, chk, den);
- if (neq25519(chk, num)) return -1;
-
- if (par25519(r[0]) === (p[31]>>7)) Z(r[0], gf0, r[0]);
-
- M(r[3], r[0], r[1]);
- return 0;
-}
-
-nacl.ll = {
-
- ge_scalarmult_base: ge_scalarmult_base,
- ge_scalarmult: ge_scalarmult,
- ge_double_scalarmult_base_vartime: ge_double_scalarmult_base_vartime,
- ge_add: ge_add,
- ge_double_scalarmult_postcomp_vartime: ge_double_scalarmult_postcomp_vartime
-
-};
-
-
-/* High-level API */
-
-function cleanup(arr) {
- for (var i = 0; i < arr.length; i++) arr[i] = 0;
-}
-
-nacl.randomBytes = function(n) {
- var b = new Uint8Array(n);
- randombytes(b, n);
- return b;
-};
-
-nacl.setPRNG = function(fn) {
- randombytes = fn;
-};
-
-(function() {
- // Initialize PRNG if environment provides CSPRNG.
- // If not, methods calling randombytes will throw.
- var crypto = typeof self !== 'undefined' ? (self.crypto || self.msCrypto) : null;
- if (crypto && crypto.getRandomValues) {
- // Browsers.
- var QUOTA = 65536;
- nacl.setPRNG(function(x, n) {
- var i, v = new Uint8Array(n);
- for (i = 0; i < n; i += QUOTA) {
- crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
- }
- for (i = 0; i < n; i++) x[i] = v[i];
- cleanup(v);
- });
- } else if (typeof require !== 'undefined') {
- // Node.js.
- crypto = require('crypto');
- if (crypto && crypto.randomBytes) {
- nacl.setPRNG(function(x, n) {
- var i, v = crypto.randomBytes(n);
- for (i = 0; i < n; i++) x[i] = v[i];
- cleanup(v);
- });
- }
- }
-})();
-
-})(typeof module !== 'undefined' && module.exports ? module.exports : (self.nacl = self.nacl || {}));
diff --git a/index.js b/index.js
deleted file mode 100644
index 3247530..0000000
--- a/index.js
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2014-2018, MyMonero.com
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-"use strict";
-//
-// NOTE: The main downside to using an index.js file like this is that it will pull in all the code - rather than the consumer requiring code module-by-module
-// It's of course possible to construct your own stripped-down index.[custom name].js file for, e.g., special webpack bundling usages.
-const mymonero_core_js = {};
-mymonero_core_js.monero_utils = require("./monero_utils/monero_cryptonote_utils_instance");
-mymonero_core_js.monero_config = require("./monero_utils/monero_config");
-mymonero_core_js.monero_txParsing_utils = require("./monero_utils/monero_txParsing_utils");
-mymonero_core_js.monero_sendingFunds_utils = require("./monero_utils/monero_sendingFunds_utils");
-mymonero_core_js.request_funds_uri_utils = require("./monero_utils/request_funds_uri_utils");
-mymonero_core_js.key_image_utils = require("./monero_utils/key_image_utils");
-mymonero_core_js.monero_paymentID_utils = require("./monero_utils/monero_paymentID_utils");
-mymonero_core_js.api_response_parser_utils = require("./hostAPI/response_parser_utils");
-//
-mymonero_core_js.nettype_utils = require("./cryptonote_utils/nettype");
-mymonero_core_js.JSBigInt = require("./cryptonote_utils/biginteger").BigInteger; // so that it is available to a hypothetical consumer's language-bridging web context for constructing string arguments to the above modules
-//
-module.exports = mymonero_core_js;
diff --git a/package.json b/package.json
index 6732530..8ad0225 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"url": "git+https://github.com/mymonero/mymonero-core-js.git"
},
"scripts": {
+ "build": "tsc",
"format": "find . -name '*.js*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override",
"test": "jest",
"test:coverage": "jest --coverage"
@@ -32,21 +33,32 @@
},
"homepage": "https://github.com/mymonero/mymonero-core-js#readme",
"dependencies": {
- "keccakjs": "^0.2.1"
+ "keccakjs": "^0.2.1",
+ "moment": "^2.22.2"
},
"devDependencies": {
"jest": "^23.1.0",
+ "ts-jest": "^23.0.1",
"ts-node": "^7.0.0",
- "tsconfig-paths": "^3.4.2"
+ "tsconfig-paths": "^3.4.2",
+ "typescript": "^2.9.2"
},
"jest": {
- "testEnvironment": "node",
+ "moduleFileExtensions": [
+ "js",
+ "jsx",
+ "json",
+ "ts",
+ "tsx"
+ ],
+ "transform": {
+ "\\.(ts|tsx)$": "/node_modules/ts-jest/preprocessor.js"
+ },
+ "modulePaths": ["/src" ],
+ "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.ts$",
"coveragePathIgnorePatterns": [
- "node_modules",
- "cryptonote_utils/biginteger.js",
- "cryptonote_utils/nacl-fast-cn.js",
- "cryptonote_utils/sha3.js",
- "cryptonote_utils/cryptonote_crypto_EMSCRIPTEN.js"
+ "/node_modules/",
+ "__test__"
]
}
}
diff --git a/cryptonote_utils/biginteger.d.ts b/src/biginteger/biginteger.d.ts
similarity index 99%
rename from cryptonote_utils/biginteger.d.ts
rename to src/biginteger/biginteger.d.ts
index 891843c..37e2db9 100644
--- a/cryptonote_utils/biginteger.d.ts
+++ b/src/biginteger/biginteger.d.ts
@@ -291,7 +291,7 @@ declare namespace BigInteger {
* @returns {BigInteger}
* @memberof BigInteger
*/
- parse(s: string, base?: number): BigInteger;
+ static parse(s: string, base?: number): BigInteger;
/**
* @description Add two .
diff --git a/cryptonote_utils/biginteger.js b/src/biginteger/biginteger.js
similarity index 100%
rename from cryptonote_utils/biginteger.js
rename to src/biginteger/biginteger.js
diff --git a/src/biginteger/index.ts b/src/biginteger/index.ts
new file mode 100644
index 0000000..7c1c190
--- /dev/null
+++ b/src/biginteger/index.ts
@@ -0,0 +1,4 @@
+import BigInteger = require("./biginteger");
+
+export const BigInt = BigInteger.BigInteger;
+export type BigInt = BigInteger.BigInteger;
diff --git a/src/cryptonote_utils/cryptonote_utils.ts b/src/cryptonote_utils/cryptonote_utils.ts
new file mode 100644
index 0000000..7069006
--- /dev/null
+++ b/src/cryptonote_utils/cryptonote_utils.ts
@@ -0,0 +1,2789 @@
+// Copyright (c) 2014-2018, MyMonero.com
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original Author: Lucas Jones
+// Modified to remove jQuery dep and support modular inclusion of deps by Paul Shapiro (2016)
+// Modified to add RingCT support by luigi1111 (2017)
+//
+// v--- These should maybe be injected into a context and supplied to currencyConfig for future platforms
+
+import cnBase58 = require("./internal_libs/bs58");
+import CNCrypto = require("./internal_libs/cn_crypto");
+import nacl = require("./internal_libs/fast_cn");
+import SHA3 = require("keccakjs");
+import nettype_utils = require("./nettype");
+import { randomBytes } from "crypto";
+import { BigInt } from "biginteger";
+import { NetType } from "./nettype";
+import { formatMoney, formatMoneyFull } from "./formatters";
+import { SecretCommitment, MixCommitment } from "types";
+import {
+ ViewSendKeys,
+ ParsedTarget,
+ Output,
+ AmountOutput,
+ Pid,
+} from "monero_utils/sending_funds/internal_libs/types";
+
+const HASH_SIZE = 32;
+const ADDRESS_CHECKSUM_SIZE = 4;
+const INTEGRATED_ID_SIZE = 8;
+const ENCRYPTED_PAYMENT_ID_TAIL = 141;
+
+const UINT64_MAX = new BigInt(2).pow(64);
+const CURRENT_TX_VERSION = 2;
+const OLD_TX_VERSION = 1;
+const RCTTypeFull = 1;
+const RCTTypeSimple = 2;
+const TX_EXTRA_NONCE_MAX_COUNT = 255;
+const TX_EXTRA_TAGS = {
+ PADDING: "00",
+ PUBKEY: "01",
+ NONCE: "02",
+ MERGE_MINING: "03",
+};
+const TX_EXTRA_NONCE_TAGS = {
+ PAYMENT_ID: "00",
+ ENCRYPTED_PAYMENT_ID: "01",
+};
+const KEY_SIZE = 32;
+const STRUCT_SIZES = {
+ GE_P3: 160,
+ GE_P2: 120,
+ GE_P1P1: 160,
+ GE_CACHED: 160,
+ EC_SCALAR: 32,
+ EC_POINT: 32,
+ KEY_IMAGE: 32,
+ GE_DSMP: 160 * 8, // ge_cached * 8
+ SIGNATURE: 64, // ec_scalar * 2
+};
+
+//RCT vars
+export const H =
+ "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94"; //base H for amounts
+
+export const l = new BigInt(
+ "7237005577332262213973186563042994240857116359379907606001950938285454250989",
+); //curve order (not RCT specific)
+
+export const I =
+ "0100000000000000000000000000000000000000000000000000000000000000"; //identity element
+
+export function identity() {
+ return I;
+}
+
+export const Z =
+ "0000000000000000000000000000000000000000000000000000000000000000"; //zero scalar
+
+//H2 object to speed up some operations
+export const H2 = [
+ "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94",
+ "8faa448ae4b3e2bb3d4d130909f55fcd79711c1c83cdbccadd42cbe1515e8712",
+ "12a7d62c7791654a57f3e67694ed50b49a7d9e3fc1e4c7a0bde29d187e9cc71d",
+ "789ab9934b49c4f9e6785c6d57a498b3ead443f04f13df110c5427b4f214c739",
+ "771e9299d94f02ac72e38e44de568ac1dcb2edc6edb61f83ca418e1077ce3de8",
+ "73b96db43039819bdaf5680e5c32d741488884d18d93866d4074a849182a8a64",
+ "8d458e1c2f68ebebccd2fd5d379f5e58f8134df3e0e88cad3d46701063a8d412",
+ "09551edbe494418e81284455d64b35ee8ac093068a5f161fa6637559177ef404",
+ "d05a8866f4df8cee1e268b1d23a4c58c92e760309786cdac0feda1d247a9c9a7",
+ "55cdaad518bd871dd1eb7bc7023e1dc0fdf3339864f88fdd2de269fe9ee1832d",
+ "e7697e951a98cfd5712b84bbe5f34ed733e9473fcb68eda66e3788df1958c306",
+ "f92a970bae72782989bfc83adfaa92a4f49c7e95918b3bba3cdc7fe88acc8d47",
+ "1f66c2d491d75af915c8db6a6d1cb0cd4f7ddcd5e63d3ba9b83c866c39ef3a2b",
+ "3eec9884b43f58e93ef8deea260004efea2a46344fc5965b1a7dd5d18997efa7",
+ "b29f8f0ccb96977fe777d489d6be9e7ebc19c409b5103568f277611d7ea84894",
+ "56b1f51265b9559876d58d249d0c146d69a103636699874d3f90473550fe3f2c",
+ "1d7a36575e22f5d139ff9cc510fa138505576b63815a94e4b012bfd457caaada",
+ "d0ac507a864ecd0593fa67be7d23134392d00e4007e2534878d9b242e10d7620",
+ "f6c6840b9cf145bb2dccf86e940be0fc098e32e31099d56f7fe087bd5deb5094",
+ "28831a3340070eb1db87c12e05980d5f33e9ef90f83a4817c9f4a0a33227e197",
+ "87632273d629ccb7e1ed1a768fa2ebd51760f32e1c0b867a5d368d5271055c6e",
+ "5c7b29424347964d04275517c5ae14b6b5ea2798b573fc94e6e44a5321600cfb",
+ "e6945042d78bc2c3bd6ec58c511a9fe859c0ad63fde494f5039e0e8232612bd5",
+ "36d56907e2ec745db6e54f0b2e1b2300abcb422e712da588a40d3f1ebbbe02f6",
+ "34db6ee4d0608e5f783650495a3b2f5273c5134e5284e4fdf96627bb16e31e6b",
+ "8e7659fb45a3787d674ae86731faa2538ec0fdf442ab26e9c791fada089467e9",
+ "3006cf198b24f31bb4c7e6346000abc701e827cfbb5df52dcfa42e9ca9ff0802",
+ "f5fd403cb6e8be21472e377ffd805a8c6083ea4803b8485389cc3ebc215f002a",
+ "3731b260eb3f9482e45f1c3f3b9dcf834b75e6eef8c40f461ea27e8b6ed9473d",
+ "9f9dab09c3f5e42855c2de971b659328a2dbc454845f396ffc053f0bb192f8c3",
+ "5e055d25f85fdb98f273e4afe08464c003b70f1ef0677bb5e25706400be620a5",
+ "868bcf3679cb6b500b94418c0b8925f9865530303ae4e4b262591865666a4590",
+ "b3db6bd3897afbd1df3f9644ab21c8050e1f0038a52f7ca95ac0c3de7558cb7a",
+ "8119b3a059ff2cac483e69bcd41d6d27149447914288bbeaee3413e6dcc6d1eb",
+ "10fc58f35fc7fe7ae875524bb5850003005b7f978c0c65e2a965464b6d00819c",
+ "5acd94eb3c578379c1ea58a343ec4fcff962776fe35521e475a0e06d887b2db9",
+ "33daf3a214d6e0d42d2300a7b44b39290db8989b427974cd865db011055a2901",
+ "cfc6572f29afd164a494e64e6f1aeb820c3e7da355144e5124a391d06e9f95ea",
+ "d5312a4b0ef615a331f6352c2ed21dac9e7c36398b939aec901c257f6cbc9e8e",
+ "551d67fefc7b5b9f9fdbf6af57c96c8a74d7e45a002078a7b5ba45c6fde93e33",
+ "d50ac7bd5ca593c656928f38428017fc7ba502854c43d8414950e96ecb405dc3",
+ "0773e18ea1be44fe1a97e239573cfae3e4e95ef9aa9faabeac1274d3ad261604",
+ "e9af0e7ca89330d2b8615d1b4137ca617e21297f2f0ded8e31b7d2ead8714660",
+ "7b124583097f1029a0c74191fe7378c9105acc706695ed1493bb76034226a57b",
+ "ec40057b995476650b3db98e9db75738a8cd2f94d863b906150c56aac19caa6b",
+ "01d9ff729efd39d83784c0fe59c4ae81a67034cb53c943fb818b9d8ae7fc33e5",
+ "00dfb3c696328c76424519a7befe8e0f6c76f947b52767916d24823f735baf2e",
+ "461b799b4d9ceea8d580dcb76d11150d535e1639d16003c3fb7e9d1fd13083a8",
+ "ee03039479e5228fdc551cbde7079d3412ea186a517ccc63e46e9fcce4fe3a6c",
+ "a8cfb543524e7f02b9f045acd543c21c373b4c9b98ac20cec417a6ddb5744e94",
+ "932b794bf89c6edaf5d0650c7c4bad9242b25626e37ead5aa75ec8c64e09dd4f",
+ "16b10c779ce5cfef59c7710d2e68441ea6facb68e9b5f7d533ae0bb78e28bf57",
+ "0f77c76743e7396f9910139f4937d837ae54e21038ac5c0b3fd6ef171a28a7e4",
+ "d7e574b7b952f293e80dde905eb509373f3f6cd109a02208b3c1e924080a20ca",
+ "45666f8c381e3da675563ff8ba23f83bfac30c34abdde6e5c0975ef9fd700cb9",
+ "b24612e454607eb1aba447f816d1a4551ef95fa7247fb7c1f503020a7177f0dd",
+ "7e208861856da42c8bb46a7567f8121362d9fb2496f131a4aa9017cf366cdfce",
+ "5b646bff6ad1100165037a055601ea02358c0f41050f9dfe3c95dccbd3087be0",
+ "746d1dccfed2f0ff1e13c51e2d50d5324375fbd5bf7ca82a8931828d801d43ab",
+ "cb98110d4a6bb97d22feadbc6c0d8930c5f8fc508b2fc5b35328d26b88db19ae",
+ "60b626a033b55f27d7676c4095eababc7a2c7ede2624b472e97f64f96b8cfc0e",
+ "e5b52bc927468df71893eb8197ef820cf76cb0aaf6e8e4fe93ad62d803983104",
+ "056541ae5da9961be2b0a5e895e5c5ba153cbb62dd561a427bad0ffd41923199",
+ "f8fef05a3fa5c9f3eba41638b247b711a99f960fe73aa2f90136aeb20329b888",
+];
+
+//begin rct new functions
+//creates a Pedersen commitment from an amount (in scalar form) and a mask
+//C = bG + aH where b = mask, a = amount
+function commit(amount: string, mask: string) {
+ if (
+ !valid_hex(mask) ||
+ mask.length !== 64 ||
+ !valid_hex(amount) ||
+ amount.length !== 64
+ ) {
+ throw Error("invalid amount or mask!");
+ }
+ const C = ge_double_scalarmult_base_vartime(amount, H, mask);
+ return C;
+}
+
+function zeroCommit(amount: string) {
+ if (!valid_hex(amount) || amount.length !== 64) {
+ throw Error("invalid amount!");
+ }
+ const C = ge_double_scalarmult_base_vartime(amount, H, I);
+ return C;
+}
+
+interface Commit {
+ mask: string;
+ amount: string;
+}
+
+export function decode_rct_ecdh(ecdh: Commit, key: string): Commit {
+ const first = hash_to_scalar(key);
+ const second = hash_to_scalar(first);
+ return {
+ mask: sc_sub(ecdh.mask, first),
+ amount: sc_sub(ecdh.amount, second),
+ };
+}
+
+export function encode_rct_ecdh(ecdh: Commit, key: string): Commit {
+ const first = hash_to_scalar(key);
+ const second = hash_to_scalar(first);
+ return {
+ mask: sc_add(ecdh.mask, first),
+ amount: sc_add(ecdh.amount, second),
+ };
+}
+
+//switch byte order for hex string
+function swapEndian(hex: string) {
+ if (hex.length % 2 !== 0) {
+ return "length must be a multiple of 2!";
+ }
+ let data = "";
+ for (let i = 1; i <= hex.length / 2; i++) {
+ data += hex.substr(0 - 2 * i, 2);
+ }
+ return data;
+}
+
+//switch byte order charwise
+function swapEndianC(str: string) {
+ let data = "";
+ for (let i = 1; i <= str.length; i++) {
+ data += str.substr(0 - i, 1);
+ }
+ return data;
+}
+
+//for most uses you'll also want to swapEndian after conversion
+//mainly to convert integer "scalars" to usable hexadecimal strings
+//uint long long to 32 byte key
+function d2h(integer: string | BigInt) {
+ let padding = "";
+ for (let i = 0; i < 63; i++) {
+ padding += "0";
+ }
+ return (padding + new BigInt(integer).toString(16).toLowerCase()).slice(
+ -64,
+ );
+}
+
+//integer (string) to scalar
+export function d2s(integer: string | BigInt) {
+ return swapEndian(d2h(integer));
+}
+
+//convert integer string to 64bit "binary" little-endian string
+function d2b(integer: string | BigInt) {
+ let padding = "";
+ for (let i = 0; i < 63; i++) {
+ padding += "0";
+ }
+ const a = new BigInt(integer);
+ if (a.toString(2).length > 64) {
+ throw Error("amount overflows uint64!");
+ }
+ return swapEndianC((padding + a.toString(2)).slice(-64));
+}
+
+//end rct new functions
+
+export function valid_hex(hex: string) {
+ const exp = new RegExp("[0-9a-fA-F]{" + hex.length + "}");
+ return exp.test(hex);
+}
+
+//simple exclusive or function for two hex inputs
+function hex_xor(hex1: string, hex2: string) {
+ if (
+ !hex1 ||
+ !hex2 ||
+ hex1.length !== hex2.length ||
+ hex1.length % 2 !== 0 ||
+ hex2.length % 2 !== 0
+ ) {
+ throw Error("Hex string(s) is/are invalid!");
+ }
+ const bin1 = hextobin(hex1);
+ const bin2 = hextobin(hex2);
+ const xor = new Uint8Array(bin1.length);
+ for (let i = 0; i < xor.length; i++) {
+ xor[i] = bin1[i] ^ bin2[i];
+ }
+ return bintohex(xor);
+}
+
+function hextobin(hex: string) {
+ if (hex.length % 2 !== 0) throw Error("Hex string has invalid length!");
+ const res = new Uint8Array(hex.length / 2);
+ for (let i = 0; i < hex.length / 2; ++i) {
+ res[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
+ }
+ return res;
+}
+
+function bintohex(bin: Uint8Array) {
+ const out = [];
+ for (let i = 0; i < bin.length; ++i) {
+ out.push(("0" + bin[i].toString(16)).slice(-2));
+ }
+ return out.join("");
+}
+
+// Generate a 256-bit / 64-char / 32-byte crypto random
+function rand_32() {
+ return randomBytes(32).toString("hex");
+}
+
+// Generate a 64-bit / 16-char / 8-byte crypto random
+export function rand_8() {
+ return randomBytes(8).toString("hex");
+}
+
+function encode_varint(input: number | string) {
+ let i = new BigInt(input);
+ let out = "";
+ // While i >= b10000000
+ while (i.compare(0x80) >= 0) {
+ // out.append i & b01111111 | b10000000
+ out += ("0" + ((i.lowVal() & 0x7f) | 0x80).toString(16)).slice(-2);
+ i = i.divide(new BigInt(2).pow(7));
+ }
+ out += ("0" + i.toJSValue().toString(16)).slice(-2);
+ return out;
+}
+
+function sc_reduce32(hex: string) {
+ const input = hextobin(hex);
+ if (input.length !== 32) {
+ throw Error("Invalid input length");
+ }
+ const mem = CNCrypto._malloc(32);
+ CNCrypto.HEAPU8.set(input, mem);
+ CNCrypto.ccall("sc_reduce32", "void", ["number"], [mem]);
+ const output = CNCrypto.HEAPU8.subarray(mem, mem + 32);
+ CNCrypto._free(mem);
+ return bintohex(output);
+}
+
+export function cn_fast_hash(input: string) {
+ if (input.length % 2 !== 0 || !valid_hex(input)) {
+ throw Error("Input invalid");
+ }
+
+ const hasher = new SHA3(256);
+ hasher.update(Buffer.from((hextobin(input).buffer as any) as Buffer));
+ return hasher.digest("hex");
+}
+
+function sec_key_to_pub(sec: string) {
+ if (sec.length !== 64) {
+ throw Error("Invalid sec length");
+ }
+ return bintohex(nacl.ge_scalarmult_base(hextobin(sec)));
+}
+
+//alias
+export function ge_scalarmult_base(sec: string) {
+ return sec_key_to_pub(sec);
+}
+
+export function ge_scalarmult(pub: string, sec: string) {
+ if (pub.length !== 64 || sec.length !== 64) {
+ throw Error("Invalid input length");
+ }
+ return bintohex(nacl.ge_scalarmult(hextobin(pub), hextobin(sec)));
+}
+
+function pubkeys_to_string(spend: string, view: string, nettype: NetType) {
+ const prefix = encode_varint(
+ nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype),
+ );
+ const data = prefix + spend + view;
+ const checksum = cn_fast_hash(data);
+ return cnBase58.encode(data + checksum.slice(0, ADDRESS_CHECKSUM_SIZE * 2));
+}
+
+export function makeIntegratedAddressFromAddressAndShortPid(
+ address: string,
+ short_pid: string,
+ nettype: NetType,
+) {
+ // throws
+ let decoded_address = decode_address(
+ address, // TODO/FIXME: not super happy about having to decode just to re-encode… this was a quick hack
+ nettype,
+ ); // throws
+ if (!short_pid || short_pid.length != 16) {
+ throw Error("expected valid short_pid");
+ }
+ const prefix = encode_varint(
+ nettype_utils.cryptonoteBase58PrefixForIntegratedAddressOn(nettype),
+ );
+ const data =
+ prefix + decoded_address.spend + decoded_address.view + short_pid;
+ const checksum = cn_fast_hash(data);
+ const encodable__data = data + checksum.slice(0, ADDRESS_CHECKSUM_SIZE * 2);
+ //
+ return cnBase58.encode(encodable__data);
+}
+
+// Generate keypair from seed
+function generate_keys(seed: string): Key {
+ if (seed.length !== 64) throw Error("Invalid input length!");
+ const sec = sc_reduce32(seed);
+ const pub = sec_key_to_pub(sec);
+ return {
+ sec: sec,
+ pub: pub,
+ };
+}
+
+export function random_keypair() {
+ return generate_keys(rand_32());
+}
+
+// Random 32-byte ec scalar
+export function random_scalar() {
+ return sc_reduce32(rand_32());
+}
+
+// alias
+export const skGen = random_scalar;
+
+interface Key {
+ pub: string;
+ sec: string;
+}
+interface Account {
+ spend: Key;
+ view: Key;
+ public_addr: string;
+}
+
+export function create_address(seed: string, nettype: NetType): Account {
+ // updated by Luigi and PS to support reduced and non-reduced seeds
+ let first;
+ if (seed.length !== 64) {
+ first = cn_fast_hash(seed);
+ } else {
+ first = sc_reduce32(seed);
+ }
+ const spend = generate_keys(first);
+ const second = cn_fast_hash(first);
+ const view = generate_keys(second);
+ const public_addr = pubkeys_to_string(spend.pub, view.pub, nettype);
+ return { spend, view, public_addr };
+}
+
+export function decode_address(address: string, nettype: NetType) {
+ let dec = cnBase58.decode(address);
+ const expectedPrefix = encode_varint(
+ nettype_utils.cryptonoteBase58PrefixForStandardAddressOn(nettype),
+ );
+ const expectedPrefixInt = encode_varint(
+ nettype_utils.cryptonoteBase58PrefixForIntegratedAddressOn(nettype),
+ );
+ const expectedPrefixSub = encode_varint(
+ nettype_utils.cryptonoteBase58PrefixForSubAddressOn(nettype),
+ );
+ const prefix = dec.slice(0, expectedPrefix.length);
+ if (
+ prefix !== expectedPrefix &&
+ prefix !== expectedPrefixInt &&
+ prefix !== expectedPrefixSub
+ ) {
+ throw Error("Invalid address prefix");
+ }
+ dec = dec.slice(expectedPrefix.length);
+ const spend = dec.slice(0, 64);
+ const view = dec.slice(64, 128);
+ let checksum;
+ let expectedChecksum;
+ let intPaymentId;
+
+ if (prefix === expectedPrefixInt) {
+ intPaymentId = dec.slice(128, 128 + INTEGRATED_ID_SIZE * 2);
+ checksum = dec.slice(
+ 128 + INTEGRATED_ID_SIZE * 2,
+ 128 + INTEGRATED_ID_SIZE * 2 + ADDRESS_CHECKSUM_SIZE * 2,
+ );
+ expectedChecksum = cn_fast_hash(
+ prefix + spend + view + intPaymentId,
+ ).slice(0, ADDRESS_CHECKSUM_SIZE * 2);
+ } else {
+ checksum = dec.slice(128, 128 + ADDRESS_CHECKSUM_SIZE * 2);
+ expectedChecksum = cn_fast_hash(prefix + spend + view).slice(
+ 0,
+ ADDRESS_CHECKSUM_SIZE * 2,
+ );
+ }
+ if (checksum !== expectedChecksum) {
+ throw Error("Invalid checksum");
+ }
+ if (intPaymentId) {
+ return {
+ spend: spend,
+ view: view,
+ intPaymentId: intPaymentId,
+ };
+ } else {
+ return {
+ spend: spend,
+ view: view,
+ };
+ }
+}
+
+export function is_subaddress(addr: string, nettype: NetType) {
+ const decoded = cnBase58.decode(addr);
+ const subaddressPrefix = encode_varint(
+ nettype_utils.cryptonoteBase58PrefixForSubAddressOn(nettype),
+ );
+ const prefix = decoded.slice(0, subaddressPrefix.length);
+ return prefix === subaddressPrefix;
+}
+
+function valid_keys(
+ view_pub: string,
+ view_sec: string,
+ spend_pub: string,
+ spend_sec: string,
+) {
+ const expected_view_pub = sec_key_to_pub(view_sec);
+ const expected_spend_pub = sec_key_to_pub(spend_sec);
+ return expected_spend_pub === spend_pub && expected_view_pub === view_pub;
+}
+
+export function hash_to_scalar(buf: string) {
+ const hash = cn_fast_hash(buf);
+ const scalar = sc_reduce32(hash);
+ return scalar;
+}
+
+export function generate_key_derivation(pub: string, sec: string) {
+ if (pub.length !== 64 || sec.length !== 64) {
+ throw Error("Invalid input length");
+ }
+ const P = ge_scalarmult(pub, sec);
+ return ge_scalarmult(P, d2s("8")); //mul8 to ensure group
+}
+
+export function derivation_to_scalar(derivation: string, output_index: number) {
+ let buf = "";
+ if (derivation.length !== STRUCT_SIZES.EC_POINT * 2) {
+ throw Error("Invalid derivation length!");
+ }
+ buf += derivation;
+ const enc = encode_varint(output_index);
+ if (enc.length > 10 * 2) {
+ throw Error("output_index didn't fit in 64-bit varint");
+ }
+ buf += enc;
+ return hash_to_scalar(buf);
+}
+
+function derive_secret_key(derivation: string, out_index: number, sec: string) {
+ if (derivation.length !== 64 || sec.length !== 64) {
+ throw Error("Invalid input length!");
+ }
+ const scalar_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ const scalar_b = hextobin(derivation_to_scalar(derivation, out_index));
+ CNCrypto.HEAPU8.set(scalar_b, scalar_m);
+ const base_m = CNCrypto._malloc(KEY_SIZE);
+ CNCrypto.HEAPU8.set(hextobin(sec), base_m);
+ const derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ CNCrypto.ccall(
+ "sc_add",
+ "void",
+ ["number", "number", "number"],
+ [derived_m, base_m, scalar_m],
+ );
+ const res = CNCrypto.HEAPU8.subarray(
+ derived_m,
+ derived_m + STRUCT_SIZES.EC_SCALAR,
+ );
+ CNCrypto._free(scalar_m);
+ CNCrypto._free(base_m);
+ CNCrypto._free(derived_m);
+ return bintohex(res);
+}
+
+export function derive_public_key(
+ derivation: string,
+ out_index: number,
+ pub: string,
+) {
+ if (derivation.length !== 64 || pub.length !== 64) {
+ throw Error("Invalid input length!");
+ }
+ const s = derivation_to_scalar(derivation, out_index);
+ return bintohex(
+ nacl.ge_add(hextobin(pub), hextobin(ge_scalarmult_base(s))),
+ );
+}
+
+// D' = P - Hs(aR|i)G
+export function derive_subaddress_public_key(
+ output_key: string,
+ derivation: string,
+ out_index: number,
+) {
+ if (output_key.length !== 64 || derivation.length !== 64) {
+ throw Error("Invalid input length!");
+ }
+ const scalar = derivation_to_scalar(derivation, out_index);
+ const point = ge_scalarmult_base(scalar);
+ return ge_sub(output_key, point);
+}
+
+function hash_to_ec(key: string) {
+ if (key.length !== KEY_SIZE * 2) {
+ throw Error("Invalid input length");
+ }
+ const h_m = CNCrypto._malloc(HASH_SIZE);
+ const point_m = CNCrypto._malloc(STRUCT_SIZES.GE_P2);
+ const point2_m = CNCrypto._malloc(STRUCT_SIZES.GE_P1P1);
+ const res_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
+ const hash = hextobin(cn_fast_hash(key));
+ CNCrypto.HEAPU8.set(hash, h_m);
+ CNCrypto.ccall(
+ "ge_fromfe_frombytes_vartime",
+ "void",
+ ["number", "number"],
+ [point_m, h_m],
+ );
+ CNCrypto.ccall(
+ "ge_mul8",
+ "void",
+ ["number", "number"],
+ [point2_m, point_m],
+ );
+ CNCrypto.ccall(
+ "ge_p1p1_to_p3",
+ "void",
+ ["number", "number"],
+ [res_m, point2_m],
+ );
+ const res = CNCrypto.HEAPU8.subarray(res_m, res_m + STRUCT_SIZES.GE_P3);
+ CNCrypto._free(h_m);
+ CNCrypto._free(point_m);
+ CNCrypto._free(point2_m);
+ CNCrypto._free(res_m);
+ return bintohex(res);
+}
+
+//returns a 32 byte point via "ge_p3_tobytes" rather than a 160 byte "p3", otherwise same as above;
+function hash_to_ec_2(key: string) {
+ if (key.length !== KEY_SIZE * 2) {
+ throw Error("Invalid input length");
+ }
+ const h_m = CNCrypto._malloc(HASH_SIZE);
+ const point_m = CNCrypto._malloc(STRUCT_SIZES.GE_P2);
+ const point2_m = CNCrypto._malloc(STRUCT_SIZES.GE_P1P1);
+ const res_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
+ const hash = hextobin(cn_fast_hash(key));
+ const res2_m = CNCrypto._malloc(KEY_SIZE);
+ CNCrypto.HEAPU8.set(hash, h_m);
+ CNCrypto.ccall(
+ "ge_fromfe_frombytes_vartime",
+ "void",
+ ["number", "number"],
+ [point_m, h_m],
+ );
+ CNCrypto.ccall(
+ "ge_mul8",
+ "void",
+ ["number", "number"],
+ [point2_m, point_m],
+ );
+ CNCrypto.ccall(
+ "ge_p1p1_to_p3",
+ "void",
+ ["number", "number"],
+ [res_m, point2_m],
+ );
+ CNCrypto.ccall(
+ "ge_p3_tobytes",
+ "void",
+ ["number", "number"],
+ [res2_m, res_m],
+ );
+ const res = CNCrypto.HEAPU8.subarray(res2_m, res2_m + KEY_SIZE);
+ CNCrypto._free(h_m);
+ CNCrypto._free(point_m);
+ CNCrypto._free(point2_m);
+ CNCrypto._free(res_m);
+ CNCrypto._free(res2_m);
+ return bintohex(res);
+}
+export const hashToPoint = hash_to_ec_2;
+
+export function generate_key_image_2(pub: string, sec: string) {
+ if (!pub || !sec || pub.length !== 64 || sec.length !== 64) {
+ throw Error("Invalid input length");
+ }
+ const pub_m = CNCrypto._malloc(KEY_SIZE);
+ const sec_m = CNCrypto._malloc(KEY_SIZE);
+ CNCrypto.HEAPU8.set(hextobin(pub), pub_m);
+ CNCrypto.HEAPU8.set(hextobin(sec), sec_m);
+ if (CNCrypto.ccall("sc_check", "number", ["number"], [sec_m]) !== 0) {
+ throw Error("sc_check(sec) != 0");
+ }
+ const point_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
+ const point2_m = CNCrypto._malloc(STRUCT_SIZES.GE_P2);
+ const point_b = hextobin(hash_to_ec(pub));
+ CNCrypto.HEAPU8.set(point_b, point_m);
+ const image_m = CNCrypto._malloc(STRUCT_SIZES.KEY_IMAGE);
+ CNCrypto.ccall(
+ "ge_scalarmult",
+ "void",
+ ["number", "number", "number"],
+ [point2_m, sec_m, point_m],
+ );
+ CNCrypto.ccall(
+ "ge_tobytes",
+ "void",
+ ["number", "number"],
+ [image_m, point2_m],
+ );
+ const res = CNCrypto.HEAPU8.subarray(
+ image_m,
+ image_m + STRUCT_SIZES.KEY_IMAGE,
+ );
+ CNCrypto._free(pub_m);
+ CNCrypto._free(sec_m);
+ CNCrypto._free(point_m);
+ CNCrypto._free(point2_m);
+ CNCrypto._free(image_m);
+ return bintohex(res);
+}
+
+export function generate_key_image(
+ tx_pub: string,
+ view_sec: string,
+ spend_pub: string,
+ spend_sec: string,
+ output_index: number,
+) {
+ if (tx_pub.length !== 64) {
+ throw Error("Invalid tx_pub length");
+ }
+ if (view_sec.length !== 64) {
+ throw Error("Invalid view_sec length");
+ }
+ if (spend_pub.length !== 64) {
+ throw Error("Invalid spend_pub length");
+ }
+ if (spend_sec.length !== 64) {
+ throw Error("Invalid spend_sec length");
+ }
+ const recv_derivation = generate_key_derivation(tx_pub, view_sec);
+ const ephemeral_pub = derive_public_key(
+ recv_derivation,
+ output_index,
+ spend_pub,
+ );
+ const ephemeral_sec = derive_secret_key(
+ recv_derivation,
+ output_index,
+ spend_sec,
+ );
+ const k_image = generate_key_image_2(ephemeral_pub, ephemeral_sec);
+ return {
+ ephemeral_pub: ephemeral_pub,
+ key_image: k_image,
+ };
+}
+
+function generate_key_image_helper_rct(
+ keys: Keys,
+ tx_pub_key: string,
+ out_index: number,
+ enc_mask?: string | null,
+) {
+ const recv_derivation = generate_key_derivation(tx_pub_key, keys.view.sec);
+ if (!recv_derivation) throw Error("Failed to generate key image");
+ const mask = enc_mask
+ ? sc_sub(
+ enc_mask,
+ hash_to_scalar(
+ derivation_to_scalar(recv_derivation, out_index),
+ ),
+ )
+ : I; //decode mask, or d2s(1) if no mask
+ const ephemeral_pub = derive_public_key(
+ recv_derivation,
+ out_index,
+ keys.spend.pub,
+ );
+ if (!ephemeral_pub) throw Error("Failed to generate key image");
+ const ephemeral_sec = derive_secret_key(
+ recv_derivation,
+ out_index,
+ keys.spend.sec,
+ );
+ const key_image = generate_key_image_2(ephemeral_pub, ephemeral_sec);
+ return {
+ in_ephemeral: {
+ pub: ephemeral_pub,
+ sec: ephemeral_sec,
+ mask: mask,
+ },
+ key_image,
+ };
+}
+
+//curve and scalar functions; split out to make their host functions cleaner and more readable
+//inverts X coordinate -- this seems correct ^_^ -luigi1111
+function ge_neg(point: string) {
+ if (point.length !== 64) {
+ throw Error("expected 64 char hex string");
+ }
+ return (
+ point.slice(0, 62) +
+ ((parseInt(point.slice(62, 63), 16) + 8) % 16).toString(16) +
+ point.slice(63, 64)
+ );
+}
+
+export function ge_add(p1: string, p2: string) {
+ if (p1.length !== 64 || p2.length !== 64) {
+ throw Error("Invalid input length!");
+ }
+ return bintohex(nacl.ge_add(hextobin(p1), hextobin(p2)));
+}
+
+//order matters
+export function ge_sub(point1: string, point2: string) {
+ const point2n = ge_neg(point2);
+ return ge_add(point1, point2n);
+}
+
+//adds two scalars together
+function sc_add(scalar1: string, scalar2: string) {
+ if (scalar1.length !== 64 || scalar2.length !== 64) {
+ throw Error("Invalid input length!");
+ }
+ const scalar1_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ const scalar2_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ CNCrypto.HEAPU8.set(hextobin(scalar1), scalar1_m);
+ CNCrypto.HEAPU8.set(hextobin(scalar2), scalar2_m);
+ const derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ CNCrypto.ccall(
+ "sc_add",
+ "void",
+ ["number", "number", "number"],
+ [derived_m, scalar1_m, scalar2_m],
+ );
+ const res = CNCrypto.HEAPU8.subarray(
+ derived_m,
+ derived_m + STRUCT_SIZES.EC_SCALAR,
+ );
+ CNCrypto._free(scalar1_m);
+ CNCrypto._free(scalar2_m);
+ CNCrypto._free(derived_m);
+ return bintohex(res);
+}
+
+//subtracts one scalar from another
+function sc_sub(scalar1: string, scalar2: string) {
+ if (scalar1.length !== 64 || scalar2.length !== 64) {
+ throw Error("Invalid input length!");
+ }
+ const scalar1_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ const scalar2_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ CNCrypto.HEAPU8.set(hextobin(scalar1), scalar1_m);
+ CNCrypto.HEAPU8.set(hextobin(scalar2), scalar2_m);
+ const derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ CNCrypto.ccall(
+ "sc_sub",
+ "void",
+ ["number", "number", "number"],
+ [derived_m, scalar1_m, scalar2_m],
+ );
+ const res = CNCrypto.HEAPU8.subarray(
+ derived_m,
+ derived_m + STRUCT_SIZES.EC_SCALAR,
+ );
+ CNCrypto._free(scalar1_m);
+ CNCrypto._free(scalar2_m);
+ CNCrypto._free(derived_m);
+ return bintohex(res);
+}
+
+//res = c - (ab) mod l; argument names copied from the signature implementation
+function sc_mulsub(sigc: string, sec: string, k: string) {
+ if (
+ k.length !== KEY_SIZE * 2 ||
+ sigc.length !== KEY_SIZE * 2 ||
+ sec.length !== KEY_SIZE * 2 ||
+ !valid_hex(k) ||
+ !valid_hex(sigc) ||
+ !valid_hex(sec)
+ ) {
+ throw Error("bad scalar");
+ }
+ const sec_m = CNCrypto._malloc(KEY_SIZE);
+ CNCrypto.HEAPU8.set(hextobin(sec), sec_m);
+ const sigc_m = CNCrypto._malloc(KEY_SIZE);
+ CNCrypto.HEAPU8.set(hextobin(sigc), sigc_m);
+ const k_m = CNCrypto._malloc(KEY_SIZE);
+ CNCrypto.HEAPU8.set(hextobin(k), k_m);
+ const res_m = CNCrypto._malloc(KEY_SIZE);
+
+ CNCrypto.ccall(
+ "sc_mulsub",
+ "void",
+ ["number", "number", "number", "number"],
+ [res_m, sigc_m, sec_m, k_m],
+ );
+ const res = CNCrypto.HEAPU8.subarray(res_m, res_m + KEY_SIZE);
+ CNCrypto._free(k_m);
+ CNCrypto._free(sec_m);
+ CNCrypto._free(sigc_m);
+ CNCrypto._free(res_m);
+ return bintohex(res);
+}
+
+function ge_double_scalarmult_base_vartime(c: string, P: string, r: string) {
+ if (c.length !== 64 || P.length !== 64 || r.length !== 64) {
+ throw Error("Invalid input length!");
+ }
+ return bintohex(
+ nacl.ge_double_scalarmult_base_vartime(
+ hextobin(c),
+ hextobin(P),
+ hextobin(r),
+ ),
+ );
+}
+
+function ge_double_scalarmult_postcomp_vartime(
+ r: string,
+ P: string,
+ c: string,
+ I: string,
+) {
+ if (
+ c.length !== 64 ||
+ P.length !== 64 ||
+ r.length !== 64 ||
+ I.length !== 64
+ ) {
+ throw Error("Invalid input length!");
+ }
+ const Pb = hash_to_ec_2(P);
+ return bintohex(
+ nacl.ge_double_scalarmult_postcomp_vartime(
+ hextobin(r),
+ hextobin(Pb),
+ hextobin(c),
+ hextobin(I),
+ ),
+ );
+}
+
+//begin RCT functions
+
+//xv: vector of secret keys, 1 per ring (nrings)
+//pm: matrix of pubkeys, indexed by size first
+//iv: vector of indexes, 1 per ring (nrings), can be a string
+//size: ring size, default 2
+//nrings: number of rings, default 64
+//extensible borromean signatures
+interface BorromeanSignature {
+ s: string[][];
+ ee: string;
+}
+
+export function genBorromean(
+ xv: string[],
+ pm: string[][],
+ iv: string[] | string,
+ size: number,
+ nrings: number,
+) {
+ if (xv.length !== nrings) {
+ throw Error("wrong xv length " + xv.length);
+ }
+ if (pm.length !== size) {
+ throw Error("wrong pm size " + pm.length);
+ }
+ for (let i = 0; i < pm.length; i++) {
+ if (pm[i].length !== nrings) {
+ throw Error("wrong pm[" + i + "] length " + pm[i].length);
+ }
+ }
+ if (iv.length !== nrings) {
+ throw Error("wrong iv length " + iv.length);
+ }
+ for (let i = 0; i < iv.length; i++) {
+ if (+iv[i] >= size) {
+ throw Error("bad indices value at: " + i + ": " + iv[i]);
+ }
+ }
+ //signature struct
+ // in the case of size 2 and nrings 64
+ // bb.s = [[64], [64]]
+
+ const bb: BorromeanSignature = {
+ s: [],
+ ee: "",
+ };
+ //signature pubkey matrix
+ const L: string[][] = [];
+ //add needed sub vectors (1 per ring size)
+ for (let i = 0; i < size; i++) {
+ bb.s[i] = [];
+ L[i] = [];
+ }
+ //compute starting at the secret index to the last row
+ let index;
+ const alpha = [];
+ for (let i = 0; i < nrings; i++) {
+ index = parseInt(iv[i]);
+ alpha[i] = random_scalar();
+ L[index][i] = ge_scalarmult_base(alpha[i]);
+ for (let j = index + 1; j < size; j++) {
+ bb.s[j][i] = random_scalar();
+ const c = hash_to_scalar(L[j - 1][i]);
+ L[j][i] = ge_double_scalarmult_base_vartime(
+ c,
+ pm[j][i],
+ bb.s[j][i],
+ );
+ }
+ }
+ //hash last row to create ee
+ let ltemp = "";
+ for (let i = 0; i < nrings; i++) {
+ ltemp += L[size - 1][i];
+ }
+ bb.ee = hash_to_scalar(ltemp);
+ //compute the rest from 0 to secret index
+ let j: number;
+ for (let i = 0; i < nrings; i++) {
+ let cc = bb.ee;
+ for (j = 0; j < +iv[i]; j++) {
+ bb.s[j][i] = random_scalar();
+ const LL = ge_double_scalarmult_base_vartime(
+ cc,
+ pm[j][i],
+ bb.s[j][i],
+ );
+ cc = hash_to_scalar(LL);
+ }
+ bb.s[j][i] = sc_mulsub(xv[i], cc, alpha[i]);
+ }
+ return bb;
+}
+
+export function verifyBorromean(
+ bb: BorromeanSignature,
+ P1: string[],
+ P2: string[],
+) {
+ let Lv1 = [];
+ let chash;
+ let LL;
+
+ let p2 = "";
+ for (let ii = 0; ii < 64; ii++) {
+ p2 = ge_double_scalarmult_base_vartime(bb.ee, P1[ii], bb.s[0][ii]);
+ LL = p2;
+ chash = hash_to_scalar(LL);
+
+ p2 = ge_double_scalarmult_base_vartime(chash, P2[ii], bb.s[1][ii]);
+ Lv1[ii] = p2;
+ }
+ const eeComputed = array_hash_to_scalar(Lv1);
+ const equalKeys = eeComputed === bb.ee;
+ console.log(`[verifyBorromean] Keys equal? ${equalKeys}
+ ${eeComputed}
+ ${bb.ee}`);
+
+ return equalKeys;
+}
+
+//proveRange
+//proveRange gives C, and mask such that \sumCi = C
+// c.f. http://eprint.iacr.org/2015/1098 section 5.1
+// and Ci is a commitment to either 0 or s^i, i=0,...,n
+// thus this proves that "amount" is in [0, s^n] (we assume s to be 4) (2 for now with v2 txes)
+// mask is a such that C = aG + bH, and b = amount
+//commitMaskObj = {C: commit, mask: mask}
+
+interface CommitMask {
+ C: string;
+ mask: string;
+}
+
+interface RangeSignature {
+ Ci: string[];
+ bsig: BorromeanSignature;
+}
+
+function proveRange(
+ commitMaskObj: CommitMask,
+ amount: string | BigInt,
+ nrings: number,
+) {
+ const size = 2;
+ let C = I; //identity
+ let mask = Z; //zero scalar
+ const indices = d2b(amount); //base 2 for now
+ const Ci: string[] = [];
+
+ const ai: string[] = [];
+ const PM: string[][] = [];
+ for (let i = 0; i < size; i++) {
+ PM[i] = [];
+ }
+ let j;
+ //start at index and fill PM left and right -- PM[0] holds Ci
+ for (let i = 0; i < nrings; i++) {
+ ai[i] = random_scalar();
+
+ j = +indices[i];
+ PM[j][i] = ge_scalarmult_base(ai[i]);
+ while (j > 0) {
+ j--;
+ PM[j][i] = ge_add(PM[j + 1][i], H2[i]); //will need to use i*2 for base 4 (or different object)
+ }
+
+ j = +indices[i];
+ while (j < size - 1) {
+ j++;
+ PM[j][i] = ge_sub(PM[j - 1][i], H2[i]); //will need to use i*2 for base 4 (or different object)
+ }
+ mask = sc_add(mask, ai[i]);
+ }
+ /*
+ * some more payload stuff here
+ */
+ //copy commitments to sig and sum them to commitment
+ for (let i = 0; i < nrings; i++) {
+ //if (i < nrings - 1) //for later version
+ Ci[i] = PM[0][i];
+ C = ge_add(C, PM[0][i]);
+ }
+
+ const sig: RangeSignature = {
+ Ci,
+ bsig: genBorromean(ai, PM, indices, size, nrings),
+ };
+ //exp: exponent //doesn't exist for now
+
+ commitMaskObj.C = C;
+ commitMaskObj.mask = mask;
+ return sig;
+}
+
+//proveRange and verRange
+//proveRange gives C, and mask such that \sumCi = C
+// c.f. http://eprint.iacr.org/2015/1098 section 5.1
+// and Ci is a commitment to either 0 or 2^i, i=0,...,63
+// thus this proves that "amount" is in [0, 2^64]
+// mask is a such that C = aG + bH, and b = amount
+//verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
+
+function verRange(C: string, as: RangeSignature, nrings = 64) {
+ try {
+ let CiH = []; // len 64
+ let asCi = []; // len 64
+ let Ctmp = identity();
+ for (let i = 0; i < nrings; i++) {
+ CiH[i] = ge_sub(as.Ci[i], H2[i]);
+ asCi[i] = as.Ci[i];
+ Ctmp = ge_add(Ctmp, as.Ci[i]);
+ }
+ const equalKeys = Ctmp === C;
+ console.log(`[verRange] Equal keys? ${equalKeys}
+ C: ${C}
+ Ctmp: ${Ctmp}`);
+ if (!equalKeys) {
+ return false;
+ }
+
+ if (!verifyBorromean(as.bsig, asCi, CiH)) {
+ return false;
+ }
+
+ return true;
+ } catch (e) {
+ console.error(`[verRange]`, e);
+ return false;
+ }
+}
+
+function array_hash_to_scalar(array: string[]) {
+ let buf = "";
+ for (let i = 0; i < array.length; i++) {
+ buf += array[i];
+ }
+ return hash_to_scalar(buf);
+}
+
+// Gen creates a signature which proves that for some column in the keymatrix "pk"
+// the signer knows a secret key for each row in that column
+// we presently only support matrices of 2 rows (pubkey, commitment)
+// this is a simplied MLSAG_Gen function to reflect that
+// because we don't want to force same secret column for all inputs
+
+interface MGSig {
+ ss: string[][];
+ cc: string;
+}
+export function MLSAG_Gen(
+ message: string,
+ pk: string[][],
+ xx: string[],
+ kimg: string,
+ index: number,
+) {
+ const cols = pk.length; //ring size
+ let i;
+
+ // secret index
+ if (index >= cols) {
+ throw Error("index out of range");
+ }
+ const rows = pk[0].length; //number of signature rows (always 2)
+ // [pub, com] = 2
+ if (rows !== 2) {
+ throw Error("wrong row count");
+ }
+ // check all are len 2
+ for (i = 0; i < cols; i++) {
+ if (pk[i].length !== rows) {
+ throw Error("pk is not rectangular");
+ }
+ }
+ if (xx.length !== rows) {
+ throw Error("bad xx size");
+ }
+
+ let c_old = "";
+ const alpha = [];
+
+ const rv: MGSig = {
+ ss: [],
+ cc: "",
+ };
+ for (i = 0; i < cols; i++) {
+ rv.ss[i] = [];
+ }
+ const toHash = []; //holds 6 elements: message, pubkey, dsRow L, dsRow R, commitment, ndsRow L
+ toHash[0] = message;
+
+ //secret index (pubkey section)
+
+ alpha[0] = random_scalar(); //need to save alphas for later
+ toHash[1] = pk[index][0]; //secret index pubkey
+
+ // this is the keyimg anyway const H1 = hashToPoint(pk[index][0]) // Hp(K_in)
+ // rv.II[0] = ge_scalarmult(H1, xx[0]) // k_in.Hp(K_in)
+
+ toHash[2] = ge_scalarmult_base(alpha[0]); //dsRow L, a.G
+ toHash[3] = generate_key_image_2(pk[index][0], alpha[0]); //dsRow R (key image check)
+ //secret index (commitment section)
+ alpha[1] = random_scalar();
+ toHash[4] = pk[index][1]; //secret index commitment
+ toHash[5] = ge_scalarmult_base(alpha[1]); //ndsRow L
+
+ c_old = array_hash_to_scalar(toHash);
+
+ i = (index + 1) % cols;
+ if (i === 0) {
+ rv.cc = c_old;
+ }
+ while (i != index) {
+ rv.ss[i][0] = random_scalar(); //dsRow ss
+ rv.ss[i][1] = random_scalar(); //ndsRow ss
+
+ //!secret index (pubkey section)
+ toHash[1] = pk[i][0];
+ toHash[2] = ge_double_scalarmult_base_vartime(
+ c_old,
+ pk[i][0],
+ rv.ss[i][0],
+ );
+ toHash[3] = ge_double_scalarmult_postcomp_vartime(
+ rv.ss[i][0],
+ pk[i][0],
+ c_old,
+ kimg,
+ );
+ //!secret index (commitment section)
+ toHash[4] = pk[i][1];
+ toHash[5] = ge_double_scalarmult_base_vartime(
+ c_old,
+ pk[i][1],
+ rv.ss[i][1],
+ );
+ c_old = array_hash_to_scalar(toHash); //hash to get next column c
+ i = (i + 1) % cols;
+ if (i === 0) {
+ rv.cc = c_old;
+ }
+ }
+ for (i = 0; i < rows; i++) {
+ rv.ss[index][i] = sc_mulsub(c_old, xx[i], alpha[i]);
+ }
+ return rv;
+}
+
+export function MLSAG_ver(
+ message: string,
+ pk: string[][],
+ rv: MGSig,
+ kimg: string,
+) {
+ // we assume that col, row, rectangular checks are already done correctly
+ // in MLSAG_gen
+ const cols = pk.length;
+ let c_old = rv.cc;
+ let i = 0;
+ let toHash = [];
+ toHash[0] = message;
+ while (i < cols) {
+ //!secret index (pubkey section)
+ toHash[1] = pk[i][0];
+ toHash[2] = ge_double_scalarmult_base_vartime(
+ c_old,
+ pk[i][0],
+ rv.ss[i][0],
+ );
+ toHash[3] = ge_double_scalarmult_postcomp_vartime(
+ rv.ss[i][0],
+ pk[i][0],
+ c_old,
+ kimg,
+ );
+
+ //!secret index (commitment section)
+ toHash[4] = pk[i][1];
+ toHash[5] = ge_double_scalarmult_base_vartime(
+ c_old,
+ pk[i][1],
+ rv.ss[i][1],
+ );
+
+ c_old = array_hash_to_scalar(toHash);
+
+ i = i + 1;
+ }
+
+ const c = sc_sub(c_old, rv.cc);
+ console.log(`[MLSAG_ver]
+ c_old: ${c_old}
+ rc.cc: ${rv.cc}
+ c: ${c}`);
+
+ return Number(c) === 0;
+}
+
+//Ring-ct MG sigs
+//Prove:
+// c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
+// This does the MG sig on the "dest" part of the given key matrix, and
+// the last row is the sum of input commitments from that column - sum output commitments
+// this shows that sum inputs = sum outputs
+//Ver:
+// verifies the above sig is created corretly
+function proveRctMG(
+ message: string,
+ pubs: MixCommitment[],
+ inSk: SecretCommitment,
+ kimg: string,
+ mask: string,
+ Cout: string,
+ index: number,
+) {
+ const cols = pubs.length;
+ if (cols < 3) {
+ throw Error("cols must be > 2 (mixin)");
+ }
+
+ const PK: string[][] = [];
+ //fill pubkey matrix (copy destination, subtract commitments)
+ for (let i = 0; i < cols; i++) {
+ PK[i] = [];
+ PK[i][0] = pubs[i].dest;
+ PK[i][1] = ge_sub(pubs[i].mask, Cout);
+ }
+
+ const xx = [inSk.x, sc_sub(inSk.a, mask)];
+ return MLSAG_Gen(message, PK, xx, kimg, index);
+}
+
+//Ring-ct MG sigs
+//Prove:
+// c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
+// This does the MG sig on the "dest" part of the given key matrix, and
+// the last row is the sum of input commitments from that column - sum output commitments
+// this shows that sum inputs = sum outputs
+//Ver:
+// verifies the above sig is created corretly
+
+function verRctMG(
+ mg: MGSig,
+ pubs: MixCommitment[][],
+ outPk: string[],
+ txnFeeKey: string,
+ message: string,
+ kimg: string,
+) {
+ const cols = pubs.length;
+ if (cols < 1) {
+ throw Error("Empty pubs");
+ }
+ const rows = pubs[0].length;
+
+ if (rows < 1) {
+ throw Error("Empty pubs");
+ }
+
+ for (let i = 0; i < cols; ++i) {
+ if (pubs[i].length !== rows) {
+ throw Error("Pubs is not rectangular");
+ }
+ }
+
+ // key matrix of (cols, tmp)
+
+ let M: string[][] = [];
+ console.log(pubs);
+ //create the matrix to mg sig
+ for (let i = 0; i < rows; i++) {
+ M[i] = [];
+ M[i][0] = pubs[0][i].dest;
+ M[i][1] = ge_add(M[i][1] || identity(), pubs[0][i].mask); // start with input commitment
+ for (let j = 0; j < outPk.length; j++) {
+ M[i][1] = ge_sub(M[i][1], outPk[j]); // subtract all output commitments
+ }
+ M[i][1] = ge_sub(M[i][1], txnFeeKey); // subtract txnfee
+ }
+
+ console.log(
+ `[MLSAG_ver input]`,
+ JSON.stringify({ message, M, mg, kimg }, null, 1),
+ );
+ return MLSAG_ver(message, M, mg, kimg);
+}
+
+// simple version, assuming only post Rct
+
+function verRctMGSimple(
+ message: string,
+ mg: MGSig,
+ pubs: MixCommitment[],
+ C: string,
+ kimg: string,
+) {
+ try {
+ const M: string[][] = pubs.map(pub => [pub.dest, ge_sub(pub.mask, C)]);
+
+ return MLSAG_ver(message, M, mg, kimg);
+ } catch (error) {
+ console.error("[verRctSimple]", error);
+ return false;
+ }
+}
+
+function verBulletProof(..._: any[]) {
+ throw Error("verBulletProof is not implemented");
+}
+
+function get_pre_mlsag_hash(rv: RCTSignatures) {
+ let hashes = "";
+ hashes += rv.message;
+ hashes += cn_fast_hash(serialize_rct_base(rv));
+ const buf = serialize_range_proofs(rv);
+ hashes += cn_fast_hash(buf);
+ return cn_fast_hash(hashes);
+}
+
+function serialize_range_proofs(rv: RCTSignatures) {
+ let buf = "";
+
+ for (let i = 0; i < rv.p.rangeSigs.length; i++) {
+ for (let j = 0; j < rv.p.rangeSigs[i].bsig.s.length; j++) {
+ for (let l = 0; l < rv.p.rangeSigs[i].bsig.s[j].length; l++) {
+ buf += rv.p.rangeSigs[i].bsig.s[j][l];
+ }
+ }
+ buf += rv.p.rangeSigs[i].bsig.ee;
+ for (let j = 0; j < rv.p.rangeSigs[i].Ci.length; j++) {
+ buf += rv.p.rangeSigs[i].Ci[j];
+ }
+ }
+ return buf;
+}
+
+//message is normal prefix hash
+//inSk is vector of x,a
+//kimg is vector of kimg
+//destinations is vector of pubkeys (we skip and proxy outAmounts instead)
+//inAmounts is vector of strings
+//outAmounts is vector of strings
+//mixRing is matrix of pubkey, commit (dest, mask)
+//amountKeys is vector of scalars
+//indices is vector
+//txnFee is string, with its endian not swapped (e.g d2s is not called before passing it in as an argument)
+//to this function
+
+interface RCTSignatures {
+ type: number;
+ message: string;
+ outPk: string[];
+ p: {
+ rangeSigs: RangeSignature[];
+ MGs: MGSig[];
+ };
+ ecdhInfo: Commit[];
+ txnFee: string;
+ pseudoOuts: string[];
+}
+export function genRct(
+ message: string,
+ inSk: SecretCommitment[],
+ kimg: string[],
+ inAmounts: (BigInt | string)[],
+ outAmounts: (BigInt | string)[],
+ mixRing: MixCommitment[][],
+ amountKeys: string[],
+ indices: number[],
+ txnFee: string,
+) {
+ if (outAmounts.length !== amountKeys.length) {
+ throw Error("different number of amounts/amount_keys");
+ }
+ for (let i = 0; i < mixRing.length; i++) {
+ if (mixRing[i].length <= indices[i]) {
+ throw Error("bad mixRing/index size");
+ }
+ }
+ if (mixRing.length !== inSk.length) {
+ throw Error("mismatched mixRing/inSk");
+ }
+
+ if (indices.length !== inSk.length) {
+ throw Error("mismatched indices/inSk");
+ }
+
+ const rv: RCTSignatures = {
+ type: inSk.length === 1 ? RCTTypeFull : RCTTypeSimple,
+ message,
+ outPk: [],
+ p: {
+ rangeSigs: [],
+ MGs: [],
+ },
+ ecdhInfo: [],
+ txnFee,
+ pseudoOuts: [],
+ };
+
+ let sumout = Z;
+ const cmObj = {
+ C: "",
+ mask: "",
+ };
+
+ const nrings = 64; //for base 2/current
+ let i;
+ //compute range proofs, etc
+ for (i = 0; i < outAmounts.length; i++) {
+ const teststart = new Date().getTime();
+ rv.p.rangeSigs[i] = proveRange(cmObj, outAmounts[i], nrings);
+ const testfinish = new Date().getTime() - teststart;
+ console.log("Time take for range proof " + i + ": " + testfinish);
+ rv.outPk[i] = cmObj.C;
+ sumout = sc_add(sumout, cmObj.mask);
+ rv.ecdhInfo[i] = encode_rct_ecdh(
+ { mask: cmObj.mask, amount: d2s(outAmounts[i]) },
+ amountKeys[i],
+ );
+ }
+
+ //simple
+ if (rv.type === 2) {
+ if (inAmounts.length !== inSk.length) {
+ throw Error("mismatched inAmounts/inSk");
+ }
+
+ const ai = [];
+ let sumpouts = Z;
+ //create pseudoOuts
+ for (i = 0; i < inAmounts.length - 1; i++) {
+ ai[i] = random_scalar();
+ sumpouts = sc_add(sumpouts, ai[i]);
+ rv.pseudoOuts[i] = commit(d2s(inAmounts[i]), ai[i]);
+ }
+ ai[i] = sc_sub(sumout, sumpouts);
+ rv.pseudoOuts[i] = commit(d2s(inAmounts[i]), ai[i]);
+ const full_message = get_pre_mlsag_hash(rv);
+ for (i = 0; i < inAmounts.length; i++) {
+ rv.p.MGs.push(
+ proveRctMG(
+ full_message,
+ mixRing[i],
+ inSk[i],
+ kimg[i],
+ ai[i],
+ rv.pseudoOuts[i],
+ indices[i],
+ ),
+ );
+ }
+ } else {
+ let sumC = I;
+ //get sum of output commitments to use in MLSAG
+ for (i = 0; i < rv.outPk.length; i++) {
+ sumC = ge_add(sumC, rv.outPk[i]);
+ }
+ sumC = ge_add(sumC, ge_scalarmult(H, d2s(rv.txnFee)));
+ const full_message = get_pre_mlsag_hash(rv);
+ rv.p.MGs.push(
+ proveRctMG(
+ full_message,
+ mixRing[0],
+ inSk[0],
+ kimg[0],
+ sumout,
+ sumC,
+ indices[0],
+ ),
+ );
+ }
+ return rv;
+}
+
+export function verRct(
+ rv: RCTSignatures,
+ semantics: boolean,
+ mixRing: MixCommitment[][],
+ kimg: string,
+) {
+ if (rv.type === 0x03) {
+ throw Error("Bulletproof validation not implemented");
+ }
+
+ // where RCTTypeFull is 0x01 and RCTTypeFullBulletproof is 0x03
+ if (rv.type !== 0x01 && rv.type !== 0x03) {
+ throw Error("verRct called on non-full rctSig");
+ }
+ if (semantics) {
+ //RCTTypeFullBulletproof checks not implemented
+ // RCTTypeFull checks
+ if (rv.outPk.length !== rv.p.rangeSigs.length) {
+ throw Error("Mismatched sizes of outPk and rv.p.rangeSigs");
+ }
+ if (rv.outPk.length !== rv.ecdhInfo.length) {
+ throw Error("Mismatched sizes of outPk and rv.ecdhInfo");
+ }
+ if (rv.p.MGs.length !== 1) {
+ throw Error("full rctSig has not one MG");
+ }
+ } else {
+ // semantics check is early, we don't have the MGs resolved yet
+ }
+ try {
+ if (semantics) {
+ const results = [];
+ for (let i = 0; i < rv.outPk.length; i++) {
+ // might want to parallelize this like its done in the c++ codebase
+ // via some abstraction library to support browser + node
+ if (rv.p.rangeSigs.length === 0) {
+ results[i] = verBulletProof((rv.p as any).bulletproofs[i]);
+ } else {
+ // mask -> C if public
+ results[i] = verRange(rv.outPk[i], rv.p.rangeSigs[i]);
+ }
+ }
+
+ for (let i = 0; i < rv.outPk.length; i++) {
+ if (!results[i]) {
+ console.error(
+ "Range proof verification failed for output",
+ i,
+ );
+ return false;
+ }
+ }
+ } else {
+ // compute txn fee
+ const txnFeeKey = ge_scalarmult(H, d2s(rv.txnFee));
+ const mgVerd = verRctMG(
+ rv.p.MGs[0],
+ mixRing,
+ rv.outPk,
+ txnFeeKey,
+ get_pre_mlsag_hash(rv),
+ kimg,
+ );
+ console.log("mg sig verified?", mgVerd);
+ if (!mgVerd) {
+ console.error("MG Signature verification failed");
+ return false;
+ }
+ }
+ return true;
+ } catch (e) {
+ console.error("Error in verRct: ", e);
+ return false;
+ }
+}
+
+//ver RingCT simple
+//assumes only post-rct style inputs (at least for max anonymity)
+export function verRctSimple(
+ rv: RCTSignatures,
+ semantics: boolean,
+ mixRing: MixCommitment[][],
+ kimgs: string[],
+) {
+ try {
+ if (rv.type === 0x04) {
+ throw Error("Simple Bulletproof validation not implemented");
+ }
+
+ if (rv.type !== 0x02 && rv.type !== 0x04) {
+ throw Error("verRctSimple called on non simple rctSig");
+ }
+
+ if (semantics) {
+ if (rv.type == 0x04) {
+ throw Error("Simple Bulletproof validation not implemented");
+ } else {
+ if (rv.outPk.length !== rv.p.rangeSigs.length) {
+ throw Error("Mismatched sizes of outPk and rv.p.rangeSigs");
+ }
+ if (rv.pseudoOuts.length !== rv.p.MGs.length) {
+ throw Error(
+ "Mismatched sizes of rv.pseudoOuts and rv.p.MGs",
+ );
+ }
+ // originally the check is rv.p.pseudoOuts.length, but this'll throw
+ // until p.pseudoOuts is added as a property to the rv object
+ if ((rv.p as any).pseudoOuts) {
+ throw Error("rv.p.pseudoOuts must be empty");
+ }
+ }
+ } else {
+ if (rv.type === 0x04) {
+ throw Error("Simple Bulletproof validation not implemented");
+ } else {
+ // semantics check is early, and mixRing/MGs aren't resolved yet
+ if (rv.pseudoOuts.length !== mixRing.length) {
+ throw Error(
+ "Mismatched sizes of rv.pseudoOuts and mixRing",
+ );
+ }
+ }
+ }
+
+ // if bulletproof, then use rv.p.pseudoOuts, otherwise use rv.pseudoOuts
+ const pseudoOuts =
+ (rv.type as number) === 0x04
+ ? (rv.p as any).pseudoOuts
+ : rv.pseudoOuts;
+
+ if (semantics) {
+ let sumOutpks = identity();
+ for (let i = 0; i < rv.outPk.length; i++) {
+ sumOutpks = ge_add(sumOutpks, rv.outPk[i]); // add all of the output commitments
+ }
+
+ const txnFeeKey = ge_scalarmult(H, d2s(rv.txnFee));
+ sumOutpks = ge_add(txnFeeKey, sumOutpks); // add txnfeekey
+
+ let sumPseudoOuts = identity();
+ for (let i = 0; i < pseudoOuts.length; i++) {
+ sumPseudoOuts = ge_add(sumPseudoOuts, pseudoOuts[i]); // sum up all of the pseudoOuts
+ }
+
+ if (sumOutpks !== sumPseudoOuts) {
+ console.error("Sum check failed");
+ return false;
+ }
+
+ const results = [];
+ for (let i = 0; i < rv.outPk.length; i++) {
+ // might want to parallelize this like its done in the c++ codebase
+ // via some abstraction library to support browser + node
+ if (rv.p.rangeSigs.length === 0) {
+ results[i] = verBulletProof((rv.p as any).bulletproofs[i]);
+ } else {
+ // mask -> C if public
+ results[i] = verRange(rv.outPk[i], rv.p.rangeSigs[i]);
+ }
+ }
+
+ for (let i = 0; i < results.length; i++) {
+ if (!results[i]) {
+ console.error(
+ "Range proof verification failed for output",
+ i,
+ );
+ return false;
+ }
+ }
+ } else {
+ const message = get_pre_mlsag_hash(rv);
+ const results = [];
+ for (let i = 0; i < mixRing.length; i++) {
+ results[i] = verRctMGSimple(
+ message,
+ rv.p.MGs[i],
+ mixRing[i],
+ pseudoOuts[i],
+ kimgs[i],
+ );
+ }
+
+ for (let i = 0; i < results.length; i++) {
+ if (!results[i]) {
+ console.error(
+ "Range proof verification failed for output",
+ i,
+ );
+ return false;
+ }
+ }
+ }
+
+ return true;
+ } catch (error) {
+ console.log("[verRctSimple]", error);
+ return false;
+ }
+}
+
+//decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1)
+// uses the attached ecdh info to find the amounts represented by each output commitment
+// must know the destination private key to find the correct amount, else will return a random number
+
+export function decodeRct(rv: RCTSignatures, sk: string, i: number) {
+ // where RCTTypeFull is 0x01 and RCTTypeFullBulletproof is 0x03
+ if (rv.type !== 0x01 && rv.type !== 0x03) {
+ throw Error("verRct called on non-full rctSig");
+ }
+ if (i >= rv.ecdhInfo.length) {
+ throw Error("Bad index");
+ }
+ if (rv.outPk.length !== rv.ecdhInfo.length) {
+ throw Error("Mismatched sizes of rv.outPk and rv.ecdhInfo");
+ }
+
+ // mask amount and mask
+ const ecdh_info = rv.ecdhInfo[i];
+ const { mask, amount } = decode_rct_ecdh(ecdh_info, sk);
+
+ const C = rv.outPk[i];
+ const Ctmp = ge_double_scalarmult_base_vartime(amount, H, mask);
+
+ console.log("[decodeRct]", C, Ctmp);
+ if (C !== Ctmp) {
+ throw Error(
+ "warning, amount decoded incorrectly, will be unable to spend",
+ );
+ }
+ return { amount, mask };
+}
+
+export function decodeRctSimple(rv: RCTSignatures, sk: string, i: number) {
+ if (rv.type !== 0x02 && rv.type !== 0x04) {
+ throw Error("verRct called on full rctSig");
+ }
+ if (i >= rv.ecdhInfo.length) {
+ throw Error("Bad index");
+ }
+ if (rv.outPk.length !== rv.ecdhInfo.length) {
+ throw Error("Mismatched sizes of rv.outPk and rv.ecdhInfo");
+ }
+
+ // mask amount and mask
+ const ecdh_info = rv.ecdhInfo[i];
+ const { mask, amount } = decode_rct_ecdh(ecdh_info, sk);
+
+ const C = rv.outPk[i];
+ const Ctmp = ge_double_scalarmult_base_vartime(amount, H, mask);
+
+ console.log("[decodeRctSimple]", C, Ctmp);
+ if (C !== Ctmp) {
+ throw Error(
+ "warning, amount decoded incorrectly, will be unable to spend",
+ );
+ }
+ return { amount, mask };
+}
+
+//end RCT functions
+
+function add_pub_key_to_extra(extra: string, pubkey: string) {
+ if (pubkey.length !== 64) throw Error("Invalid pubkey length");
+ // Append pubkey tag and pubkey
+ extra += TX_EXTRA_TAGS.PUBKEY + pubkey;
+ return extra;
+}
+
+function add_nonce_to_extra(extra: string, nonce: string) {
+ // Append extra nonce
+ if (nonce.length % 2 !== 0) {
+ throw Error("Invalid extra nonce");
+ }
+ if (nonce.length / 2 > TX_EXTRA_NONCE_MAX_COUNT) {
+ throw Error(
+ "Extra nonce must be at most " +
+ TX_EXTRA_NONCE_MAX_COUNT +
+ " bytes",
+ );
+ }
+ // Add nonce tag
+ extra += TX_EXTRA_TAGS.NONCE;
+ // Encode length of nonce
+ extra += ("0" + (nonce.length / 2).toString(16)).slice(-2);
+ // Write nonce
+ extra += nonce;
+ return extra;
+}
+
+function get_payment_id_nonce(payment_id: string, pid_encrypt: boolean) {
+ if (payment_id.length !== 64 && payment_id.length !== 16) {
+ throw Error("Invalid payment id");
+ }
+ let res = "";
+ if (pid_encrypt) {
+ res += TX_EXTRA_NONCE_TAGS.ENCRYPTED_PAYMENT_ID;
+ } else {
+ res += TX_EXTRA_NONCE_TAGS.PAYMENT_ID;
+ }
+ res += payment_id;
+ return res;
+}
+
+function abs_to_rel_offsets(offsets: string[]) {
+ if (offsets.length === 0) return offsets;
+ for (let i = offsets.length - 1; i >= 1; --i) {
+ offsets[i] = new BigInt(offsets[i]).subtract(offsets[i - 1]).toString();
+ }
+ return offsets;
+}
+
+function get_tx_prefix_hash(tx: SignedTransaction) {
+ const prefix = serialize_tx(tx, true);
+ return cn_fast_hash(prefix);
+}
+
+export function serialize_tx(tx: SignedTransaction, headeronly?: boolean) {
+ //tx: {
+ // version: uint64,
+ // unlock_time: uint64,
+ // extra: hex,
+ // vin: [{amount: uint64, k_image: hex, key_offsets: [uint64,..]},...],
+ // vout: [{amount: uint64, target: {key: hex}},...],
+ // signatures: [[s,s,...],...]
+ //}
+
+ if (!tx.signatures) {
+ throw Error("This transaction does not contain pre rct signatures");
+ }
+
+ let buf = "";
+ buf += encode_varint(tx.version);
+ buf += encode_varint(tx.unlock_time);
+ buf += encode_varint(tx.vin.length);
+ let i, j;
+ for (i = 0; i < tx.vin.length; i++) {
+ const vin = tx.vin[i];
+ switch (vin.type) {
+ case "input_to_key":
+ buf += "02";
+ buf += encode_varint(vin.amount);
+ buf += encode_varint(vin.key_offsets.length);
+ for (j = 0; j < vin.key_offsets.length; j++) {
+ buf += encode_varint(vin.key_offsets[j]);
+ }
+ buf += vin.k_image;
+ break;
+ default:
+ throw Error("Unhandled vin type: " + vin.type);
+ }
+ }
+ buf += encode_varint(tx.vout.length);
+ for (i = 0; i < tx.vout.length; i++) {
+ const vout = tx.vout[i];
+ buf += encode_varint(vout.amount);
+ switch (vout.target.type) {
+ case "txout_to_key":
+ buf += "02";
+ buf += vout.target.key;
+ break;
+ default:
+ throw Error("Unhandled txout target type: " + vout.target.type);
+ }
+ }
+ if (!valid_hex(tx.extra)) {
+ throw Error("Tx extra has invalid hex");
+ }
+ buf += encode_varint(tx.extra.length / 2);
+ buf += tx.extra;
+ if (!headeronly) {
+ if (tx.vin.length !== tx.signatures.length) {
+ throw Error("Signatures length != vin length");
+ }
+ for (i = 0; i < tx.vin.length; i++) {
+ for (j = 0; j < tx.signatures[i].length; j++) {
+ buf += tx.signatures[i][j];
+ }
+ }
+ }
+ return buf;
+}
+
+// RCT only
+export function serialize_rct_tx_with_hash(tx: SignedTransaction) {
+ if (!tx.rct_signatures) {
+ throw Error("This transaction does not contain rct_signatures");
+ }
+
+ let hashes = "";
+ let buf = "";
+ buf += serialize_tx(tx, true);
+ hashes += cn_fast_hash(buf);
+ const buf2 = serialize_rct_base(tx.rct_signatures);
+ hashes += cn_fast_hash(buf2);
+ buf += buf2;
+ let buf3 = serialize_range_proofs(tx.rct_signatures);
+ //add MGs
+ for (let i = 0; i < tx.rct_signatures.p.MGs.length; i++) {
+ for (let j = 0; j < tx.rct_signatures.p.MGs[i].ss.length; j++) {
+ buf3 += tx.rct_signatures.p.MGs[i].ss[j][0];
+ buf3 += tx.rct_signatures.p.MGs[i].ss[j][1];
+ }
+ buf3 += tx.rct_signatures.p.MGs[i].cc;
+ }
+ hashes += cn_fast_hash(buf3);
+ buf += buf3;
+ const hash = cn_fast_hash(hashes);
+ return {
+ raw: buf,
+ hash: hash,
+ };
+}
+
+function serialize_rct_base(rv: RCTSignatures) {
+ let buf = "";
+ buf += encode_varint(rv.type);
+ buf += encode_varint(rv.txnFee);
+ if (rv.type === 2) {
+ for (let i = 0; i < rv.pseudoOuts.length; i++) {
+ buf += rv.pseudoOuts[i];
+ }
+ }
+ if (rv.ecdhInfo.length !== rv.outPk.length) {
+ throw Error("mismatched outPk/ecdhInfo!");
+ }
+ for (let i = 0; i < rv.ecdhInfo.length; i++) {
+ buf += rv.ecdhInfo[i].mask;
+ buf += rv.ecdhInfo[i].amount;
+ }
+ for (let i = 0; i < rv.outPk.length; i++) {
+ buf += rv.outPk[i];
+ }
+ return buf;
+}
+
+function generate_ring_signature(
+ prefix_hash: string,
+ k_image: string,
+ keys: string[],
+ sec: string,
+ real_index: number,
+) {
+ if (k_image.length !== STRUCT_SIZES.KEY_IMAGE * 2) {
+ throw Error("invalid key image length");
+ }
+ if (sec.length !== KEY_SIZE * 2) {
+ throw Error("Invalid secret key length");
+ }
+ if (prefix_hash.length !== HASH_SIZE * 2 || !valid_hex(prefix_hash)) {
+ throw Error("Invalid prefix hash");
+ }
+ if (real_index >= keys.length || real_index < 0) {
+ throw Error("real_index is invalid");
+ }
+ const _ge_tobytes = CNCrypto.cwrap("ge_tobytes", "void", [
+ "number",
+ "number",
+ ]);
+ const _ge_p3_tobytes = CNCrypto.cwrap("ge_p3_tobytes", "void", [
+ "number",
+ "number",
+ ]);
+ const _ge_scalarmult_base = CNCrypto.cwrap("ge_scalarmult_base", "void", [
+ "number",
+ "number",
+ ]);
+ const _ge_scalarmult = CNCrypto.cwrap("ge_scalarmult", "void", [
+ "number",
+ "number",
+ "number",
+ ]);
+ const _sc_add = CNCrypto.cwrap("sc_add", "void", [
+ "number",
+ "number",
+ "number",
+ ]);
+ const _sc_sub = CNCrypto.cwrap("sc_sub", "void", [
+ "number",
+ "number",
+ "number",
+ ]);
+ const _sc_mulsub = CNCrypto.cwrap("sc_mulsub", "void", [
+ "number",
+ "number",
+ "number",
+ "number",
+ ]);
+ const _sc_0 = CNCrypto.cwrap("sc_0", "void", ["number"]);
+ const _ge_double_scalarmult_base_vartime = CNCrypto.cwrap(
+ "ge_double_scalarmult_base_vartime",
+ "void",
+ ["number", "number", "number", "number"],
+ );
+ const _ge_double_scalarmult_precomp_vartime = CNCrypto.cwrap(
+ "ge_double_scalarmult_precomp_vartime",
+ "void",
+ ["number", "number", "number", "number", "number"],
+ );
+ const _ge_frombytes_vartime = CNCrypto.cwrap(
+ "ge_frombytes_vartime",
+ "number",
+ ["number", "number"],
+ );
+ const _ge_dsm_precomp = CNCrypto.cwrap("ge_dsm_precomp", "void", [
+ "number",
+ "number",
+ ]);
+
+ const buf_size = STRUCT_SIZES.EC_POINT * 2 * keys.length;
+ const buf_m = CNCrypto._malloc(buf_size);
+ const sig_size = STRUCT_SIZES.SIGNATURE * keys.length;
+ const sig_m = CNCrypto._malloc(sig_size);
+
+ // Struct pointer helper functions
+ function buf_a(i: number) {
+ return buf_m + STRUCT_SIZES.EC_POINT * (2 * i);
+ }
+ function buf_b(i: number) {
+ return buf_m + STRUCT_SIZES.EC_POINT * (2 * i + 1);
+ }
+ function sig_c(i: number) {
+ return sig_m + STRUCT_SIZES.EC_SCALAR * (2 * i);
+ }
+ function sig_r(i: number) {
+ return sig_m + STRUCT_SIZES.EC_SCALAR * (2 * i + 1);
+ }
+ const image_m = CNCrypto._malloc(STRUCT_SIZES.KEY_IMAGE);
+ CNCrypto.HEAPU8.set(hextobin(k_image), image_m);
+ let i;
+ const image_unp_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
+ const image_pre_m = CNCrypto._malloc(STRUCT_SIZES.GE_DSMP);
+ const sum_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ const k_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ const h_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR);
+ const tmp2_m = CNCrypto._malloc(STRUCT_SIZES.GE_P2);
+ const tmp3_m = CNCrypto._malloc(STRUCT_SIZES.GE_P3);
+ const pub_m = CNCrypto._malloc(KEY_SIZE);
+ const sec_m = CNCrypto._malloc(KEY_SIZE);
+ CNCrypto.HEAPU8.set(hextobin(sec), sec_m);
+ if (_ge_frombytes_vartime(image_unp_m, image_m) != 0) {
+ throw Error("failed to call ge_frombytes_vartime");
+ }
+ _ge_dsm_precomp(image_pre_m, image_unp_m);
+ _sc_0(sum_m);
+ for (i = 0; i < keys.length; i++) {
+ if (i === real_index) {
+ // Real key
+ const rand = random_scalar();
+ CNCrypto.HEAPU8.set(hextobin(rand), k_m);
+ _ge_scalarmult_base(tmp3_m, k_m);
+ _ge_p3_tobytes(buf_a(i), tmp3_m);
+ const ec = hash_to_ec(keys[i]);
+ CNCrypto.HEAPU8.set(hextobin(ec), tmp3_m);
+ _ge_scalarmult(tmp2_m, k_m, tmp3_m);
+ _ge_tobytes(buf_b(i), tmp2_m);
+ } else {
+ CNCrypto.HEAPU8.set(hextobin(random_scalar()), sig_c(i));
+ CNCrypto.HEAPU8.set(hextobin(random_scalar()), sig_r(i));
+ CNCrypto.HEAPU8.set(hextobin(keys[i]), pub_m);
+ if (
+ CNCrypto.ccall(
+ "ge_frombytes_vartime",
+ "void",
+ ["number", "number"],
+ [tmp3_m, pub_m],
+ ) !== 0
+ ) {
+ throw Error("Failed to call ge_frombytes_vartime");
+ }
+ _ge_double_scalarmult_base_vartime(
+ tmp2_m,
+ sig_c(i),
+ tmp3_m,
+ sig_r(i),
+ );
+ _ge_tobytes(buf_a(i), tmp2_m);
+ const ec = hash_to_ec(keys[i]);
+ CNCrypto.HEAPU8.set(hextobin(ec), tmp3_m);
+ _ge_double_scalarmult_precomp_vartime(
+ tmp2_m,
+ sig_r(i),
+ tmp3_m,
+ sig_c(i),
+ image_pre_m,
+ );
+ _ge_tobytes(buf_b(i), tmp2_m);
+ _sc_add(sum_m, sum_m, sig_c(i));
+ }
+ }
+ const buf_bin = CNCrypto.HEAPU8.subarray(buf_m, buf_m + buf_size);
+ const scalar = hash_to_scalar(prefix_hash + bintohex(buf_bin));
+ CNCrypto.HEAPU8.set(hextobin(scalar), h_m);
+ _sc_sub(sig_c(real_index), h_m, sum_m);
+ _sc_mulsub(sig_r(real_index), sig_c(real_index), sec_m, k_m);
+ const sig_data = bintohex(
+ CNCrypto.HEAPU8.subarray(sig_m, sig_m + sig_size),
+ );
+ const sigs = [];
+ for (let k = 0; k < keys.length; k++) {
+ sigs.push(
+ sig_data.slice(
+ STRUCT_SIZES.SIGNATURE * 2 * k,
+ STRUCT_SIZES.SIGNATURE * 2 * (k + 1),
+ ),
+ );
+ }
+ CNCrypto._free(image_m);
+ CNCrypto._free(image_unp_m);
+ CNCrypto._free(image_pre_m);
+ CNCrypto._free(sum_m);
+ CNCrypto._free(k_m);
+ CNCrypto._free(h_m);
+ CNCrypto._free(tmp2_m);
+ CNCrypto._free(tmp3_m);
+ CNCrypto._free(buf_m);
+ CNCrypto._free(sig_m);
+ CNCrypto._free(pub_m);
+ CNCrypto._free(sec_m);
+ return sigs;
+}
+
+interface TransactionInput {
+ type: string;
+ amount: string;
+ k_image: string;
+ key_offsets: string[];
+}
+
+interface TransactionOutput {
+ amount: string;
+ target: {
+ type: string;
+ key: string;
+ };
+}
+
+export interface SignedTransaction {
+ unlock_time: number;
+ version: number;
+ extra: string;
+ vin: TransactionInput[];
+ vout: TransactionOutput[];
+ rct_signatures?: RCTSignatures;
+ signatures?: string[][];
+}
+
+function construct_tx(
+ keys: Keys,
+ sources: Source[],
+ dsts: ParsedTarget[],
+ fee_amount: BigInt,
+ payment_id: string | null,
+ pid_encrypt: boolean,
+ realDestViewKey: string | undefined,
+ unlock_time: number,
+ rct: boolean,
+ nettype: NetType,
+) {
+ //we move payment ID stuff here, because we need txkey to encrypt
+ const txkey = random_keypair();
+ console.log(txkey);
+ let extra = "";
+ if (payment_id) {
+ if (pid_encrypt && payment_id.length !== INTEGRATED_ID_SIZE * 2) {
+ throw Error(
+ "payment ID must be " +
+ INTEGRATED_ID_SIZE +
+ " bytes to be encrypted!",
+ );
+ }
+ console.log("Adding payment id: " + payment_id);
+ if (pid_encrypt) {
+ if (!realDestViewKey) {
+ throw Error("RealDestViewKey not found");
+ }
+ //get the derivation from our passed viewkey, then hash that + tail to get encryption key
+ const pid_key = cn_fast_hash(
+ generate_key_derivation(realDestViewKey, txkey.sec) +
+ ENCRYPTED_PAYMENT_ID_TAIL.toString(16),
+ ).slice(0, INTEGRATED_ID_SIZE * 2);
+ console.log("Txkeys:", txkey, "Payment ID key:", pid_key);
+ payment_id = hex_xor(payment_id, pid_key);
+ }
+ const nonce = get_payment_id_nonce(payment_id, pid_encrypt);
+ console.log("Extra nonce: " + nonce);
+ extra = add_nonce_to_extra(extra, nonce);
+ }
+
+ const tx: SignedTransaction = {
+ unlock_time,
+ version: rct ? CURRENT_TX_VERSION : OLD_TX_VERSION,
+ extra,
+ vin: [],
+ vout: [],
+ rct_signatures: undefined,
+ signatures: undefined,
+ };
+
+ const inputs_money = sources.reduce(
+ (totalAmount, { amount }) => totalAmount.add(amount),
+ BigInt.ZERO,
+ );
+
+ let i;
+ console.log("Sources: ");
+
+ //run the for loop twice to sort ins by key image
+ //first generate key image and other construction data to sort it all in one go
+ const sourcesWithKeyImgAndKeys = sources.map((source, idx) => {
+ console.log(idx + ": " + formatMoneyFull(source.amount));
+ if (source.real_out >= source.outputs.length) {
+ throw Error("real index >= outputs.length");
+ }
+ const { key_image, in_ephemeral } = generate_key_image_helper_rct(
+ keys,
+ source.real_out_tx_key,
+ source.real_out_in_tx,
+ source.mask,
+ ); //mask will be undefined for non-rct
+
+ if (in_ephemeral.pub !== source.outputs[source.real_out].key) {
+ throw Error("in_ephemeral.pub != source.real_out.key");
+ }
+
+ return {
+ ...source,
+ key_image,
+ in_ephemeral,
+ };
+ });
+
+ //sort ins
+ sourcesWithKeyImgAndKeys.sort((a, b) => {
+ return (
+ BigInt.parse(a.key_image, 16).compare(
+ BigInt.parse(b.key_image, 16),
+ ) * -1
+ );
+ });
+
+ const in_contexts = sourcesWithKeyImgAndKeys.map(
+ source => source.in_ephemeral,
+ );
+
+ //copy the sorted sourcesWithKeyImgAndKeys data to tx
+ const vin = sourcesWithKeyImgAndKeys.map(source => {
+ const input_to_key = {
+ type: "input_to_key",
+ amount: source.amount,
+ k_image: source.key_image,
+ key_offsets: source.outputs.map(s => s.index),
+ };
+
+ input_to_key.key_offsets = abs_to_rel_offsets(input_to_key.key_offsets);
+ return input_to_key;
+ });
+
+ tx.vin = vin;
+
+ const dstsWithKeys = dsts.map(d => {
+ if (d.amount.compare(0) < 0) {
+ throw Error("dst.amount < 0"); //amount can be zero if no change
+ }
+ const keys = decode_address(d.address, nettype);
+ return { ...d, keys };
+ });
+
+ const outputs_money = dstsWithKeys.reduce(
+ (outputs_money, { amount }) => outputs_money.add(amount),
+ BigInt.ZERO,
+ );
+
+ interface Ret {
+ amountKeys: string[];
+ vout: TransactionOutput[];
+ }
+
+ const ret: Ret = { amountKeys: [], vout: [] };
+ //amountKeys is rct only
+ const { amountKeys, vout } = dstsWithKeys.reduce(
+ ({ amountKeys, vout }, dstWKey, out_index) => {
+ // R = rD for subaddresses
+ if (is_subaddress(dstWKey.address, nettype)) {
+ if (payment_id) {
+ // this could stand to be placed earlier in the function but we save repeating a little algo time this way
+ throw Error(
+ "Payment ID must not be supplied when sending to a subaddress",
+ );
+ }
+ txkey.pub = ge_scalarmult(dstWKey.keys.spend, txkey.sec);
+ }
+ // send change to ourselves
+ const out_derivation =
+ dstWKey.keys.view === keys.view.pub
+ ? generate_key_derivation(txkey.pub, keys.view.sec)
+ : generate_key_derivation(dstWKey.keys.view, txkey.sec);
+
+ const out_ephemeral_pub = derive_public_key(
+ out_derivation,
+ out_index,
+ dstWKey.keys.spend,
+ );
+
+ const out = {
+ amount: dstWKey.amount.toString(),
+ // txout_to_key
+ target: {
+ type: "txout_to_key",
+ key: out_ephemeral_pub,
+ },
+ };
+
+ const nextAmountKeys = rct
+ ? [
+ ...amountKeys,
+ derivation_to_scalar(out_derivation, out_index),
+ ]
+ : amountKeys;
+ const nextVout = [...vout, out];
+ const nextVal: Ret = { amountKeys: nextAmountKeys, vout: nextVout };
+ return nextVal;
+ },
+ ret,
+ );
+
+ tx.vout = vout;
+
+ // add pub key to extra after we know whether to use R = rG or R = rD
+ tx.extra = add_pub_key_to_extra(tx.extra, txkey.pub);
+
+ if (outputs_money.add(fee_amount).compare(inputs_money) > 0) {
+ throw Error(
+ `outputs money:${formatMoneyFull(
+ outputs_money,
+ )} + fee:${formatMoneyFull(
+ fee_amount,
+ )} > inputs money:${formatMoneyFull(inputs_money)}`,
+ );
+ }
+
+ if (!rct) {
+ const signatures = sourcesWithKeyImgAndKeys.map((src, i) => {
+ const src_keys = src.outputs.map(s => s.key);
+ const sigs = generate_ring_signature(
+ get_tx_prefix_hash(tx),
+ tx.vin[i].k_image,
+ src_keys,
+ in_contexts[i].sec,
+ sourcesWithKeyImgAndKeys[i].real_out,
+ );
+ return sigs;
+ });
+ tx.signatures = signatures;
+ } else {
+ //rct
+ const keyimages: string[] = [];
+ const inSk: SecretCommitment[] = [];
+ const inAmounts: string[] = [];
+ const mixRing: MixCommitment[][] = [];
+ const indices: number[] = [];
+
+ tx.vin.forEach((input, i) => {
+ keyimages.push(input.k_image);
+ inSk.push({
+ x: in_contexts[i].sec,
+ a: in_contexts[i].mask,
+ });
+ inAmounts.push(input.amount);
+
+ if (in_contexts[i].mask !== I) {
+ //if input is rct (has a valid mask), 0 out amount
+ input.amount = "0";
+ }
+
+ mixRing[i] = sourcesWithKeyImgAndKeys[i].outputs.map(o => {
+ if (!o.commit) {
+ throw Error("Commit not found");
+ }
+ return {
+ dest: o.key,
+ mask: o.commit,
+ };
+ });
+
+ indices.push(sourcesWithKeyImgAndKeys[i].real_out);
+ });
+
+ const outAmounts = [];
+ for (i = 0; i < tx.vout.length; i++) {
+ outAmounts.push(tx.vout[i].amount);
+ tx.vout[i].amount = "0"; //zero out all rct outputs
+ }
+ const tx_prefix_hash = get_tx_prefix_hash(tx);
+ tx.rct_signatures = genRct(
+ tx_prefix_hash,
+ inSk,
+ keyimages,
+ /*destinations, */ inAmounts,
+ outAmounts,
+ mixRing,
+ amountKeys,
+ indices,
+ fee_amount.toString(),
+ );
+ }
+ console.log(tx);
+ return tx;
+}
+
+interface Keys {
+ view: Key;
+ spend: Key;
+}
+
+interface SourceOutput {
+ index: string;
+ key: string;
+ commit?: string;
+}
+
+interface Source {
+ amount: string;
+ outputs: SourceOutput[];
+ real_out_tx_key: string;
+ real_out: number;
+ real_out_in_tx: number;
+ mask?: string | null;
+}
+export function create_transaction(
+ pub_keys: ViewSendKeys,
+ sec_keys: ViewSendKeys,
+ dsts: ParsedTarget[],
+ outputs: Output[],
+ mix_outs: AmountOutput[] | undefined,
+ fake_outputs_count: number,
+ fee_amount: BigInt,
+ payment_id: Pid,
+ pid_encrypt: boolean,
+ realDestViewKey: string | undefined,
+ unlock_time: number,
+ rct: boolean,
+ nettype: NetType,
+) {
+ unlock_time = unlock_time || 0;
+ mix_outs = mix_outs || [];
+ let i, j;
+ if (dsts.length === 0) {
+ throw Error("Destinations empty");
+ }
+ if (mix_outs.length !== outputs.length && fake_outputs_count !== 0) {
+ throw Error(
+ "Wrong number of mix outs provided (" +
+ outputs.length +
+ " outputs, " +
+ mix_outs.length +
+ " mix outs)",
+ );
+ }
+ for (i = 0; i < mix_outs.length; i++) {
+ if ((mix_outs[i].outputs || []).length < fake_outputs_count) {
+ throw Error("Not enough outputs to mix with");
+ }
+ }
+ const keys: Keys = {
+ view: {
+ pub: pub_keys.view,
+ sec: sec_keys.view,
+ },
+ spend: {
+ pub: pub_keys.spend,
+ sec: sec_keys.spend,
+ },
+ };
+ if (
+ !valid_keys(
+ keys.view.pub,
+ keys.view.sec,
+ keys.spend.pub,
+ keys.spend.sec,
+ )
+ ) {
+ throw Error("Invalid secret keys!");
+ }
+ let needed_money = BigInt.ZERO;
+ for (i = 0; i < dsts.length; ++i) {
+ needed_money = needed_money.add(dsts[i].amount);
+ if (needed_money.compare(UINT64_MAX) !== -1) {
+ throw Error("Output overflow!");
+ }
+ }
+ let found_money = BigInt.ZERO;
+ const sources = [];
+ console.log("Selected transfers: ", outputs);
+ for (i = 0; i < outputs.length; ++i) {
+ found_money = found_money.add(outputs[i].amount);
+ if (found_money.compare(UINT64_MAX) !== -1) {
+ throw Error("Input overflow!");
+ }
+
+ const src: Source = {
+ amount: outputs[i].amount,
+ outputs: [],
+ real_out: 0,
+ real_out_in_tx: 0,
+ real_out_tx_key: "",
+ };
+
+ if (mix_outs.length !== 0) {
+ // Sort fake outputs by global index
+ mix_outs[i].outputs.sort(function(a, b) {
+ return new BigInt(a.global_index).compare(b.global_index);
+ });
+ j = 0;
+ while (
+ src.outputs.length < fake_outputs_count &&
+ j < mix_outs[i].outputs.length
+ ) {
+ const out = mix_outs[i].outputs[j];
+ if (+out.global_index === outputs[i].global_index) {
+ console.log("got mixin the same as output, skipping");
+ j++;
+ continue;
+ }
+
+ const oe: SourceOutput = {
+ index: out.global_index.toString(),
+ key: out.public_key,
+ };
+
+ if (rct) {
+ if (out.rct) {
+ oe.commit = out.rct.slice(0, 64); //add commitment from rct mix outs
+ } else {
+ if (outputs[i].rct) {
+ throw Error("mix rct outs missing commit");
+ }
+ oe.commit = zeroCommit(d2s(src.amount)); //create identity-masked commitment for non-rct mix input
+ }
+ }
+ src.outputs.push(oe);
+ j++;
+ }
+ }
+ const real_oe: SourceOutput = {
+ index: outputs[i].global_index.toString(),
+ key: outputs[i].public_key,
+ };
+
+ if (rct) {
+ if (outputs[i].rct) {
+ real_oe.commit = outputs[i].rct.slice(0, 64); //add commitment for real input
+ } else {
+ real_oe.commit = zeroCommit(d2s(src.amount)); //create identity-masked commitment for non-rct input
+ }
+ }
+
+ let real_index = src.outputs.length;
+ for (j = 0; j < src.outputs.length; j++) {
+ if (new BigInt(real_oe.index).compare(src.outputs[j].index) < 0) {
+ real_index = j;
+ break;
+ }
+ }
+ // Add real_oe to outputs
+ src.outputs.splice(real_index, 0, real_oe);
+ src.real_out_tx_key = outputs[i].tx_pub_key;
+ // Real output entry index
+ src.real_out = real_index;
+ src.real_out_in_tx = outputs[i].index;
+ if (rct) {
+ // if rct, slice encrypted, otherwise will be set by generate_key_image_helper_rct
+ src.mask = outputs[i].rct ? outputs[i].rct.slice(64, 128) : null;
+ }
+ sources.push(src);
+ }
+ console.log("sources: ", sources);
+ const change = {
+ amount: BigInt.ZERO,
+ };
+ const cmp = needed_money.compare(found_money);
+ if (cmp < 0) {
+ change.amount = found_money.subtract(needed_money);
+ if (change.amount.compare(fee_amount) !== 0) {
+ throw Error("early fee calculation != later");
+ }
+ } else if (cmp > 0) {
+ throw Error("Need more money than found! (have: ") +
+ formatMoney(found_money) +
+ " need: " +
+ formatMoney(needed_money) +
+ ")";
+ }
+ return construct_tx(
+ keys,
+ sources,
+ dsts,
+ fee_amount,
+ payment_id,
+ pid_encrypt,
+ realDestViewKey,
+ unlock_time,
+ rct,
+ nettype,
+ );
+}
+
+export function estimateRctSize(
+ inputs: number,
+ mixin: number,
+ outputs: number,
+) {
+ let size = 0;
+ // tx prefix
+ // first few bytes
+ size += 1 + 6;
+ size += inputs * (1 + 6 + (mixin + 1) * 3 + 32); // original C implementation is *2+32 but author advised to change 2 to 3 as key offsets are variable size and this constitutes a best guess
+ // vout
+ size += outputs * (6 + 32);
+ // extra
+ size += 40;
+ // rct signatures
+ // type
+ size += 1;
+ // rangeSigs
+ size += (2 * 64 * 32 + 32 + 64 * 32) * outputs;
+ // MGs
+ size += inputs * (32 * (mixin + 1) + 32);
+ // mixRing - not serialized, can be reconstructed
+ /* size += 2 * 32 * (mixin+1) * inputs; */
+ // pseudoOuts
+ size += 32 * inputs;
+ // ecdhInfo
+ size += 2 * 32 * outputs;
+ // outPk - only commitment is saved
+ size += 32 * outputs;
+ // txnFee
+ size += 4;
+ // const logStr = `estimated rct tx size for ${inputs} at mixin ${mixin} and ${outputs} : ${size} (${((32 * inputs/*+1*/) + 2 * 32 * (mixin+1) * inputs + 32 * outputs)}) saved)`
+ // console.log(logStr)
+
+ return size;
+}
diff --git a/src/cryptonote_utils/formatters.ts b/src/cryptonote_utils/formatters.ts
new file mode 100644
index 0000000..a00c051
--- /dev/null
+++ b/src/cryptonote_utils/formatters.ts
@@ -0,0 +1,141 @@
+import { config } from "monero_utils/monero_config";
+import { BigInt } from "biginteger";
+import { ParsedTarget } from "monero_utils/sending_funds/internal_libs/types";
+
+export function formatMoneyFull(units: BigInt | string) {
+ let strUnits = units.toString();
+ const symbol = strUnits[0] === "-" ? "-" : "";
+ if (symbol === "-") {
+ strUnits = strUnits.slice(1);
+ }
+ let decimal;
+ if (strUnits.length >= config.coinUnitPlaces) {
+ decimal = strUnits.substr(
+ strUnits.length - config.coinUnitPlaces,
+ config.coinUnitPlaces,
+ );
+ } else {
+ decimal = padLeft(strUnits, config.coinUnitPlaces, "0");
+ }
+ return (
+ symbol +
+ (strUnits.substr(0, strUnits.length - config.coinUnitPlaces) || "0") +
+ "." +
+ decimal
+ );
+}
+
+export function formatMoneyFullSymbol(units: BigInt | string) {
+ return formatMoneyFull(units) + " " + config.coinSymbol;
+}
+
+export function formatMoney(units: BigInt | string) {
+ const f = trimRight(formatMoneyFull(units), "0");
+ if (f[f.length - 1] === ".") {
+ return f.slice(0, f.length - 1);
+ }
+ return f;
+}
+
+export function formatMoneySymbol(units: BigInt | string) {
+ return formatMoney(units) + " " + config.coinSymbol;
+}
+
+/**
+ *
+ * @param {string} str
+ */
+export function parseMoney(str: string): BigInt {
+ if (!str) return BigInt.ZERO;
+ const negative = str[0] === "-";
+ if (negative) {
+ str = str.slice(1);
+ }
+ const decimalIndex = str.indexOf(".");
+ if (decimalIndex == -1) {
+ if (negative) {
+ return config.coinUnits.multiply(str).negate();
+ }
+ return config.coinUnits.multiply(str);
+ }
+ if (decimalIndex + config.coinUnitPlaces + 1 < str.length) {
+ str = str.substr(0, decimalIndex + config.coinUnitPlaces + 1);
+ }
+ if (negative) {
+ return new BigInt(str.substr(0, decimalIndex))
+ .exp10(config.coinUnitPlaces)
+ .add(
+ new BigInt(str.substr(decimalIndex + 1)).exp10(
+ decimalIndex + config.coinUnitPlaces - str.length + 1,
+ ),
+ )
+ .negate();
+ }
+ return new BigInt(str.substr(0, decimalIndex))
+ .exp10(config.coinUnitPlaces)
+ .add(
+ new BigInt(str.substr(decimalIndex + 1)).exp10(
+ decimalIndex + config.coinUnitPlaces - str.length + 1,
+ ),
+ );
+}
+
+export function printDsts(dsts: ParsedTarget[]) {
+ for (let i = 0; i < dsts.length; i++) {
+ console.log(dsts[i].address + ": " + formatMoneyFull(dsts[i].amount));
+ }
+}
+
+export function decompose_tx_destinations(dsts: ParsedTarget[], rct: boolean) {
+ const out = [];
+ if (rct) {
+ for (let i = 0; i < dsts.length; i++) {
+ out.push({
+ address: dsts[i].address,
+ amount: dsts[i].amount,
+ });
+ }
+ } else {
+ for (let i = 0; i < dsts.length; i++) {
+ const digits = decompose_amount_into_digits(dsts[i].amount);
+ for (let j = 0; j < digits.length; j++) {
+ if (digits[j].compare(0) > 0) {
+ out.push({
+ address: dsts[i].address,
+ amount: digits[j],
+ });
+ }
+ }
+ }
+ }
+ return out.sort((a, b) => a.amount.subtract(b.amount).toJSValue());
+}
+
+function trimRight(str: string, char: string) {
+ while (str[str.length - 1] == char) str = str.slice(0, -1);
+ return str;
+}
+
+export function padLeft(str: string, len: number, char: string) {
+ while (str.length < len) {
+ str = char + str;
+ }
+ return str;
+}
+
+function decompose_amount_into_digits(amount: BigInt) {
+ let amtStr = amount.toString();
+ const ret = [];
+ while (amtStr.length > 0) {
+ //check so we don't create 0s
+ if (amtStr[0] !== "0") {
+ let digit = amtStr[0];
+ while (digit.length < amtStr.length) {
+ digit += "0";
+ }
+ ret.push(new BigInt(digit));
+ }
+ amtStr = amtStr.slice(1);
+ }
+ return ret;
+}
diff --git a/src/cryptonote_utils/index.ts b/src/cryptonote_utils/index.ts
new file mode 100644
index 0000000..87bf143
--- /dev/null
+++ b/src/cryptonote_utils/index.ts
@@ -0,0 +1,2 @@
+export * from "./cryptonote_utils";
+export * from "./nettype";
diff --git a/cryptonote_utils/internal_libs/bs58/cryptonote_base58.d.ts b/src/cryptonote_utils/internal_libs/bs58/cryptonote_base58.d.ts
similarity index 100%
rename from cryptonote_utils/internal_libs/bs58/cryptonote_base58.d.ts
rename to src/cryptonote_utils/internal_libs/bs58/cryptonote_base58.d.ts
diff --git a/cryptonote_utils/internal_libs/bs58/cryptonote_base58.js b/src/cryptonote_utils/internal_libs/bs58/cryptonote_base58.js
similarity index 92%
rename from cryptonote_utils/internal_libs/bs58/cryptonote_base58.js
rename to src/cryptonote_utils/internal_libs/bs58/cryptonote_base58.js
index a57b15e..66d59d5 100644
--- a/cryptonote_utils/internal_libs/bs58/cryptonote_base58.js
+++ b/src/cryptonote_utils/internal_libs/bs58/cryptonote_base58.js
@@ -27,7 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// v--- These should maybe be injected into context
-const JSBigInt = require("../../biginteger").BigInteger;
+const JSBigInt = require("../../../biginteger").BigInt;
var cnBase58 = (function() {
var b58 = {};
@@ -47,7 +47,7 @@ var cnBase58 = (function() {
var UINT64_MAX = new JSBigInt(2).pow(64);
function hextobin(hex) {
- if (hex.length % 2 !== 0) throw "Hex string has invalid length!";
+ if (hex.length % 2 !== 0) throw Error("Hex string has invalid length!");
var res = new Uint8Array(hex.length / 2);
for (var i = 0; i < hex.length / 2; ++i) {
res[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
@@ -81,7 +81,7 @@ var cnBase58 = (function() {
function uint8_be_to_64(data) {
if (data.length < 1 || data.length > 8) {
- throw "Invalid input length";
+ throw Error("Invalid input length");
}
var res = JSBigInt.ZERO;
var twopow8 = new JSBigInt(2).pow(8);
@@ -105,7 +105,7 @@ var cnBase58 = (function() {
res = res.multiply(twopow8).add(data[i++]);
break;
default:
- throw "Impossible condition";
+ throw Error("Impossible condition");
}
return res;
}
@@ -113,7 +113,7 @@ var cnBase58 = (function() {
function uint64_to_8be(num, size) {
var res = new Uint8Array(size);
if (size < 1 || size > 8) {
- throw "Invalid input length";
+ throw Error("Invalid input length");
}
var twopow8 = new JSBigInt(2).pow(8);
for (var i = size - 1; i >= 0; i--) {
@@ -125,7 +125,7 @@ var cnBase58 = (function() {
b58.encode_block = function(data, buf, index) {
if (data.length < 1 || data.length > full_encoded_block_size) {
- throw "Invalid block length: " + data.length;
+ throw Error("Invalid block length: " + data.length);
}
var num = uint8_be_to_64(data);
var i = encoded_block_sizes[data.length] - 1;
@@ -183,24 +183,24 @@ var cnBase58 = (function() {
b58.decode_block = function(data, buf, index) {
if (data.length < 1 || data.length > full_encoded_block_size) {
- throw "Invalid block length: " + data.length;
+ throw Error("Invalid block length: " + data.length);
}
var res_size = encoded_block_sizes.indexOf(data.length);
if (res_size <= 0) {
- throw "Invalid block size";
+ throw Error("Invalid block size");
}
var res_num = new JSBigInt(0);
var order = new JSBigInt(1);
for (var i = data.length - 1; i >= 0; i--) {
var digit = alphabet.indexOf(data[i]);
if (digit < 0) {
- throw "Invalid symbol";
+ throw Error("Invalid symbol");
}
var product = order.multiply(digit).add(res_num);
// if product > UINT64_MAX
if (product.compare(UINT64_MAX) === 1) {
- throw "Overflow";
+ throw Error("Overflow");
}
res_num = product;
order = order.multiply(alphabet_size);
@@ -209,7 +209,7 @@ var cnBase58 = (function() {
res_size < full_block_size &&
new JSBigInt(2).pow(8 * res_size).compare(res_num) <= 0
) {
- throw "Overflow 2";
+ throw Error("Overflow 2");
}
buf.set(uint64_to_8be(res_num, res_size), index);
return buf;
@@ -226,7 +226,7 @@ var cnBase58 = (function() {
last_block_size,
);
if (last_block_decoded_size < 0) {
- throw "Invalid encoded length";
+ throw Error("Invalid encoded length");
}
var data_size =
full_block_count * full_block_size + last_block_decoded_size;
diff --git a/cryptonote_utils/internal_libs/bs58/index.ts b/src/cryptonote_utils/internal_libs/bs58/index.ts
similarity index 100%
rename from cryptonote_utils/internal_libs/bs58/index.ts
rename to src/cryptonote_utils/internal_libs/bs58/index.ts
diff --git a/cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.d.ts b/src/cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.d.ts
similarity index 68%
rename from cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.d.ts
rename to src/cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.d.ts
index 363bc56..0899a8a 100644
--- a/cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.d.ts
+++ b/src/cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.d.ts
@@ -1,7 +1,6 @@
declare namespace CNCrypto {
- type Memory = string & { _tag_: "memory" };
- function _malloc(bytes: number): Memory;
- function _free(mem: Memory): mem is never;
+ function _malloc(bytes: number): number;
+ function _free(mem: number): mem is never;
var HEAPU8: Uint8Array;
diff --git a/cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.js b/src/cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.js
similarity index 100%
rename from cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.js
rename to src/cryptonote_utils/internal_libs/cn_crypto/cryptonote_crypto_EMSCRIPTEN.js
diff --git a/cryptonote_utils/internal_libs/cn_crypto/index.ts b/src/cryptonote_utils/internal_libs/cn_crypto/index.ts
similarity index 100%
rename from cryptonote_utils/internal_libs/cn_crypto/index.ts
rename to src/cryptonote_utils/internal_libs/cn_crypto/index.ts
diff --git a/cryptonote_utils/internal_libs/fast_cn/index.ts b/src/cryptonote_utils/internal_libs/fast_cn/index.ts
similarity index 100%
rename from cryptonote_utils/internal_libs/fast_cn/index.ts
rename to src/cryptonote_utils/internal_libs/fast_cn/index.ts
diff --git a/cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.d.ts b/src/cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.d.ts
similarity index 100%
rename from cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.d.ts
rename to src/cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.d.ts
diff --git a/src/cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.js b/src/cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.js
new file mode 100644
index 0000000..1029f7e
--- /dev/null
+++ b/src/cryptonote_utils/internal_libs/fast_cn/nacl-fast-cn.js
@@ -0,0 +1,1026 @@
+(function(nacl) {
+ "use strict";
+
+ // Ported in 2014 by Dmitry Chestnykh and Devi Mandiri.
+ // Public domain.
+ //
+ // Implementation derived from TweetNaCl version 20140427.
+ // See for details: http://tweetnacl.cr.yp.to/
+
+ // modified 2017 for some CN functions by luigi1111
+
+ var gf = function(init) {
+ var i,
+ r = new Float64Array(16);
+ if (init) for (i = 0; i < init.length; i++) r[i] = init[i];
+ return r;
+ };
+
+ // Pluggable, initialized in high-level API below.
+ var randombytes = function(/* x, n */) {
+ throw new Error("no PRNG");
+ };
+
+ var _0 = new Uint8Array(16);
+ var _9 = new Uint8Array(32);
+ _9[0] = 9;
+
+ var gf0 = gf(),
+ gf1 = gf([1]),
+ _121665 = gf([0xdb41, 1]),
+ D = gf([
+ 0x78a3,
+ 0x1359,
+ 0x4dca,
+ 0x75eb,
+ 0xd8ab,
+ 0x4141,
+ 0x0a4d,
+ 0x0070,
+ 0xe898,
+ 0x7779,
+ 0x4079,
+ 0x8cc7,
+ 0xfe73,
+ 0x2b6f,
+ 0x6cee,
+ 0x5203,
+ ]),
+ D2 = gf([
+ 0xf159,
+ 0x26b2,
+ 0x9b94,
+ 0xebd6,
+ 0xb156,
+ 0x8283,
+ 0x149a,
+ 0x00e0,
+ 0xd130,
+ 0xeef3,
+ 0x80f2,
+ 0x198e,
+ 0xfce7,
+ 0x56df,
+ 0xd9dc,
+ 0x2406,
+ ]),
+ X = gf([
+ 0xd51a,
+ 0x8f25,
+ 0x2d60,
+ 0xc956,
+ 0xa7b2,
+ 0x9525,
+ 0xc760,
+ 0x692c,
+ 0xdc5c,
+ 0xfdd6,
+ 0xe231,
+ 0xc0a4,
+ 0x53fe,
+ 0xcd6e,
+ 0x36d3,
+ 0x2169,
+ ]),
+ Y = gf([
+ 0x6658,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ 0x6666,
+ ]),
+ I = gf([
+ 0xa0b0,
+ 0x4a0e,
+ 0x1b27,
+ 0xc4ee,
+ 0xe478,
+ 0xad2f,
+ 0x1806,
+ 0x2f43,
+ 0xd7a7,
+ 0x3dfb,
+ 0x0099,
+ 0x2b4d,
+ 0xdf0b,
+ 0x4fc1,
+ 0x2480,
+ 0x2b83,
+ ]);
+
+ function vn(x, xi, y, yi, n) {
+ var i,
+ d = 0;
+ for (i = 0; i < n; i++) d |= x[xi + i] ^ y[yi + i];
+ return (1 & ((d - 1) >>> 8)) - 1;
+ }
+
+ function crypto_verify_16(x, xi, y, yi) {
+ return vn(x, xi, y, yi, 16);
+ }
+
+ function crypto_verify_32(x, xi, y, yi) {
+ return vn(x, xi, y, yi, 32);
+ }
+
+ function set25519(r, a) {
+ var i;
+ for (i = 0; i < 16; i++) r[i] = a[i] | 0;
+ }
+
+ function car25519(o) {
+ var i,
+ v,
+ c = 1;
+ for (i = 0; i < 16; i++) {
+ v = o[i] + c + 65535;
+ c = Math.floor(v / 65536);
+ o[i] = v - c * 65536;
+ }
+ o[0] += c - 1 + 37 * (c - 1);
+ }
+
+ function sel25519(p, q, b) {
+ var t,
+ c = ~(b - 1);
+ for (var i = 0; i < 16; i++) {
+ t = c & (p[i] ^ q[i]);
+ p[i] ^= t;
+ q[i] ^= t;
+ }
+ }
+
+ function pack25519(o, n) {
+ var i, j, b;
+ var m = gf(),
+ t = gf();
+ for (i = 0; i < 16; i++) t[i] = n[i];
+ car25519(t);
+ car25519(t);
+ car25519(t);
+ for (j = 0; j < 2; j++) {
+ m[0] = t[0] - 0xffed;
+ for (i = 1; i < 15; i++) {
+ m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
+ m[i - 1] &= 0xffff;
+ }
+ m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
+ b = (m[15] >> 16) & 1;
+ m[14] &= 0xffff;
+ sel25519(t, m, 1 - b);
+ }
+ for (i = 0; i < 16; i++) {
+ o[2 * i] = t[i] & 0xff;
+ o[2 * i + 1] = t[i] >> 8;
+ }
+ }
+
+ function neq25519(a, b) {
+ var c = new Uint8Array(32),
+ d = new Uint8Array(32);
+ pack25519(c, a);
+ pack25519(d, b);
+ return crypto_verify_32(c, 0, d, 0);
+ }
+
+ function par25519(a) {
+ var d = new Uint8Array(32);
+ pack25519(d, a);
+ return d[0] & 1;
+ }
+
+ function unpack25519(o, n) {
+ var i;
+ for (i = 0; i < 16; i++) o[i] = n[2 * i] + (n[2 * i + 1] << 8);
+ o[15] &= 0x7fff;
+ }
+
+ function A(o, a, b) {
+ for (var i = 0; i < 16; i++) o[i] = a[i] + b[i];
+ }
+
+ function Z(o, a, b) {
+ for (var i = 0; i < 16; i++) o[i] = a[i] - b[i];
+ }
+
+ function M(o, a, b) {
+ var v,
+ c,
+ t0 = 0,
+ t1 = 0,
+ t2 = 0,
+ t3 = 0,
+ t4 = 0,
+ t5 = 0,
+ t6 = 0,
+ t7 = 0,
+ t8 = 0,
+ t9 = 0,
+ t10 = 0,
+ t11 = 0,
+ t12 = 0,
+ t13 = 0,
+ t14 = 0,
+ t15 = 0,
+ t16 = 0,
+ t17 = 0,
+ t18 = 0,
+ t19 = 0,
+ t20 = 0,
+ t21 = 0,
+ t22 = 0,
+ t23 = 0,
+ t24 = 0,
+ t25 = 0,
+ t26 = 0,
+ t27 = 0,
+ t28 = 0,
+ t29 = 0,
+ t30 = 0,
+ b0 = b[0],
+ b1 = b[1],
+ b2 = b[2],
+ b3 = b[3],
+ b4 = b[4],
+ b5 = b[5],
+ b6 = b[6],
+ b7 = b[7],
+ b8 = b[8],
+ b9 = b[9],
+ b10 = b[10],
+ b11 = b[11],
+ b12 = b[12],
+ b13 = b[13],
+ b14 = b[14],
+ b15 = b[15];
+
+ v = a[0];
+ t0 += v * b0;
+ t1 += v * b1;
+ t2 += v * b2;
+ t3 += v * b3;
+ t4 += v * b4;
+ t5 += v * b5;
+ t6 += v * b6;
+ t7 += v * b7;
+ t8 += v * b8;
+ t9 += v * b9;
+ t10 += v * b10;
+ t11 += v * b11;
+ t12 += v * b12;
+ t13 += v * b13;
+ t14 += v * b14;
+ t15 += v * b15;
+ v = a[1];
+ t1 += v * b0;
+ t2 += v * b1;
+ t3 += v * b2;
+ t4 += v * b3;
+ t5 += v * b4;
+ t6 += v * b5;
+ t7 += v * b6;
+ t8 += v * b7;
+ t9 += v * b8;
+ t10 += v * b9;
+ t11 += v * b10;
+ t12 += v * b11;
+ t13 += v * b12;
+ t14 += v * b13;
+ t15 += v * b14;
+ t16 += v * b15;
+ v = a[2];
+ t2 += v * b0;
+ t3 += v * b1;
+ t4 += v * b2;
+ t5 += v * b3;
+ t6 += v * b4;
+ t7 += v * b5;
+ t8 += v * b6;
+ t9 += v * b7;
+ t10 += v * b8;
+ t11 += v * b9;
+ t12 += v * b10;
+ t13 += v * b11;
+ t14 += v * b12;
+ t15 += v * b13;
+ t16 += v * b14;
+ t17 += v * b15;
+ v = a[3];
+ t3 += v * b0;
+ t4 += v * b1;
+ t5 += v * b2;
+ t6 += v * b3;
+ t7 += v * b4;
+ t8 += v * b5;
+ t9 += v * b6;
+ t10 += v * b7;
+ t11 += v * b8;
+ t12 += v * b9;
+ t13 += v * b10;
+ t14 += v * b11;
+ t15 += v * b12;
+ t16 += v * b13;
+ t17 += v * b14;
+ t18 += v * b15;
+ v = a[4];
+ t4 += v * b0;
+ t5 += v * b1;
+ t6 += v * b2;
+ t7 += v * b3;
+ t8 += v * b4;
+ t9 += v * b5;
+ t10 += v * b6;
+ t11 += v * b7;
+ t12 += v * b8;
+ t13 += v * b9;
+ t14 += v * b10;
+ t15 += v * b11;
+ t16 += v * b12;
+ t17 += v * b13;
+ t18 += v * b14;
+ t19 += v * b15;
+ v = a[5];
+ t5 += v * b0;
+ t6 += v * b1;
+ t7 += v * b2;
+ t8 += v * b3;
+ t9 += v * b4;
+ t10 += v * b5;
+ t11 += v * b6;
+ t12 += v * b7;
+ t13 += v * b8;
+ t14 += v * b9;
+ t15 += v * b10;
+ t16 += v * b11;
+ t17 += v * b12;
+ t18 += v * b13;
+ t19 += v * b14;
+ t20 += v * b15;
+ v = a[6];
+ t6 += v * b0;
+ t7 += v * b1;
+ t8 += v * b2;
+ t9 += v * b3;
+ t10 += v * b4;
+ t11 += v * b5;
+ t12 += v * b6;
+ t13 += v * b7;
+ t14 += v * b8;
+ t15 += v * b9;
+ t16 += v * b10;
+ t17 += v * b11;
+ t18 += v * b12;
+ t19 += v * b13;
+ t20 += v * b14;
+ t21 += v * b15;
+ v = a[7];
+ t7 += v * b0;
+ t8 += v * b1;
+ t9 += v * b2;
+ t10 += v * b3;
+ t11 += v * b4;
+ t12 += v * b5;
+ t13 += v * b6;
+ t14 += v * b7;
+ t15 += v * b8;
+ t16 += v * b9;
+ t17 += v * b10;
+ t18 += v * b11;
+ t19 += v * b12;
+ t20 += v * b13;
+ t21 += v * b14;
+ t22 += v * b15;
+ v = a[8];
+ t8 += v * b0;
+ t9 += v * b1;
+ t10 += v * b2;
+ t11 += v * b3;
+ t12 += v * b4;
+ t13 += v * b5;
+ t14 += v * b6;
+ t15 += v * b7;
+ t16 += v * b8;
+ t17 += v * b9;
+ t18 += v * b10;
+ t19 += v * b11;
+ t20 += v * b12;
+ t21 += v * b13;
+ t22 += v * b14;
+ t23 += v * b15;
+ v = a[9];
+ t9 += v * b0;
+ t10 += v * b1;
+ t11 += v * b2;
+ t12 += v * b3;
+ t13 += v * b4;
+ t14 += v * b5;
+ t15 += v * b6;
+ t16 += v * b7;
+ t17 += v * b8;
+ t18 += v * b9;
+ t19 += v * b10;
+ t20 += v * b11;
+ t21 += v * b12;
+ t22 += v * b13;
+ t23 += v * b14;
+ t24 += v * b15;
+ v = a[10];
+ t10 += v * b0;
+ t11 += v * b1;
+ t12 += v * b2;
+ t13 += v * b3;
+ t14 += v * b4;
+ t15 += v * b5;
+ t16 += v * b6;
+ t17 += v * b7;
+ t18 += v * b8;
+ t19 += v * b9;
+ t20 += v * b10;
+ t21 += v * b11;
+ t22 += v * b12;
+ t23 += v * b13;
+ t24 += v * b14;
+ t25 += v * b15;
+ v = a[11];
+ t11 += v * b0;
+ t12 += v * b1;
+ t13 += v * b2;
+ t14 += v * b3;
+ t15 += v * b4;
+ t16 += v * b5;
+ t17 += v * b6;
+ t18 += v * b7;
+ t19 += v * b8;
+ t20 += v * b9;
+ t21 += v * b10;
+ t22 += v * b11;
+ t23 += v * b12;
+ t24 += v * b13;
+ t25 += v * b14;
+ t26 += v * b15;
+ v = a[12];
+ t12 += v * b0;
+ t13 += v * b1;
+ t14 += v * b2;
+ t15 += v * b3;
+ t16 += v * b4;
+ t17 += v * b5;
+ t18 += v * b6;
+ t19 += v * b7;
+ t20 += v * b8;
+ t21 += v * b9;
+ t22 += v * b10;
+ t23 += v * b11;
+ t24 += v * b12;
+ t25 += v * b13;
+ t26 += v * b14;
+ t27 += v * b15;
+ v = a[13];
+ t13 += v * b0;
+ t14 += v * b1;
+ t15 += v * b2;
+ t16 += v * b3;
+ t17 += v * b4;
+ t18 += v * b5;
+ t19 += v * b6;
+ t20 += v * b7;
+ t21 += v * b8;
+ t22 += v * b9;
+ t23 += v * b10;
+ t24 += v * b11;
+ t25 += v * b12;
+ t26 += v * b13;
+ t27 += v * b14;
+ t28 += v * b15;
+ v = a[14];
+ t14 += v * b0;
+ t15 += v * b1;
+ t16 += v * b2;
+ t17 += v * b3;
+ t18 += v * b4;
+ t19 += v * b5;
+ t20 += v * b6;
+ t21 += v * b7;
+ t22 += v * b8;
+ t23 += v * b9;
+ t24 += v * b10;
+ t25 += v * b11;
+ t26 += v * b12;
+ t27 += v * b13;
+ t28 += v * b14;
+ t29 += v * b15;
+ v = a[15];
+ t15 += v * b0;
+ t16 += v * b1;
+ t17 += v * b2;
+ t18 += v * b3;
+ t19 += v * b4;
+ t20 += v * b5;
+ t21 += v * b6;
+ t22 += v * b7;
+ t23 += v * b8;
+ t24 += v * b9;
+ t25 += v * b10;
+ t26 += v * b11;
+ t27 += v * b12;
+ t28 += v * b13;
+ t29 += v * b14;
+ t30 += v * b15;
+
+ t0 += 38 * t16;
+ t1 += 38 * t17;
+ t2 += 38 * t18;
+ t3 += 38 * t19;
+ t4 += 38 * t20;
+ t5 += 38 * t21;
+ t6 += 38 * t22;
+ t7 += 38 * t23;
+ t8 += 38 * t24;
+ t9 += 38 * t25;
+ t10 += 38 * t26;
+ t11 += 38 * t27;
+ t12 += 38 * t28;
+ t13 += 38 * t29;
+ t14 += 38 * t30; // t15 left as is
+
+ // first car
+ c = 1;
+ v = t0 + c + 65535;
+ c = Math.floor(v / 65536);
+ t0 = v - c * 65536;
+ v = t1 + c + 65535;
+ c = Math.floor(v / 65536);
+ t1 = v - c * 65536;
+ v = t2 + c + 65535;
+ c = Math.floor(v / 65536);
+ t2 = v - c * 65536;
+ v = t3 + c + 65535;
+ c = Math.floor(v / 65536);
+ t3 = v - c * 65536;
+ v = t4 + c + 65535;
+ c = Math.floor(v / 65536);
+ t4 = v - c * 65536;
+ v = t5 + c + 65535;
+ c = Math.floor(v / 65536);
+ t5 = v - c * 65536;
+ v = t6 + c + 65535;
+ c = Math.floor(v / 65536);
+ t6 = v - c * 65536;
+ v = t7 + c + 65535;
+ c = Math.floor(v / 65536);
+ t7 = v - c * 65536;
+ v = t8 + c + 65535;
+ c = Math.floor(v / 65536);
+ t8 = v - c * 65536;
+ v = t9 + c + 65535;
+ c = Math.floor(v / 65536);
+ t9 = v - c * 65536;
+ v = t10 + c + 65535;
+ c = Math.floor(v / 65536);
+ t10 = v - c * 65536;
+ v = t11 + c + 65535;
+ c = Math.floor(v / 65536);
+ t11 = v - c * 65536;
+ v = t12 + c + 65535;
+ c = Math.floor(v / 65536);
+ t12 = v - c * 65536;
+ v = t13 + c + 65535;
+ c = Math.floor(v / 65536);
+ t13 = v - c * 65536;
+ v = t14 + c + 65535;
+ c = Math.floor(v / 65536);
+ t14 = v - c * 65536;
+ v = t15 + c + 65535;
+ c = Math.floor(v / 65536);
+ t15 = v - c * 65536;
+ t0 += c - 1 + 37 * (c - 1);
+
+ // second car
+ c = 1;
+ v = t0 + c + 65535;
+ c = Math.floor(v / 65536);
+ t0 = v - c * 65536;
+ v = t1 + c + 65535;
+ c = Math.floor(v / 65536);
+ t1 = v - c * 65536;
+ v = t2 + c + 65535;
+ c = Math.floor(v / 65536);
+ t2 = v - c * 65536;
+ v = t3 + c + 65535;
+ c = Math.floor(v / 65536);
+ t3 = v - c * 65536;
+ v = t4 + c + 65535;
+ c = Math.floor(v / 65536);
+ t4 = v - c * 65536;
+ v = t5 + c + 65535;
+ c = Math.floor(v / 65536);
+ t5 = v - c * 65536;
+ v = t6 + c + 65535;
+ c = Math.floor(v / 65536);
+ t6 = v - c * 65536;
+ v = t7 + c + 65535;
+ c = Math.floor(v / 65536);
+ t7 = v - c * 65536;
+ v = t8 + c + 65535;
+ c = Math.floor(v / 65536);
+ t8 = v - c * 65536;
+ v = t9 + c + 65535;
+ c = Math.floor(v / 65536);
+ t9 = v - c * 65536;
+ v = t10 + c + 65535;
+ c = Math.floor(v / 65536);
+ t10 = v - c * 65536;
+ v = t11 + c + 65535;
+ c = Math.floor(v / 65536);
+ t11 = v - c * 65536;
+ v = t12 + c + 65535;
+ c = Math.floor(v / 65536);
+ t12 = v - c * 65536;
+ v = t13 + c + 65535;
+ c = Math.floor(v / 65536);
+ t13 = v - c * 65536;
+ v = t14 + c + 65535;
+ c = Math.floor(v / 65536);
+ t14 = v - c * 65536;
+ v = t15 + c + 65535;
+ c = Math.floor(v / 65536);
+ t15 = v - c * 65536;
+ t0 += c - 1 + 37 * (c - 1);
+
+ o[0] = t0;
+ o[1] = t1;
+ o[2] = t2;
+ o[3] = t3;
+ o[4] = t4;
+ o[5] = t5;
+ o[6] = t6;
+ o[7] = t7;
+ o[8] = t8;
+ o[9] = t9;
+ o[10] = t10;
+ o[11] = t11;
+ o[12] = t12;
+ o[13] = t13;
+ o[14] = t14;
+ o[15] = t15;
+ }
+
+ function S(o, a) {
+ M(o, a, a);
+ }
+
+ function inv25519(o, i) {
+ var c = gf();
+ var a;
+ for (a = 0; a < 16; a++) c[a] = i[a];
+ for (a = 253; a >= 0; a--) {
+ S(c, c);
+ if (a !== 2 && a !== 4) M(c, c, i);
+ }
+ for (a = 0; a < 16; a++) o[a] = c[a];
+ }
+
+ function pow2523(o, i) {
+ var c = gf();
+ var a;
+ for (a = 0; a < 16; a++) c[a] = i[a];
+ for (a = 250; a >= 0; a--) {
+ S(c, c);
+ if (a !== 1) M(c, c, i);
+ }
+ for (a = 0; a < 16; a++) o[a] = c[a];
+ }
+
+ function add(p, q) {
+ var a = gf(),
+ b = gf(),
+ c = gf(),
+ d = gf(),
+ e = gf(),
+ f = gf(),
+ g = gf(),
+ h = gf(),
+ t = gf();
+
+ Z(a, p[1], p[0]);
+ Z(t, q[1], q[0]);
+ M(a, a, t);
+ A(b, p[0], p[1]);
+ A(t, q[0], q[1]);
+ M(b, b, t);
+ M(c, p[3], q[3]);
+ M(c, c, D2);
+ M(d, p[2], q[2]);
+ A(d, d, d);
+ Z(e, b, a);
+ Z(f, d, c);
+ A(g, d, c);
+ A(h, b, a);
+
+ M(p[0], e, f);
+ M(p[1], h, g);
+ M(p[2], g, f);
+ M(p[3], e, h);
+ }
+
+ function cswap(p, q, b) {
+ var i;
+ for (i = 0; i < 4; i++) {
+ sel25519(p[i], q[i], b);
+ }
+ }
+
+ function pack(r, p) {
+ var tx = gf(),
+ ty = gf(),
+ zi = gf();
+ inv25519(zi, p[2]);
+ M(tx, p[0], zi);
+ M(ty, p[1], zi);
+ pack25519(r, ty);
+ r[31] ^= par25519(tx) << 7;
+ }
+
+ function scalarmult(p, q, s) {
+ var b, i;
+ set25519(p[0], gf0);
+ set25519(p[1], gf1);
+ set25519(p[2], gf1);
+ set25519(p[3], gf0);
+ for (i = 255; i >= 0; --i) {
+ b = (s[(i / 8) | 0] >> (i & 7)) & 1;
+ cswap(p, q, b);
+ add(q, p);
+ add(p, p);
+ cswap(p, q, b);
+ }
+ }
+
+ function scalarbase(p, s) {
+ var q = [gf(), gf(), gf(), gf()];
+ set25519(q[0], X);
+ set25519(q[1], Y);
+ set25519(q[2], gf1);
+ M(q[3], X, Y);
+ scalarmult(p, q, s);
+ }
+
+ //new functions for CN - scalar operations are handled externally
+ // this only handles curve operations, except for Hp()
+
+ //why do we negate points when unpacking them???
+ function ge_neg(pub) {
+ pub[31] ^= 0x80;
+ }
+
+ //res = s*G
+ function ge_scalarmult_base(s) {
+ var p = [gf(), gf(), gf(), gf()];
+ scalarbase(p, s);
+ var pk = new Uint8Array(32);
+ pack(pk, p);
+ return pk;
+ }
+
+ //res = s*P
+ function ge_scalarmult(P, s) {
+ var p = [gf(), gf(), gf(), gf()],
+ upk = [gf(), gf(), gf(), gf()],
+ res = new Uint8Array(32);
+ ge_neg(P);
+ if (unpackneg(upk, P) !== 0) throw Error("non-0 error on point decode");
+ scalarmult(p, upk, s);
+ pack(res, p);
+ return res;
+ }
+
+ //res = c*P + r*G
+ function ge_double_scalarmult_base_vartime(c, P, r) {
+ var uP = [gf(), gf(), gf(), gf()],
+ cP = [gf(), gf(), gf(), gf()],
+ rG = [gf(), gf(), gf(), gf()],
+ res = new Uint8Array(32);
+ ge_neg(P);
+ if (unpackneg(uP, P) !== 0) throw Error("non-0 error on point decode");
+ scalarmult(cP, uP, c);
+ scalarbase(rG, r);
+ add(rG, cP);
+ pack(res, rG);
+ return res;
+ }
+
+ //name changed to reflect not using precomp; res = r*Pb + c*I
+ function ge_double_scalarmult_postcomp_vartime(r, Pb, c, I) {
+ var uPb = [gf(), gf(), gf(), gf()],
+ uI = [gf(), gf(), gf(), gf()],
+ cI = [gf(), gf(), gf(), gf()],
+ rPb = [gf(), gf(), gf(), gf()],
+ res = new Uint8Array(32);
+ ge_neg(Pb);
+ if (unpackneg(uPb, Pb) !== 0)
+ throw Error("non-0 error on point decode");
+ scalarmult(rPb, uPb, r);
+ ge_neg(I);
+ if (unpackneg(uI, I) !== 0) throw Error("non-0 error on point decode");
+ scalarmult(cI, uI, c);
+ add(rPb, cI);
+ pack(res, rPb);
+ return res;
+ }
+
+ //res = P + Q
+ function ge_add(P, Q) {
+ var uP = [gf(), gf(), gf(), gf()],
+ uQ = [gf(), gf(), gf(), gf()],
+ res = new Uint8Array(32);
+ ge_neg(P);
+ ge_neg(Q);
+ if (unpackneg(uP, P) !== 0) throw Error("non-0 error on point decode");
+ if (unpackneg(uQ, Q) !== 0) throw Error("non-0 error on point decode");
+ add(uP, uQ);
+ pack(res, uP);
+ return res;
+ }
+
+ var L = new Float64Array([
+ 0xed,
+ 0xd3,
+ 0xf5,
+ 0x5c,
+ 0x1a,
+ 0x63,
+ 0x12,
+ 0x58,
+ 0xd6,
+ 0x9c,
+ 0xf7,
+ 0xa2,
+ 0xde,
+ 0xf9,
+ 0xde,
+ 0x14,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0x10,
+ ]);
+
+ function modL(r, x) {
+ var carry, i, j, k;
+ for (i = 63; i >= 32; --i) {
+ carry = 0;
+ for (j = i - 32, k = i - 12; j < k; ++j) {
+ x[j] += carry - 16 * x[i] * L[j - (i - 32)];
+ carry = (x[j] + 128) >> 8;
+ x[j] -= carry * 256;
+ }
+ x[j] += carry;
+ x[i] = 0;
+ }
+ carry = 0;
+ for (j = 0; j < 32; j++) {
+ x[j] += carry - (x[31] >> 4) * L[j];
+ carry = x[j] >> 8;
+ x[j] &= 255;
+ }
+ for (j = 0; j < 32; j++) x[j] -= carry * L[j];
+ for (i = 0; i < 32; i++) {
+ x[i + 1] += x[i] >> 8;
+ r[i] = x[i] & 255;
+ }
+ }
+
+ function reduce(r) {
+ var x = new Float64Array(64),
+ i;
+ for (i = 0; i < 64; i++) x[i] = r[i];
+ for (i = 0; i < 64; i++) r[i] = 0;
+ modL(r, x);
+ }
+
+ function unpackneg(r, p) {
+ var t = gf(),
+ chk = gf(),
+ num = gf(),
+ den = gf(),
+ den2 = gf(),
+ den4 = gf(),
+ den6 = gf();
+
+ set25519(r[2], gf1);
+ unpack25519(r[1], p);
+ S(num, r[1]);
+ M(den, num, D);
+ Z(num, num, r[2]);
+ A(den, r[2], den);
+
+ S(den2, den);
+ S(den4, den2);
+ M(den6, den4, den2);
+ M(t, den6, num);
+ M(t, t, den);
+
+ pow2523(t, t);
+ M(t, t, num);
+ M(t, t, den);
+ M(t, t, den);
+ M(r[0], t, den);
+
+ S(chk, r[0]);
+ M(chk, chk, den);
+ if (neq25519(chk, num)) M(r[0], r[0], I);
+
+ S(chk, r[0]);
+ M(chk, chk, den);
+ if (neq25519(chk, num)) return -1;
+
+ if (par25519(r[0]) === p[31] >> 7) Z(r[0], gf0, r[0]);
+
+ M(r[3], r[0], r[1]);
+ return 0;
+ }
+
+ nacl.ll = {
+ ge_scalarmult_base: ge_scalarmult_base,
+ ge_scalarmult: ge_scalarmult,
+ ge_double_scalarmult_base_vartime: ge_double_scalarmult_base_vartime,
+ ge_add: ge_add,
+ ge_double_scalarmult_postcomp_vartime: ge_double_scalarmult_postcomp_vartime,
+ };
+
+ /* High-level API */
+
+ function cleanup(arr) {
+ for (var i = 0; i < arr.length; i++) arr[i] = 0;
+ }
+
+ nacl.randomBytes = function(n) {
+ var b = new Uint8Array(n);
+ randombytes(b, n);
+ return b;
+ };
+
+ nacl.setPRNG = function(fn) {
+ randombytes = fn;
+ };
+
+ (function() {
+ // Initialize PRNG if environment provides CSPRNG.
+ // If not, methods calling randombytes will throw.
+ var crypto =
+ typeof self !== "undefined" ? self.crypto || self.msCrypto : null;
+ if (crypto && crypto.getRandomValues) {
+ // Browsers.
+ var QUOTA = 65536;
+ nacl.setPRNG(function(x, n) {
+ var i,
+ v = new Uint8Array(n);
+ for (i = 0; i < n; i += QUOTA) {
+ crypto.getRandomValues(
+ v.subarray(i, i + Math.min(n - i, QUOTA)),
+ );
+ }
+ for (i = 0; i < n; i++) x[i] = v[i];
+ cleanup(v);
+ });
+ } else if (typeof require !== "undefined") {
+ // Node.js.
+ crypto = require("crypto");
+ if (crypto && crypto.randomBytes) {
+ nacl.setPRNG(function(x, n) {
+ var i,
+ v = crypto.randomBytes(n);
+ for (i = 0; i < n; i++) x[i] = v[i];
+ cleanup(v);
+ });
+ }
+ }
+ })();
+})(
+ typeof module !== "undefined" && module.exports
+ ? module.exports
+ : (self.nacl = self.nacl || {}),
+);
diff --git a/cryptonote_utils/nettype.ts b/src/cryptonote_utils/nettype.ts
similarity index 83%
rename from cryptonote_utils/nettype.ts
rename to src/cryptonote_utils/nettype.ts
index d243b71..1a5a919 100644
--- a/cryptonote_utils/nettype.ts
+++ b/src/cryptonote_utils/nettype.ts
@@ -45,44 +45,34 @@ const __STAGENET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 25;
const __STAGENET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 36;
export function cryptonoteBase58PrefixForStandardAddressOn(nettype: NetType) {
- if (!nettype) {
- console.warn("Unexpected nil nettype");
- }
-
- if (nettype == NetType.MAINNET) {
+ if (nettype === NetType.MAINNET) {
return __MAINNET_CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
- } else if (nettype == NetType.TESTNET) {
+ } else if (nettype === NetType.TESTNET) {
return __TESTNET_CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
- } else if (nettype == NetType.STAGENET) {
+ } else if (nettype === NetType.STAGENET) {
return __STAGENET_CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
}
- throw "Illegal nettype";
+ throw Error("Illegal nettype");
}
export function cryptonoteBase58PrefixForIntegratedAddressOn(nettype: NetType) {
- if (!nettype) {
- console.warn("Unexpected nil nettype");
- }
- if (nettype == NetType.MAINNET) {
+ if (nettype === NetType.MAINNET) {
return __MAINNET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
- } else if (nettype == NetType.TESTNET) {
+ } else if (nettype === NetType.TESTNET) {
return __TESTNET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
- } else if (nettype == NetType.STAGENET) {
+ } else if (nettype === NetType.STAGENET) {
return __STAGENET_CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
}
- throw "Illegal nettype";
+ throw Error("Illegal nettype");
}
export function cryptonoteBase58PrefixForSubAddressOn(nettype: NetType) {
- if (!nettype) {
- console.warn("Unexpected nil nettype");
- }
- if (nettype == NetType.MAINNET) {
+ if (nettype === NetType.MAINNET) {
return __MAINNET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
- } else if (nettype == NetType.TESTNET) {
+ } else if (nettype === NetType.TESTNET) {
return __TESTNET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
- } else if (nettype == NetType.STAGENET) {
+ } else if (nettype === NetType.STAGENET) {
return __STAGENET_CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
}
- throw "Illegal nettype";
+ throw Error("Illegal nettype");
}
diff --git a/extern-types/keccakjs.d.ts b/src/extern-types/keccakjs.d.ts
similarity index 76%
rename from extern-types/keccakjs.d.ts
rename to src/extern-types/keccakjs.d.ts
index 5ad534d..751a1a2 100644
--- a/extern-types/keccakjs.d.ts
+++ b/src/extern-types/keccakjs.d.ts
@@ -2,6 +2,8 @@ declare module "keccakjs" {
type Message = Buffer | string;
class Hasher {
+ constructor(bitlength: number);
+
/**
* Update hash
*
@@ -12,7 +14,7 @@ declare module "keccakjs" {
/**
* Return hash in integer array.
*/
- digest(): number[];
+ digest(encoding?: "hex" | "binary"): string;
}
export = Hasher;
diff --git a/src/hostAPI/index.ts b/src/hostAPI/index.ts
new file mode 100644
index 0000000..39d9e4e
--- /dev/null
+++ b/src/hostAPI/index.ts
@@ -0,0 +1,2 @@
+export * from "./net_service_utils";
+export * from "./response_parser_utils";
diff --git a/hostAPI/net_service_utils.ts b/src/hostAPI/net_service_utils.ts
similarity index 100%
rename from hostAPI/net_service_utils.ts
rename to src/hostAPI/net_service_utils.ts
diff --git a/hostAPI/response_parser_utils/index.ts b/src/hostAPI/response_parser_utils/index.ts
similarity index 100%
rename from hostAPI/response_parser_utils/index.ts
rename to src/hostAPI/response_parser_utils/index.ts
diff --git a/hostAPI/response_parser_utils/response_parser_utils.ts b/src/hostAPI/response_parser_utils/response_parser_utils.ts
similarity index 95%
rename from hostAPI/response_parser_utils/response_parser_utils.ts
rename to src/hostAPI/response_parser_utils/response_parser_utils.ts
index c58adf1..5d05ff2 100644
--- a/hostAPI/response_parser_utils/response_parser_utils.ts
+++ b/src/hostAPI/response_parser_utils/response_parser_utils.ts
@@ -26,7 +26,7 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
import {
genKeyImage,
KeyImageCache,
@@ -88,16 +88,16 @@ export function parseAddressInfo(
);
if (!isKeyImageEqual(spent_output, key_image)) {
- total_sent = new JSBigInt(total_sent)
+ total_sent = new BigInt(total_sent)
.subtract(spent_output.amount)
.toString();
}
}
return {
- total_received: new JSBigInt(total_received),
- locked_balance: new JSBigInt(locked_balance),
- total_sent: new JSBigInt(total_sent),
+ total_received: new BigInt(total_received),
+ locked_balance: new BigInt(locked_balance),
+ total_sent: new BigInt(total_sent),
spent_outputs,
account_scanned_tx_height,
@@ -123,7 +123,7 @@ export function parseAddressTransactions(
scanned_block_height: account_scanned_block_height,
scanned_height: account_scanned_height,
start_height: account_scan_start_height,
- total_received,
+ /*total_received*/
transaction_height,
transactions,
} = normalizeAddressTransactions(data);
@@ -145,7 +145,7 @@ export function parseAddressTransactions(
);
if (!isKeyImageEqual(transaction.spent_outputs[j], keyImage)) {
- transaction.total_sent = new JSBigInt(
+ transaction.total_sent = new BigInt(
transaction.total_sent,
).subtract(transaction.spent_outputs[j].amount);
@@ -267,6 +267,6 @@ export function parseUnspentOuts(
return {
unspentOuts,
unusedOuts: [...unspentOuts],
- per_kb_fee: new JSBigInt(per_kb_fee),
+ per_kb_fee: new BigInt(per_kb_fee),
};
}
diff --git a/hostAPI/response_parser_utils/types.ts b/src/hostAPI/response_parser_utils/types.ts
similarity index 93%
rename from hostAPI/response_parser_utils/types.ts
rename to src/hostAPI/response_parser_utils/types.ts
index 4a77e78..5ea774a 100644
--- a/hostAPI/response_parser_utils/types.ts
+++ b/src/hostAPI/response_parser_utils/types.ts
@@ -1,4 +1,5 @@
-import { JSBigInt, Omit } from "types";
+import { Omit } from "types";
+import { BigInt } from "biginteger";
export interface SpentOutput {
amount: string;
@@ -35,8 +36,8 @@ export interface AddressTransactions {
export interface NormalizedTransaction
extends Required> {
- total_sent: JSBigInt;
- amount: JSBigInt;
+ total_sent: BigInt;
+ amount: BigInt;
approx_float_amount: number;
timestamp: Date;
}
diff --git a/hostAPI/response_parser_utils/utils.ts b/src/hostAPI/response_parser_utils/utils.ts
similarity index 91%
rename from hostAPI/response_parser_utils/utils.ts
rename to src/hostAPI/response_parser_utils/utils.ts
index f565e56..9fef1f9 100644
--- a/hostAPI/response_parser_utils/utils.ts
+++ b/src/hostAPI/response_parser_utils/utils.ts
@@ -1,4 +1,3 @@
-import monero_utils from "monero_utils/monero_cryptonote_utils_instance";
import {
AddressTransactions,
AddressTransactionsTx,
@@ -7,7 +6,9 @@ import {
AddressInfo,
UnspentOutput,
} from "./types";
-import { JSBigInt, Omit } from "types";
+import { Omit } from "types";
+import { BigInt } from "biginteger";
+import { formatMoney } from "cryptonote_utils/formatters";
export function isKeyImageEqual({ key_image }: SpentOutput, keyImage: string) {
return key_image === keyImage;
@@ -56,7 +57,7 @@ export function normalizeTransaction(
tx: AddressTransactionsTx,
): NormalizedTransaction {
const defaultObj: Omit = {
- amount: new JSBigInt(0),
+ amount: new BigInt(0),
approx_float_amount: 0,
hash: "",
height: 0,
@@ -66,7 +67,7 @@ export function normalizeTransaction(
mixin: 0,
spent_outputs: [] as SpentOutput[],
total_received: "0",
- total_sent: new JSBigInt(0),
+ total_sent: new BigInt(0),
unlock_time: 0,
payment_id: "",
};
@@ -75,7 +76,7 @@ export function normalizeTransaction(
...defaultObj,
...tx,
total_sent: tx.total_sent
- ? new JSBigInt(tx.total_sent)
+ ? new BigInt(tx.total_sent)
: defaultObj.total_sent,
timestamp: new Date(tx.timestamp),
};
@@ -92,18 +93,18 @@ export function zeroTransactionAmount({
total_received,
total_sent,
}: NormalizedTransaction) {
- return new JSBigInt(total_received).add(total_sent).compare(0) <= 0;
+ return new BigInt(total_received).add(total_sent).compare(0) <= 0;
}
export function calculateTransactionAmount({
total_received,
total_sent,
}: NormalizedTransaction) {
- return new JSBigInt(total_received).subtract(total_sent);
+ return new BigInt(total_received).subtract(total_sent);
}
export function estimateTransactionAmount({ amount }: NormalizedTransaction) {
- return parseFloat(monero_utils.formatMoney(amount));
+ return parseFloat(formatMoney(amount));
}
/**
diff --git a/monero_utils/monero_cryptonote_utils_instance.js b/src/index.ts
similarity index 84%
rename from monero_utils/monero_cryptonote_utils_instance.js
rename to src/index.ts
index b5a51c4..d760ce0 100644
--- a/monero_utils/monero_cryptonote_utils_instance.js
+++ b/src/index.ts
@@ -25,11 +25,8 @@
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-"use strict";
-//
-const monero_config = require("./monero_config");
-const cryptonote_utils = require("../cryptonote_utils/cryptonote_utils").cnUtil;
-const monero_cryptonote_utils_instance = cryptonote_utils(monero_config);
-//
-module.exports = monero_cryptonote_utils_instance;
+
+export * from "./biginteger";
+export * from "./cryptonote_utils";
+export * from "./hostAPI";
+export * from "./monero_utils";
diff --git a/src/monero_utils/index.ts b/src/monero_utils/index.ts
new file mode 100644
index 0000000..b86da03
--- /dev/null
+++ b/src/monero_utils/index.ts
@@ -0,0 +1,6 @@
+export * from "./sending_funds";
+export * from "./key_image_utils";
+export * from "./monero_config";
+export * from "./monero_paymentID_utils";
+export * from "./monero_txParsing_utils";
+export * from "./request_funds_uri_utils";
diff --git a/monero_utils/key_image_utils.ts b/src/monero_utils/key_image_utils.ts
similarity index 94%
rename from monero_utils/key_image_utils.ts
rename to src/monero_utils/key_image_utils.ts
index 2f4aa70..453ca8c 100644
--- a/monero_utils/key_image_utils.ts
+++ b/src/monero_utils/key_image_utils.ts
@@ -1,3 +1,5 @@
+import { generate_key_image } from "cryptonote_utils";
+
// Copyright (c) 2014-2018, MyMonero.com
//
// All rights reserved.
@@ -26,8 +28,6 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-const monero_utils = require("./monero_cryptonote_utils_instance");
-
// Managed caches - Can be used by apps which can't send a mutable_keyImagesByCacheKey
export type KeyImageCache = { [cacheIndex: string]: string };
@@ -62,7 +62,7 @@ export function genKeyImage(
return cachedKeyImage;
}
- const { key_image } = monero_utils.generate_key_image(
+ const { key_image } = generate_key_image(
txPubKey,
privViewKey,
pubSpendKey,
@@ -110,7 +110,7 @@ export function clearKeyImageCache(address: string) {
const cache = keyImagesByWalletId[cacheId];
if (cache) {
- throw "Key image cache still exists after deletion";
+ throw Error("Key image cache still exists after deletion");
}
}
@@ -122,7 +122,7 @@ function parseAddress(address: string) {
// NOTE: making the assumption that public_address is unique enough to identify a wallet for caching....
// FIXME: with subaddresses, is that still the case? would we need to split them up by subaddr anyway?
if (!address) {
- throw "Address does not exist";
+ throw Error("Address does not exist");
}
return address.toString();
diff --git a/monero_utils/monero_config.ts b/src/monero_utils/monero_config.ts
similarity index 92%
rename from monero_utils/monero_config.ts
rename to src/monero_utils/monero_config.ts
index a3d8fdc..9723682 100644
--- a/monero_utils/monero_config.ts
+++ b/src/monero_utils/monero_config.ts
@@ -1,4 +1,4 @@
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
// Copyright (c) 2014-2018, MyMonero.com
//
@@ -30,6 +30,7 @@ import { JSBigInt } from "types";
export interface XMRConfig {
readonly coinUnitPlaces: 12;
+ readonly coinUnits: BigInt;
readonly txMinConfirms: 10;
readonly coinSymbol: "XMR";
readonly openAliasPrefix: "xmr";
@@ -38,14 +39,18 @@ export interface XMRConfig {
readonly addressPrefix: 18;
readonly integratedAddressPrefix: 19;
readonly subaddressPrefix: 42;
- readonly dustThreshold: JSBigInt;
+ readonly dustThreshold: BigInt;
readonly maxBlockNumber: 500000000;
readonly avgBlockTime: 60;
}
+const coinUnitPlaces = 12;
+
export const config: XMRConfig = {
// Number of atomic units in one unit of currency. e.g. 12 => 10^12 = 1000000000000
- coinUnitPlaces: 12,
+ coinUnitPlaces,
+
+ coinUnits: new BigInt(10).pow(coinUnitPlaces),
// Minimum number of confirmations for a transaction to show as confirmed
txMinConfirms: 10,
@@ -69,7 +74,7 @@ export const config: XMRConfig = {
// Dust threshold in atomic units
// 2*10^9 used for choosing outputs/change - we decompose all the way down if the receiver wants now regardless of threshold
- dustThreshold: new JSBigInt("2000000000"),
+ dustThreshold: new BigInt("2000000000"),
// Maximum block number, used for tx unlock time
maxBlockNumber: 500000000,
diff --git a/monero_utils/monero_paymentID_utils.ts b/src/monero_utils/monero_paymentID_utils.ts
similarity index 93%
rename from monero_utils/monero_paymentID_utils.ts
rename to src/monero_utils/monero_paymentID_utils.ts
index dd7f537..1789455 100644
--- a/monero_utils/monero_paymentID_utils.ts
+++ b/src/monero_utils/monero_paymentID_utils.ts
@@ -1,3 +1,5 @@
+import { rand_8 } from "cryptonote_utils";
+
// Copyright (c) 2014-2018, MyMonero.com
//
// All rights reserved.
@@ -26,13 +28,11 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import monero_utils from "./monero_cryptonote_utils_instance";
-
export function makePaymentID() {
- return monero_utils.rand_8();
+ return rand_8();
}
-export function isValidOrNoPaymentID(pid?: string) {
+export function isValidOrNoPaymentID(pid?: string | null) {
if (!pid) {
return true;
}
diff --git a/monero_utils/monero_txParsing_utils.ts b/src/monero_utils/monero_txParsing_utils.ts
similarity index 60%
rename from monero_utils/monero_txParsing_utils.ts
rename to src/monero_utils/monero_txParsing_utils.ts
index e3f1f1c..fbc2e28 100644
--- a/monero_utils/monero_txParsing_utils.ts
+++ b/src/monero_utils/monero_txParsing_utils.ts
@@ -1,4 +1,5 @@
import { NormalizedTransaction } from "hostAPI/response_parser_utils/types";
+import moment from "moment";
// Copyright (c) 2014-2018, MyMonero.com
//
@@ -29,7 +30,6 @@ import { NormalizedTransaction } from "hostAPI/response_parser_utils/types";
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import { config } from "./monero_config";
-import monero_utils from "./monero_cryptonote_utils_instance";
export function IsTransactionConfirmed(
tx: NormalizedTransaction,
@@ -39,15 +39,58 @@ export function IsTransactionConfirmed(
}
export function IsTransactionUnlocked(
- tx: NormalizedTransaction,
+ { unlock_time }: NormalizedTransaction,
blockchainHeight: number,
) {
- return monero_utils.is_tx_unlocked(tx.unlock_time, blockchainHeight);
+ if (!config.maxBlockNumber) {
+ throw Error("Max block number is not set in config!");
+ }
+ if (unlock_time < config.maxBlockNumber) {
+ // unlock time is block height
+ return blockchainHeight >= unlock_time;
+ } else {
+ // unlock time is timestamp
+ const current_time = Math.round(new Date().getTime() / 1000);
+ return current_time >= unlock_time;
+ }
}
export function TransactionLockedReason(
- tx: NormalizedTransaction,
+ { unlock_time }: NormalizedTransaction,
blockchainHeight: number,
) {
- return monero_utils.tx_locked_reason(tx.unlock_time, blockchainHeight);
+ if (unlock_time < config.maxBlockNumber) {
+ // unlock time is block height
+ const numBlocks = unlock_time - blockchainHeight;
+ if (numBlocks <= 0) {
+ return "Transaction is unlocked";
+ }
+ const unlock_prediction = moment().add(
+ numBlocks * config.avgBlockTime,
+ "seconds",
+ );
+ return (
+ "Will be unlocked in " +
+ numBlocks +
+ " blocks, ~" +
+ unlock_prediction.fromNow(true) +
+ ", " +
+ unlock_prediction.calendar() +
+ ""
+ );
+ } else {
+ // unlock time is timestamp
+ const current_time = Math.round(new Date().getTime() / 1000);
+ const time_difference = unlock_time - current_time;
+ if (time_difference <= 0) {
+ return "Transaction is unlocked";
+ }
+ const unlock_moment = moment(unlock_time * 1000);
+ return (
+ "Will be unlocked " +
+ unlock_moment.fromNow() +
+ ", " +
+ unlock_moment.calendar()
+ );
+ }
}
diff --git a/monero_utils/request_funds_uri_utils.ts b/src/monero_utils/request_funds_uri_utils.ts
similarity index 94%
rename from monero_utils/request_funds_uri_utils.ts
rename to src/monero_utils/request_funds_uri_utils.ts
index 2e12950..277d054 100644
--- a/monero_utils/request_funds_uri_utils.ts
+++ b/src/monero_utils/request_funds_uri_utils.ts
@@ -27,10 +27,10 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import { config } from "./monero_config";
-import monero_utils from "./monero_cryptonote_utils_instance";
import { NetType } from "cryptonote_utils/nettype";
import { possibleOAAddress } from "./sending_funds/internal_libs/open_alias_lite";
import { Omit } from "types";
+import { decode_address } from "cryptonote_utils";
export enum URITypes {
addressAsFirstPathComponent = 1,
@@ -53,7 +53,7 @@ type FundRequestPayload = {
export function encodeFundRequest(args: FundRequestPayload) {
const address = args.address;
if (!address) {
- throw "missing address";
+ throw Error("missing address");
}
let mutable_uri = config.coinUriPrefix;
@@ -64,7 +64,7 @@ export function encodeFundRequest(args: FundRequestPayload) {
} else if (uriType === URITypes.addressAsFirstPathComponent) {
// nothing to do
} else {
- throw "Illegal args.uriType";
+ throw Error("Illegal args.uriType");
}
mutable_uri += address;
@@ -116,7 +116,7 @@ export function decodeFundRequest(
if (!str.startsWith(config.coinUriPrefix)) {
if (str.includes("?")) {
// fairly sure this is correct.. (just an extra failsafe/filter)
- throw "Unrecognized URI format";
+ throw Error("Unrecognized URI format");
}
if (possibleOAAddress(str)) {
@@ -126,9 +126,9 @@ export function decodeFundRequest(
}
try {
- monero_utils.decode_address(str, nettype);
+ decode_address(str, nettype);
} catch (e) {
- throw "No Monero request info";
+ throw Error("No Monero request info");
}
// then it looks like a monero address
@@ -141,7 +141,7 @@ export function decodeFundRequest(
const protocol = url.protocol;
if (protocol !== config.coinUriPrefix) {
- throw "Request URI has non-Monero protocol";
+ throw Error("Request URI has non-Monero protocol");
}
// it seems that if the URL has // in it, pathname will be empty, but host will contain the address instead
diff --git a/monero_utils/sending_funds/index.ts b/src/monero_utils/sending_funds/index.ts
similarity index 100%
rename from monero_utils/sending_funds/index.ts
rename to src/monero_utils/sending_funds/index.ts
diff --git a/monero_utils/sending_funds/internal_libs/amt_utils.ts b/src/monero_utils/sending_funds/internal_libs/amt_utils.ts
similarity index 80%
rename from monero_utils/sending_funds/internal_libs/amt_utils.ts
rename to src/monero_utils/sending_funds/internal_libs/amt_utils.ts
index 8df8573..5c3935c 100644
--- a/monero_utils/sending_funds/internal_libs/amt_utils.ts
+++ b/src/monero_utils/sending_funds/internal_libs/amt_utils.ts
@@ -1,4 +1,4 @@
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
/**
* @description Gets a starting total amount.
@@ -11,19 +11,19 @@ import { JSBigInt } from "types";
*
* @export
* @param {boolean} isSweeping
- * @param {JSBigInt} feelessTotal
- * @param {JSBigInt} networkFee
+ * @param {BigInt} feelessTotal
+ * @param {BigInt} networkFee
* @returns
*/
export function getBaseTotalAmount(
isSweeping: boolean,
- feelessTotal: JSBigInt,
- networkFee: JSBigInt,
+ feelessTotal: BigInt,
+ networkFee: BigInt,
) {
// const hostingService_chargeAmount = hostedMoneroAPIClient.HostingServiceChargeFor_transactionWithNetworkFee(attemptAt_network_minimumFee)
if (isSweeping) {
- return new JSBigInt("18450000000000000000"); //~uint64 max
+ return new BigInt("18450000000000000000"); //~uint64 max
} else {
return feelessTotal.add(
networkFee,
diff --git a/monero_utils/sending_funds/internal_libs/arr_utils.ts b/src/monero_utils/sending_funds/internal_libs/arr_utils.ts
similarity index 100%
rename from monero_utils/sending_funds/internal_libs/arr_utils.ts
rename to src/monero_utils/sending_funds/internal_libs/arr_utils.ts
diff --git a/monero_utils/sending_funds/internal_libs/async_node_api.ts b/src/monero_utils/sending_funds/internal_libs/async_node_api.ts
similarity index 94%
rename from monero_utils/sending_funds/internal_libs/async_node_api.ts
rename to src/monero_utils/sending_funds/internal_libs/async_node_api.ts
index c3a1cc1..0e52053 100644
--- a/monero_utils/sending_funds/internal_libs/async_node_api.ts
+++ b/src/monero_utils/sending_funds/internal_libs/async_node_api.ts
@@ -1,7 +1,7 @@
import { ViewSendKeys, Output, AmountOutput } from "./types";
import { Log } from "./logger";
import { ERR } from "./errors";
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
export class WrappedNodeApi {
private api: any;
@@ -18,7 +18,7 @@ export class WrappedNodeApi {
) {
type ResolveVal = {
unusedOuts: Output[];
- dynamicFeePerKB: JSBigInt;
+ dynamicFeePerKB: BigInt;
};
return new Promise((resolve, reject) => {
@@ -28,7 +28,7 @@ export class WrappedNodeApi {
err: Error,
_: Output[], // unspent outs, the original copy of unusedOuts
unusedOuts: Output[],
- dynamicFeePerKB: JSBigInt,
+ dynamicFeePerKB: BigInt,
) => {
if (err) {
return reject(err);
diff --git a/monero_utils/sending_funds/internal_libs/construct_tx_and_send/construct_tx_and_send.ts b/src/monero_utils/sending_funds/internal_libs/construct_tx_and_send/construct_tx_and_send.ts
similarity index 100%
rename from monero_utils/sending_funds/internal_libs/construct_tx_and_send/construct_tx_and_send.ts
rename to src/monero_utils/sending_funds/internal_libs/construct_tx_and_send/construct_tx_and_send.ts
diff --git a/monero_utils/sending_funds/internal_libs/construct_tx_and_send/index.ts b/src/monero_utils/sending_funds/internal_libs/construct_tx_and_send/index.ts
similarity index 100%
rename from monero_utils/sending_funds/internal_libs/construct_tx_and_send/index.ts
rename to src/monero_utils/sending_funds/internal_libs/construct_tx_and_send/index.ts
diff --git a/monero_utils/sending_funds/internal_libs/construct_tx_and_send/types.ts b/src/monero_utils/sending_funds/internal_libs/construct_tx_and_send/types.ts
similarity index 82%
rename from monero_utils/sending_funds/internal_libs/construct_tx_and_send/types.ts
rename to src/monero_utils/sending_funds/internal_libs/construct_tx_and_send/types.ts
index 228d4a3..c9715ed 100644
--- a/monero_utils/sending_funds/internal_libs/construct_tx_and_send/types.ts
+++ b/src/monero_utils/sending_funds/internal_libs/construct_tx_and_send/types.ts
@@ -2,13 +2,13 @@ import { WrappedNodeApi } from "../async_node_api";
import { NetType } from "cryptonote_utils/nettype";
import {
ViewSendKeys,
- JSBigInt,
ParsedTarget,
Pid,
Output,
AmountOutput,
} from "../types";
import { Status } from "../../status_update_constants";
+import { BigInt } from "biginteger";
export type GetFundTargetsAndFeeParams = {
senderAddress: string;
@@ -22,9 +22,9 @@ export type GetFundTargetsAndFeeParams = {
unusedOuts: Output[];
simplePriority: number;
- feelessTotal: JSBigInt;
- feePerKB: JSBigInt; // obtained from server, so passed in
- networkFee: JSBigInt;
+ feelessTotal: BigInt;
+ feePerKB: BigInt; // obtained from server, so passed in
+ networkFee: BigInt;
isSweeping: boolean;
isRingCT: boolean;
@@ -52,9 +52,9 @@ export type CreateTxAndAttemptToSendParams = {
usingOuts: Output[];
simplePriority: number;
- feelessTotal: JSBigInt;
- feePerKB: JSBigInt; // obtained from server, so passed in
- networkFee: JSBigInt;
+ feelessTotal: BigInt;
+ feePerKB: BigInt; // obtained from server, so passed in
+ networkFee: BigInt;
isSweeping: boolean;
isRingCT: boolean;
diff --git a/monero_utils/sending_funds/internal_libs/errors.ts b/src/monero_utils/sending_funds/internal_libs/errors.ts
similarity index 86%
rename from monero_utils/sending_funds/internal_libs/errors.ts
rename to src/monero_utils/sending_funds/internal_libs/errors.ts
index 6d4b219..36de8d2 100644
--- a/monero_utils/sending_funds/internal_libs/errors.ts
+++ b/src/monero_utils/sending_funds/internal_libs/errors.ts
@@ -1,6 +1,6 @@
-import { JSBigInt } from "types";
import { config } from "monero_utils/monero_config";
-import monero_utils from "monero_utils/monero_cryptonote_utils_instance";
+import { BigInt } from "biginteger";
+import { formatMoney } from "cryptonote_utils/formatters";
export namespace ERR {
export namespace RING {
@@ -29,10 +29,10 @@ export namespace ERR {
}
export namespace BAL {
- export function insuff(amtAvail: JSBigInt, requiredAmt: JSBigInt) {
+ export function insuff(amtAvail: BigInt, requiredAmt: BigInt) {
const { coinSymbol } = config;
- const amtAvailStr = monero_utils.formatMoney(amtAvail);
- const requiredAmtStr = monero_utils.formatMoney(requiredAmt);
+ const amtAvailStr = formatMoney(amtAvail);
+ const requiredAmtStr = formatMoney(requiredAmt);
const errStr = `Your spendable balance is too low. Have ${amtAvailStr} ${coinSymbol} spendable, need ${requiredAmtStr} ${coinSymbol}.`;
return Error(errStr);
}
diff --git a/monero_utils/sending_funds/internal_libs/fee_utils.ts b/src/monero_utils/sending_funds/internal_libs/fee_utils.ts
similarity index 66%
rename from monero_utils/sending_funds/internal_libs/fee_utils.ts
rename to src/monero_utils/sending_funds/internal_libs/fee_utils.ts
index 59bb523..68edfab 100644
--- a/monero_utils/sending_funds/internal_libs/fee_utils.ts
+++ b/src/monero_utils/sending_funds/internal_libs/fee_utils.ts
@@ -1,23 +1,23 @@
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
export const DEFAULT_FEE_PRIORITY = 1;
export function calculateFee(
- feePerKB: JSBigInt,
+ feePerKB: BigInt,
numOfBytes: number,
feeMultiplier: number,
) {
- const numberOf_kB = new JSBigInt((numOfBytes + 1023.0) / 1024.0); // i.e. ceil
+ const numberOf_kB = new BigInt((numOfBytes + 1023.0) / 1024.0); // i.e. ceil
return calculateFeeKb(feePerKB, numberOf_kB, feeMultiplier);
}
export function calculateFeeKb(
- feePerKB: JSBigInt,
- numOfBytes: JSBigInt | number,
+ feePerKB: BigInt,
+ numOfBytes: BigInt | number,
feeMultiplier: number,
) {
- const numberOf_kB = new JSBigInt(numOfBytes);
+ const numberOf_kB = new BigInt(numOfBytes);
const fee = feePerKB.multiply(feeMultiplier).multiply(numberOf_kB);
return fee;
@@ -29,7 +29,9 @@ export function multiplyFeePriority(prio: number) {
const priority = prio || DEFAULT_FEE_PRIORITY;
if (priority <= 0 || priority > fee_multiplier.length) {
- throw "fee_multiplier_for_priority: simple_priority out of bounds";
+ throw Error(
+ "fee_multiplier_for_priority: simple_priority out of bounds",
+ );
}
const priority_idx = priority - 1;
return fee_multiplier[priority_idx];
diff --git a/monero_utils/sending_funds/internal_libs/key_utils.ts b/src/monero_utils/sending_funds/internal_libs/key_utils.ts
similarity index 64%
rename from monero_utils/sending_funds/internal_libs/key_utils.ts
rename to src/monero_utils/sending_funds/internal_libs/key_utils.ts
index 00d11ef..88c7327 100644
--- a/monero_utils/sending_funds/internal_libs/key_utils.ts
+++ b/src/monero_utils/sending_funds/internal_libs/key_utils.ts
@@ -1,6 +1,6 @@
import { NetType } from "cryptonote_utils/nettype";
-import monero_utils from "monero_utils/monero_cryptonote_utils_instance";
import { Log } from "./logger";
+import { decode_address } from "cryptonote_utils";
export function getTargetPubViewKey(
encPid: boolean,
@@ -8,7 +8,7 @@ export function getTargetPubViewKey(
nettype: NetType,
): string | undefined {
if (encPid) {
- const key = monero_utils.decode_address(targetAddress, nettype).view;
+ const key = decode_address(targetAddress, nettype).view;
Log.Target.viewKey(key);
return key;
diff --git a/monero_utils/sending_funds/internal_libs/logger.ts b/src/monero_utils/sending_funds/internal_libs/logger.ts
similarity index 62%
rename from monero_utils/sending_funds/internal_libs/logger.ts
rename to src/monero_utils/sending_funds/internal_libs/logger.ts
index 3430b02..7ebea50 100644
--- a/monero_utils/sending_funds/internal_libs/logger.ts
+++ b/src/monero_utils/sending_funds/internal_libs/logger.ts
@@ -1,28 +1,35 @@
-import monero_utils from "monero_utils/monero_cryptonote_utils_instance";
import { ParsedTarget, Output } from "./types";
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
+import {
+ formatMoneySymbol,
+ formatMoneyFullSymbol,
+ formatMoney,
+ formatMoneyFull,
+ printDsts,
+} from "cryptonote_utils/formatters";
+import { SignedTransaction } from "cryptonote_utils";
export namespace Log {
export namespace Amount {
- export function beforeFee(feelessTotal: JSBigInt, isSweeping: boolean) {
+ export function beforeFee(feelessTotal: BigInt, isSweeping: boolean) {
const feeless_total = isSweeping
? "all"
- : monero_utils.formatMoney(feelessTotal);
+ : formatMoney(feelessTotal);
console.log(`💬 Total to send, before fee: ${feeless_total}`);
}
- export function change(changeAmount: JSBigInt) {
+ export function change(changeAmount: BigInt) {
console.log("changeAmount", changeAmount);
}
- export function changeAmountDivRem(amt: [JSBigInt, JSBigInt]) {
+ export function changeAmountDivRem(amt: [BigInt, BigInt]) {
console.log("💬 changeAmountDivRem", amt);
}
- export function toSelf(changeAmount: JSBigInt, selfAddress: string) {
+ export function toSelf(changeAmount: BigInt, selfAddress: string) {
console.log(
"Sending change of " +
- monero_utils.formatMoneySymbol(changeAmount) +
+ formatMoneySymbol(changeAmount) +
" to " +
selfAddress,
);
@@ -30,41 +37,41 @@ export namespace Log {
}
export namespace Fee {
- export function dynPerKB(dynFeePerKB: JSBigInt) {
+ export function dynPerKB(dynFeePerKB: BigInt) {
console.log(
"Received dynamic per kb fee",
- monero_utils.formatMoneySymbol(dynFeePerKB),
+ formatMoneySymbol(dynFeePerKB),
);
}
export function basedOnInputs(
- newNeededFee: JSBigInt,
+ newNeededFee: BigInt,
usingOuts: Output[],
) {
console.log(
"New fee: " +
- monero_utils.formatMoneySymbol(newNeededFee) +
+ formatMoneySymbol(newNeededFee) +
" for " +
usingOuts.length +
" inputs",
);
}
- export function belowDustThreshold(changeDivDustRemainder: JSBigInt) {
+ export function belowDustThreshold(changeDivDustRemainder: BigInt) {
console.log(
"💬 Miners will add change of " +
- monero_utils.formatMoneyFullSymbol(changeDivDustRemainder) +
+ formatMoneyFullSymbol(changeDivDustRemainder) +
" to transaction fee (below dust threshold)",
);
}
export function estLowerThanReal(
- estMinNetworkFee: JSBigInt,
- feeActuallyNeededByNetwork: JSBigInt,
+ estMinNetworkFee: BigInt,
+ feeActuallyNeededByNetwork: BigInt,
) {
console.log(
"💬 Need to reconstruct the tx with enough of a network fee. Previous fee: " +
- monero_utils.formatMoneyFull(estMinNetworkFee) +
+ formatMoneyFull(estMinNetworkFee) +
" New fee: " +
- monero_utils.formatMoneyFull(feeActuallyNeededByNetwork),
+ formatMoneyFull(feeActuallyNeededByNetwork),
);
console.log("Reconstructing tx....");
}
@@ -72,45 +79,40 @@ export namespace Log {
export function txKB(
txBlobBytes: number,
numOfKB: number,
- estMinNetworkFee: JSBigInt,
+ estMinNetworkFee: BigInt,
) {
console.log(
txBlobBytes +
" bytes <= " +
numOfKB +
" KB (current fee: " +
- monero_utils.formatMoneyFull(estMinNetworkFee) +
+ formatMoneyFull(estMinNetworkFee) +
")",
);
}
- export function successfulTx(finalNetworkFee: JSBigInt) {
+ export function successfulTx(finalNetworkFee: BigInt) {
console.log(
"💬 Successful tx generation, submitting tx. Going with final_networkFee of ",
- monero_utils.formatMoney(finalNetworkFee),
+ formatMoney(finalNetworkFee),
);
}
}
export namespace Balance {
- export function requiredBase(
- totalAmount: JSBigInt,
- isSweeping: boolean,
- ) {
+ export function requiredBase(totalAmount: BigInt, isSweeping: boolean) {
if (isSweeping) {
console.log("Balance required: all");
} else {
console.log(
- "Balance required: " +
- monero_utils.formatMoneySymbol(totalAmount),
+ "Balance required: " + formatMoneySymbol(totalAmount),
);
}
}
- export function requiredPostRct(totalAmount: JSBigInt) {
+ export function requiredPostRct(totalAmount: BigInt) {
console.log(
- "~ Balance required: " +
- monero_utils.formatMoneySymbol(totalAmount),
+ "~ Balance required: " + formatMoneySymbol(totalAmount),
);
}
}
@@ -126,7 +128,7 @@ export namespace Log {
export function display(out: Output) {
console.log(
"Using output: " +
- monero_utils.formatMoney(out.amount) +
+ formatMoney(out.amount) +
" - " +
JSON.stringify(out),
);
@@ -140,12 +142,12 @@ export namespace Log {
export function fullDisplay(fundTargets: ParsedTarget[]) {
console.log("Destinations: ");
- monero_utils.printDsts(fundTargets);
+ printDsts(fundTargets);
}
export function displayDecomposed(splitDestinations: ParsedTarget[]) {
console.log("Decomposed destinations:");
- monero_utils.printDsts(splitDestinations);
+ printDsts(splitDestinations);
}
export function viewKey(viewKey: string) {
@@ -154,7 +156,7 @@ export namespace Log {
}
export namespace Transaction {
- export function signed(signedTx) {
+ export function signed(signedTx: SignedTransaction) {
console.log("signed tx: ", JSON.stringify(signedTx));
}
export function serializedAndHash(
@@ -167,10 +169,10 @@ export namespace Log {
}
export namespace SelectOutsAndAmtForMix {
- export function target(targetAmount: JSBigInt) {
+ export function target(targetAmount: BigInt) {
console.log(
"Selecting outputs to use. target: " +
- monero_utils.formatMoney(targetAmount),
+ formatMoney(targetAmount),
);
}
@@ -192,11 +194,11 @@ export namespace Log {
}
}
- export function usingOut(outAmount: JSBigInt, out: Output) {
+ export function usingOut(outAmount: BigInt, out: Output) {
console.log(
- `Using output: ${monero_utils.formatMoney(
- outAmount,
- )} - ${JSON.stringify(out)}`,
+ `Using output: ${formatMoney(outAmount)} - ${JSON.stringify(
+ out,
+ )}`,
);
}
}
diff --git a/monero_utils/sending_funds/internal_libs/open_alias_lite.ts b/src/monero_utils/sending_funds/internal_libs/open_alias_lite.ts
similarity index 100%
rename from monero_utils/sending_funds/internal_libs/open_alias_lite.ts
rename to src/monero_utils/sending_funds/internal_libs/open_alias_lite.ts
diff --git a/monero_utils/sending_funds/internal_libs/output_selection.ts b/src/monero_utils/sending_funds/internal_libs/output_selection.ts
similarity index 89%
rename from monero_utils/sending_funds/internal_libs/output_selection.ts
rename to src/monero_utils/sending_funds/internal_libs/output_selection.ts
index 9fb0b53..085a844 100644
--- a/monero_utils/sending_funds/internal_libs/output_selection.ts
+++ b/src/monero_utils/sending_funds/internal_libs/output_selection.ts
@@ -2,17 +2,17 @@ import { Output } from "./types";
import { popRandElement } from "./arr_utils";
import { Log } from "./logger";
import { config } from "monero_utils/monero_config";
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
export function selectOutputsAndAmountForMixin(
- targetAmount: JSBigInt,
+ targetAmount: BigInt,
unusedOuts: Output[],
isRingCT: boolean,
sweeping: boolean,
) {
Log.SelectOutsAndAmtForMix.target(targetAmount);
- let usingOutsAmount = new JSBigInt(0);
+ let usingOutsAmount = new BigInt(0);
const usingOuts: Output[] = [];
const remainingUnusedOuts = unusedOuts.slice(); // take copy so as to prevent issue if we must re-enter tx building fn if fee too low after building
while (
@@ -24,7 +24,7 @@ export function selectOutputsAndAmountForMixin(
// out.rct is set by the server
continue; // skip rct outputs if not creating rct tx
}
- const outAmount = new JSBigInt(out.amount);
+ const outAmount = new BigInt(out.amount);
if (outAmount.compare(config.dustThreshold) < 0) {
// amount is dusty..
if (!sweeping) {
diff --git a/monero_utils/sending_funds/internal_libs/parse_target.ts b/src/monero_utils/sending_funds/internal_libs/parse_target.ts
similarity index 84%
rename from monero_utils/sending_funds/internal_libs/parse_target.ts
rename to src/monero_utils/sending_funds/internal_libs/parse_target.ts
index b2abf13..4bdd409 100644
--- a/monero_utils/sending_funds/internal_libs/parse_target.ts
+++ b/src/monero_utils/sending_funds/internal_libs/parse_target.ts
@@ -1,9 +1,9 @@
import { ParsedTarget, RawTarget } from "./types";
import { NetType } from "cryptonote_utils/nettype";
import { ERR } from "./errors";
-import monero_utils from "monero_utils/monero_cryptonote_utils_instance";
import { possibleOAAddress } from "./open_alias_lite";
-import { JSBigInt } from "types";
+import { decode_address } from "cryptonote_utils";
+import { parseMoney } from "cryptonote_utils/formatters";
/**
* @description Map through the provided targets and normalize each address/amount pair
@@ -35,13 +35,13 @@ export function parseTargets(
const amountStr = amount.toString();
try {
- monero_utils.decode_address(address, nettype);
+ decode_address(address, nettype);
} catch (e) {
throw ERR.PARSE_TRGT.decodeAddress(address, e);
}
try {
- const parsedAmount: JSBigInt = monero_utils.parseMoney(amountStr);
+ const parsedAmount = parseMoney(amountStr);
return { address, amount: parsedAmount };
} catch (e) {
throw ERR.PARSE_TRGT.amount(amountStr, e);
diff --git a/monero_utils/sending_funds/internal_libs/pid_utils.ts b/src/monero_utils/sending_funds/internal_libs/pid_utils.ts
similarity index 80%
rename from monero_utils/sending_funds/internal_libs/pid_utils.ts
rename to src/monero_utils/sending_funds/internal_libs/pid_utils.ts
index 1302a11..9510bdc 100644
--- a/monero_utils/sending_funds/internal_libs/pid_utils.ts
+++ b/src/monero_utils/sending_funds/internal_libs/pid_utils.ts
@@ -1,7 +1,7 @@
-import monero_utils from "monero_utils/monero_cryptonote_utils_instance";
-import monero_paymentID_utils from "monero_utils/monero_paymentID_utils";
+import { isValidOrNoPaymentID } from "monero_utils/monero_paymentID_utils";
import { NetType } from "cryptonote_utils/nettype";
import { ERR } from "./errors";
+import { decode_address, is_subaddress } from "cryptonote_utils";
/**
*
@@ -35,13 +35,13 @@ export function checkAddressAndPidValidity(
let retPid = pid;
let encryptPid = false;
- const decodedAddress = monero_utils.decode_address(address, nettype);
+ const decodedAddress = decode_address(address, nettype);
// assert that the target address is not of type integrated nor subaddress
// if a payment id is included
if (retPid) {
if (decodedAddress.intPaymentId) {
throw ERR.PID.NO_INTEG_ADDR;
- } else if (monero_utils.is_subaddress(address, nettype)) {
+ } else if (is_subaddress(address, nettype)) {
throw ERR.PID.NO_SUB_ADDR;
}
}
@@ -52,7 +52,7 @@ export function checkAddressAndPidValidity(
if (decodedAddress.intPaymentId) {
retPid = decodedAddress.intPaymentId;
encryptPid = true;
- } else if (!monero_paymentID_utils.IsValidPaymentIDOrNoPaymentID(retPid)) {
+ } else if (!isValidOrNoPaymentID(retPid)) {
throw ERR.PID.INVAL;
}
diff --git a/monero_utils/sending_funds/internal_libs/tx_utils/index.ts b/src/monero_utils/sending_funds/internal_libs/tx_utils/index.ts
similarity index 100%
rename from monero_utils/sending_funds/internal_libs/tx_utils/index.ts
rename to src/monero_utils/sending_funds/internal_libs/tx_utils/index.ts
diff --git a/monero_utils/sending_funds/internal_libs/tx_utils/tx_utils.ts b/src/monero_utils/sending_funds/internal_libs/tx_utils/tx_utils.ts
similarity index 90%
rename from monero_utils/sending_funds/internal_libs/tx_utils/tx_utils.ts
rename to src/monero_utils/sending_funds/internal_libs/tx_utils/tx_utils.ts
index 9929168..b6c2bd7 100644
--- a/monero_utils/sending_funds/internal_libs/tx_utils/tx_utils.ts
+++ b/src/monero_utils/sending_funds/internal_libs/tx_utils/tx_utils.ts
@@ -1,4 +1,3 @@
-import monero_utils from "monero_utils/monero_cryptonote_utils_instance";
import { config } from "monero_utils/monero_config";
import { getTargetPubViewKey } from "../key_utils";
import {
@@ -12,7 +11,18 @@ import { Log } from "../logger";
import { popRandElement } from "../arr_utils";
import { calculateFee, multiplyFeePriority } from "../fee_utils";
import { ParsedTarget } from "../types";
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
+import {
+ estimateRctSize,
+ random_scalar,
+ create_address,
+ create_transaction,
+ serialize_tx,
+ cn_fast_hash,
+ serialize_rct_tx_with_hash,
+ SignedTransaction,
+} from "cryptonote_utils";
+import { decompose_tx_destinations } from "cryptonote_utils/formatters";
// #region totalAmtAndEstFee
@@ -65,7 +75,7 @@ function estRctFeeAndAmt(params: EstRctFeeAndAmtParams) {
let feeBasedOnOuts = calculateFee(
feePerKB,
- monero_utils.estimateRctSize(usingOuts.length, mixin, 2),
+ estimateRctSize(usingOuts.length, mixin, 2),
multiplyFeePriority(simplePriority),
);
@@ -82,7 +92,7 @@ function estRctFeeAndAmt(params: EstRctFeeAndAmtParams) {
return { totalAmount, newFee };
}
-function estRctSwpingAmt(usingOutsAmount: JSBigInt, fee: JSBigInt) {
+function estRctSwpingAmt(usingOutsAmount: BigInt, fee: BigInt) {
/*
// When/if sending to multiple destinations supported, uncomment and port this:
if (dsts.length !== 1) {
@@ -107,7 +117,7 @@ function estRctSwpingAmt(usingOutsAmount: JSBigInt, fee: JSBigInt) {
return [totalAmount, fee];
}
-function estRctNonSwpAmt(params: EstRctFeeAndAmtParams, fee: JSBigInt) {
+function estRctNonSwpAmt(params: EstRctFeeAndAmtParams, fee: BigInt) {
const {
mixin,
remainingUnusedOuts,
@@ -141,7 +151,7 @@ function estRctNonSwpAmt(params: EstRctFeeAndAmtParams, fee: JSBigInt) {
// and recalculate invalidated values
newFee = calculateFee(
feePerKB,
- monero_utils.estimateRctSize(usingOuts.length, mixin, 2),
+ estimateRctSize(usingOuts.length, mixin, 2),
multiplyFeePriority(simplePriority),
);
currTotalAmount = feelessTotal.add(newFee);
@@ -267,16 +277,14 @@ export function validateAndConstructFundTargets(
if (isRingCT) {
// then create random destination to keep 2 outputs always in case of 0 change
// so we dont create 1 output (outlier)
- const fakeAddress = monero_utils.create_address(
- monero_utils.random_scalar(),
- nettype,
- ).public_addr;
+ const fakeAddress = create_address(random_scalar(), nettype)
+ .public_addr;
Log.Output.uniformity(fakeAddress);
fundTargets.push({
address: fakeAddress,
- amount: JSBigInt.ZERO,
+ amount: BigInt.ZERO,
});
}
}
@@ -327,13 +335,13 @@ function makeSignedTx(params: ConstructTxParams) {
nettype,
);
- const splitDestinations: ParsedTarget[] = monero_utils.decompose_tx_destinations(
+ const splitDestinations: ParsedTarget[] = decompose_tx_destinations(
fundTargets,
isRingCT,
);
Log.Target.displayDecomposed(splitDestinations);
- const signedTx = monero_utils.create_transaction(
+ const signedTx = create_transaction(
senderPublicKeys,
senderPrivateKeys,
splitDestinations,
@@ -357,7 +365,7 @@ function makeSignedTx(params: ConstructTxParams) {
}
}
-function getSerializedTxAndHash(signedTx) {
+function getSerializedTxAndHash(signedTx: SignedTransaction) {
type ReturnVal = {
serializedSignedTx: string;
txHash: string;
@@ -365,8 +373,8 @@ function getSerializedTxAndHash(signedTx) {
// pre rct
if (signedTx.version === 1) {
- const serializedSignedTx = monero_utils.serialize_tx(signedTx);
- const txHash = monero_utils.cn_fast_hash(serializedSignedTx);
+ const serializedSignedTx = serialize_tx(signedTx);
+ const txHash = cn_fast_hash(serializedSignedTx);
const ret: ReturnVal = {
serializedSignedTx,
@@ -379,7 +387,7 @@ function getSerializedTxAndHash(signedTx) {
}
// rct
else {
- const { raw, hash } = monero_utils.serialize_rct_tx_with_hash(signedTx);
+ const { raw, hash } = serialize_rct_tx_with_hash(signedTx);
const ret: ReturnVal = {
serializedSignedTx: raw,
@@ -392,7 +400,7 @@ function getSerializedTxAndHash(signedTx) {
}
}
-function getTxSize(serializedSignedTx: string, estMinNetworkFee: JSBigInt) {
+function getTxSize(serializedSignedTx: string, estMinNetworkFee: BigInt) {
// work out per-kb fee for transaction and verify that it's enough
const txBlobBytes = serializedSignedTx.length / 2;
let numOfKB = Math.floor(txBlobBytes / 1024);
diff --git a/monero_utils/sending_funds/internal_libs/tx_utils/types.ts b/src/monero_utils/sending_funds/internal_libs/tx_utils/types.ts
similarity index 68%
rename from monero_utils/sending_funds/internal_libs/tx_utils/types.ts
rename to src/monero_utils/sending_funds/internal_libs/tx_utils/types.ts
index f6fc0f0..b7a577c 100644
--- a/monero_utils/sending_funds/internal_libs/tx_utils/types.ts
+++ b/src/monero_utils/sending_funds/internal_libs/tx_utils/types.ts
@@ -6,7 +6,7 @@ import {
Output,
AmountOutput,
} from "../types";
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
export type ConstructTxParams = {
senderPublicKeys: ViewSendKeys;
@@ -22,7 +22,7 @@ export type ConstructTxParams = {
mixin: number;
usingOuts: Output[];
- networkFee: JSBigInt;
+ networkFee: BigInt;
isRingCT: boolean;
@@ -30,17 +30,17 @@ export type ConstructTxParams = {
};
export type TotalAmtAndEstFeeParams = {
- usingOutsAmount: JSBigInt;
- baseTotalAmount: JSBigInt;
+ usingOutsAmount: BigInt;
+ baseTotalAmount: BigInt;
mixin: number;
remainingUnusedOuts: Output[];
usingOuts: Output[];
simplePriority: number;
- feelessTotal: JSBigInt;
- feePerKB: JSBigInt; // obtained from server, so passed in
- networkFee: JSBigInt;
+ feelessTotal: BigInt;
+ feePerKB: BigInt; // obtained from server, so passed in
+ networkFee: BigInt;
isSweeping: boolean;
isRingCT: boolean;
@@ -48,14 +48,14 @@ export type TotalAmtAndEstFeeParams = {
export type EstRctFeeAndAmtParams = {
mixin: number;
- usingOutsAmount: JSBigInt;
+ usingOutsAmount: BigInt;
remainingUnusedOuts: Output[];
usingOuts: Output[];
simplePriority: number;
- feelessTotal: JSBigInt;
- feePerKB: JSBigInt; // obtained from server, so passed in
- networkFee: JSBigInt;
+ feelessTotal: BigInt;
+ feePerKB: BigInt; // obtained from server, so passed in
+ networkFee: BigInt;
isSweeping: boolean;
};
@@ -64,9 +64,9 @@ export type ConstructFundTargetsParams = {
senderAddress: string;
targetAddress: string;
- feelessTotal: JSBigInt;
- totalAmount: JSBigInt;
- usingOutsAmount: JSBigInt;
+ feelessTotal: BigInt;
+ totalAmount: BigInt;
+ usingOutsAmount: BigInt;
isSweeping: boolean;
isRingCT: boolean;
diff --git a/monero_utils/sending_funds/internal_libs/types.ts b/src/monero_utils/sending_funds/internal_libs/types.ts
similarity index 92%
rename from monero_utils/sending_funds/internal_libs/types.ts
rename to src/monero_utils/sending_funds/internal_libs/types.ts
index e0ddf1a..e8d45d5 100644
--- a/monero_utils/sending_funds/internal_libs/types.ts
+++ b/src/monero_utils/sending_funds/internal_libs/types.ts
@@ -1,4 +1,4 @@
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
export type ViewSendKeys = {
view: string;
@@ -11,7 +11,7 @@ export type RawTarget = {
export type ParsedTarget = {
address: string;
- amount: JSBigInt;
+ amount: BigInt;
};
export type Pid = string | null;
diff --git a/monero_utils/sending_funds/mixin_utils.ts b/src/monero_utils/sending_funds/mixin_utils.ts
similarity index 100%
rename from monero_utils/sending_funds/mixin_utils.ts
rename to src/monero_utils/sending_funds/mixin_utils.ts
diff --git a/monero_utils/sending_funds/sending_funds.ts b/src/monero_utils/sending_funds/sending_funds.ts
similarity index 95%
rename from monero_utils/sending_funds/sending_funds.ts
rename to src/monero_utils/sending_funds/sending_funds.ts
index d3d60b6..28f57f8 100644
--- a/monero_utils/sending_funds/sending_funds.ts
+++ b/src/monero_utils/sending_funds/sending_funds.ts
@@ -27,7 +27,6 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-import monero_utils from "monero_utils/monero_cryptonote_utils_instance";
import { NetType } from "cryptonote_utils/nettype";
import { RawTarget, Pid, ViewSendKeys } from "./internal_libs/types";
import {
@@ -46,11 +45,13 @@ import {
getRestOfTxData,
createTxAndAttemptToSend,
} from "./internal_libs/construct_tx_and_send";
-import { JSBigInt } from "types";
+import { BigInt } from "biginteger";
+import { estimateRctSize } from "cryptonote_utils";
+import { formatMoneyFull } from "cryptonote_utils/formatters";
export function estimatedTransactionNetworkFee(
nonZeroMixin: number,
- feePerKB: JSBigInt,
+ feePerKB: BigInt,
simplePriority: number,
) {
const numOfInputs = 2; // this might change -- might select inputs
@@ -58,7 +59,7 @@ export function estimatedTransactionNetworkFee(
1 /*dest*/ + 1 /*change*/ + 0; /*no mymonero fee presently*/
// TODO: update est tx size for bulletproofs
// TODO: normalize est tx size fn naming
- const estimatedTxSize = monero_utils.estimateRctSize(
+ const estimatedTxSize = estimateRctSize(
numOfInputs,
nonZeroMixin,
numOfOutputs,
@@ -77,7 +78,7 @@ export type SendFundsRet = {
sentAmount: number;
pid: Pid;
txHash: string;
- txFee: JSBigInt;
+ txFee: BigInt;
};
export async function SendFunds(
@@ -118,7 +119,7 @@ export async function SendFunds(
}
const { address, amount } = singleTarget;
- const feelessTotal = new JSBigInt(amount);
+ const feelessTotal = new BigInt(amount);
Log.Amount.beforeFee(feelessTotal, isSweeping);
@@ -228,7 +229,7 @@ export async function SendFunds(
if (success) {
const sentAmount = isSweeping
- ? parseFloat(monero_utils.formatMoneyFull(feelessTotal))
+ ? parseFloat(formatMoneyFull(feelessTotal))
: targetAmount;
return {
diff --git a/monero_utils/sending_funds/status_update_constants.ts b/src/monero_utils/sending_funds/status_update_constants.ts
similarity index 100%
rename from monero_utils/sending_funds/status_update_constants.ts
rename to src/monero_utils/sending_funds/status_update_constants.ts
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..49823a0
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,11 @@
+export type Omit = Pick>;
+
+export interface SecretCommitment {
+ x: string;
+ a: string;
+}
+
+export interface MixCommitment {
+ dest: string;
+ mask: string;
+}
diff --git a/tsconfig.json b/tsconfig.json
index cdf00ab..9e9a074 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,7 +3,8 @@
"target": "es2017",
"sourceMap": false,
"module": "commonjs",
- "baseUrl": "./",
+ "baseUrl": "src",
+ "outDir": "dist",
"lib": ["es2017", "dom"],
"allowSyntheticDefaultImports": true,
@@ -20,5 +21,5 @@
"pretty": true,
"resolveJsonModule": true
},
- "include": ["./src/", "./__test__/", "examples", "./"]
+ "include": ["./src/", "./__test__/", "examples"]
}
diff --git a/types.ts b/types.ts
deleted file mode 100644
index 5bd49bc..0000000
--- a/types.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import BigInt = require("cryptonote_utils/biginteger");
-export const JSBigInt = BigInt.BigInteger;
-
-export type JSBigInt = BigInt.BigInteger;
-
-export type Omit = Pick>;
diff --git a/yarn.lock b/yarn.lock
index 7a7133a..043d6a3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -77,6 +77,13 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
+anymatch@^1.3.0:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a"
+ dependencies:
+ micromatch "^2.1.5"
+ normalize-path "^2.0.0"
+
anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -129,6 +136,18 @@ array-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
+array-filter@~0.0.0:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
+
+array-map@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
+
+array-reduce@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
+
array-unique@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
@@ -157,6 +176,10 @@ astral-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
+async-each@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
+
async-limiter@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
@@ -246,6 +269,13 @@ babel-jest@^23.0.1:
babel-plugin-istanbul "^4.1.6"
babel-preset-jest "^23.0.1"
+babel-jest@^23.4.0:
+ version "23.4.0"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.4.0.tgz#22c34c392e2176f6a4c367992a7fcff69d2e8557"
+ dependencies:
+ babel-plugin-istanbul "^4.1.6"
+ babel-preset-jest "^23.2.0"
+
babel-messages@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
@@ -265,10 +295,21 @@ babel-plugin-jest-hoist@^23.0.1:
version "23.0.1"
resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.0.1.tgz#eaa11c964563aea9c21becef2bdf7853f7f3c148"
+babel-plugin-jest-hoist@^23.2.0:
+ version "23.2.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167"
+
babel-plugin-syntax-object-rest-spread@^6.13.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
+babel-preset-jest@^23.0.0, babel-preset-jest@^23.2.0:
+ version "23.2.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz#8ec7a03a138f001a1a8fb1e8113652bf1a55da46"
+ dependencies:
+ babel-plugin-jest-hoist "^23.2.0"
+ babel-plugin-syntax-object-rest-spread "^6.13.0"
+
babel-preset-jest@^23.0.1:
version "23.0.1"
resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-23.0.1.tgz#631cc545c6cf021943013bcaf22f45d87fe62198"
@@ -288,7 +329,7 @@ babel-register@^6.26.0:
mkdirp "^0.5.1"
source-map-support "^0.4.15"
-babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
@@ -305,7 +346,7 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
babylon "^6.18.0"
lodash "^4.17.4"
-babel-traverse@^6.18.0, babel-traverse@^6.26.0:
+babel-traverse@^6.0.0, babel-traverse@^6.18.0, babel-traverse@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
dependencies:
@@ -319,7 +360,7 @@ babel-traverse@^6.18.0, babel-traverse@^6.26.0:
invariant "^2.2.2"
lodash "^4.17.4"
-babel-types@^6.18.0, babel-types@^6.26.0:
+babel-types@^6.0.0, babel-types@^6.18.0, babel-types@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
dependencies:
@@ -354,6 +395,10 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
+binary-extensions@^1.0.0:
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
+
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -388,7 +433,7 @@ browser-process-hrtime@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e"
-browser-resolve@^1.11.2:
+browser-resolve@^1.11.2, browser-resolve@^1.11.3:
version "1.11.3"
resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6"
dependencies:
@@ -475,6 +520,21 @@ chalk@^2.0.0, chalk@^2.0.1:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
+chokidar@^1.6.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
+ dependencies:
+ anymatch "^1.3.0"
+ async-each "^1.0.0"
+ glob-parent "^2.0.0"
+ inherits "^2.0.1"
+ is-binary-path "^1.0.0"
+ is-glob "^2.0.0"
+ path-is-absolute "^1.0.0"
+ readdirp "^2.0.0"
+ optionalDependencies:
+ fsevents "^1.0.0"
+
chownr@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
@@ -571,6 +631,22 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+cpx@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/cpx/-/cpx-1.5.0.tgz#185be018511d87270dedccc293171e37655ab88f"
+ dependencies:
+ babel-runtime "^6.9.2"
+ chokidar "^1.6.0"
+ duplexer "^0.1.1"
+ glob "^7.0.5"
+ glob2base "^0.0.12"
+ minimatch "^3.0.2"
+ mkdirp "^0.5.1"
+ resolve "^1.1.7"
+ safe-buffer "^5.0.1"
+ shell-quote "^1.6.1"
+ subarg "^1.0.0"
+
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -619,6 +695,12 @@ decamelize@^1.0.0, decamelize@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+decamelize@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7"
+ dependencies:
+ xregexp "4.0.0"
+
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@@ -699,6 +781,10 @@ domexception@^1.0.0:
dependencies:
webidl-conversions "^4.0.2"
+duplexer@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
+
ecc-jsbn@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
@@ -817,6 +903,17 @@ expect@^23.1.0:
jest-message-util "^23.1.0"
jest-regex-util "^23.0.0"
+expect@^23.4.0:
+ version "23.4.0"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-23.4.0.tgz#6da4ecc99c1471253e7288338983ad1ebadb60c3"
+ dependencies:
+ ansi-styles "^3.2.0"
+ jest-diff "^23.2.0"
+ jest-get-type "^22.1.0"
+ jest-matcher-utils "^23.2.0"
+ jest-message-util "^23.4.0"
+ jest-regex-util "^23.3.0"
+
extend-shallow@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
@@ -909,6 +1006,10 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
+find-index@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4"
+
find-up@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@@ -922,6 +1023,12 @@ find-up@^2.1.0:
dependencies:
locate-path "^2.0.0"
+find-up@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ dependencies:
+ locate-path "^3.0.0"
+
for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -954,6 +1061,14 @@ fragment-cache@^0.2.1:
dependencies:
map-cache "^0.2.2"
+fs-extra@6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b"
+ dependencies:
+ graceful-fs "^4.1.2"
+ jsonfile "^4.0.0"
+ universalify "^0.1.0"
+
fs-minipass@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
@@ -964,7 +1079,7 @@ fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
-fsevents@^1.2.3:
+fsevents@^1.0.0, fsevents@^1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426"
dependencies:
@@ -1019,6 +1134,12 @@ glob-parent@^2.0.0:
dependencies:
is-glob "^2.0.0"
+glob2base@^0.0.12:
+ version "0.0.12"
+ resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56"
+ dependencies:
+ find-index "^0.1.1"
+
glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
@@ -1034,7 +1155,7 @@ globals@^9.18.0:
version "9.18.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
-graceful-fs@^4.1.11, graceful-fs@^4.1.2:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@@ -1173,7 +1294,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@~2.0.3:
+inherits@2, inherits@^2.0.1, inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
@@ -1207,6 +1328,12 @@ is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+is-binary-path@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+ dependencies:
+ binary-extensions "^1.0.0"
+
is-buffer@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
@@ -1503,6 +1630,24 @@ jest-cli@^23.1.0:
which "^1.2.12"
yargs "^11.0.0"
+jest-config@^23.0.0:
+ version "23.4.1"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.4.1.tgz#3172fa21f0507d7f8a088ed1dbe4157057f201e9"
+ dependencies:
+ babel-core "^6.0.0"
+ babel-jest "^23.4.0"
+ chalk "^2.0.1"
+ glob "^7.1.1"
+ jest-environment-jsdom "^23.4.0"
+ jest-environment-node "^23.4.0"
+ jest-get-type "^22.1.0"
+ jest-jasmine2 "^23.4.1"
+ jest-regex-util "^23.3.0"
+ jest-resolve "^23.4.1"
+ jest-util "^23.4.0"
+ jest-validate "^23.4.0"
+ pretty-format "^23.2.0"
+
jest-config@^23.1.0:
version "23.1.0"
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.1.0.tgz#708ca0f431d356ee424fb4895d3308006bdd8241"
@@ -1530,6 +1675,15 @@ jest-diff@^23.0.1:
jest-get-type "^22.1.0"
pretty-format "^23.0.1"
+jest-diff@^23.2.0:
+ version "23.2.0"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.2.0.tgz#9f2cf4b51e12c791550200abc16b47130af1062a"
+ dependencies:
+ chalk "^2.0.1"
+ diff "^3.2.0"
+ jest-get-type "^22.1.0"
+ pretty-format "^23.2.0"
+
jest-docblock@^23.0.1:
version "23.0.1"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-23.0.1.tgz#deddd18333be5dc2415260a04ef3fce9276b5725"
@@ -1543,6 +1697,13 @@ jest-each@^23.1.0:
chalk "^2.0.1"
pretty-format "^23.0.1"
+jest-each@^23.4.0:
+ version "23.4.0"
+ resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.4.0.tgz#2fa9edd89daa1a4edc9ff9bf6062a36b71345143"
+ dependencies:
+ chalk "^2.0.1"
+ pretty-format "^23.2.0"
+
jest-environment-jsdom@^23.1.0:
version "23.1.0"
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.1.0.tgz#85929914e23bed3577dac9755f4106d0697c479c"
@@ -1551,6 +1712,14 @@ jest-environment-jsdom@^23.1.0:
jest-util "^23.1.0"
jsdom "^11.5.1"
+jest-environment-jsdom@^23.4.0:
+ version "23.4.0"
+ resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz#056a7952b3fea513ac62a140a2c368c79d9e6023"
+ dependencies:
+ jest-mock "^23.2.0"
+ jest-util "^23.4.0"
+ jsdom "^11.5.1"
+
jest-environment-node@^23.1.0:
version "23.1.0"
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.1.0.tgz#452c0bf949cfcbbacda1e1762eeed70bc784c7d5"
@@ -1558,6 +1727,13 @@ jest-environment-node@^23.1.0:
jest-mock "^23.1.0"
jest-util "^23.1.0"
+jest-environment-node@^23.4.0:
+ version "23.4.0"
+ resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.4.0.tgz#57e80ed0841dea303167cce8cd79521debafde10"
+ dependencies:
+ jest-mock "^23.2.0"
+ jest-util "^23.4.0"
+
jest-get-type@^22.1.0:
version "22.4.3"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4"
@@ -1590,6 +1766,22 @@ jest-jasmine2@^23.1.0:
jest-util "^23.1.0"
pretty-format "^23.0.1"
+jest-jasmine2@^23.4.1:
+ version "23.4.1"
+ resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.4.1.tgz#fa192262430d418e827636e4a98423e5e7ff0fce"
+ dependencies:
+ chalk "^2.0.1"
+ co "^4.6.0"
+ expect "^23.4.0"
+ is-generator-fn "^1.0.0"
+ jest-diff "^23.2.0"
+ jest-each "^23.4.0"
+ jest-matcher-utils "^23.2.0"
+ jest-message-util "^23.4.0"
+ jest-snapshot "^23.4.1"
+ jest-util "^23.4.0"
+ pretty-format "^23.2.0"
+
jest-leak-detector@^23.0.1:
version "23.0.1"
resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-23.0.1.tgz#9dba07505ac3495c39d3ec09ac1e564599e861a0"
@@ -1604,6 +1796,14 @@ jest-matcher-utils@^23.0.1:
jest-get-type "^22.1.0"
pretty-format "^23.0.1"
+jest-matcher-utils@^23.2.0:
+ version "23.2.0"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.2.0.tgz#4d4981f23213e939e3cedf23dc34c747b5ae1913"
+ dependencies:
+ chalk "^2.0.1"
+ jest-get-type "^22.1.0"
+ pretty-format "^23.2.0"
+
jest-message-util@^23.1.0:
version "23.1.0"
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.1.0.tgz#9a809ba487ecac5ce511d4e698ee3b5ee2461ea9"
@@ -1614,14 +1814,32 @@ jest-message-util@^23.1.0:
slash "^1.0.0"
stack-utils "^1.0.1"
+jest-message-util@^23.4.0:
+ version "23.4.0"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.4.0.tgz#17610c50942349508d01a3d1e0bda2c079086a9f"
+ dependencies:
+ "@babel/code-frame" "^7.0.0-beta.35"
+ chalk "^2.0.1"
+ micromatch "^2.3.11"
+ slash "^1.0.0"
+ stack-utils "^1.0.1"
+
jest-mock@^23.1.0:
version "23.1.0"
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-23.1.0.tgz#a381c31b121ab1f60c462a2dadb7b86dcccac487"
+jest-mock@^23.2.0:
+ version "23.2.0"
+ resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-23.2.0.tgz#ad1c60f29e8719d47c26e1138098b6d18b261134"
+
jest-regex-util@^23.0.0:
version "23.0.0"
resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.0.0.tgz#dd5c1fde0c46f4371314cf10f7a751a23f4e8f76"
+jest-regex-util@^23.3.0:
+ version "23.3.0"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.3.0.tgz#5f86729547c2785c4002ceaa8f849fe8ca471bc5"
+
jest-resolve-dependencies@^23.0.1:
version "23.0.1"
resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.0.1.tgz#d01a10ddad9152c4cecdf5eac2b88571c4b6a64d"
@@ -1637,6 +1855,14 @@ jest-resolve@^23.1.0:
chalk "^2.0.1"
realpath-native "^1.0.0"
+jest-resolve@^23.4.1:
+ version "23.4.1"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.4.1.tgz#7f3c17104732a2c0c940a01256025ed745814982"
+ dependencies:
+ browser-resolve "^1.11.3"
+ chalk "^2.0.1"
+ realpath-native "^1.0.0"
+
jest-runner@^23.1.0:
version "23.1.0"
resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.1.0.tgz#fa20a933fff731a5432b3561e7f6426594fa29b5"
@@ -1696,6 +1922,22 @@ jest-snapshot@^23.0.1:
natural-compare "^1.4.0"
pretty-format "^23.0.1"
+jest-snapshot@^23.4.1:
+ version "23.4.1"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.4.1.tgz#090de9acae927f6a3af3005bda40d912b83e9c96"
+ dependencies:
+ babel-traverse "^6.0.0"
+ babel-types "^6.0.0"
+ chalk "^2.0.1"
+ jest-diff "^23.2.0"
+ jest-matcher-utils "^23.2.0"
+ jest-message-util "^23.4.0"
+ jest-resolve "^23.4.1"
+ mkdirp "^0.5.1"
+ natural-compare "^1.4.0"
+ pretty-format "^23.2.0"
+ semver "^5.5.0"
+
jest-util@^23.1.0:
version "23.1.0"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.1.0.tgz#c0251baf34644c6dd2fea78a962f4263ac55772d"
@@ -1709,6 +1951,19 @@ jest-util@^23.1.0:
slash "^1.0.0"
source-map "^0.6.0"
+jest-util@^23.4.0:
+ version "23.4.0"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.4.0.tgz#4d063cb927baf0a23831ff61bec2cbbf49793561"
+ dependencies:
+ callsites "^2.0.0"
+ chalk "^2.0.1"
+ graceful-fs "^4.1.11"
+ is-ci "^1.0.10"
+ jest-message-util "^23.4.0"
+ mkdirp "^0.5.1"
+ slash "^1.0.0"
+ source-map "^0.6.0"
+
jest-validate@^23.0.1:
version "23.0.1"
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.0.1.tgz#cd9f01a89d26bb885f12a8667715e9c865a5754f"
@@ -1718,6 +1973,15 @@ jest-validate@^23.0.1:
leven "^2.1.0"
pretty-format "^23.0.1"
+jest-validate@^23.4.0:
+ version "23.4.0"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.4.0.tgz#d96eede01ef03ac909c009e9c8e455197d48c201"
+ dependencies:
+ chalk "^2.0.1"
+ jest-get-type "^22.1.0"
+ leven "^2.1.0"
+ pretty-format "^23.2.0"
+
jest-watcher@^23.1.0:
version "23.1.0"
resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.1.0.tgz#a8d5842e38d9fb4afff823df6abb42a58ae6cdbd"
@@ -1809,6 +2073,16 @@ json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+jsonfile@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jsonify@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -1887,6 +2161,13 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
+locate-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ dependencies:
+ p-locate "^3.0.0"
+ path-exists "^3.0.0"
+
lodash.sortby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
@@ -1952,7 +2233,7 @@ merge@^1.1.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da"
-micromatch@^2.3.11:
+micromatch@^2.1.5, micromatch@^2.3.11:
version "2.3.11"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
dependencies:
@@ -2002,7 +2283,7 @@ mimic-fn@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
-minimatch@^3.0.3, minimatch@^3.0.4:
+minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
@@ -2012,7 +2293,7 @@ minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@^1.1.1, minimist@^1.2.0:
+minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -2046,6 +2327,10 @@ mkdirp@^0.5.0, mkdirp@^0.5.1:
dependencies:
minimist "0.0.8"
+moment@^2.22.2:
+ version "2.22.2"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
+
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -2127,7 +2412,7 @@ normalize-package-data@^2.3.2:
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
-normalize-path@^2.0.1, normalize-path@^2.1.1:
+normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
dependencies:
@@ -2270,16 +2555,32 @@ p-limit@^1.1.0:
dependencies:
p-try "^1.0.0"
+p-limit@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec"
+ dependencies:
+ p-try "^2.0.0"
+
p-locate@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
dependencies:
p-limit "^1.1.0"
+p-locate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ dependencies:
+ p-limit "^2.0.0"
+
p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+p-try@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
+
parse-glob@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
@@ -2357,6 +2658,12 @@ pkg-dir@^2.0.0:
dependencies:
find-up "^2.1.0"
+pkg-dir@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+ dependencies:
+ find-up "^3.0.0"
+
pn@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
@@ -2380,6 +2687,13 @@ pretty-format@^23.0.1:
ansi-regex "^3.0.0"
ansi-styles "^3.2.0"
+pretty-format@^23.2.0:
+ version "23.2.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.2.0.tgz#3b0aaa63c018a53583373c1cb3a5d96cc5e83017"
+ dependencies:
+ ansi-regex "^3.0.0"
+ ansi-styles "^3.2.0"
+
private@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@@ -2440,7 +2754,7 @@ read-pkg@^1.0.0:
normalize-package-data "^2.3.2"
path-type "^1.0.0"
-readable-stream@^2.0.1, readable-stream@^2.0.6:
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
dependencies:
@@ -2452,6 +2766,15 @@ readable-stream@^2.0.1, readable-stream@^2.0.6:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
+readdirp@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
+ dependencies:
+ graceful-fs "^4.1.2"
+ minimatch "^3.0.2"
+ readable-stream "^2.0.2"
+ set-immediate-shim "^1.0.1"
+
realpath-native@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.0.tgz#7885721a83b43bd5327609f0ddecb2482305fdf0"
@@ -2558,6 +2881,12 @@ resolve@1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+resolve@^1.1.7:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
+ dependencies:
+ path-parse "^1.0.5"
+
ret@~0.1.10:
version "0.1.15"
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
@@ -2611,7 +2940,7 @@ sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
-"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1:
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
@@ -2619,6 +2948,10 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+set-immediate-shim@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+
set-value@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1"
@@ -2653,6 +2986,15 @@ shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+shell-quote@^1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
+ dependencies:
+ array-filter "~0.0.0"
+ array-map "~0.0.0"
+ array-reduce "~0.0.0"
+ jsonify "~0.0.0"
+
shellwords@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
@@ -2853,6 +3195,12 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+subarg@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
+ dependencies:
+ minimist "^1.1.0"
+
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -2952,6 +3300,19 @@ trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+ts-jest@^23.0.1:
+ version "23.0.1"
+ resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-23.0.1.tgz#c90e747b2787d3394319cde77c2748a87aaf2f48"
+ dependencies:
+ babel-plugin-istanbul "^4.1.6"
+ babel-preset-jest "^23.0.0"
+ cpx "^1.5.0"
+ fs-extra "6.0.1"
+ jest-config "^23.0.0"
+ lodash "^4.17.10"
+ pkg-dir "^3.0.0"
+ yargs "^12.0.1"
+
ts-node@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.0.tgz#a94a13c75e5e1aa6b82814b84c68deb339ba7bff"
@@ -2990,6 +3351,10 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
+typescript@^2.9.2:
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
+
uglify-js@^2.6:
version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
@@ -3012,6 +3377,10 @@ union-value@^1.0.0:
is-extendable "^0.1.1"
set-value "^0.4.3"
+universalify@^0.1.0:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+
unset-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
@@ -3162,10 +3531,18 @@ xml-name-validator@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
+xregexp@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
+
y18n@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+"y18n@^3.2.1 || ^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
@@ -3174,6 +3551,12 @@ yallist@^3.0.0, yallist@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"
+yargs-parser@^10.1.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"
+ dependencies:
+ camelcase "^4.1.0"
+
yargs-parser@^9.0.2:
version "9.0.2"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077"
@@ -3197,6 +3580,23 @@ yargs@^11.0.0:
y18n "^3.2.1"
yargs-parser "^9.0.2"
+yargs@^12.0.1:
+ version "12.0.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.1.tgz#6432e56123bb4e7c3562115401e98374060261c2"
+ dependencies:
+ cliui "^4.0.0"
+ decamelize "^2.0.0"
+ find-up "^3.0.0"
+ get-caller-file "^1.0.1"
+ os-locale "^2.0.0"
+ require-directory "^2.1.1"
+ require-main-filename "^1.0.1"
+ set-blocking "^2.0.0"
+ string-width "^2.0.0"
+ which-module "^2.0.0"
+ y18n "^3.2.1 || ^4.0.0"
+ yargs-parser "^10.1.0"
+
yargs@~3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"