The great modularization.
Use ES6 module syntax and SystemJS modules for everything. Some testing stubs were added as well.
This commit is contained in:
parent
dd19e0ecbe
commit
473503a246
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.zip
|
||||
*.xpi
|
4
extension/.gitignore
vendored
4
extension/.gitignore
vendored
@ -1,3 +1,7 @@
|
||||
node_modules/
|
||||
*.js.map
|
||||
background/*.js
|
||||
lib/wallet/*.js
|
||||
lib/*.js
|
||||
popup/*.js
|
||||
test/tests/*.js
|
||||
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2015 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
"use strict";
|
||||
const DB_NAME = "taler";
|
||||
const DB_VERSION = 1;
|
||||
/**
|
||||
* Return a promise that resolves
|
||||
* to the taler wallet db.
|
||||
*/
|
||||
function openTalerDb() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let req = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
req.onerror = (e) => {
|
||||
reject(e);
|
||||
};
|
||||
req.onsuccess = (e) => {
|
||||
resolve(req.result);
|
||||
};
|
||||
req.onupgradeneeded = (e) => {
|
||||
let db = req.result;
|
||||
console.log("DB: upgrade needed: oldVersion = " + e.oldVersion);
|
||||
switch (e.oldVersion) {
|
||||
case 0:
|
||||
let mints = db.createObjectStore("mints", { keyPath: "baseUrl" });
|
||||
mints.createIndex("pubKey", "keys.master_public_key");
|
||||
db.createObjectStore("reserves", { keyPath: "reserve_pub" });
|
||||
db.createObjectStore("denoms", { keyPath: "denomPub" });
|
||||
let coins = db.createObjectStore("coins", { keyPath: "coinPub" });
|
||||
coins.createIndex("mintBaseUrl", "mintBaseUrl");
|
||||
db.createObjectStore("transactions", { keyPath: "contractHash" });
|
||||
db.createObjectStore("precoins", { keyPath: "coinPub", autoIncrement: true });
|
||||
db.createObjectStore("history", { keyPath: "id", autoIncrement: true });
|
||||
break;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
function exportDb(db) {
|
||||
let dump = {
|
||||
name: db.name,
|
||||
version: db.version,
|
||||
stores: {}
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
let tx = db.transaction(db.objectStoreNames);
|
||||
tx.addEventListener("complete", (e) => {
|
||||
resolve(dump);
|
||||
});
|
||||
for (let i = 0; i < db.objectStoreNames.length; i++) {
|
||||
let name = db.objectStoreNames[i];
|
||||
let storeDump = {};
|
||||
dump.stores[name] = storeDump;
|
||||
let store = tx.objectStore(name)
|
||||
.openCursor()
|
||||
.addEventListener("success", (e) => {
|
||||
let cursor = e.target.result;
|
||||
if (cursor) {
|
||||
storeDump[cursor.key] = cursor.value;
|
||||
cursor.continue();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
@ -1,643 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2015 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* High-level interface to emscripten-compiled modules used
|
||||
* by the wallet.
|
||||
* @module EmscriptIf
|
||||
* @author Florian Dold
|
||||
*/
|
||||
"use strict";
|
||||
// Size of a native pointer.
|
||||
const PTR_SIZE = 4;
|
||||
const GNUNET_OK = 1;
|
||||
const GNUNET_YES = 1;
|
||||
const GNUNET_NO = 0;
|
||||
const GNUNET_SYSERR = -1;
|
||||
let getEmsc = (...args) => Module.cwrap.apply(null, args);
|
||||
var emsc = {
|
||||
free: (ptr) => Module._free(ptr),
|
||||
get_value: getEmsc('TALER_WR_get_value', 'number', ['number']),
|
||||
get_fraction: getEmsc('TALER_WR_get_fraction', 'number', ['number']),
|
||||
get_currency: getEmsc('TALER_WR_get_currency', 'string', ['number']),
|
||||
amount_add: getEmsc('TALER_amount_add', 'number', ['number', 'number', 'number']),
|
||||
amount_subtract: getEmsc('TALER_amount_subtract', 'number', ['number', 'number', 'number']),
|
||||
amount_normalize: getEmsc('TALER_amount_normalize', 'void', ['number']),
|
||||
amount_get_zero: getEmsc('TALER_amount_get_zero', 'number', ['string', 'number']),
|
||||
amount_cmp: getEmsc('TALER_amount_cmp', 'number', ['number', 'number']),
|
||||
amount_hton: getEmsc('TALER_amount_hton', 'void', ['number', 'number']),
|
||||
amount_ntoh: getEmsc('TALER_amount_ntoh', 'void', ['number', 'number']),
|
||||
hash: getEmsc('GNUNET_CRYPTO_hash', 'void', ['number', 'number', 'number']),
|
||||
memmove: getEmsc('memmove', 'number', ['number', 'number', 'number']),
|
||||
rsa_public_key_free: getEmsc('GNUNET_CRYPTO_rsa_public_key_free', 'void', ['number']),
|
||||
rsa_signature_free: getEmsc('GNUNET_CRYPTO_rsa_signature_free', 'void', ['number']),
|
||||
string_to_data: getEmsc('GNUNET_STRINGS_string_to_data', 'number', ['number', 'number', 'number', 'number']),
|
||||
eddsa_sign: getEmsc('GNUNET_CRYPTO_eddsa_sign', 'number', ['number', 'number', 'number']),
|
||||
hash_create_random: getEmsc('GNUNET_CRYPTO_hash_create_random', 'void', ['number', 'number']),
|
||||
rsa_blinding_key_destroy: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_free', 'void', ['number']),
|
||||
};
|
||||
var emscAlloc = {
|
||||
get_amount: getEmsc('TALER_WRALL_get_amount', 'number', ['number', 'number', 'number', 'string']),
|
||||
eddsa_key_create: getEmsc('GNUNET_CRYPTO_eddsa_key_create', 'number', []),
|
||||
eddsa_public_key_from_private: getEmsc('TALER_WRALL_eddsa_public_key_from_private', 'number', ['number']),
|
||||
data_to_string_alloc: getEmsc('GNUNET_STRINGS_data_to_string_alloc', 'number', ['number', 'number']),
|
||||
purpose_create: getEmsc('TALER_WRALL_purpose_create', 'number', ['number', 'number', 'number']),
|
||||
rsa_blind: getEmsc('GNUNET_CRYPTO_rsa_blind', 'number', ['number', 'number', 'number', 'number']),
|
||||
rsa_blinding_key_create: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_create', 'number', ['number']),
|
||||
rsa_blinding_key_encode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_encode', 'number', ['number', 'number']),
|
||||
rsa_signature_encode: getEmsc('GNUNET_CRYPTO_rsa_signature_encode', 'number', ['number', 'number']),
|
||||
rsa_blinding_key_decode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_decode', 'number', ['number', 'number']),
|
||||
rsa_public_key_decode: getEmsc('GNUNET_CRYPTO_rsa_public_key_decode', 'number', ['number', 'number']),
|
||||
rsa_signature_decode: getEmsc('GNUNET_CRYPTO_rsa_signature_decode', 'number', ['number', 'number']),
|
||||
rsa_public_key_encode: getEmsc('GNUNET_CRYPTO_rsa_public_key_encode', 'number', ['number', 'number']),
|
||||
rsa_unblind: getEmsc('GNUNET_CRYPTO_rsa_unblind', 'number', ['number', 'number', 'number']),
|
||||
malloc: (size) => Module._malloc(size),
|
||||
};
|
||||
var SignaturePurpose;
|
||||
(function (SignaturePurpose) {
|
||||
SignaturePurpose[SignaturePurpose["RESERVE_WITHDRAW"] = 1200] = "RESERVE_WITHDRAW";
|
||||
SignaturePurpose[SignaturePurpose["WALLET_COIN_DEPOSIT"] = 1201] = "WALLET_COIN_DEPOSIT";
|
||||
})(SignaturePurpose || (SignaturePurpose = {}));
|
||||
var RandomQuality;
|
||||
(function (RandomQuality) {
|
||||
RandomQuality[RandomQuality["WEAK"] = 0] = "WEAK";
|
||||
RandomQuality[RandomQuality["STRONG"] = 1] = "STRONG";
|
||||
RandomQuality[RandomQuality["NONCE"] = 2] = "NONCE";
|
||||
})(RandomQuality || (RandomQuality = {}));
|
||||
class ArenaObject {
|
||||
constructor(arena) {
|
||||
this.nativePtr = null;
|
||||
if (!arena) {
|
||||
if (arenaStack.length == 0) {
|
||||
throw Error("No arena available");
|
||||
}
|
||||
arena = arenaStack[arenaStack.length - 1];
|
||||
}
|
||||
arena.put(this);
|
||||
this.arena = arena;
|
||||
}
|
||||
getNative() {
|
||||
// We want to allow latent allocation
|
||||
// of native wrappers, but we never want to
|
||||
// pass 'undefined' to emscripten.
|
||||
if (this._nativePtr === undefined) {
|
||||
throw Error("Native pointer not initialized");
|
||||
}
|
||||
return this._nativePtr;
|
||||
}
|
||||
free() {
|
||||
if (this.nativePtr !== undefined) {
|
||||
emsc.free(this.nativePtr);
|
||||
this.nativePtr = undefined;
|
||||
}
|
||||
}
|
||||
alloc(size) {
|
||||
if (this.nativePtr !== undefined) {
|
||||
throw Error("Double allocation");
|
||||
}
|
||||
this.nativePtr = emscAlloc.malloc(size);
|
||||
}
|
||||
setNative(n) {
|
||||
if (n === undefined) {
|
||||
throw Error("Native pointer must be a number or null");
|
||||
}
|
||||
this._nativePtr = n;
|
||||
}
|
||||
set nativePtr(v) {
|
||||
this.setNative(v);
|
||||
}
|
||||
get nativePtr() {
|
||||
return this.getNative();
|
||||
}
|
||||
}
|
||||
class DefaultArena {
|
||||
constructor() {
|
||||
this.heap = [];
|
||||
}
|
||||
put(obj) {
|
||||
this.heap.push(obj);
|
||||
}
|
||||
destroy() {
|
||||
for (let obj of this.heap) {
|
||||
obj.destroy();
|
||||
}
|
||||
this.heap = [];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Arena that destroys all its objects once control has returned to the message
|
||||
* loop and a small interval has passed.
|
||||
*/
|
||||
class SyncArena extends DefaultArena {
|
||||
constructor() {
|
||||
super();
|
||||
let me = this;
|
||||
this.timer = new Worker('background/timerThread.js');
|
||||
this.timer.onmessage = () => {
|
||||
this.destroy();
|
||||
};
|
||||
//this.timer.postMessage({interval: 50});
|
||||
}
|
||||
destroy() {
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
let arenaStack = [];
|
||||
arenaStack.push(new SyncArena());
|
||||
class Amount extends ArenaObject {
|
||||
constructor(args, arena) {
|
||||
super(arena);
|
||||
if (args) {
|
||||
this.nativePtr = emscAlloc.get_amount(args.value, 0, args.fraction, args.currency);
|
||||
}
|
||||
else {
|
||||
this.nativePtr = emscAlloc.get_amount(0, 0, 0, "");
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
if (this.nativePtr != 0) {
|
||||
emsc.free(this.nativePtr);
|
||||
}
|
||||
}
|
||||
static getZero(currency, a) {
|
||||
let am = new Amount(null, a);
|
||||
let r = emsc.amount_get_zero(currency, am.getNative());
|
||||
if (r != GNUNET_OK) {
|
||||
throw Error("invalid currency");
|
||||
}
|
||||
return am;
|
||||
}
|
||||
toNbo(a) {
|
||||
let x = new AmountNbo(a);
|
||||
x.alloc();
|
||||
emsc.amount_hton(x.nativePtr, this.nativePtr);
|
||||
return x;
|
||||
}
|
||||
fromNbo(nbo) {
|
||||
emsc.amount_ntoh(this.nativePtr, nbo.nativePtr);
|
||||
}
|
||||
get value() {
|
||||
return emsc.get_value(this.nativePtr);
|
||||
}
|
||||
get fraction() {
|
||||
return emsc.get_fraction(this.nativePtr);
|
||||
}
|
||||
get currency() {
|
||||
return emsc.get_currency(this.nativePtr);
|
||||
}
|
||||
toJson() {
|
||||
return {
|
||||
value: emsc.get_value(this.nativePtr),
|
||||
fraction: emsc.get_fraction(this.nativePtr),
|
||||
currency: emsc.get_currency(this.nativePtr)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Add an amount to this amount.
|
||||
*/
|
||||
add(a) {
|
||||
let res = emsc.amount_add(this.nativePtr, a.nativePtr, this.nativePtr);
|
||||
if (res < 1) {
|
||||
// Overflow
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Perform saturating subtraction on amounts.
|
||||
*/
|
||||
sub(a) {
|
||||
// this = this - a
|
||||
let res = emsc.amount_subtract(this.nativePtr, this.nativePtr, a.nativePtr);
|
||||
if (res == 0) {
|
||||
// Underflow
|
||||
return false;
|
||||
}
|
||||
if (res > 0) {
|
||||
return true;
|
||||
}
|
||||
throw Error("Incompatible currencies");
|
||||
}
|
||||
cmp(a) {
|
||||
return emsc.amount_cmp(this.nativePtr, a.nativePtr);
|
||||
}
|
||||
normalize() {
|
||||
emsc.amount_normalize(this.nativePtr);
|
||||
}
|
||||
}
|
||||
class PackedArenaObject extends ArenaObject {
|
||||
constructor(a) {
|
||||
super(a);
|
||||
}
|
||||
toCrock() {
|
||||
var d = emscAlloc.data_to_string_alloc(this.nativePtr, this.size());
|
||||
var s = Module.Pointer_stringify(d);
|
||||
emsc.free(d);
|
||||
return s;
|
||||
}
|
||||
toJson() {
|
||||
// Per default, the json encoding of
|
||||
// packed arena objects is just the crockford encoding.
|
||||
// Subclasses typically want to override this.
|
||||
return this.toCrock();
|
||||
}
|
||||
loadCrock(s) {
|
||||
this.alloc();
|
||||
// We need to get the javascript string
|
||||
// to the emscripten heap first.
|
||||
let buf = ByteArray.fromString(s);
|
||||
let res = emsc.string_to_data(buf.nativePtr, s.length, this.nativePtr, this.size());
|
||||
buf.destroy();
|
||||
if (res < 1) {
|
||||
throw { error: "wrong encoding" };
|
||||
}
|
||||
}
|
||||
alloc() {
|
||||
if (this.nativePtr === null) {
|
||||
this.nativePtr = emscAlloc.malloc(this.size());
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
emsc.free(this.nativePtr);
|
||||
this.nativePtr = 0;
|
||||
}
|
||||
hash() {
|
||||
var x = new HashCode();
|
||||
x.alloc();
|
||||
emsc.hash(this.nativePtr, this.size(), x.nativePtr);
|
||||
return x;
|
||||
}
|
||||
hexdump() {
|
||||
let bytes = [];
|
||||
for (let i = 0; i < this.size(); i++) {
|
||||
let b = Module.getValue(this.getNative() + i, "i8");
|
||||
b = (b + 256) % 256;
|
||||
bytes.push("0".concat(b.toString(16)).slice(-2));
|
||||
}
|
||||
let lines = [];
|
||||
for (let i = 0; i < bytes.length; i += 8) {
|
||||
lines.push(bytes.slice(i, i + 8).join(","));
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
}
|
||||
class AmountNbo extends PackedArenaObject {
|
||||
size() {
|
||||
return 24;
|
||||
}
|
||||
toJson() {
|
||||
let a = new DefaultArena();
|
||||
let am = new Amount(null, a);
|
||||
am.fromNbo(this);
|
||||
let json = am.toJson();
|
||||
a.destroy();
|
||||
return json;
|
||||
}
|
||||
}
|
||||
class EddsaPrivateKey extends PackedArenaObject {
|
||||
static create(a) {
|
||||
let obj = new EddsaPrivateKey(a);
|
||||
obj.nativePtr = emscAlloc.eddsa_key_create();
|
||||
return obj;
|
||||
}
|
||||
size() {
|
||||
return 32;
|
||||
}
|
||||
getPublicKey(a) {
|
||||
let obj = new EddsaPublicKey(a);
|
||||
obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
mixinStatic(EddsaPrivateKey, fromCrock);
|
||||
function fromCrock(s) {
|
||||
let x = new this();
|
||||
x.alloc();
|
||||
x.loadCrock(s);
|
||||
return x;
|
||||
}
|
||||
function mixin(obj, method, name) {
|
||||
if (!name) {
|
||||
name = method.name;
|
||||
}
|
||||
if (!name) {
|
||||
throw Error("Mixin needs a name.");
|
||||
}
|
||||
obj.prototype[method.name] = method;
|
||||
}
|
||||
function mixinStatic(obj, method, name) {
|
||||
if (!name) {
|
||||
name = method.name;
|
||||
}
|
||||
if (!name) {
|
||||
throw Error("Mixin needs a name.");
|
||||
}
|
||||
obj[method.name] = method;
|
||||
}
|
||||
class EddsaPublicKey extends PackedArenaObject {
|
||||
size() {
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
mixinStatic(EddsaPublicKey, fromCrock);
|
||||
function makeFromCrock(decodeFn) {
|
||||
function fromCrock(s, a) {
|
||||
let obj = new this(a);
|
||||
let buf = ByteArray.fromCrock(s);
|
||||
obj.setNative(decodeFn(buf.getNative(), buf.size()));
|
||||
buf.destroy();
|
||||
return obj;
|
||||
}
|
||||
return fromCrock;
|
||||
}
|
||||
function makeToCrock(encodeFn) {
|
||||
function toCrock() {
|
||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||
let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr);
|
||||
let res = new ByteArray(size, Module.getValue(ptr, '*'));
|
||||
let s = res.toCrock();
|
||||
emsc.free(ptr);
|
||||
res.destroy();
|
||||
return s;
|
||||
}
|
||||
return toCrock;
|
||||
}
|
||||
class RsaBlindingKey extends ArenaObject {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.toCrock = makeToCrock(emscAlloc.rsa_blinding_key_encode);
|
||||
}
|
||||
static create(len, a) {
|
||||
let o = new RsaBlindingKey(a);
|
||||
o.nativePtr = emscAlloc.rsa_blinding_key_create(len);
|
||||
return o;
|
||||
}
|
||||
destroy() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
mixinStatic(RsaBlindingKey, makeFromCrock(emscAlloc.rsa_blinding_key_decode));
|
||||
class HashCode extends PackedArenaObject {
|
||||
size() {
|
||||
return 64;
|
||||
}
|
||||
random(qualStr) {
|
||||
let qual;
|
||||
switch (qualStr) {
|
||||
case "weak":
|
||||
qual = RandomQuality.WEAK;
|
||||
break;
|
||||
case "strong":
|
||||
case null:
|
||||
case undefined:
|
||||
qual = RandomQuality.STRONG;
|
||||
break;
|
||||
case "nonce":
|
||||
qual = RandomQuality.NONCE;
|
||||
break;
|
||||
default:
|
||||
throw Error(format("unknown crypto quality: {0}", qual));
|
||||
}
|
||||
this.alloc();
|
||||
emsc.hash_create_random(qual, this.nativePtr);
|
||||
}
|
||||
}
|
||||
mixinStatic(HashCode, fromCrock);
|
||||
class ByteArray extends PackedArenaObject {
|
||||
constructor(desiredSize, init, a) {
|
||||
super(a);
|
||||
if (init === undefined || init === null) {
|
||||
this.nativePtr = emscAlloc.malloc(desiredSize);
|
||||
}
|
||||
else {
|
||||
this.nativePtr = init;
|
||||
}
|
||||
this.allocatedSize = desiredSize;
|
||||
}
|
||||
size() {
|
||||
return this.allocatedSize;
|
||||
}
|
||||
static fromString(s, a) {
|
||||
let hstr = emscAlloc.malloc(s.length + 1);
|
||||
Module.writeStringToMemory(s, hstr);
|
||||
return new ByteArray(s.length, hstr, a);
|
||||
}
|
||||
static fromCrock(s, a) {
|
||||
let hstr = emscAlloc.malloc(s.length + 1);
|
||||
Module.writeStringToMemory(s, hstr);
|
||||
let decodedLen = Math.floor((s.length * 5) / 8);
|
||||
let ba = new ByteArray(decodedLen, null, a);
|
||||
let res = emsc.string_to_data(hstr, s.length, ba.nativePtr, decodedLen);
|
||||
emsc.free(hstr);
|
||||
if (res != GNUNET_OK) {
|
||||
throw Error("decoding failed");
|
||||
}
|
||||
return ba;
|
||||
}
|
||||
}
|
||||
class EccSignaturePurpose extends PackedArenaObject {
|
||||
constructor(purpose, payload, a) {
|
||||
super(a);
|
||||
this.nativePtr = emscAlloc.purpose_create(purpose, payload.nativePtr, payload.size());
|
||||
this.payloadSize = payload.size();
|
||||
}
|
||||
size() {
|
||||
return this.payloadSize + 8;
|
||||
}
|
||||
}
|
||||
class SignatureStruct {
|
||||
constructor(x) {
|
||||
this.members = {};
|
||||
for (let k in x) {
|
||||
this.set(k, x[k]);
|
||||
}
|
||||
}
|
||||
toPurpose(a) {
|
||||
let totalSize = 0;
|
||||
for (let f of this.fieldTypes()) {
|
||||
let name = f[0];
|
||||
let member = this.members[name];
|
||||
if (!member) {
|
||||
throw Error(format("Member {0} not set", name));
|
||||
}
|
||||
totalSize += member.size();
|
||||
}
|
||||
let buf = emscAlloc.malloc(totalSize);
|
||||
let ptr = buf;
|
||||
for (let f of this.fieldTypes()) {
|
||||
let name = f[0];
|
||||
let member = this.members[name];
|
||||
let size = member.size();
|
||||
emsc.memmove(ptr, member.nativePtr, size);
|
||||
ptr += size;
|
||||
}
|
||||
let ba = new ByteArray(totalSize, buf, a);
|
||||
return new EccSignaturePurpose(this.purpose(), ba);
|
||||
}
|
||||
toJson() {
|
||||
let res = {};
|
||||
for (let f of this.fieldTypes()) {
|
||||
let name = f[0];
|
||||
let member = this.members[name];
|
||||
if (!member) {
|
||||
throw Error(format("Member {0} not set", name));
|
||||
}
|
||||
res[name] = member.toJson();
|
||||
}
|
||||
res["purpose"] = this.purpose();
|
||||
return res;
|
||||
}
|
||||
set(name, value) {
|
||||
let typemap = {};
|
||||
for (let f of this.fieldTypes()) {
|
||||
typemap[f[0]] = f[1];
|
||||
}
|
||||
if (!(name in typemap)) {
|
||||
throw Error(format("Key {0} not found", name));
|
||||
}
|
||||
if (!(value instanceof typemap[name])) {
|
||||
throw Error(format("Wrong type for {0}", name));
|
||||
}
|
||||
this.members[name] = value;
|
||||
}
|
||||
}
|
||||
class WithdrawRequestPS extends SignatureStruct {
|
||||
constructor(w) {
|
||||
super(w);
|
||||
}
|
||||
purpose() {
|
||||
return SignaturePurpose.RESERVE_WITHDRAW;
|
||||
}
|
||||
fieldTypes() {
|
||||
return [
|
||||
["reserve_pub", EddsaPublicKey],
|
||||
["amount_with_fee", AmountNbo],
|
||||
["withdraw_fee", AmountNbo],
|
||||
["h_denomination_pub", HashCode],
|
||||
["h_coin_envelope", HashCode]
|
||||
];
|
||||
}
|
||||
}
|
||||
class AbsoluteTimeNbo extends PackedArenaObject {
|
||||
static fromTalerString(s) {
|
||||
let x = new AbsoluteTimeNbo();
|
||||
x.alloc();
|
||||
let r = /Date\(([0-9]+)\)/;
|
||||
let m = r.exec(s);
|
||||
if (m.length != 2) {
|
||||
throw Error();
|
||||
}
|
||||
let n = parseInt(m[1]) * 1000000;
|
||||
// XXX: This only works up to 54 bit numbers.
|
||||
set64(x.getNative(), n);
|
||||
return x;
|
||||
}
|
||||
size() {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
// XXX: This only works up to 54 bit numbers.
|
||||
function set64(p, n) {
|
||||
for (let i = 0; i < 8; ++i) {
|
||||
Module.setValue(p + (7 - i), n & 0xFF, "i8");
|
||||
n = Math.floor(n / 256);
|
||||
}
|
||||
}
|
||||
class UInt64 extends PackedArenaObject {
|
||||
static fromNumber(n) {
|
||||
let x = new UInt64();
|
||||
x.alloc();
|
||||
set64(x.getNative(), n);
|
||||
return x;
|
||||
}
|
||||
size() {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
class DepositRequestPS extends SignatureStruct {
|
||||
constructor(w) {
|
||||
super(w);
|
||||
}
|
||||
purpose() {
|
||||
return SignaturePurpose.WALLET_COIN_DEPOSIT;
|
||||
}
|
||||
fieldTypes() {
|
||||
return [
|
||||
["h_contract", HashCode],
|
||||
["h_wire", HashCode],
|
||||
["timestamp", AbsoluteTimeNbo],
|
||||
["refund_deadline", AbsoluteTimeNbo],
|
||||
["transaction_id", UInt64],
|
||||
["amount_with_fee", AmountNbo],
|
||||
["deposit_fee", AmountNbo],
|
||||
["merchant", EddsaPublicKey],
|
||||
["coin_pub", EddsaPublicKey],
|
||||
];
|
||||
}
|
||||
}
|
||||
function makeEncode(encodeFn) {
|
||||
function encode(arena) {
|
||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||
let len = encodeFn(this.getNative(), ptr);
|
||||
let res = new ByteArray(len, null, arena);
|
||||
res.setNative(Module.getValue(ptr, '*'));
|
||||
emsc.free(ptr);
|
||||
return res;
|
||||
}
|
||||
return encode;
|
||||
}
|
||||
class RsaPublicKey extends ArenaObject {
|
||||
toCrock() {
|
||||
return this.encode().toCrock();
|
||||
}
|
||||
destroy() {
|
||||
emsc.rsa_public_key_free(this.nativePtr);
|
||||
this.nativePtr = 0;
|
||||
}
|
||||
}
|
||||
mixinStatic(RsaPublicKey, makeFromCrock(emscAlloc.rsa_public_key_decode));
|
||||
mixin(RsaPublicKey, makeEncode(emscAlloc.rsa_public_key_encode));
|
||||
class EddsaSignature extends PackedArenaObject {
|
||||
size() {
|
||||
return 64;
|
||||
}
|
||||
}
|
||||
class RsaSignature extends ArenaObject {
|
||||
destroy() {
|
||||
emsc.rsa_signature_free(this.getNative());
|
||||
this.setNative(0);
|
||||
}
|
||||
}
|
||||
mixinStatic(RsaSignature, makeFromCrock(emscAlloc.rsa_signature_decode));
|
||||
mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
|
||||
function rsaBlind(hashCode, blindingKey, pkey, arena) {
|
||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||
let s = emscAlloc.rsa_blind(hashCode.nativePtr, blindingKey.nativePtr, pkey.nativePtr, ptr);
|
||||
return new ByteArray(s, Module.getValue(ptr, '*'), arena);
|
||||
}
|
||||
function eddsaSign(purpose, priv, a) {
|
||||
let sig = new EddsaSignature(a);
|
||||
sig.alloc();
|
||||
let res = emsc.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
|
||||
if (res < 1) {
|
||||
throw Error("EdDSA signing failed");
|
||||
}
|
||||
return sig;
|
||||
}
|
||||
function rsaUnblind(sig, bk, pk, a) {
|
||||
let x = new RsaSignature(a);
|
||||
x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr, bk.nativePtr, pk.nativePtr);
|
||||
return x;
|
||||
}
|
42
extension/background/main.ts
Normal file
42
extension/background/main.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
// Entry point for the background page.
|
||||
|
||||
"use strict";
|
||||
|
||||
System.config({
|
||||
defaultJSExtensions: true,
|
||||
});
|
||||
|
||||
var Module: any;
|
||||
|
||||
if ("object" !== typeof Module) {
|
||||
throw Error("emscripten not loaded, no 'Module' defined");
|
||||
}
|
||||
|
||||
let mod = System.newModule({Module: Module});
|
||||
let modName = System.normalizeSync("../lib/emscripten/emsc");
|
||||
console.log("registering", modName);
|
||||
System.set(modName, mod);
|
||||
|
||||
System.import("../lib/wallet/wxmessaging")
|
||||
.then((wxmessaging) => {
|
||||
wxmessaging.wxMain();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("import failed", e.stack);
|
||||
});
|
@ -1,502 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2015 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* High-level wallet operations that should be indepentent from the underlying
|
||||
* browser extension interface.
|
||||
* @module Wallet
|
||||
* @author Florian Dold
|
||||
*/
|
||||
/// <reference path="../decl/urijs/URIjs.d.ts" />
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
let AmountJson = class {
|
||||
};
|
||||
__decorate([
|
||||
Checkable.Number
|
||||
], AmountJson.prototype, "value", void 0);
|
||||
__decorate([
|
||||
Checkable.Number
|
||||
], AmountJson.prototype, "fraction", void 0);
|
||||
__decorate([
|
||||
Checkable.String
|
||||
], AmountJson.prototype, "currency", void 0);
|
||||
AmountJson = __decorate([
|
||||
Checkable.Class
|
||||
], AmountJson);
|
||||
let CoinPaySig = class {
|
||||
};
|
||||
__decorate([
|
||||
Checkable.String
|
||||
], CoinPaySig.prototype, "coin_sig", void 0);
|
||||
__decorate([
|
||||
Checkable.String
|
||||
], CoinPaySig.prototype, "coin_pub", void 0);
|
||||
__decorate([
|
||||
Checkable.String
|
||||
], CoinPaySig.prototype, "ub_sig", void 0);
|
||||
__decorate([
|
||||
Checkable.String
|
||||
], CoinPaySig.prototype, "denom_pub", void 0);
|
||||
__decorate([
|
||||
Checkable.Value(AmountJson)
|
||||
], CoinPaySig.prototype, "f", void 0);
|
||||
CoinPaySig = __decorate([
|
||||
Checkable.Class
|
||||
], CoinPaySig);
|
||||
/**
|
||||
* See http://api.taler.net/wallet.html#general
|
||||
*/
|
||||
function canonicalizeBaseUrl(url) {
|
||||
let x = new URI(url);
|
||||
if (!x.protocol()) {
|
||||
x.protocol("https");
|
||||
}
|
||||
x.path(x.path() + "/").normalizePath();
|
||||
x.fragment();
|
||||
x.query();
|
||||
return x.href();
|
||||
}
|
||||
function copy(o) {
|
||||
return JSON.parse(JSON.stringify(o));
|
||||
}
|
||||
function rankDenom(denom1, denom2) {
|
||||
// Slow ... we should find a better way than to convert it evert time.
|
||||
let v1 = new Amount(denom1.value);
|
||||
let v2 = new Amount(denom2.value);
|
||||
return (-1) * v1.cmp(v2);
|
||||
}
|
||||
class Wallet {
|
||||
constructor(db, http, badge) {
|
||||
this.db = db;
|
||||
this.http = http;
|
||||
this.badge = badge;
|
||||
}
|
||||
static signDeposit(offer, cds) {
|
||||
let ret = [];
|
||||
let amountSpent = Amount.getZero(cds[0].coin.currentAmount.currency);
|
||||
let amountRemaining = new Amount(offer.contract.amount);
|
||||
cds = copy(cds);
|
||||
for (let cd of cds) {
|
||||
let coinSpend;
|
||||
if (amountRemaining.value == 0 && amountRemaining.fraction == 0) {
|
||||
break;
|
||||
}
|
||||
if (amountRemaining.cmp(new Amount(cd.coin.currentAmount)) < 0) {
|
||||
coinSpend = new Amount(amountRemaining.toJson());
|
||||
}
|
||||
else {
|
||||
coinSpend = new Amount(cd.coin.currentAmount);
|
||||
}
|
||||
amountSpent.add(coinSpend);
|
||||
amountRemaining.sub(coinSpend);
|
||||
let newAmount = new Amount(cd.coin.currentAmount);
|
||||
newAmount.sub(coinSpend);
|
||||
cd.coin.currentAmount = newAmount.toJson();
|
||||
let args = {
|
||||
h_contract: HashCode.fromCrock(offer.H_contract),
|
||||
h_wire: HashCode.fromCrock(offer.contract.H_wire),
|
||||
amount_with_fee: coinSpend.toNbo(),
|
||||
coin_pub: EddsaPublicKey.fromCrock(cd.coin.coinPub),
|
||||
deposit_fee: new Amount(cd.denom.fee_deposit).toNbo(),
|
||||
merchant: EddsaPublicKey.fromCrock(offer.contract.merchant_pub),
|
||||
refund_deadline: AbsoluteTimeNbo.fromTalerString(offer.contract.refund_deadline),
|
||||
timestamp: AbsoluteTimeNbo.fromTalerString(offer.contract.timestamp),
|
||||
transaction_id: UInt64.fromNumber(offer.contract.transaction_id),
|
||||
};
|
||||
let d = new DepositRequestPS(args);
|
||||
let coinSig = eddsaSign(d.toPurpose(), EddsaPrivateKey.fromCrock(cd.coin.coinPriv))
|
||||
.toCrock();
|
||||
let s = {
|
||||
coin_sig: coinSig,
|
||||
coin_pub: cd.coin.coinPub,
|
||||
ub_sig: cd.coin.denomSig,
|
||||
denom_pub: cd.coin.denomPub,
|
||||
f: coinSpend.toJson(),
|
||||
};
|
||||
ret.push({ sig: s, updatedCoin: cd.coin });
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Get mints and associated coins that are still spendable,
|
||||
* but only if the sum the coins' remaining value exceeds the payment amount.
|
||||
* @param paymentAmount
|
||||
* @param depositFeeLimit
|
||||
* @param allowedMints
|
||||
*/
|
||||
getPossibleMintCoins(paymentAmount, depositFeeLimit, allowedMints) {
|
||||
let m = {};
|
||||
function storeMintCoin(mc) {
|
||||
let mint = mc[0];
|
||||
let coin = mc[1];
|
||||
let cd = {
|
||||
coin: coin,
|
||||
denom: mint.keys.denoms.find((e) => e.denom_pub === coin.denomPub)
|
||||
};
|
||||
if (!cd.denom) {
|
||||
throw Error("denom not found (database inconsistent)");
|
||||
}
|
||||
let x = m[mint.baseUrl];
|
||||
if (!x) {
|
||||
m[mint.baseUrl] = [cd];
|
||||
}
|
||||
else {
|
||||
x.push(cd);
|
||||
}
|
||||
}
|
||||
let ps = allowedMints.map((info) => {
|
||||
return Query(this.db)
|
||||
.iterIndex("mints", "pubKey", info.master_pub)
|
||||
.indexJoin("coins", "mintBaseUrl", (mint) => mint.baseUrl)
|
||||
.reduce(storeMintCoin);
|
||||
});
|
||||
return Promise.all(ps).then(() => {
|
||||
let ret = {};
|
||||
nextMint: for (let key in m) {
|
||||
let coins = m[key].map((x) => ({
|
||||
a: new Amount(x.denom.fee_deposit),
|
||||
c: x
|
||||
}));
|
||||
// Sort by ascending deposit fee
|
||||
coins.sort((o1, o2) => o1.a.cmp(o2.a));
|
||||
let maxFee = new Amount(depositFeeLimit);
|
||||
let minAmount = new Amount(paymentAmount);
|
||||
let accFee = new Amount(coins[0].c.denom.fee_deposit);
|
||||
let accAmount = Amount.getZero(coins[0].c.coin.currentAmount.currency);
|
||||
let usableCoins = [];
|
||||
nextCoin: for (let i = 0; i < coins.length; i++) {
|
||||
let coinAmount = new Amount(coins[i].c.coin.currentAmount);
|
||||
let coinFee = coins[i].a;
|
||||
if (coinAmount.cmp(coinFee) <= 0) {
|
||||
continue nextCoin;
|
||||
}
|
||||
accFee.add(coinFee);
|
||||
accAmount.add(coinAmount);
|
||||
if (accFee.cmp(maxFee) >= 0) {
|
||||
console.log("too much fees");
|
||||
continue nextMint;
|
||||
}
|
||||
usableCoins.push(coins[i].c);
|
||||
if (accAmount.cmp(minAmount) >= 0) {
|
||||
ret[key] = usableCoins;
|
||||
continue nextMint;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
executePay(offer, payCoinInfo, merchantBaseUrl, chosenMint) {
|
||||
let payReq = {};
|
||||
payReq["H_wire"] = offer.contract.H_wire;
|
||||
payReq["H_contract"] = offer.H_contract;
|
||||
payReq["transaction_id"] = offer.contract.transaction_id;
|
||||
payReq["refund_deadline"] = offer.contract.refund_deadline;
|
||||
payReq["mint"] = URI(chosenMint).href();
|
||||
payReq["coins"] = payCoinInfo.map((x) => x.sig);
|
||||
payReq["timestamp"] = offer.contract.timestamp;
|
||||
let payUrl = URI(offer.pay_url).absoluteTo(merchantBaseUrl);
|
||||
let t = {
|
||||
contractHash: offer.H_contract,
|
||||
contract: offer.contract,
|
||||
payUrl: payUrl.href(),
|
||||
payReq: payReq
|
||||
};
|
||||
return Query(this.db)
|
||||
.put("transactions", t)
|
||||
.putAll("coins", payCoinInfo.map((pci) => pci.updatedCoin))
|
||||
.finish();
|
||||
}
|
||||
confirmPay(offer, merchantPageUrl) {
|
||||
return Promise.resolve().then(() => {
|
||||
return this.getPossibleMintCoins(offer.contract.amount, offer.contract.max_fee, offer.contract.mints);
|
||||
}).then((mcs) => {
|
||||
if (Object.keys(mcs).length == 0) {
|
||||
throw Error("Not enough coins.");
|
||||
}
|
||||
let mintUrl = Object.keys(mcs)[0];
|
||||
let ds = Wallet.signDeposit(offer, mcs[mintUrl]);
|
||||
return this.executePay(offer, ds, merchantPageUrl, mintUrl);
|
||||
});
|
||||
}
|
||||
doPayment(H_contract) {
|
||||
return Promise.resolve().then(() => {
|
||||
return Query(this.db)
|
||||
.get("transactions", H_contract)
|
||||
.then((t) => {
|
||||
if (!t) {
|
||||
throw Error("contract not found");
|
||||
}
|
||||
let resp = {
|
||||
payUrl: t.payUrl,
|
||||
payReq: t.payReq
|
||||
};
|
||||
return resp;
|
||||
});
|
||||
});
|
||||
}
|
||||
confirmReserve(req) {
|
||||
let reservePriv = EddsaPrivateKey.create();
|
||||
let reservePub = reservePriv.getPublicKey();
|
||||
let form = new FormData();
|
||||
let now = (new Date()).toString();
|
||||
form.append(req.field_amount, req.amount_str);
|
||||
form.append(req.field_reserve_pub, reservePub.toCrock());
|
||||
form.append(req.field_mint, req.mint);
|
||||
// TODO: set bank-specified fields.
|
||||
let mintBaseUrl = canonicalizeBaseUrl(req.mint);
|
||||
return this.http.postForm(req.post_url, form)
|
||||
.then((hresp) => {
|
||||
let resp = {
|
||||
status: hresp.status,
|
||||
text: hresp.responseText,
|
||||
success: undefined,
|
||||
backlink: undefined
|
||||
};
|
||||
let reserveRecord = {
|
||||
reserve_pub: reservePub.toCrock(),
|
||||
reserve_priv: reservePriv.toCrock(),
|
||||
mint_base_url: mintBaseUrl,
|
||||
created: now,
|
||||
last_query: null,
|
||||
current_amount: null,
|
||||
// XXX: set to actual amount
|
||||
initial_amount: null
|
||||
};
|
||||
if (hresp.status != 200) {
|
||||
resp.success = false;
|
||||
return resp;
|
||||
}
|
||||
resp.success = true;
|
||||
// We can't show the page directly, so
|
||||
// we show some generic page from the wallet.
|
||||
resp.backlink = null;
|
||||
return Query(this.db)
|
||||
.put("reserves", reserveRecord)
|
||||
.finish()
|
||||
.then(() => {
|
||||
// Do this in the background
|
||||
this.updateMintFromUrl(reserveRecord.mint_base_url)
|
||||
.then((mint) => this.updateReserve(reservePub, mint)
|
||||
.then((reserve) => this.depleteReserve(reserve, mint)));
|
||||
return resp;
|
||||
});
|
||||
});
|
||||
}
|
||||
withdrawPrepare(denom, reserve) {
|
||||
let reservePriv = new EddsaPrivateKey();
|
||||
reservePriv.loadCrock(reserve.reserve_priv);
|
||||
let reservePub = new EddsaPublicKey();
|
||||
reservePub.loadCrock(reserve.reserve_pub);
|
||||
let denomPub = RsaPublicKey.fromCrock(denom.denom_pub);
|
||||
let coinPriv = EddsaPrivateKey.create();
|
||||
let coinPub = coinPriv.getPublicKey();
|
||||
let blindingFactor = RsaBlindingKey.create(1024);
|
||||
let pubHash = coinPub.hash();
|
||||
let ev = rsaBlind(pubHash, blindingFactor, denomPub);
|
||||
if (!denom.fee_withdraw) {
|
||||
throw Error("Field fee_withdraw missing");
|
||||
}
|
||||
let amountWithFee = new Amount(denom.value);
|
||||
amountWithFee.add(new Amount(denom.fee_withdraw));
|
||||
let withdrawFee = new Amount(denom.fee_withdraw);
|
||||
// Signature
|
||||
let withdrawRequest = new WithdrawRequestPS({
|
||||
reserve_pub: reservePub,
|
||||
amount_with_fee: amountWithFee.toNbo(),
|
||||
withdraw_fee: withdrawFee.toNbo(),
|
||||
h_denomination_pub: denomPub.encode().hash(),
|
||||
h_coin_envelope: ev.hash()
|
||||
});
|
||||
var sig = eddsaSign(withdrawRequest.toPurpose(), reservePriv);
|
||||
let preCoin = {
|
||||
reservePub: reservePub.toCrock(),
|
||||
blindingKey: blindingFactor.toCrock(),
|
||||
coinPub: coinPub.toCrock(),
|
||||
coinPriv: coinPriv.toCrock(),
|
||||
denomPub: denomPub.encode().toCrock(),
|
||||
mintBaseUrl: reserve.mint_base_url,
|
||||
withdrawSig: sig.toCrock(),
|
||||
coinEv: ev.toCrock(),
|
||||
coinValue: denom.value
|
||||
};
|
||||
return Query(this.db).put("precoins", preCoin).finish().then(() => preCoin);
|
||||
}
|
||||
withdrawExecute(pc) {
|
||||
return Query(this.db)
|
||||
.get("reserves", pc.reservePub)
|
||||
.then((r) => {
|
||||
let wd = {};
|
||||
wd.denom_pub = pc.denomPub;
|
||||
wd.reserve_pub = pc.reservePub;
|
||||
wd.reserve_sig = pc.withdrawSig;
|
||||
wd.coin_ev = pc.coinEv;
|
||||
let reqUrl = URI("reserve/withdraw").absoluteTo(r.mint_base_url);
|
||||
return this.http.postJson(reqUrl, wd);
|
||||
})
|
||||
.then(resp => {
|
||||
if (resp.status != 200) {
|
||||
throw new RequestException({
|
||||
hint: "Withdrawal failed",
|
||||
status: resp.status
|
||||
});
|
||||
}
|
||||
let r = JSON.parse(resp.responseText);
|
||||
let denomSig = rsaUnblind(RsaSignature.fromCrock(r.ev_sig), RsaBlindingKey.fromCrock(pc.blindingKey), RsaPublicKey.fromCrock(pc.denomPub));
|
||||
let coin = {
|
||||
coinPub: pc.coinPub,
|
||||
coinPriv: pc.coinPriv,
|
||||
denomPub: pc.denomPub,
|
||||
denomSig: denomSig.encode().toCrock(),
|
||||
currentAmount: pc.coinValue,
|
||||
mintBaseUrl: pc.mintBaseUrl,
|
||||
};
|
||||
return coin;
|
||||
});
|
||||
}
|
||||
updateBadge() {
|
||||
function countNonEmpty(c, n) {
|
||||
if (c.currentAmount.fraction != 0 || c.currentAmount.value != 0) {
|
||||
return n + 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
function doBadge(n) {
|
||||
this.badge.setText(n.toString());
|
||||
this.badge.setColor("#0F0");
|
||||
}
|
||||
Query(this.db)
|
||||
.iter("coins")
|
||||
.reduce(countNonEmpty, 0)
|
||||
.then(doBadge.bind(this));
|
||||
}
|
||||
storeCoin(coin) {
|
||||
Query(this.db)
|
||||
.delete("precoins", coin.coinPub)
|
||||
.add("coins", coin)
|
||||
.finish()
|
||||
.then(() => {
|
||||
this.updateBadge();
|
||||
});
|
||||
}
|
||||
withdraw(denom, reserve) {
|
||||
return this.withdrawPrepare(denom, reserve)
|
||||
.then((pc) => this.withdrawExecute(pc))
|
||||
.then((c) => this.storeCoin(c));
|
||||
}
|
||||
/**
|
||||
* Withdraw coins from a reserve until it is empty.
|
||||
*/
|
||||
depleteReserve(reserve, mint) {
|
||||
let denoms = copy(mint.keys.denoms);
|
||||
let remaining = new Amount(reserve.current_amount);
|
||||
denoms.sort(rankDenom);
|
||||
let workList = [];
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
let found = false;
|
||||
for (let d of denoms) {
|
||||
let cost = new Amount(d.value);
|
||||
cost.add(new Amount(d.fee_withdraw));
|
||||
if (remaining.cmp(cost) < 0) {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
remaining.sub(cost);
|
||||
workList.push(d);
|
||||
}
|
||||
if (!found) {
|
||||
console.log("did not find coins for remaining ", remaining.toJson());
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Do the request one by one.
|
||||
let next = () => {
|
||||
if (workList.length == 0) {
|
||||
return;
|
||||
}
|
||||
let d = workList.pop();
|
||||
this.withdraw(d, reserve)
|
||||
.then(() => next());
|
||||
};
|
||||
// Asynchronous recursion
|
||||
next();
|
||||
}
|
||||
updateReserve(reservePub, mint) {
|
||||
let reservePubStr = reservePub.toCrock();
|
||||
return Query(this.db)
|
||||
.get("reserves", reservePubStr)
|
||||
.then((reserve) => {
|
||||
let reqUrl = URI("reserve/status").absoluteTo(mint.baseUrl);
|
||||
reqUrl.query({ 'reserve_pub': reservePubStr });
|
||||
return this.http.get(reqUrl).then(resp => {
|
||||
if (resp.status != 200) {
|
||||
throw Error();
|
||||
}
|
||||
let reserveInfo = JSON.parse(resp.responseText);
|
||||
if (!reserveInfo) {
|
||||
throw Error();
|
||||
}
|
||||
reserve.current_amount = reserveInfo.balance;
|
||||
return Query(this.db)
|
||||
.put("reserves", reserve)
|
||||
.finish()
|
||||
.then(() => reserve);
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Update or add mint DB entry by fetching the /keys information.
|
||||
* Optionally link the reserve entry to the new or existing
|
||||
* mint entry in then DB.
|
||||
*/
|
||||
updateMintFromUrl(baseUrl) {
|
||||
let reqUrl = URI("keys").absoluteTo(baseUrl);
|
||||
return this.http.get(reqUrl).then((resp) => {
|
||||
if (resp.status != 200) {
|
||||
throw Error("/keys request failed");
|
||||
}
|
||||
let mintKeysJson = JSON.parse(resp.responseText);
|
||||
if (!mintKeysJson) {
|
||||
throw new RequestException({ url: reqUrl, hint: "keys invalid" });
|
||||
}
|
||||
let mint = {
|
||||
baseUrl: baseUrl,
|
||||
keys: mintKeysJson
|
||||
};
|
||||
return Query(this.db).put("mints", mint).finish().then(() => mint);
|
||||
});
|
||||
}
|
||||
getBalances() {
|
||||
function collectBalances(c, byCurrency) {
|
||||
let acc = byCurrency[c.currentAmount.currency];
|
||||
if (!acc) {
|
||||
acc = Amount.getZero(c.currentAmount.currency).toJson();
|
||||
}
|
||||
let am = new Amount(c.currentAmount);
|
||||
am.add(new Amount(acc));
|
||||
byCurrency[c.currentAmount.currency] = am.toJson();
|
||||
return byCurrency;
|
||||
}
|
||||
return Query(this.db)
|
||||
.iter("coins")
|
||||
.reduce(collectBalances, {});
|
||||
}
|
||||
}
|
@ -15,29 +15,30 @@
|
||||
*/
|
||||
// Script that is injected into pages in order to allow merchants pages to
|
||||
// query the availability of Taler.
|
||||
/// <reference path="../lib/decl/chrome/chrome.d.ts" />
|
||||
"use strict";
|
||||
document.addEventListener("taler-checkout-probe", function (e) {
|
||||
let evt = new Event("taler-wallet-present");
|
||||
var evt = new Event("taler-wallet-present");
|
||||
document.dispatchEvent(evt);
|
||||
console.log("merchant handshake done");
|
||||
});
|
||||
document.addEventListener("taler-wire-probe", function (e) {
|
||||
let evt = new Event("taler-wallet-present");
|
||||
var evt = new Event("taler-wallet-present");
|
||||
document.dispatchEvent(evt);
|
||||
console.log("bank handshake done");
|
||||
});
|
||||
document.addEventListener("taler-checkout-probe", function (e) {
|
||||
let evt = new Event("taler-wallet-present");
|
||||
var evt = new Event("taler-wallet-present");
|
||||
document.dispatchEvent(evt);
|
||||
console.log("merchant handshake done");
|
||||
});
|
||||
document.addEventListener("taler-create-reserve", function (e) {
|
||||
let $ = (x) => document.getElementById(x);
|
||||
var $ = function (x) { return document.getElementById(x); };
|
||||
console.log("taler-create-reserve with " + JSON.stringify(e.detail));
|
||||
let form_uri = $(e.detail.form_id).action;
|
||||
var form_uri = $(e.detail.form_id).action;
|
||||
// TODO: validate event fields
|
||||
// TODO: also send extra bank-defined form fields
|
||||
let params = {
|
||||
var params = {
|
||||
post_url: URI(form_uri).absoluteTo(document.location.href).href(),
|
||||
// TODO: This should change in the future, we should not deal with the
|
||||
// amount as a bank-specific string here.
|
||||
@ -47,14 +48,14 @@ document.addEventListener("taler-create-reserve", function (e) {
|
||||
field_reserve_pub: $(e.detail.input_pub).name,
|
||||
field_mint: $(e.detail.mint_rcv).name,
|
||||
};
|
||||
let uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html"));
|
||||
var uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html"));
|
||||
document.location.href = uri.query(params).href();
|
||||
});
|
||||
document.addEventListener("taler-contract", function (e) {
|
||||
// XXX: the merchant should just give us the parsed data ...
|
||||
let offer = JSON.parse(e.detail);
|
||||
let uri = URI(chrome.extension.getURL("pages/confirm-contract.html"));
|
||||
let params = {
|
||||
var offer = JSON.parse(e.detail);
|
||||
var uri = URI(chrome.extension.getURL("pages/confirm-contract.html"));
|
||||
var params = {
|
||||
offer: JSON.stringify(offer),
|
||||
merchantPageUrl: document.location.href,
|
||||
cookie: document.cookie,
|
||||
@ -63,23 +64,23 @@ document.addEventListener("taler-contract", function (e) {
|
||||
});
|
||||
document.addEventListener('taler-execute-payment', function (e) {
|
||||
console.log("got taler-execute-payment in content page");
|
||||
let msg = {
|
||||
var msg = {
|
||||
type: "execute-payment",
|
||||
detail: {
|
||||
H_contract: e.detail.H_contract
|
||||
},
|
||||
};
|
||||
chrome.runtime.sendMessage(msg, (resp) => {
|
||||
chrome.runtime.sendMessage(msg, function (resp) {
|
||||
if (!resp.success) {
|
||||
console.log("failure!");
|
||||
return;
|
||||
}
|
||||
console.log("Making request to ", resp.payUrl);
|
||||
let r = new XMLHttpRequest();
|
||||
var r = new XMLHttpRequest();
|
||||
r.open('post', resp.payUrl);
|
||||
r.send(JSON.stringify(resp.payReq));
|
||||
let detail = {};
|
||||
r.onload = (e) => {
|
||||
var detail = {};
|
||||
r.onload = function (e) {
|
||||
switch (r.status) {
|
||||
case 200:
|
||||
detail.success = true;
|
||||
@ -101,3 +102,4 @@ document.addEventListener('taler-execute-payment', function (e) {
|
||||
};
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=notify.js.map
|
@ -17,6 +17,8 @@
|
||||
// Script that is injected into pages in order to allow merchants pages to
|
||||
// query the availability of Taler.
|
||||
|
||||
/// <reference path="../lib/decl/chrome/chrome.d.ts" />
|
||||
|
||||
"use strict";
|
||||
|
||||
document.addEventListener("taler-checkout-probe", function(e) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2015 GNUnet e.V.
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
@ -14,8 +14,6 @@
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
Handlebars.registerHelper('prettyAmount', function (amount) {
|
||||
let v = amount.value + amount.fraction / 1e6;
|
||||
return v.toFixed(2) + " " + amount.currency;
|
18634
extension/lib/decl/lib.es6.d.ts
vendored
Normal file
18634
extension/lib/decl/lib.es6.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
20
extension/lib/decl/systemjs/systemjs.d.ts
vendored
Normal file
20
extension/lib/decl/systemjs/systemjs.d.ts
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
interface System {
|
||||
import(name: string): Promise<any>;
|
||||
defined: any;
|
||||
amdDefine: () => void;
|
||||
amdRequire: () => void;
|
||||
baseURL: string;
|
||||
paths: { [key: string]: string };
|
||||
meta: { [key: string]: Object };
|
||||
config: any;
|
||||
newModule(obj: Object): any;
|
||||
normalizeSync(name: string): string;
|
||||
set(moduleName: string, module: any)
|
||||
}
|
||||
|
||||
|
||||
declare var System: System;
|
||||
|
||||
declare module "systemjs" {
|
||||
export = System;
|
||||
}
|
41
extension/lib/emscripten/emsc.d.ts
vendored
Normal file
41
extension/lib/emscripten/emsc.d.ts
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
export interface EmscFunGen {
|
||||
(name: string,
|
||||
ret: string,
|
||||
args: string[]): ((...x: (number|string)[]) => any);
|
||||
(name: string,
|
||||
ret: 'number',
|
||||
args: string[]): ((...x: (number|string)[]) => number);
|
||||
(name: string,
|
||||
ret: 'void',
|
||||
args: string[]): ((...x: (number|string)[]) => void);
|
||||
(name: string,
|
||||
ret: 'string',
|
||||
args: string[]): ((...x: (number|string)[]) => string);
|
||||
}
|
||||
|
||||
|
||||
export declare namespace Module {
|
||||
var cwrap: EmscFunGen;
|
||||
function _free(ptr: number);
|
||||
function _malloc(n: number): number;
|
||||
function Pointer_stringify(p: number, len?: number): string;
|
||||
function getValue(ptr: number, type: string, noSafe?: boolean): number;
|
||||
function setValue(ptr: number, value: number, type: string, noSafe?: boolean);
|
||||
function writeStringToMemory(s: string, buffer: number, dontAddNull?: boolean);
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,16 +0,0 @@
|
||||
"use strict";
|
||||
let React = {
|
||||
createElement: function (tag, props, ...children) {
|
||||
let e = document.createElement(tag);
|
||||
for (let k in props) {
|
||||
e.setAttribute(k, props[k]);
|
||||
}
|
||||
for (let child of children) {
|
||||
if ("string" === typeof child || "number" == typeof child) {
|
||||
child = document.createTextNode(child);
|
||||
}
|
||||
e.appendChild(child);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
};
|
6
extension/lib/refs.ts
Normal file
6
extension/lib/refs.ts
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
// Help the TypeScript compiler find declarations.
|
||||
|
||||
/// <reference path="decl/lib.es6.d.ts" />
|
||||
/// <reference path="decl/urijs/URIjs.d.ts" />
|
||||
/// <reference path="decl/systemjs/systemjs.d.ts" />
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2015 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
'use strict';
|
||||
/**
|
||||
* Parse an amount that is specified like '5.42 EUR'.
|
||||
* Returns a {currency,value,fraction} object or null
|
||||
* if the input is invalid.
|
||||
*/
|
||||
function amount_parse_pretty(s) {
|
||||
let pattern = /(\d+)(.\d+)?\s*([a-zA-Z]+)/;
|
||||
let matches = pattern.exec(s);
|
||||
if (null == matches) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
// Always succeeds due to regex
|
||||
value: parseInt(matches[1]),
|
||||
// Should we warn / fail on lost precision?
|
||||
fraction: Math.round(parseFloat(matches[2] || "0") * 1000000),
|
||||
currency: matches[3],
|
||||
};
|
||||
}
|
||||
function format(s, ...args) {
|
||||
function r(m, n) {
|
||||
let i = parseInt(n);
|
||||
return args[i];
|
||||
}
|
||||
s = s.replace(/{{/g, '{');
|
||||
s = s.replace(/}}/g, '}');
|
||||
s = s.replace(/{([0-9]+)}/g, r);
|
||||
return s;
|
||||
}
|
||||
function promiseFinally(p, fn) {
|
||||
return p.then((x) => { fn(); return x; })
|
||||
.catch((e) => { fn(); throw e; });
|
||||
}
|
4843
extension/lib/vendor/system.src.js
vendored
Normal file
4843
extension/lib/vendor/system.src.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
@ -23,8 +24,7 @@
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
|
||||
namespace Checkable {
|
||||
export namespace Checkable {
|
||||
let chkSym = Symbol("checkable");
|
||||
|
||||
function checkNumber(target, prop): any {
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2015 GNUnet e.V.
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
@ -24,51 +24,6 @@
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
|
||||
namespace Db {
|
||||
export interface Mint {
|
||||
baseUrl: string;
|
||||
keys: Keys
|
||||
}
|
||||
|
||||
export interface CoinWithDenom {
|
||||
coin: Coin;
|
||||
denom: Denomination;
|
||||
}
|
||||
|
||||
export interface Keys {
|
||||
denoms: Denomination[];
|
||||
}
|
||||
|
||||
export interface Denomination {
|
||||
value: AmountJson_interface;
|
||||
denom_pub: string;
|
||||
fee_withdraw: AmountJson_interface;
|
||||
fee_deposit: AmountJson_interface;
|
||||
}
|
||||
|
||||
export interface PreCoin {
|
||||
coinPub: string;
|
||||
coinPriv: string;
|
||||
reservePub: string;
|
||||
denomPub: string;
|
||||
blindingKey: string;
|
||||
withdrawSig: string;
|
||||
coinEv: string;
|
||||
mintBaseUrl: string;
|
||||
coinValue: AmountJson_interface;
|
||||
}
|
||||
|
||||
export interface Coin {
|
||||
coinPub: string;
|
||||
coinPriv: string;
|
||||
denomPub: string;
|
||||
denomSig: string;
|
||||
currentAmount: AmountJson_interface;
|
||||
mintBaseUrl: string;
|
||||
}
|
||||
}
|
||||
|
||||
const DB_NAME = "taler";
|
||||
const DB_VERSION = 1;
|
||||
|
||||
@ -76,7 +31,7 @@ const DB_VERSION = 1;
|
||||
* Return a promise that resolves
|
||||
* to the taler wallet db.
|
||||
*/
|
||||
function openTalerDb(): Promise<IDBDatabase> {
|
||||
export function openTalerDb(): Promise<IDBDatabase> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let req = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
req.onerror = (e) => {
|
||||
@ -107,7 +62,7 @@ function openTalerDb(): Promise<IDBDatabase> {
|
||||
}
|
||||
|
||||
|
||||
function exportDb(db): Promise<any> {
|
||||
export function exportDb(db): Promise<any> {
|
||||
let dump = {
|
||||
name: db.name,
|
||||
version: db.version,
|
||||
@ -136,3 +91,7 @@ function exportDb(db): Promise<any> {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteDb() {
|
||||
indexedDB.deleteDatabase(DB_NAME);
|
||||
}
|
@ -14,6 +14,9 @@
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import {AmountJson_interface} from "./types";
|
||||
import * as EmscWrapper from "../emscripten/emsc";
|
||||
|
||||
/**
|
||||
* High-level interface to emscripten-compiled modules used
|
||||
* by the wallet.
|
||||
@ -21,37 +24,8 @@
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
|
||||
"use strict";
|
||||
|
||||
declare var Module: EmscModule;
|
||||
|
||||
interface EmscModule {
|
||||
cwrap: EmscFunGen;
|
||||
_free(ptr: number);
|
||||
_malloc(n: number): number;
|
||||
Pointer_stringify(p: number, len?: number): string;
|
||||
getValue(ptr: number, type: string, noSafe?: boolean): number;
|
||||
setValue(ptr: number, value: number, type: string, noSafe?: boolean);
|
||||
writeStringToMemory(s: string, buffer: number, dontAddNull?: boolean);
|
||||
}
|
||||
|
||||
interface EmscFunGen {
|
||||
(name: string,
|
||||
ret: string,
|
||||
args: string[]): ((...x: (number|string)[]) => any);
|
||||
(name: string,
|
||||
ret: 'number',
|
||||
args: string[]): ((...x: (number|string)[]) => number);
|
||||
(name: string,
|
||||
ret: 'void',
|
||||
args: string[]): ((...x: (number|string)[]) => void);
|
||||
(name: string,
|
||||
ret: 'string',
|
||||
args: string[]): ((...x: (number|string)[]) => string);
|
||||
}
|
||||
|
||||
|
||||
// Size of a native pointer.
|
||||
const PTR_SIZE = 4;
|
||||
|
||||
@ -60,8 +34,9 @@ const GNUNET_YES = 1;
|
||||
const GNUNET_NO = 0;
|
||||
const GNUNET_SYSERR = -1;
|
||||
|
||||
let Module = EmscWrapper.Module;
|
||||
|
||||
let getEmsc: EmscFunGen = (...args) => Module.cwrap.apply(null, args);
|
||||
let getEmsc: EmscWrapper.EmscFunGen = (...args) => Module.cwrap.apply(null, args);
|
||||
|
||||
var emsc = {
|
||||
free: (ptr) => Module._free(ptr),
|
||||
@ -264,33 +239,54 @@ class DefaultArena implements Arena {
|
||||
}
|
||||
|
||||
|
||||
function mySetTimeout(ms: number, fn: () => void) {
|
||||
// We need to use different timeouts, depending on whether
|
||||
// we run in node or a web extension
|
||||
if ("function" === typeof setTimeout) {
|
||||
setTimeout(fn, ms);
|
||||
} else {
|
||||
chrome.extension.getBackgroundPage().setTimeout(fn, ms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Arena that destroys all its objects once control has returned to the message
|
||||
* loop and a small interval has passed.
|
||||
*/
|
||||
class SyncArena extends DefaultArena {
|
||||
timer: Worker;
|
||||
private isScheduled: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
let me = this;
|
||||
this.timer = new Worker('background/timerThread.js');
|
||||
this.timer.onmessage = () => {
|
||||
this.destroy();
|
||||
};
|
||||
//this.timer.postMessage({interval: 50});
|
||||
}
|
||||
|
||||
pub(obj) {
|
||||
super.put(obj);
|
||||
if (!this.isScheduled) {
|
||||
this.schedule();
|
||||
}
|
||||
this.heap.push(obj);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
private schedule() {
|
||||
this.isScheduled = true;
|
||||
mySetTimeout(50, () => {
|
||||
this.isScheduled = false;
|
||||
this.destroy();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let arenaStack: Arena[] = [];
|
||||
arenaStack.push(new SyncArena());
|
||||
|
||||
|
||||
class Amount extends ArenaObject {
|
||||
export class Amount extends ArenaObject {
|
||||
constructor(args?: AmountJson_interface, arena?: Arena) {
|
||||
super(arena);
|
||||
if (args) {
|
||||
@ -459,7 +455,7 @@ abstract class PackedArenaObject extends ArenaObject {
|
||||
}
|
||||
|
||||
|
||||
class AmountNbo extends PackedArenaObject {
|
||||
export class AmountNbo extends PackedArenaObject {
|
||||
size() {
|
||||
return 24;
|
||||
}
|
||||
@ -474,7 +470,7 @@ class AmountNbo extends PackedArenaObject {
|
||||
}
|
||||
|
||||
|
||||
class EddsaPrivateKey extends PackedArenaObject {
|
||||
export class EddsaPrivateKey extends PackedArenaObject {
|
||||
static create(a?: Arena): EddsaPrivateKey {
|
||||
let obj = new EddsaPrivateKey(a);
|
||||
obj.nativePtr = emscAlloc.eddsa_key_create();
|
||||
@ -526,7 +522,7 @@ function mixinStatic(obj, method, name?: string) {
|
||||
}
|
||||
|
||||
|
||||
class EddsaPublicKey extends PackedArenaObject {
|
||||
export class EddsaPublicKey extends PackedArenaObject {
|
||||
size() {
|
||||
return 32;
|
||||
}
|
||||
@ -560,7 +556,7 @@ function makeToCrock(encodeFn: (po: number, ps: number) => number): () => string
|
||||
return toCrock;
|
||||
}
|
||||
|
||||
class RsaBlindingKey extends ArenaObject {
|
||||
export class RsaBlindingKey extends ArenaObject {
|
||||
static create(len: number, a?: Arena) {
|
||||
let o = new RsaBlindingKey(a);
|
||||
o.nativePtr = emscAlloc.rsa_blinding_key_create(len);
|
||||
@ -577,7 +573,7 @@ class RsaBlindingKey extends ArenaObject {
|
||||
mixinStatic(RsaBlindingKey, makeFromCrock(emscAlloc.rsa_blinding_key_decode));
|
||||
|
||||
|
||||
class HashCode extends PackedArenaObject {
|
||||
export class HashCode extends PackedArenaObject {
|
||||
size() {
|
||||
return 64;
|
||||
}
|
||||
@ -608,7 +604,7 @@ class HashCode extends PackedArenaObject {
|
||||
mixinStatic(HashCode, fromCrock);
|
||||
|
||||
|
||||
class ByteArray extends PackedArenaObject {
|
||||
export class ByteArray extends PackedArenaObject {
|
||||
private allocatedSize: number;
|
||||
|
||||
size() {
|
||||
@ -646,7 +642,7 @@ class ByteArray extends PackedArenaObject {
|
||||
}
|
||||
|
||||
|
||||
class EccSignaturePurpose extends PackedArenaObject {
|
||||
export class EccSignaturePurpose extends PackedArenaObject {
|
||||
size() {
|
||||
return this.payloadSize + 8;
|
||||
}
|
||||
@ -734,7 +730,7 @@ abstract class SignatureStruct {
|
||||
|
||||
|
||||
// It's redundant, but more type safe.
|
||||
interface WithdrawRequestPS_Args {
|
||||
export interface WithdrawRequestPS_Args {
|
||||
reserve_pub: EddsaPublicKey;
|
||||
amount_with_fee: AmountNbo;
|
||||
withdraw_fee: AmountNbo;
|
||||
@ -743,7 +739,7 @@ interface WithdrawRequestPS_Args {
|
||||
}
|
||||
|
||||
|
||||
class WithdrawRequestPS extends SignatureStruct {
|
||||
export class WithdrawRequestPS extends SignatureStruct {
|
||||
constructor(w: WithdrawRequestPS_Args) {
|
||||
super(w);
|
||||
}
|
||||
@ -764,7 +760,7 @@ class WithdrawRequestPS extends SignatureStruct {
|
||||
}
|
||||
|
||||
|
||||
class AbsoluteTimeNbo extends PackedArenaObject {
|
||||
export class AbsoluteTimeNbo extends PackedArenaObject {
|
||||
static fromTalerString(s: string): AbsoluteTimeNbo {
|
||||
let x = new AbsoluteTimeNbo();
|
||||
x.alloc();
|
||||
@ -795,7 +791,7 @@ function set64(p: number, n: number) {
|
||||
}
|
||||
|
||||
|
||||
class UInt64 extends PackedArenaObject {
|
||||
export class UInt64 extends PackedArenaObject {
|
||||
static fromNumber(n: number): UInt64 {
|
||||
let x = new UInt64();
|
||||
x.alloc();
|
||||
@ -810,7 +806,7 @@ class UInt64 extends PackedArenaObject {
|
||||
|
||||
|
||||
// It's redundant, but more type safe.
|
||||
interface DepositRequestPS_Args {
|
||||
export interface DepositRequestPS_Args {
|
||||
h_contract: HashCode;
|
||||
h_wire: HashCode;
|
||||
timestamp: AbsoluteTimeNbo;
|
||||
@ -823,7 +819,7 @@ interface DepositRequestPS_Args {
|
||||
}
|
||||
|
||||
|
||||
class DepositRequestPS extends SignatureStruct {
|
||||
export class DepositRequestPS extends SignatureStruct {
|
||||
constructor(w: DepositRequestPS_Args) {
|
||||
super(w);
|
||||
}
|
||||
@ -865,7 +861,7 @@ function makeEncode(encodeFn) {
|
||||
}
|
||||
|
||||
|
||||
class RsaPublicKey extends ArenaObject implements Encodeable {
|
||||
export class RsaPublicKey extends ArenaObject implements Encodeable {
|
||||
static fromCrock: (s: string, a?: Arena) => RsaPublicKey;
|
||||
|
||||
toCrock() {
|
||||
@ -883,14 +879,14 @@ mixinStatic(RsaPublicKey, makeFromCrock(emscAlloc.rsa_public_key_decode));
|
||||
mixin(RsaPublicKey, makeEncode(emscAlloc.rsa_public_key_encode));
|
||||
|
||||
|
||||
class EddsaSignature extends PackedArenaObject {
|
||||
export class EddsaSignature extends PackedArenaObject {
|
||||
size() {
|
||||
return 64;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RsaSignature extends ArenaObject implements Encodeable{
|
||||
export class RsaSignature extends ArenaObject implements Encodeable{
|
||||
static fromCrock: (s: string, a?: Arena) => RsaSignature;
|
||||
|
||||
encode: (arena?: Arena) => ByteArray;
|
||||
@ -904,7 +900,7 @@ mixinStatic(RsaSignature, makeFromCrock(emscAlloc.rsa_signature_decode));
|
||||
mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
|
||||
|
||||
|
||||
function rsaBlind(hashCode: HashCode,
|
||||
export function rsaBlind(hashCode: HashCode,
|
||||
blindingKey: RsaBlindingKey,
|
||||
pkey: RsaPublicKey,
|
||||
arena?: Arena): ByteArray {
|
||||
@ -917,7 +913,7 @@ function rsaBlind(hashCode: HashCode,
|
||||
}
|
||||
|
||||
|
||||
function eddsaSign(purpose: EccSignaturePurpose,
|
||||
export function eddsaSign(purpose: EccSignaturePurpose,
|
||||
priv: EddsaPrivateKey,
|
||||
a?: Arena): EddsaSignature {
|
||||
let sig = new EddsaSignature(a);
|
||||
@ -930,7 +926,7 @@ function eddsaSign(purpose: EccSignaturePurpose,
|
||||
}
|
||||
|
||||
|
||||
function rsaUnblind(sig: RsaSignature,
|
||||
export function rsaUnblind(sig: RsaSignature,
|
||||
bk: RsaBlindingKey,
|
||||
pk: RsaPublicKey,
|
||||
a?: Arena): RsaSignature {
|
@ -22,13 +22,15 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
interface HttpResponse {
|
||||
|
||||
|
||||
export interface HttpResponse {
|
||||
status: number;
|
||||
responseText: string;
|
||||
}
|
||||
|
||||
|
||||
class BrowserHttpLib {
|
||||
export class BrowserHttpLib {
|
||||
req(method: string,
|
||||
url: string|uri.URI,
|
||||
options?: any): Promise<HttpResponse> {
|
||||
@ -76,7 +78,7 @@ class BrowserHttpLib {
|
||||
}
|
||||
|
||||
|
||||
class RequestException {
|
||||
export class RequestException {
|
||||
constructor(detail) {
|
||||
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/// <reference path="../decl/chrome/chrome.d.ts" />
|
||||
/// <reference path="../decl/urijs/URIjs.d.ts" />
|
||||
|
||||
|
||||
/**
|
||||
@ -26,7 +26,7 @@
|
||||
"use strict";
|
||||
|
||||
|
||||
function Query(db) {
|
||||
export function Query(db) {
|
||||
return new QueryRoot(db);
|
||||
}
|
||||
|
@ -6,5 +6,5 @@
|
||||
*/
|
||||
|
||||
onmessage = function(e) {
|
||||
self.setInterval(() => postMessage(true), e.data.interval);
|
||||
self.setInterval(() => postMessage(true, "timerThread"), e.data.interval);
|
||||
};
|
109
extension/lib/wallet/types.ts
Normal file
109
extension/lib/wallet/types.ts
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// TODO: factor into multiple files
|
||||
|
||||
export interface Mint {
|
||||
baseUrl: string;
|
||||
keys: Keys
|
||||
}
|
||||
|
||||
export interface CoinWithDenom {
|
||||
coin: Coin;
|
||||
denom: Denomination;
|
||||
}
|
||||
|
||||
export interface Keys {
|
||||
denoms: Denomination[];
|
||||
}
|
||||
|
||||
export interface Denomination {
|
||||
value: AmountJson_interface;
|
||||
denom_pub: string;
|
||||
fee_withdraw: AmountJson_interface;
|
||||
fee_deposit: AmountJson_interface;
|
||||
}
|
||||
|
||||
export interface PreCoin {
|
||||
coinPub: string;
|
||||
coinPriv: string;
|
||||
reservePub: string;
|
||||
denomPub: string;
|
||||
blindingKey: string;
|
||||
withdrawSig: string;
|
||||
coinEv: string;
|
||||
mintBaseUrl: string;
|
||||
coinValue: AmountJson_interface;
|
||||
}
|
||||
|
||||
export interface Coin {
|
||||
coinPub: string;
|
||||
coinPriv: string;
|
||||
denomPub: string;
|
||||
denomSig: string;
|
||||
currentAmount: AmountJson_interface;
|
||||
mintBaseUrl: string;
|
||||
}
|
||||
|
||||
|
||||
export interface AmountJson_interface {
|
||||
value: number;
|
||||
fraction: number
|
||||
currency: string;
|
||||
}
|
||||
|
||||
export interface ConfirmReserveRequest {
|
||||
/**
|
||||
* Name of the form field for the amount.
|
||||
*/
|
||||
field_amount;
|
||||
|
||||
/**
|
||||
* Name of the form field for the reserve public key.
|
||||
*/
|
||||
field_reserve_pub;
|
||||
|
||||
/**
|
||||
* Name of the form field for the reserve public key.
|
||||
*/
|
||||
field_mint;
|
||||
|
||||
/**
|
||||
* The actual amount in string form.
|
||||
* TODO: where is this format specified?
|
||||
*/
|
||||
amount_str;
|
||||
|
||||
/**
|
||||
* Target URL for the reserve creation request.
|
||||
*/
|
||||
post_url;
|
||||
|
||||
/**
|
||||
* Mint URL where the bank should create the reserve.
|
||||
*/
|
||||
mint;
|
||||
}
|
||||
|
||||
|
||||
export interface ConfirmReserveResponse {
|
||||
backlink: string;
|
||||
success: boolean;
|
||||
status: number;
|
||||
text: string;
|
||||
}
|
@ -21,8 +21,35 @@
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
import {Amount} from "./emscriptif"
|
||||
import {AmountJson_interface} from "./types";
|
||||
import {CoinWithDenom} from "./types";
|
||||
import {DepositRequestPS_Args} from "./emscriptif";
|
||||
import {HashCode} from "./emscriptif";
|
||||
import {EddsaPublicKey} from "./emscriptif";
|
||||
import {Coin} from "./types";
|
||||
import {AbsoluteTimeNbo} from "./emscriptif";
|
||||
import {UInt64} from "./emscriptif";
|
||||
import {DepositRequestPS} from "./emscriptif";
|
||||
import {eddsaSign} from "./emscriptif";
|
||||
import {EddsaPrivateKey} from "./emscriptif";
|
||||
import {ConfirmReserveRequest} from "./types";
|
||||
import {ConfirmReserveResponse} from "./types";
|
||||
import {RsaPublicKey} from "./emscriptif";
|
||||
import {Denomination} from "./types";
|
||||
import {RsaBlindingKey} from "./emscriptif";
|
||||
import {ByteArray} from "./emscriptif";
|
||||
import {rsaBlind} from "./emscriptif";
|
||||
import {WithdrawRequestPS} from "./emscriptif";
|
||||
import {PreCoin} from "./types";
|
||||
import {rsaUnblind} from "./emscriptif";
|
||||
import {RsaSignature} from "./emscriptif";
|
||||
import {Mint} from "./types";
|
||||
import {Checkable} from "./checkable";
|
||||
import {HttpResponse} from "./http";
|
||||
import {RequestException} from "./http";
|
||||
import {Query} from "./query";
|
||||
|
||||
/// <reference path="../decl/urijs/URIjs.d.ts" />
|
||||
"use strict";
|
||||
|
||||
@Checkable.Class
|
||||
@ -60,11 +87,6 @@ class CoinPaySig {
|
||||
static check: (v: any) => CoinPaySig;
|
||||
}
|
||||
|
||||
interface AmountJson_interface {
|
||||
value: number;
|
||||
fraction: number
|
||||
currency: string;
|
||||
}
|
||||
|
||||
interface ConfirmPayRequest {
|
||||
merchantPageUrl: string;
|
||||
@ -72,7 +94,7 @@ interface ConfirmPayRequest {
|
||||
}
|
||||
|
||||
interface MintCoins {
|
||||
[mintUrl: string]: Db.CoinWithDenom[];
|
||||
[mintUrl: string]: CoinWithDenom[];
|
||||
}
|
||||
|
||||
|
||||
@ -136,49 +158,13 @@ interface PaymentResponse {
|
||||
}
|
||||
|
||||
|
||||
interface ConfirmReserveRequest {
|
||||
/**
|
||||
* Name of the form field for the amount.
|
||||
*/
|
||||
field_amount;
|
||||
|
||||
/**
|
||||
* Name of the form field for the reserve public key.
|
||||
*/
|
||||
field_reserve_pub;
|
||||
|
||||
/**
|
||||
* Name of the form field for the reserve public key.
|
||||
*/
|
||||
field_mint;
|
||||
|
||||
/**
|
||||
* The actual amount in string form.
|
||||
* TODO: where is this format specified?
|
||||
*/
|
||||
amount_str;
|
||||
|
||||
/**
|
||||
* Target URL for the reserve creation request.
|
||||
*/
|
||||
post_url;
|
||||
|
||||
/**
|
||||
* Mint URL where the bank should create the reserve.
|
||||
*/
|
||||
mint;
|
||||
export interface Badge {
|
||||
setText(s: string): void;
|
||||
setColor(c: string): void;
|
||||
}
|
||||
|
||||
|
||||
interface ConfirmReserveResponse {
|
||||
backlink: string;
|
||||
success: boolean;
|
||||
status: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
|
||||
type PayCoinInfo = Array<{ updatedCoin: Db.Coin, sig: CoinPaySig_interface }>;
|
||||
type PayCoinInfo = Array<{ updatedCoin: Coin, sig: CoinPaySig_interface }>;
|
||||
|
||||
|
||||
/**
|
||||
@ -208,11 +194,6 @@ interface HttpRequestLibrary {
|
||||
postForm(url: string|uri.URI, form): Promise<HttpResponse>;
|
||||
}
|
||||
|
||||
interface Badge {
|
||||
setText(s: string): void;
|
||||
setColor(c: string): void;
|
||||
}
|
||||
|
||||
|
||||
function copy(o) {
|
||||
return JSON.parse(JSON.stringify(o));
|
||||
@ -227,7 +208,7 @@ function rankDenom(denom1: any, denom2: any) {
|
||||
}
|
||||
|
||||
|
||||
class Wallet {
|
||||
export class Wallet {
|
||||
private db: IDBDatabase;
|
||||
private http: HttpRequestLibrary;
|
||||
private badge: Badge;
|
||||
@ -239,7 +220,7 @@ class Wallet {
|
||||
}
|
||||
|
||||
static signDeposit(offer: Offer,
|
||||
cds: Db.CoinWithDenom[]): PayCoinInfo {
|
||||
cds: CoinWithDenom[]): PayCoinInfo {
|
||||
let ret = [];
|
||||
let amountSpent = Amount.getZero(cds[0].coin.currentAmount.currency);
|
||||
let amountRemaining = new Amount(offer.contract.amount);
|
||||
@ -349,7 +330,7 @@ class Wallet {
|
||||
let minAmount = new Amount(paymentAmount);
|
||||
let accFee = new Amount(coins[0].c.denom.fee_deposit);
|
||||
let accAmount = Amount.getZero(coins[0].c.coin.currentAmount.currency);
|
||||
let usableCoins: Db.CoinWithDenom[] = [];
|
||||
let usableCoins: CoinWithDenom[] = [];
|
||||
nextCoin:
|
||||
for (let i = 0; i < coins.length; i++) {
|
||||
let coinAmount = new Amount(coins[i].c.coin.currentAmount);
|
||||
@ -488,8 +469,8 @@ class Wallet {
|
||||
});
|
||||
}
|
||||
|
||||
withdrawPrepare(denom: Db.Denomination,
|
||||
reserve: Reserve): Promise<Db.PreCoin> {
|
||||
withdrawPrepare(denom: Denomination,
|
||||
reserve: Reserve): Promise<PreCoin> {
|
||||
let reservePriv = new EddsaPrivateKey();
|
||||
reservePriv.loadCrock(reserve.reserve_priv);
|
||||
let reservePub = new EddsaPublicKey();
|
||||
@ -520,7 +501,7 @@ class Wallet {
|
||||
|
||||
var sig = eddsaSign(withdrawRequest.toPurpose(), reservePriv);
|
||||
|
||||
let preCoin: Db.PreCoin = {
|
||||
let preCoin: PreCoin = {
|
||||
reservePub: reservePub.toCrock(),
|
||||
blindingKey: blindingFactor.toCrock(),
|
||||
coinPub: coinPub.toCrock(),
|
||||
@ -536,7 +517,7 @@ class Wallet {
|
||||
}
|
||||
|
||||
|
||||
withdrawExecute(pc: Db.PreCoin): Promise<Db.Coin> {
|
||||
withdrawExecute(pc: PreCoin): Promise<Coin> {
|
||||
return Query(this.db)
|
||||
.get("reserves", pc.reservePub)
|
||||
.then((r) => {
|
||||
@ -559,7 +540,7 @@ class Wallet {
|
||||
let denomSig = rsaUnblind(RsaSignature.fromCrock(r.ev_sig),
|
||||
RsaBlindingKey.fromCrock(pc.blindingKey),
|
||||
RsaPublicKey.fromCrock(pc.denomPub));
|
||||
let coin: Db.Coin = {
|
||||
let coin: Coin = {
|
||||
coinPub: pc.coinPub,
|
||||
coinPriv: pc.coinPriv,
|
||||
denomPub: pc.denomPub,
|
||||
@ -591,7 +572,7 @@ class Wallet {
|
||||
.then(doBadge.bind(this));
|
||||
}
|
||||
|
||||
storeCoin(coin: Db.Coin) {
|
||||
storeCoin(coin: Coin) {
|
||||
Query(this.db)
|
||||
.delete("precoins", coin.coinPub)
|
||||
.add("coins", coin)
|
||||
@ -688,7 +669,7 @@ class Wallet {
|
||||
if (!mintKeysJson) {
|
||||
throw new RequestException({url: reqUrl, hint: "keys invalid"});
|
||||
}
|
||||
let mint: Db.Mint = {
|
||||
let mint: Mint = {
|
||||
baseUrl: baseUrl,
|
||||
keys: mintKeysJson
|
||||
};
|
||||
@ -698,7 +679,7 @@ class Wallet {
|
||||
|
||||
|
||||
getBalances(): Promise<any> {
|
||||
function collectBalances(c: Db.Coin, byCurrency) {
|
||||
function collectBalances(c: Coin, byCurrency) {
|
||||
let acc: AmountJson_interface = byCurrency[c.currentAmount.currency];
|
||||
if (!acc) {
|
||||
acc = Amount.getZero(c.currentAmount.currency).toJson();
|
144
extension/lib/wallet/wxmessaging.js
Normal file
144
extension/lib/wallet/wxmessaging.js
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
System.register(["./wallet", "./db", "./http"], function(exports_1) {
|
||||
"use strict";
|
||||
var wallet_1, db_1, db_2, db_3, http_1;
|
||||
var ChromeBadge;
|
||||
function makeHandlers(wallet) {
|
||||
return (_a = {},
|
||||
_a["balances"] = function (db, detail, sendResponse) {
|
||||
wallet.getBalances().then(sendResponse);
|
||||
return true;
|
||||
},
|
||||
_a["dump-db"] = function (db, detail, sendResponse) {
|
||||
db_1.exportDb(db).then(sendResponse);
|
||||
return true;
|
||||
},
|
||||
_a["reset"] = function (db, detail, sendResponse) {
|
||||
var tx = db.transaction(db.objectStoreNames, 'readwrite');
|
||||
for (var i = 0; i < db.objectStoreNames.length; i++) {
|
||||
tx.objectStore(db.objectStoreNames[i]).clear();
|
||||
}
|
||||
db_2.deleteDb();
|
||||
chrome.browserAction.setBadgeText({ text: "" });
|
||||
console.log("reset done");
|
||||
// Response is synchronous
|
||||
return false;
|
||||
},
|
||||
_a["confirm-reserve"] = function (db, detail, sendResponse) {
|
||||
// TODO: make it a checkable
|
||||
var req = {
|
||||
field_amount: detail.field_amount,
|
||||
field_mint: detail.field_mint,
|
||||
field_reserve_pub: detail.field_reserve_pub,
|
||||
post_url: detail.post_url,
|
||||
mint: detail.mint,
|
||||
amount_str: detail.amount_str
|
||||
};
|
||||
wallet.confirmReserve(req)
|
||||
.then(function (resp) {
|
||||
if (resp.success) {
|
||||
resp.backlink = chrome.extension.getURL("pages/reserve-success.html");
|
||||
}
|
||||
sendResponse(resp);
|
||||
});
|
||||
return true;
|
||||
},
|
||||
_a["confirm-pay"] = function (db, detail, sendResponse) {
|
||||
wallet.confirmPay(detail.offer, detail.merchantPageUrl)
|
||||
.then(function () {
|
||||
sendResponse({ success: true });
|
||||
})
|
||||
.catch(function (e) {
|
||||
sendResponse({ error: e.message });
|
||||
});
|
||||
return true;
|
||||
},
|
||||
_a["execute-payment"] = function (db, detail, sendResponse) {
|
||||
wallet.doPayment(detail.H_contract)
|
||||
.then(function (r) {
|
||||
sendResponse({
|
||||
success: true,
|
||||
payUrl: r.payUrl,
|
||||
payReq: r.payReq
|
||||
});
|
||||
})
|
||||
.catch(function (e) {
|
||||
sendResponse({ success: false, error: e.message });
|
||||
});
|
||||
// async sendResponse
|
||||
return true;
|
||||
},
|
||||
_a
|
||||
);
|
||||
var _a;
|
||||
}
|
||||
function wxMain() {
|
||||
chrome.browserAction.setBadgeText({ text: "" });
|
||||
db_3.openTalerDb().then(function (db) {
|
||||
var http = new http_1.BrowserHttpLib();
|
||||
var badge = new ChromeBadge();
|
||||
var wallet = new wallet_1.Wallet(db, http, badge);
|
||||
var handlers = makeHandlers(wallet);
|
||||
wallet.updateBadge();
|
||||
chrome.runtime.onMessage.addListener(function (req, sender, onresponse) {
|
||||
if (req.type in handlers) {
|
||||
return handlers[req.type](db, req.detail, onresponse);
|
||||
}
|
||||
console.error(format("Request type {1} unknown, req {0}", JSON.stringify(req), req.type));
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
exports_1("wxMain", wxMain);
|
||||
return {
|
||||
setters:[
|
||||
function (wallet_1_1) {
|
||||
wallet_1 = wallet_1_1;
|
||||
},
|
||||
function (db_1_1) {
|
||||
db_1 = db_1_1;
|
||||
db_2 = db_1_1;
|
||||
db_3 = db_1_1;
|
||||
},
|
||||
function (http_1_1) {
|
||||
http_1 = http_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
/**
|
||||
* Messaging for the WebExtensions wallet. Should contain
|
||||
* parts that are specific for WebExtensions, but as little business
|
||||
* logic as possible.
|
||||
* @module Messaging
|
||||
* @author Florian Dold
|
||||
*/
|
||||
"use strict";
|
||||
ChromeBadge = (function () {
|
||||
function ChromeBadge() {
|
||||
}
|
||||
ChromeBadge.prototype.setText = function (s) {
|
||||
chrome.browserAction.setBadgeText({ text: s });
|
||||
};
|
||||
ChromeBadge.prototype.setColor = function (c) {
|
||||
chrome.browserAction.setBadgeBackgroundColor({ color: c });
|
||||
};
|
||||
return ChromeBadge;
|
||||
}());
|
||||
wxMain();
|
||||
}
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=wxmessaging.js.map
|
@ -15,6 +15,13 @@
|
||||
*/
|
||||
|
||||
|
||||
import {ConfirmReserveRequest} from "./types";
|
||||
import {Wallet} from "./wallet";
|
||||
import {exportDb} from "./db";
|
||||
import {deleteDb} from "./db";
|
||||
import {openTalerDb} from "./db";
|
||||
import {BrowserHttpLib} from "./http";
|
||||
import {Badge} from "./wallet";
|
||||
/**
|
||||
* Messaging for the WebExtensions wallet. Should contain
|
||||
* parts that are specific for WebExtensions, but as little business
|
||||
@ -40,7 +47,8 @@ function makeHandlers(wallet) {
|
||||
for (let i = 0; i < db.objectStoreNames.length; i++) {
|
||||
tx.objectStore(db.objectStoreNames[i]).clear();
|
||||
}
|
||||
indexedDB.deleteDatabase(DB_NAME);
|
||||
deleteDb();
|
||||
|
||||
chrome.browserAction.setBadgeText({text: ""});
|
||||
console.log("reset done");
|
||||
// Response is synchronous
|
||||
@ -94,7 +102,7 @@ function makeHandlers(wallet) {
|
||||
};
|
||||
}
|
||||
|
||||
class ChromeBadge {
|
||||
class ChromeBadge implements Badge {
|
||||
setText(s: string) {
|
||||
chrome.browserAction.setBadgeText({text: s});
|
||||
}
|
||||
@ -105,7 +113,7 @@ class ChromeBadge {
|
||||
}
|
||||
|
||||
|
||||
function wxMain() {
|
||||
export function wxMain() {
|
||||
chrome.browserAction.setBadgeText({text: ""});
|
||||
|
||||
openTalerDb().then((db) => {
|
@ -26,9 +26,10 @@
|
||||
{
|
||||
"matches": ["*://*/*"],
|
||||
"js": [
|
||||
"content_scripts/notify.js",
|
||||
"lib/URI.js",
|
||||
"lib/util.js"
|
||||
"lib/vendor/system.src.js",
|
||||
"lib/vendor/URI.js",
|
||||
"lib/util.js",
|
||||
"content_scripts/notify.js"
|
||||
],
|
||||
"run_at": "document_start"
|
||||
}
|
||||
@ -41,16 +42,12 @@
|
||||
|
||||
"background": {
|
||||
"scripts": [
|
||||
"lib/vendor/URI.js",
|
||||
"lib/vendor/handlebars-v4.0.5.js",
|
||||
"lib/util.js",
|
||||
"lib/URI.js",
|
||||
"background/checkable.js",
|
||||
"background/libwrapper.js",
|
||||
"background/emscriptif.js",
|
||||
"background/db.js",
|
||||
"background/query.js",
|
||||
"background/messaging.js",
|
||||
"background/http.js",
|
||||
"background/wallet.js"
|
||||
"lib/emscripten/libwrapper.js",
|
||||
"lib/vendor/system.src.js",
|
||||
"background/main.js"
|
||||
]
|
||||
},
|
||||
|
||||
|
20
extension/package.json
Normal file
20
extension/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "taler-wallet",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"main": "wxwallet.js",
|
||||
"scripts": {
|
||||
"test": "mocha --delay"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://git.taler.net/wallet.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"better-assert": "^1.0.2",
|
||||
"mocha": "^2.3.4",
|
||||
"systemjs": "^0.19.14"
|
||||
}
|
||||
}
|
@ -3,8 +3,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Taler Wallet: Confirm Reserve Creation</title>
|
||||
<script src="../lib/URI.js"></script>
|
||||
<script src="../lib/handlebars-v4.0.5.js"></script>
|
||||
<script src="../lib/vendor/URI.js"></script>
|
||||
<script src="../lib/vendor/handlebars-v4.0.5.js"></script>
|
||||
<script src="../lib/commonHelpers.js"></script>
|
||||
<script src="confirm-contract.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../style/wallet.css">
|
||||
|
@ -13,13 +13,13 @@
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/// <reference path="../decl/handlebars/handlebars.d.ts" />
|
||||
/// <reference path="../lib/decl/handlebars/handlebars.d.ts" />
|
||||
"use strict";
|
||||
let url = URI(document.location.href);
|
||||
let query = URI.parseQuery(url.query());
|
||||
let $_ = (x) => document.getElementById(x);
|
||||
var url = URI(document.location.href);
|
||||
var query = URI.parseQuery(url.query());
|
||||
var $_ = function (x) { return document.getElementById(x); };
|
||||
function renderContract(contract) {
|
||||
let showAmount = document.getElementById("show-amount");
|
||||
var showAmount = document.getElementById("show-amount");
|
||||
$_('merchant-name').innerText = contract.merchant.name;
|
||||
}
|
||||
function clone(obj) {
|
||||
@ -27,26 +27,26 @@ function clone(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
Handlebars.registerHelper('prettyAmount', function (amount) {
|
||||
let v = amount.value + amount.fraction / 10e6;
|
||||
var v = amount.value + amount.fraction / 10e6;
|
||||
return v.toFixed(2) + " " + amount.currency;
|
||||
});
|
||||
document.addEventListener("DOMContentLoaded", (e) => {
|
||||
let offer = JSON.parse(query.offer);
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
var offer = JSON.parse(query.offer);
|
||||
console.dir(offer);
|
||||
let source = $_("contract-template").innerHTML;
|
||||
let template = Handlebars.compile(source);
|
||||
var source = $_("contract-template").innerHTML;
|
||||
var template = Handlebars.compile(source);
|
||||
$_("render-contract").innerHTML = template(offer.contract);
|
||||
document.getElementById("confirm-pay").addEventListener("click", (e) => {
|
||||
document.getElementById("confirm-pay").addEventListener("click", function (e) {
|
||||
console.log("Query:", JSON.stringify(query));
|
||||
let d = {
|
||||
var d = {
|
||||
offer: JSON.parse(query.offer),
|
||||
merchantPageUrl: query.merchantPageUrl
|
||||
};
|
||||
chrome.runtime.sendMessage({ type: 'confirm-pay', detail: d }, (resp) => {
|
||||
chrome.runtime.sendMessage({ type: 'confirm-pay', detail: d }, function (resp) {
|
||||
if (!resp.success) {
|
||||
let source = $_("error-template").innerHTML;
|
||||
let template = Handlebars.compile(source);
|
||||
$_("status").innerHTML = template(resp);
|
||||
var source_1 = $_("error-template").innerHTML;
|
||||
var template_1 = Handlebars.compile(source_1);
|
||||
$_("status").innerHTML = template_1(resp);
|
||||
return;
|
||||
}
|
||||
document.location.href = URI(d.offer.exec_url)
|
||||
@ -56,3 +56,4 @@ document.addEventListener("DOMContentLoaded", (e) => {
|
||||
});
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=confirm-contract.js.map
|
@ -14,7 +14,7 @@
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/// <reference path="../decl/handlebars/handlebars.d.ts" />
|
||||
/// <reference path="../lib/decl/handlebars/handlebars.d.ts" />
|
||||
"use strict";
|
||||
|
||||
let url = URI(document.location.href);
|
||||
|
@ -3,7 +3,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Taler Wallet: Select Taler Provider</title>
|
||||
<script src="../lib/URI.js"></script>
|
||||
<script src="../lib/vendor/URI.js"></script>
|
||||
<script src="../lib/polyfill-react.js"></script>
|
||||
<script src="confirm-create-reserve.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../style/wallet.css">
|
||||
|
@ -16,12 +16,12 @@
|
||||
"use strict";
|
||||
var ConfirmCreateReserve;
|
||||
(function (ConfirmCreateReserve) {
|
||||
let url = URI(document.location.href);
|
||||
let query = URI.parseQuery(url.query());
|
||||
var url = URI(document.location.href);
|
||||
var query = URI.parseQuery(url.query());
|
||||
function updateAmount() {
|
||||
let showAmount = document.getElementById("show-amount");
|
||||
var showAmount = document.getElementById("show-amount");
|
||||
console.log("Query is " + JSON.stringify(query));
|
||||
let s = query.amount_str;
|
||||
var s = query.amount_str;
|
||||
if (!s) {
|
||||
document.body.innerHTML = "Oops, something went wrong.";
|
||||
return;
|
||||
@ -32,25 +32,21 @@ var ConfirmCreateReserve;
|
||||
// This is faster than it looks ...
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", (e) => {
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
updateAmount();
|
||||
document.getElementById("confirm").addEventListener("click", (e) => {
|
||||
let d = clone(query);
|
||||
document.getElementById("confirm").addEventListener("click", function (e) {
|
||||
var d = clone(query);
|
||||
d.mint = document.getElementById('mint-url').value;
|
||||
chrome.runtime.sendMessage({ type: 'confirm-reserve', detail: d }, (resp) => {
|
||||
chrome.runtime.sendMessage({ type: 'confirm-reserve', detail: d }, function (resp) {
|
||||
if (resp.success === true) {
|
||||
document.location.href = resp.backlink;
|
||||
}
|
||||
else {
|
||||
document.body.innerHTML =
|
||||
`
|
||||
Oops, something went wrong.
|
||||
The bank responded with HTTP status code ${resp.status}.
|
||||
Here is some more info:
|
||||
<pre>${resp.text}</pre>
|
||||
</div>`;
|
||||
"\n Oops, something went wrong.\n The bank responded with HTTP status code " + resp.status + ".\n Here is some more info:\n <pre>" + resp.text + "</pre>\n </div>";
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})(ConfirmCreateReserve || (ConfirmCreateReserve = {}));
|
||||
//# sourceMappingURL=confirm-create-reserve.js.map
|
@ -5,8 +5,8 @@
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="popup.css" type="text/css">
|
||||
<script src="../lib/util.js" type="text/javascript"></script>
|
||||
<script src="../lib/handlebars-v4.0.5.js"></script>
|
||||
<script src="../lib/commonHelpers.js"></script>
|
||||
<script src="../lib/vendor/handlebars-v4.0.5.js" type="text/javascript"></script>
|
||||
<script src="../lib/commonHelpers.js" type="text/javascript"></script>
|
||||
<script src="balance-overview.js" type="text/javascript"></script>
|
||||
|
||||
<script id="balance-template" type="text/x-handlebars-template">
|
||||
|
@ -14,30 +14,31 @@
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
"use strict";
|
||||
document.addEventListener("DOMContentLoaded", (e) => {
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
console.log("content loaded");
|
||||
chrome.runtime.sendMessage({ type: "balances" }, function (wallet) {
|
||||
let context = document.getElementById("balance-template").innerHTML;
|
||||
let template = Handlebars.compile(context);
|
||||
var context = document.getElementById("balance-template").innerHTML;
|
||||
var template = Handlebars.compile(context);
|
||||
document.getElementById("content").innerHTML = template(wallet);
|
||||
console.log("got wallet", JSON.stringify(wallet));
|
||||
let el = document.getElementById("link-kudos");
|
||||
var el = document.getElementById("link-kudos");
|
||||
if (el) {
|
||||
el.onclick = (e) => {
|
||||
let target = e.target;
|
||||
el.onclick = function (e) {
|
||||
var target = e.target;
|
||||
chrome.tabs.create({
|
||||
"url": target.href
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
document.getElementById("debug").addEventListener("click", (e) => {
|
||||
document.getElementById("debug").addEventListener("click", function (e) {
|
||||
chrome.tabs.create({
|
||||
"url": chrome.extension.getURL("pages/debug.html")
|
||||
});
|
||||
});
|
||||
document.getElementById("reset").addEventListener("click", (e) => {
|
||||
document.getElementById("reset").addEventListener("click", function (e) {
|
||||
chrome.runtime.sendMessage({ type: "reset" });
|
||||
window.close();
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=balance-overview.js.map
|
55
extension/test/run_tests.js
Normal file
55
extension/test/run_tests.js
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
/**
|
||||
* Bridge between the mocha test runner / nodejs
|
||||
* and the typescript / the wallet's module system.
|
||||
*
|
||||
* The test cases use better-assert as assert library
|
||||
* with mocha's bdd UI.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
let assert = require("better-assert");
|
||||
let vm = require("vm");
|
||||
let fs = require("fs");
|
||||
|
||||
|
||||
if ("function" !== typeof run) {
|
||||
throw Error("test must be run with 'mocha --delay ...'");
|
||||
}
|
||||
|
||||
console.log("typeof require (here)", typeof require);
|
||||
|
||||
// We might need thins in the future ...
|
||||
global.nodeRequire = function (modulePath) {
|
||||
return require(modulePath);
|
||||
};
|
||||
|
||||
global.require = global.nodeRequire;
|
||||
|
||||
let data = fs.readFileSync("lib/emscripten/libwrapper.js");
|
||||
vm.runInThisContext(data);
|
||||
|
||||
// Do it here, since it breaks 'require''
|
||||
let System = require("systemjs");
|
||||
|
||||
System.config({
|
||||
defaultJSExtensions: true
|
||||
});
|
||||
|
||||
let mod = System.newModule({Module: Module});
|
||||
let modName = System.normalizeSync(__dirname + "/../lib/emscripten/emsc");
|
||||
console.log("registering", modName);
|
||||
System.set(modName, mod);
|
||||
|
||||
|
||||
System.import("./test/tests/taler.js")
|
||||
.then((t) => {
|
||||
t.declareTests(assert, context, it);
|
||||
run();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("failed to load module", e.stack);
|
||||
});
|
||||
|
||||
|
10
extension/test/tests/taler.ts
Normal file
10
extension/test/tests/taler.ts
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
import * as Emsc from '../../lib/wallet/emscriptif';
|
||||
|
||||
export function declareTests(assert, context, it) {
|
||||
it("works!", function() {
|
||||
let x = new Emsc.Amount({value: 42, fraction:42, currency: "EUR"});
|
||||
let j = x.toJson();
|
||||
assert("value" in j);
|
||||
});
|
||||
}
|
@ -1,23 +1,32 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"experimentalDecorators": true
|
||||
"experimentalDecorators": true,
|
||||
"module": "system",
|
||||
"noLib": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"files": [
|
||||
"background/wallet.ts",
|
||||
"background/emscriptif.ts",
|
||||
"background/db.ts",
|
||||
"background/query.ts",
|
||||
"background/http.ts",
|
||||
"background/checkable.ts",
|
||||
"background/messaging.ts",
|
||||
"lib/refs.ts",
|
||||
"lib/wallet/wallet.ts",
|
||||
"lib/wallet/emscriptif.ts",
|
||||
"lib/wallet/db.ts",
|
||||
"lib/wallet/query.ts",
|
||||
"lib/wallet/http.ts",
|
||||
"lib/wallet/checkable.ts",
|
||||
"lib/wallet/wxmessaging.ts",
|
||||
"lib/wallet/types.ts",
|
||||
"lib/util.ts",
|
||||
"lib/commonHelpers.ts",
|
||||
"lib/polyfill-react.ts",
|
||||
"lib/wallet/timerThread.ts",
|
||||
"content_scripts/notify.ts",
|
||||
"background/main.ts",
|
||||
"popup/balance-overview.tsx",
|
||||
"popup/history.tsx",
|
||||
"pages/confirm-contract.tsx",
|
||||
"pages/confirm-create-reserve.tsx"
|
||||
"pages/confirm-create-reserve.tsx",
|
||||
"test/tests/taler.ts"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user