Towards payment.
This commit is contained in:
parent
5f907c13fc
commit
38c947d771
@ -1,5 +1,34 @@
|
||||
"use strict";
|
||||
const DB_NAME = "taler";
|
||||
const DB_VERSION = 1;
|
||||
/**
|
||||
* Declarations and helpers for
|
||||
* things that are stored in the wallet's
|
||||
* database.
|
||||
* 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 });
|
||||
break;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
89
extension/background/db.ts
Normal file
89
extension/background/db.ts
Normal file
@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Declarations and helpers for
|
||||
* things that are stored in the wallet's
|
||||
* database.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
namespace Db {
|
||||
export interface Mint {
|
||||
baseUrl: string;
|
||||
keys: Keys
|
||||
}
|
||||
|
||||
export interface CoinWithDenom {
|
||||
coin: Coin;
|
||||
denom: Denomination;
|
||||
}
|
||||
|
||||
export interface Keys {
|
||||
denoms: { [key: string]: Denomination };
|
||||
}
|
||||
|
||||
export interface Denomination {
|
||||
value: AmountJson;
|
||||
denom_pub: string;
|
||||
fee_withdraw: AmountJson;
|
||||
fee_deposit: AmountJson;
|
||||
}
|
||||
|
||||
export interface PreCoin {
|
||||
coinPub: string;
|
||||
coinPriv: string;
|
||||
reservePub: string;
|
||||
denomPub: string;
|
||||
blindingKey: string;
|
||||
withdrawSig: string;
|
||||
coinEv: string;
|
||||
mintBaseUrl: string;
|
||||
coinValue: AmountJson;
|
||||
}
|
||||
|
||||
export interface Coin {
|
||||
coinPub: string;
|
||||
coinPriv: string;
|
||||
denomPub: string;
|
||||
denomSig: string;
|
||||
currentAmount: AmountJson;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
const DB_NAME = "taler";
|
||||
const DB_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Return a promise that resolves
|
||||
* to the taler wallet db.
|
||||
*/
|
||||
function openTalerDb(): Promise<IDBDatabase> {
|
||||
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: // DB does not exist yet
|
||||
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 });
|
||||
break;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
/*
|
||||
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
|
||||
|
||||
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 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.
|
||||
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, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
"use strict";
|
||||
// Size of a native pointer.
|
||||
const PTR_SIZE = 4;
|
||||
@ -30,6 +30,7 @@ var emsc = {
|
||||
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']),
|
||||
@ -62,6 +63,7 @@ var emscAlloc = {
|
||||
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) {
|
||||
@ -138,7 +140,7 @@ class SyncArena extends DefaultArena {
|
||||
super();
|
||||
let me = this;
|
||||
this.timer = new Worker('background/timerThread.js');
|
||||
this.timer.onmessage = (e) => {
|
||||
this.timer.onmessage = () => {
|
||||
this.destroy();
|
||||
};
|
||||
//this.timer.postMessage({interval: 50});
|
||||
@ -164,6 +166,14 @@ class Amount extends ArenaObject {
|
||||
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();
|
||||
@ -260,7 +270,9 @@ class PackedArenaObject extends ArenaObject {
|
||||
}
|
||||
}
|
||||
class AmountNbo extends PackedArenaObject {
|
||||
size() { return 24; }
|
||||
size() {
|
||||
return 24;
|
||||
}
|
||||
}
|
||||
class EddsaPrivateKey extends PackedArenaObject {
|
||||
static create(a) {
|
||||
@ -268,30 +280,60 @@ class EddsaPrivateKey extends PackedArenaObject {
|
||||
obj.nativePtr = emscAlloc.eddsa_key_create();
|
||||
return obj;
|
||||
}
|
||||
size() { return 32; }
|
||||
size() {
|
||||
return 32;
|
||||
}
|
||||
getPublicKey(a) {
|
||||
let obj = new EddsaPublicKey(a);
|
||||
obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
class EddsaPublicKey extends PackedArenaObject {
|
||||
size() { return 32; }
|
||||
mixinStatic(EddsaPrivateKey, fromCrock);
|
||||
function fromCrock(s) {
|
||||
let x = new this();
|
||||
x.alloc();
|
||||
x.loadCrock(s);
|
||||
return x;
|
||||
}
|
||||
class RsaBlindingKey extends ArenaObject {
|
||||
static create(len, a) {
|
||||
let o = new RsaBlindingKey(a);
|
||||
o.nativePtr = emscAlloc.rsa_blinding_key_create(len);
|
||||
return o;
|
||||
function mixin(obj, method, name) {
|
||||
if (!name) {
|
||||
name = method.name;
|
||||
}
|
||||
static fromCrock(s, a) {
|
||||
if (!name) {
|
||||
throw Error("Mixin needs a name.");
|
||||
}
|
||||
console.log("mixing in", name, "into", obj.name);
|
||||
obj.prototype[method.name] = method;
|
||||
}
|
||||
function mixinStatic(obj, method, name) {
|
||||
if (!name) {
|
||||
name = method.name;
|
||||
}
|
||||
if (!name) {
|
||||
throw Error("Mixin needs a name.");
|
||||
}
|
||||
console.log("mixing in", name, "into", obj.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(emscAlloc.rsa_blinding_key_decode(buf.getNative(), buf.size()));
|
||||
obj.setNative(decodeFn(buf.getNative(), buf.size()));
|
||||
buf.destroy();
|
||||
return obj;
|
||||
}
|
||||
toCrock() {
|
||||
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, '*'));
|
||||
@ -300,12 +342,27 @@ class RsaBlindingKey extends ArenaObject {
|
||||
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; }
|
||||
size() {
|
||||
return 64;
|
||||
}
|
||||
random(qualStr) {
|
||||
let qual;
|
||||
switch (qualStr) {
|
||||
@ -328,6 +385,7 @@ class HashCode extends PackedArenaObject {
|
||||
emsc.hash_create_random(qual, this.nativePtr);
|
||||
}
|
||||
}
|
||||
mixinStatic(HashCode, fromCrock);
|
||||
class ByteArray extends PackedArenaObject {
|
||||
constructor(desiredSize, init, a) {
|
||||
super(a);
|
||||
@ -339,7 +397,9 @@ class ByteArray extends PackedArenaObject {
|
||||
}
|
||||
this.allocatedSize = desiredSize;
|
||||
}
|
||||
size() { return this.allocatedSize; }
|
||||
size() {
|
||||
return this.allocatedSize;
|
||||
}
|
||||
static fromString(s, a) {
|
||||
let hstr = emscAlloc.malloc(s.length + 1);
|
||||
Module.writeStringToMemory(s, hstr);
|
||||
@ -364,7 +424,9 @@ class EccSignaturePurpose extends PackedArenaObject {
|
||||
this.nativePtr = emscAlloc.purpose_create(purpose, payload.nativePtr, payload.size());
|
||||
this.payloadSize = payload.size();
|
||||
}
|
||||
size() { return this.payloadSize + 8; }
|
||||
size() {
|
||||
return this.payloadSize + 8;
|
||||
}
|
||||
}
|
||||
class SignatureStruct {
|
||||
constructor(x) {
|
||||
@ -393,8 +455,7 @@ class SignatureStruct {
|
||||
ptr += size;
|
||||
}
|
||||
let ba = new ByteArray(totalSize, buf, a);
|
||||
let x = new EccSignaturePurpose(this.purpose(), ba);
|
||||
return x;
|
||||
return new EccSignaturePurpose(this.purpose(), ba);
|
||||
}
|
||||
set(name, value) {
|
||||
let typemap = {};
|
||||
@ -414,32 +475,68 @@ class WithdrawRequestPS extends SignatureStruct {
|
||||
constructor(w) {
|
||||
super(w);
|
||||
}
|
||||
purpose() { return SignaturePurpose.RESERVE_WITHDRAW; }
|
||||
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]];
|
||||
["h_coin_envelope", HashCode]
|
||||
];
|
||||
}
|
||||
}
|
||||
function encodeWith(obj, fn, arena) {
|
||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||
let len = fn(obj.getNative(), ptr);
|
||||
let res = new ByteArray(len, null, arena);
|
||||
res.setNative(Module.getValue(ptr, '*'));
|
||||
emsc.free(ptr);
|
||||
return res;
|
||||
class AbsoluteTimeNbo extends PackedArenaObject {
|
||||
static fromTalerString(s) {
|
||||
throw Error();
|
||||
}
|
||||
size() {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
class UInt64 extends PackedArenaObject {
|
||||
static fromNumber(n) {
|
||||
throw Error();
|
||||
}
|
||||
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 {
|
||||
static fromCrock(s, a) {
|
||||
let obj = new RsaPublicKey(a);
|
||||
let buf = ByteArray.fromCrock(s);
|
||||
obj.nativePtr = emscAlloc.rsa_public_key_decode(buf.nativePtr, buf.size());
|
||||
buf.destroy();
|
||||
return obj;
|
||||
}
|
||||
toCrock() {
|
||||
return this.encode().toCrock();
|
||||
}
|
||||
@ -447,38 +544,26 @@ class RsaPublicKey extends ArenaObject {
|
||||
emsc.rsa_public_key_free(this.nativePtr);
|
||||
this.nativePtr = 0;
|
||||
}
|
||||
encode(arena) {
|
||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||
let len = emscAlloc.rsa_public_key_encode(this.nativePtr, ptr);
|
||||
let res = new ByteArray(len, Module.getValue(ptr, '*'), arena);
|
||||
emsc.free(ptr);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
mixinStatic(RsaPublicKey, makeFromCrock(emscAlloc.rsa_public_key_decode));
|
||||
mixin(RsaPublicKey, makeEncode(emscAlloc.rsa_public_key_encode));
|
||||
class EddsaSignature extends PackedArenaObject {
|
||||
size() { return 64; }
|
||||
size() {
|
||||
return 64;
|
||||
}
|
||||
}
|
||||
class RsaSignature extends ArenaObject {
|
||||
static fromCrock(s, a) {
|
||||
let obj = new this(a);
|
||||
let buf = ByteArray.fromCrock(s);
|
||||
obj.setNative(emscAlloc.rsa_signature_decode(buf.getNative(), buf.size()));
|
||||
buf.destroy();
|
||||
return obj;
|
||||
}
|
||||
encode(arena) {
|
||||
return encodeWith(this, emscAlloc.rsa_signature_encode);
|
||||
}
|
||||
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);
|
||||
let res = new ByteArray(s, Module.getValue(ptr, '*'), arena);
|
||||
return res;
|
||||
return new ByteArray(s, Module.getValue(ptr, '*'), arena);
|
||||
}
|
||||
function eddsaSign(purpose, priv, a) {
|
||||
let sig = new EddsaSignature(a);
|
||||
|
@ -1,23 +1,23 @@
|
||||
/*
|
||||
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
|
||||
|
||||
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 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.
|
||||
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, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
declare var Module : any;
|
||||
declare var Module: any;
|
||||
|
||||
|
||||
// Size of a native pointer.
|
||||
@ -30,10 +30,18 @@ const GNUNET_SYSERR = -1;
|
||||
|
||||
|
||||
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);
|
||||
(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);
|
||||
}
|
||||
|
||||
let getEmsc: EmscFunGen = (...args) => Module.cwrap.apply(null, args);
|
||||
@ -58,15 +66,18 @@ var emsc = {
|
||||
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']),
|
||||
'void',
|
||||
['number', 'number']),
|
||||
amount_ntoh: getEmsc('TALER_amount_ntoh',
|
||||
'void',
|
||||
['number', 'number']),
|
||||
'void',
|
||||
['number', 'number']),
|
||||
hash: getEmsc('GNUNET_CRYPTO_hash',
|
||||
'void',
|
||||
['number', 'number', 'number']),
|
||||
@ -77,8 +88,8 @@ var emsc = {
|
||||
'void',
|
||||
['number']),
|
||||
rsa_signature_free: getEmsc('GNUNET_CRYPTO_rsa_signature_free',
|
||||
'void',
|
||||
['number']),
|
||||
'void',
|
||||
['number']),
|
||||
string_to_data: getEmsc('GNUNET_STRINGS_string_to_data',
|
||||
'number',
|
||||
['number', 'number', 'number', 'number']),
|
||||
@ -89,8 +100,8 @@ var emsc = {
|
||||
'void',
|
||||
['number', 'number']),
|
||||
rsa_blinding_key_destroy: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_free',
|
||||
'void',
|
||||
['number']),
|
||||
'void',
|
||||
['number']),
|
||||
};
|
||||
|
||||
var emscAlloc = {
|
||||
@ -99,9 +110,10 @@ var emscAlloc = {
|
||||
['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']),
|
||||
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']),
|
||||
@ -112,35 +124,36 @@ var emscAlloc = {
|
||||
'number',
|
||||
['number', 'number', 'number', 'number']),
|
||||
rsa_blinding_key_create: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_create',
|
||||
'number',
|
||||
['number']),
|
||||
'number',
|
||||
['number']),
|
||||
rsa_blinding_key_encode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_encode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_signature_encode: getEmsc('GNUNET_CRYPTO_rsa_signature_encode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_blinding_key_decode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_decode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_public_key_decode: getEmsc('GNUNET_CRYPTO_rsa_public_key_decode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_signature_decode: getEmsc('GNUNET_CRYPTO_rsa_signature_decode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_public_key_encode: getEmsc('GNUNET_CRYPTO_rsa_public_key_encode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_unblind: getEmsc('GNUNET_CRYPTO_rsa_unblind',
|
||||
'number',
|
||||
['number', 'number', 'number']),
|
||||
'number',
|
||||
['number', 'number', 'number']),
|
||||
malloc: (size: number) => Module._malloc(size),
|
||||
};
|
||||
|
||||
|
||||
enum SignaturePurpose {
|
||||
RESERVE_WITHDRAW = 1200
|
||||
RESERVE_WITHDRAW = 1200,
|
||||
WALLET_COIN_DEPOSIT = 1201,
|
||||
}
|
||||
|
||||
enum RandomQuality {
|
||||
@ -153,6 +166,7 @@ enum RandomQuality {
|
||||
abstract class ArenaObject {
|
||||
private _nativePtr: number;
|
||||
arena: Arena;
|
||||
|
||||
abstract destroy(): void;
|
||||
|
||||
constructor(arena?: Arena) {
|
||||
@ -172,7 +186,7 @@ abstract class ArenaObject {
|
||||
// of native wrappers, but we never want to
|
||||
// pass 'undefined' to emscripten.
|
||||
if (this._nativePtr === undefined) {
|
||||
throw Error("Native pointer not initialized");
|
||||
throw Error("Native pointer not initialized");
|
||||
}
|
||||
return this._nativePtr;
|
||||
}
|
||||
@ -198,11 +212,11 @@ abstract class ArenaObject {
|
||||
this._nativePtr = n;
|
||||
}
|
||||
|
||||
set nativePtr (v) {
|
||||
set nativePtr(v) {
|
||||
this.setNative(v);
|
||||
}
|
||||
|
||||
get nativePtr () {
|
||||
get nativePtr() {
|
||||
return this.getNative();
|
||||
}
|
||||
|
||||
@ -215,6 +229,7 @@ interface Arena {
|
||||
|
||||
class DefaultArena implements Arena {
|
||||
heap: Array<ArenaObject>;
|
||||
|
||||
constructor() {
|
||||
this.heap = [];
|
||||
}
|
||||
@ -238,31 +253,35 @@ class DefaultArena implements Arena {
|
||||
*/
|
||||
class SyncArena extends DefaultArena {
|
||||
timer: Worker;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
let me = this;
|
||||
this.timer = new Worker('background/timerThread.js');
|
||||
this.timer.onmessage = (e) => {
|
||||
this.timer.onmessage = () => {
|
||||
this.destroy();
|
||||
};
|
||||
//this.timer.postMessage({interval: 50});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
let arenaStack: Arena[] = [];
|
||||
arenaStack.push(new SyncArena());
|
||||
|
||||
|
||||
class Amount extends ArenaObject {
|
||||
constructor(args?: any, arena?: Arena) {
|
||||
constructor(args?: AmountJson, arena?: Arena) {
|
||||
super(arena);
|
||||
if (args) {
|
||||
this.nativePtr = emscAlloc.get_amount(args.value, 0, args.fraction, args.currency);
|
||||
this.nativePtr = emscAlloc.get_amount(args.value,
|
||||
0,
|
||||
args.fraction,
|
||||
args.currency);
|
||||
} else {
|
||||
this.nativePtr = emscAlloc.get_amount(0, 0, 0, "");
|
||||
}
|
||||
@ -274,6 +293,17 @@ class Amount extends ArenaObject {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static getZero(currency: string, a?: Arena) {
|
||||
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?: Arena): AmountNbo {
|
||||
let x = new AmountNbo(a);
|
||||
x.alloc();
|
||||
@ -362,7 +392,10 @@ abstract class PackedArenaObject extends ArenaObject {
|
||||
// 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());
|
||||
let res = emsc.string_to_data(buf.nativePtr,
|
||||
s.length,
|
||||
this.nativePtr,
|
||||
this.size());
|
||||
buf.destroy();
|
||||
if (res < 1) {
|
||||
throw {error: "wrong encoding"};
|
||||
@ -390,7 +423,9 @@ abstract class PackedArenaObject extends ArenaObject {
|
||||
|
||||
|
||||
class AmountNbo extends PackedArenaObject {
|
||||
size() { return 24; }
|
||||
size() {
|
||||
return 24;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -401,37 +436,76 @@ class EddsaPrivateKey extends PackedArenaObject {
|
||||
return obj;
|
||||
}
|
||||
|
||||
size() { return 32; }
|
||||
size() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
getPublicKey(a?: Arena): EddsaPublicKey {
|
||||
let obj = new EddsaPublicKey(a);
|
||||
obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static fromCrock: (string) => EddsaPrivateKey;
|
||||
}
|
||||
mixinStatic(EddsaPrivateKey, fromCrock);
|
||||
|
||||
|
||||
function fromCrock(s: string) {
|
||||
let x = new this();
|
||||
x.alloc();
|
||||
x.loadCrock(s);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
function mixin(obj, method, name?: string) {
|
||||
if (!name) {
|
||||
name = method.name;
|
||||
}
|
||||
if (!name) {
|
||||
throw Error("Mixin needs a name.");
|
||||
}
|
||||
console.log("mixing in", name, "into", obj.name);
|
||||
obj.prototype[method.name] = method;
|
||||
}
|
||||
|
||||
|
||||
function mixinStatic(obj, method, name?: string) {
|
||||
if (!name) {
|
||||
name = method.name;
|
||||
}
|
||||
if (!name) {
|
||||
throw Error("Mixin needs a name.");
|
||||
}
|
||||
console.log("mixing in", name, "into", obj.name);
|
||||
obj[method.name] = method;
|
||||
}
|
||||
|
||||
|
||||
class EddsaPublicKey extends PackedArenaObject {
|
||||
size() { return 32; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
class RsaBlindingKey extends ArenaObject {
|
||||
static create(len: number, a?: Arena) {
|
||||
let o = new RsaBlindingKey(a);
|
||||
o.nativePtr = emscAlloc.rsa_blinding_key_create(len);
|
||||
return o;
|
||||
size() {
|
||||
return 32;
|
||||
}
|
||||
static fromCrock(s: string, a?: Arena): RsaBlindingKey {
|
||||
static fromCrock: (s: string) => EddsaPublicKey;
|
||||
}
|
||||
mixinStatic(EddsaPublicKey, fromCrock);
|
||||
|
||||
function makeFromCrock(decodeFn: (p: number, s: number) => number) {
|
||||
function fromCrock(s: string, a?: Arena) {
|
||||
let obj = new this(a);
|
||||
let buf = ByteArray.fromCrock(s);
|
||||
obj.setNative(emscAlloc.rsa_blinding_key_decode(buf.getNative(), buf.size()));
|
||||
obj.setNative(decodeFn(buf.getNative(),
|
||||
buf.size()));
|
||||
buf.destroy();
|
||||
return obj;
|
||||
}
|
||||
|
||||
toCrock(): string {
|
||||
return fromCrock;
|
||||
}
|
||||
|
||||
function makeToCrock(encodeFn: (po: number, ps: number) => number): () => string {
|
||||
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, '*'));
|
||||
@ -440,15 +514,32 @@ class RsaBlindingKey extends ArenaObject {
|
||||
res.destroy();
|
||||
return s;
|
||||
}
|
||||
return toCrock;
|
||||
}
|
||||
|
||||
class RsaBlindingKey extends ArenaObject {
|
||||
static create(len: number, a?: Arena) {
|
||||
let o = new RsaBlindingKey(a);
|
||||
o.nativePtr = emscAlloc.rsa_blinding_key_create(len);
|
||||
return o;
|
||||
}
|
||||
|
||||
static fromCrock: (s: string, a?: Arena) => RsaBlindingKey;
|
||||
toCrock = makeToCrock(emscAlloc.rsa_blinding_key_encode);
|
||||
|
||||
destroy() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
mixinStatic(RsaBlindingKey, makeFromCrock(emscAlloc.rsa_blinding_key_decode));
|
||||
|
||||
|
||||
class HashCode extends PackedArenaObject {
|
||||
size() { return 64; }
|
||||
size() {
|
||||
return 64;
|
||||
}
|
||||
|
||||
static fromCrock: (s: string) => HashCode;
|
||||
|
||||
random(qualStr: string) {
|
||||
let qual: RandomQuality;
|
||||
@ -472,11 +563,15 @@ class HashCode extends PackedArenaObject {
|
||||
emsc.hash_create_random(qual, this.nativePtr);
|
||||
}
|
||||
}
|
||||
mixinStatic(HashCode, fromCrock);
|
||||
|
||||
|
||||
class ByteArray extends PackedArenaObject {
|
||||
private allocatedSize: number;
|
||||
size() { return this.allocatedSize; }
|
||||
|
||||
size() {
|
||||
return this.allocatedSize;
|
||||
}
|
||||
|
||||
constructor(desiredSize: number, init: number, a?: Arena) {
|
||||
super(a);
|
||||
@ -510,11 +605,19 @@ class ByteArray extends PackedArenaObject {
|
||||
|
||||
|
||||
class EccSignaturePurpose extends PackedArenaObject {
|
||||
size() { return this.payloadSize + 8; }
|
||||
size() {
|
||||
return this.payloadSize + 8;
|
||||
}
|
||||
|
||||
payloadSize: number;
|
||||
constructor(purpose: SignaturePurpose, payload: PackedArenaObject, a?: Arena) {
|
||||
|
||||
constructor(purpose: SignaturePurpose,
|
||||
payload: PackedArenaObject,
|
||||
a?: Arena) {
|
||||
super(a);
|
||||
this.nativePtr = emscAlloc.purpose_create(purpose, payload.nativePtr, payload.size());
|
||||
this.nativePtr = emscAlloc.purpose_create(purpose,
|
||||
payload.nativePtr,
|
||||
payload.size());
|
||||
this.payloadSize = payload.size();
|
||||
}
|
||||
}
|
||||
@ -522,7 +625,9 @@ class EccSignaturePurpose extends PackedArenaObject {
|
||||
|
||||
abstract class SignatureStruct {
|
||||
abstract fieldTypes(): Array<any>;
|
||||
|
||||
abstract purpose(): SignaturePurpose;
|
||||
|
||||
private members: any = {};
|
||||
|
||||
constructor(x: { [name: string]: any }) {
|
||||
@ -552,12 +657,11 @@ abstract class SignatureStruct {
|
||||
ptr += size;
|
||||
}
|
||||
let ba = new ByteArray(totalSize, buf, a);
|
||||
let x = new EccSignaturePurpose(this.purpose(), ba);
|
||||
return x;
|
||||
return new EccSignaturePurpose(this.purpose(), ba);
|
||||
}
|
||||
|
||||
protected set(name: string, value: PackedArenaObject) {
|
||||
let typemap: any = {}
|
||||
let typemap: any = {};
|
||||
for (let f of this.fieldTypes()) {
|
||||
typemap[f[0]] = f[1];
|
||||
}
|
||||
@ -586,38 +690,104 @@ class WithdrawRequestPS extends SignatureStruct {
|
||||
constructor(w: WithdrawRequestPS_Args) {
|
||||
super(w);
|
||||
}
|
||||
purpose() { return SignaturePurpose.RESERVE_WITHDRAW; }
|
||||
|
||||
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]];
|
||||
["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: string): AbsoluteTimeNbo {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
function encodeWith(obj, fn, arena?: Arena) {
|
||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||
let len = fn(obj.getNative(), ptr);
|
||||
let res = new ByteArray(len, null, arena);
|
||||
res.setNative(Module.getValue(ptr, '*'));
|
||||
emsc.free(ptr);
|
||||
return res;
|
||||
size() {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RsaPublicKey extends ArenaObject {
|
||||
static fromCrock(s: string, a?: Arena): RsaPublicKey {
|
||||
let obj = new RsaPublicKey(a);
|
||||
let buf = ByteArray.fromCrock(s);
|
||||
obj.nativePtr = emscAlloc.rsa_public_key_decode(buf.nativePtr, buf.size());
|
||||
buf.destroy();
|
||||
return obj;
|
||||
class UInt64 extends PackedArenaObject {
|
||||
static fromNumber(n: number): UInt64 {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
size() {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// It's redundant, but more type safe.
|
||||
interface DepositRequestPS_Args {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
class DepositRequestPS extends SignatureStruct {
|
||||
constructor(w: DepositRequestPS_Args) {
|
||||
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],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface Encodeable {
|
||||
encode(arena?: Arena): ByteArray;
|
||||
}
|
||||
|
||||
function makeEncode(encodeFn) {
|
||||
function encode(arena?: 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 implements Encodeable {
|
||||
static fromCrock: (s: string, a?: Arena) => RsaPublicKey;
|
||||
|
||||
toCrock() {
|
||||
return this.encode().toCrock();
|
||||
}
|
||||
@ -627,57 +797,49 @@ class RsaPublicKey extends ArenaObject {
|
||||
this.nativePtr = 0;
|
||||
}
|
||||
|
||||
encode(arena?: Arena): ByteArray {
|
||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||
let len = emscAlloc.rsa_public_key_encode(this.nativePtr, ptr);
|
||||
let res = new ByteArray(len, Module.getValue(ptr, '*'), arena);
|
||||
emsc.free(ptr);
|
||||
return res;
|
||||
}
|
||||
|
||||
encode: (arena?: Arena) => ByteArray;
|
||||
}
|
||||
mixinStatic(RsaPublicKey, makeFromCrock(emscAlloc.rsa_public_key_decode));
|
||||
mixin(RsaPublicKey, makeEncode(emscAlloc.rsa_public_key_encode));
|
||||
|
||||
|
||||
class EddsaSignature extends PackedArenaObject {
|
||||
size() { return 64; }
|
||||
size() {
|
||||
return 64;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RsaSignature extends ArenaObject {
|
||||
static fromCrock(s: string, a?: Arena): RsaSignature {
|
||||
let obj = new this(a);
|
||||
let buf = ByteArray.fromCrock(s);
|
||||
obj.setNative(emscAlloc.rsa_signature_decode(buf.getNative(), buf.size()));
|
||||
buf.destroy();
|
||||
return obj;
|
||||
}
|
||||
class RsaSignature extends ArenaObject implements Encodeable{
|
||||
static fromCrock: (s: string, a?: Arena) => RsaSignature;
|
||||
|
||||
encode(arena?: Arena): ByteArray {
|
||||
return encodeWith(this, emscAlloc.rsa_signature_encode);
|
||||
}
|
||||
encode: (arena?: Arena) => ByteArray;
|
||||
|
||||
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: HashCode,
|
||||
blindingKey: RsaBlindingKey,
|
||||
blindingKey: RsaBlindingKey,
|
||||
pkey: RsaPublicKey,
|
||||
arena?: Arena): ByteArray
|
||||
{
|
||||
arena?: Arena): ByteArray {
|
||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||
let s = emscAlloc.rsa_blind(hashCode.nativePtr, blindingKey.nativePtr, pkey.nativePtr, ptr);
|
||||
let res = new ByteArray(s, Module.getValue(ptr, '*'), arena);
|
||||
return res;
|
||||
let s = emscAlloc.rsa_blind(hashCode.nativePtr,
|
||||
blindingKey.nativePtr,
|
||||
pkey.nativePtr,
|
||||
ptr);
|
||||
return new ByteArray(s, Module.getValue(ptr, '*'), arena);
|
||||
}
|
||||
|
||||
|
||||
function eddsaSign(purpose: EccSignaturePurpose,
|
||||
priv: EddsaPrivateKey,
|
||||
a?: Arena): EddsaSignature
|
||||
{
|
||||
a?: Arena): EddsaSignature {
|
||||
let sig = new EddsaSignature(a);
|
||||
sig.alloc();
|
||||
let res = emsc.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
|
||||
@ -687,13 +849,14 @@ function eddsaSign(purpose: EccSignaturePurpose,
|
||||
return sig;
|
||||
}
|
||||
|
||||
|
||||
function rsaUnblind(sig: RsaSignature,
|
||||
bk: RsaBlindingKey,
|
||||
pk: RsaPublicKey,
|
||||
a?: Arena): RsaSignature {
|
||||
let x = new RsaSignature(a);
|
||||
x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr, bk.nativePtr, pk.nativePtr);
|
||||
x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr,
|
||||
bk.nativePtr,
|
||||
pk.nativePtr);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,38 +1,6 @@
|
||||
/// <reference path="../decl/urijs/URIjs.d.ts" />
|
||||
/// <reference path="../decl/chrome/chrome.d.ts" />
|
||||
'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:
|
||||
db.createObjectStore("mints", { keyPath: "baseUrl" });
|
||||
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 });
|
||||
break;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* See http://api.taler.net/wallet.html#general
|
||||
*/
|
||||
@ -46,8 +14,141 @@ function canonicalizeBaseUrl(url) {
|
||||
x.query();
|
||||
return x.href();
|
||||
}
|
||||
function grantCoins(db, feeThreshold, paymentAmount, mintBaseUrl) {
|
||||
throw "not implemented";
|
||||
function signDeposit(db, 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.cmp(new Amount(cd.coin.currentAmount)) < 0) {
|
||||
coinSpend = new Amount(amountRemaining.toJson());
|
||||
}
|
||||
else {
|
||||
coinSpend = new Amount(cd.coin.currentAmount);
|
||||
}
|
||||
let d = new DepositRequestPS({
|
||||
h_contract: HashCode.fromCrock(offer.H_contract),
|
||||
h_wire: HashCode.fromCrock(offer.contract.H_wire),
|
||||
amount_with_fee: new Amount(cd.coin.currentAmount).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),
|
||||
});
|
||||
amountSpent.add(coinSpend);
|
||||
let newAmount = new Amount(cd.coin.currentAmount);
|
||||
newAmount.sub(coinSpend);
|
||||
cd.coin.currentAmount = newAmount.toJson();
|
||||
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: amountSpent.toJson(),
|
||||
};
|
||||
ret.push({ sig: coinSig, 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 db
|
||||
* @param paymentAmount
|
||||
* @param depositFeeLimit
|
||||
* @param mintKeys
|
||||
*/
|
||||
function getPossibleMintCoins(db, paymentAmount, depositFeeLimit, allowedMints) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let m = {};
|
||||
let found = false;
|
||||
let tx = db.transaction(["mints", "coins"]);
|
||||
// First pass: Get all coins from acceptable mints.
|
||||
for (let info of allowedMints) {
|
||||
let req_mints = tx.objectStore("mints")
|
||||
.index("pubKey")
|
||||
.get(info.master_pub);
|
||||
req_mints.onsuccess = (e) => {
|
||||
let mint = req_mints.result;
|
||||
let req_coins = tx.objectStore("coins")
|
||||
.index("mintBaseUrl")
|
||||
.openCursor(IDBKeyRange.only(mint.baseUrl));
|
||||
req_coins.onsuccess = (e) => {
|
||||
let cursor = req_coins.result;
|
||||
if (!cursor) {
|
||||
return;
|
||||
}
|
||||
let cd = {
|
||||
coin: cursor.value,
|
||||
denom: mint.keys.denoms[cursor.value.denomPub]
|
||||
};
|
||||
let x = m[mint.baseUrl];
|
||||
if (!x) {
|
||||
m[mint.baseUrl] = [cd];
|
||||
}
|
||||
else {
|
||||
x.push(cd);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
tx.oncomplete = (e) => {
|
||||
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 = new Amount(coins[0].c.coin.currentAmount);
|
||||
for (let i = 0; i < coins.length; i++) {
|
||||
if (accFee.cmp(maxFee) >= 0) {
|
||||
continue nextMint;
|
||||
}
|
||||
if (accAmount.cmp(minAmount) >= 0) {
|
||||
ret[key] = m[key];
|
||||
continue nextMint;
|
||||
}
|
||||
accFee.add(coins[i].a);
|
||||
accFee.add(new Amount(coins[i].c.coin.currentAmount));
|
||||
}
|
||||
}
|
||||
resolve(ret);
|
||||
};
|
||||
tx.onerror = (e) => {
|
||||
reject();
|
||||
};
|
||||
});
|
||||
}
|
||||
function executePay(db, offer, payCoinInfo, merchantBaseUrl, chosenMint) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reqData = {};
|
||||
reqData["H_wire"] = offer.contract.H_wire;
|
||||
reqData["H_contract"] = offer.H_contract;
|
||||
reqData["transaction_id"] = offer.contract.transaction_id;
|
||||
reqData["refund_deadline"] = offer.contract.refund_deadline;
|
||||
reqData["mint"] = chosenMint;
|
||||
reqData["coins"] = payCoinInfo.map((x) => x.sig);
|
||||
let payUrl = URI(merchantBaseUrl).absoluteTo(merchantBaseUrl);
|
||||
console.log("Merchant URL", payUrl);
|
||||
let req = new XMLHttpRequest();
|
||||
req.open('post', payUrl.href());
|
||||
req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
req.addEventListener('readystatechange', (e) => {
|
||||
if (req.readyState == XMLHttpRequest.DONE) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function confirmPay(db, detail, sendResponse) {
|
||||
console.log("confirmPay", JSON.stringify(detail));
|
||||
@ -57,10 +158,17 @@ function confirmPay(db, detail, sendResponse) {
|
||||
contract: detail.offer.contract,
|
||||
sig: detail.offer
|
||||
};
|
||||
let contract = detail.offer.contract;
|
||||
//let chosenCoinPromise = chooseCoins(db, contract.max_fee, contract.amount)
|
||||
// .then(x => generateDepositPermissions(db, x))
|
||||
// .then(executePayment);
|
||||
let offer = detail.offer;
|
||||
getPossibleMintCoins(db, offer.contract.amount, offer.contract.max_fee, offer.contract.mints)
|
||||
.then((mcs) => {
|
||||
if (Object.keys(mcs).length == 0) {
|
||||
sendResponse({ error: "Not enough coins." });
|
||||
return;
|
||||
}
|
||||
let mintUrl = Object.keys(mcs)[0];
|
||||
let ds = signDeposit(db, offer, mcs[mintUrl]);
|
||||
return executePay(db, offer, ds, detail.merchantPageUrl, mintUrl);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
function confirmReserve(db, detail, sendResponse) {
|
||||
@ -110,7 +218,10 @@ function confirmReserve(db, detail, sendResponse) {
|
||||
sendResponse(resp);
|
||||
var mint;
|
||||
updateMintFromUrl(db, reserveRecord.mint_base_url)
|
||||
.then((m) => { mint = m; return updateReserve(db, reservePub, mint); })
|
||||
.then((m) => {
|
||||
mint = m;
|
||||
return updateReserve(db, reservePub, mint);
|
||||
})
|
||||
.then((reserve) => depleteReserve(db, reserve, mint));
|
||||
});
|
||||
break;
|
||||
@ -213,7 +324,7 @@ function withdrawExecute(db, pc) {
|
||||
console.log("Withdrawal successful");
|
||||
console.log(myRequest.responseText);
|
||||
let resp = JSON.parse(myRequest.responseText);
|
||||
let denomSig = rsaUnblind(RsaSignature.fromCrock(resp.coin_ev), RsaBlindingKey.fromCrock(pc.blindingKey), RsaPublicKey.fromCrock(pc.denomPub));
|
||||
let denomSig = rsaUnblind(RsaSignature.fromCrock(resp.ev_sig), RsaBlindingKey.fromCrock(pc.blindingKey), RsaPublicKey.fromCrock(pc.denomPub));
|
||||
let coin = {
|
||||
coinPub: pc.coinPub,
|
||||
coinPriv: pc.coinPriv,
|
||||
@ -397,7 +508,9 @@ function dumpDb(db, detail, sendResponse) {
|
||||
let name = db.objectStoreNames[i];
|
||||
let storeDump = {};
|
||||
dump.stores[name] = storeDump;
|
||||
let store = tx.objectStore(name).openCursor().addEventListener('success', (e) => {
|
||||
let store = tx.objectStore(name)
|
||||
.openCursor()
|
||||
.addEventListener('success', (e) => {
|
||||
let cursor = e.target.result;
|
||||
if (cursor) {
|
||||
storeDump[cursor.key] = cursor.value;
|
||||
|
@ -2,39 +2,11 @@
|
||||
/// <reference path="../decl/chrome/chrome.d.ts" />
|
||||
'use strict';
|
||||
|
||||
const DB_NAME = "taler";
|
||||
const DB_VERSION = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Return a promise that resolves
|
||||
* to the taler wallet db.
|
||||
*/
|
||||
function openTalerDb(): Promise<IDBDatabase> {
|
||||
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: // DB does not exist yet
|
||||
db.createObjectStore("mints", { keyPath: "baseUrl" });
|
||||
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 });
|
||||
break;
|
||||
}
|
||||
};
|
||||
});
|
||||
interface AmountJson {
|
||||
value: number;
|
||||
fraction: number;
|
||||
currency: string;
|
||||
}
|
||||
|
||||
|
||||
@ -52,38 +24,244 @@ function canonicalizeBaseUrl(url) {
|
||||
return x.href()
|
||||
}
|
||||
|
||||
|
||||
interface ConfirmPayRequest {
|
||||
offer: any;
|
||||
selectedMint: any;
|
||||
merchantPageUrl: string;
|
||||
offer: Offer;
|
||||
}
|
||||
|
||||
interface MintCoins {
|
||||
[mintUrl: string]: Db.CoinWithDenom[];
|
||||
}
|
||||
|
||||
interface Offer {
|
||||
contract: Contract;
|
||||
sig: string;
|
||||
H_contract: string;
|
||||
}
|
||||
|
||||
interface Contract {
|
||||
H_wire: string;
|
||||
amount: AmountJson;
|
||||
auditors: string[];
|
||||
expiry: string,
|
||||
locations: string[];
|
||||
max_fee: AmountJson;
|
||||
merchant: any;
|
||||
merchant_pub: string;
|
||||
mints: MintInfo[];
|
||||
pay_url: string;
|
||||
products: string[];
|
||||
refund_deadline: string;
|
||||
timestamp: string;
|
||||
transaction_id: number;
|
||||
}
|
||||
|
||||
|
||||
function grantCoins(db: IDBDatabase,
|
||||
feeThreshold: AmountJson,
|
||||
paymentAmount: AmountJson,
|
||||
mintBaseUrl: string): Promise<any> {
|
||||
throw "not implemented";
|
||||
interface CoinPaySig {
|
||||
coin_sig: string;
|
||||
coin_pub: string;
|
||||
ub_sig: string;
|
||||
denom_pub: string;
|
||||
f: AmountJson;
|
||||
}
|
||||
|
||||
|
||||
function confirmPay(db, detail: ConfirmPayRequest, sendResponse) {
|
||||
type PayCoinInfo = Array<{ updatedCoin: Db.Coin, sig: CoinPaySig }>;
|
||||
|
||||
|
||||
function signDeposit(db: IDBDatabase,
|
||||
offer: Offer,
|
||||
cds: Db.CoinWithDenom[]): PayCoinInfo {
|
||||
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.cmp(new Amount(cd.coin.currentAmount)) < 0) {
|
||||
coinSpend = new Amount(amountRemaining.toJson());
|
||||
} else {
|
||||
coinSpend = new Amount(cd.coin.currentAmount);
|
||||
}
|
||||
|
||||
let d = new DepositRequestPS({
|
||||
h_contract: HashCode.fromCrock(offer.H_contract),
|
||||
h_wire: HashCode.fromCrock(offer.contract.H_wire),
|
||||
amount_with_fee: new Amount(cd.coin.currentAmount).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),
|
||||
});
|
||||
|
||||
amountSpent.add(coinSpend);
|
||||
|
||||
let newAmount = new Amount(cd.coin.currentAmount);
|
||||
newAmount.sub(coinSpend);
|
||||
cd.coin.currentAmount = newAmount.toJson();
|
||||
|
||||
let coinSig = eddsaSign(d.toPurpose(),
|
||||
EddsaPrivateKey.fromCrock(cd.coin.coinPriv))
|
||||
.toCrock();
|
||||
|
||||
let s: CoinPaySig = {
|
||||
coin_sig: coinSig,
|
||||
coin_pub: cd.coin.coinPub,
|
||||
ub_sig: cd.coin.denomSig,
|
||||
denom_pub: cd.coin.denomPub,
|
||||
f: amountSpent.toJson(),
|
||||
};
|
||||
ret.push({sig: coinSig, updatedCoin: cd.coin});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
interface MintInfo {
|
||||
master_pub: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get mints and associated coins that are still spendable,
|
||||
* but only if the sum the coins' remaining value exceeds the payment amount.
|
||||
* @param db
|
||||
* @param paymentAmount
|
||||
* @param depositFeeLimit
|
||||
* @param mintKeys
|
||||
*/
|
||||
function getPossibleMintCoins(db: IDBDatabase,
|
||||
paymentAmount: AmountJson,
|
||||
depositFeeLimit: AmountJson,
|
||||
allowedMints: MintInfo[]): Promise<MintCoins> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let m: MintCoins = {};
|
||||
let found = false;
|
||||
let tx = db.transaction(["mints", "coins"]);
|
||||
// First pass: Get all coins from acceptable mints.
|
||||
for (let info of allowedMints) {
|
||||
let req_mints = tx.objectStore("mints")
|
||||
.index("pubKey")
|
||||
.get(info.master_pub);
|
||||
req_mints.onsuccess = (e) => {
|
||||
let mint: Db.Mint = req_mints.result;
|
||||
let req_coins = tx.objectStore("coins")
|
||||
.index("mintBaseUrl")
|
||||
.openCursor(IDBKeyRange.only(mint.baseUrl));
|
||||
req_coins.onsuccess = (e) => {
|
||||
let cursor: IDBCursorWithValue = req_coins.result;
|
||||
if (!cursor) {
|
||||
return;
|
||||
}
|
||||
let cd = {
|
||||
coin: cursor.value,
|
||||
denom: mint.keys.denoms[cursor.value.denomPub]
|
||||
};
|
||||
let x = m[mint.baseUrl];
|
||||
if (!x) {
|
||||
m[mint.baseUrl] = [cd];
|
||||
} else {
|
||||
x.push(cd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tx.oncomplete = (e) => {
|
||||
let ret: MintCoins = {};
|
||||
|
||||
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 = new Amount(coins[0].c.coin.currentAmount);
|
||||
for (let i = 0; i < coins.length; i++) {
|
||||
if (accFee.cmp(maxFee) >= 0) {
|
||||
continue nextMint;
|
||||
}
|
||||
if (accAmount.cmp(minAmount) >= 0) {
|
||||
ret[key] = m[key];
|
||||
continue nextMint;
|
||||
}
|
||||
accFee.add(coins[i].a);
|
||||
accFee.add(new Amount(coins[i].c.coin.currentAmount));
|
||||
}
|
||||
}
|
||||
resolve(ret);
|
||||
};
|
||||
|
||||
tx.onerror = (e) => {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function executePay(db,
|
||||
offer: Offer,
|
||||
payCoinInfo: PayCoinInfo,
|
||||
merchantBaseUrl: string,
|
||||
chosenMint: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reqData = {};
|
||||
reqData["H_wire"] = offer.contract.H_wire;
|
||||
reqData["H_contract"] = offer.H_contract;
|
||||
reqData["transaction_id"] = offer.contract.transaction_id;
|
||||
reqData["refund_deadline"] = offer.contract.refund_deadline;
|
||||
reqData["mint"] = chosenMint;
|
||||
reqData["coins"] = payCoinInfo.map((x) => x.sig);
|
||||
let payUrl = URI(merchantBaseUrl).absoluteTo(merchantBaseUrl);
|
||||
console.log("Merchant URL", payUrl);
|
||||
let req = new XMLHttpRequest();
|
||||
req.open('post', payUrl.href());
|
||||
req.setRequestHeader("Content-Type",
|
||||
"application/json;charset=UTF-8");
|
||||
req.addEventListener('readystatechange', (e) => {
|
||||
if (req.readyState == XMLHttpRequest.DONE) {
|
||||
resolve()
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function confirmPay(db, detail: ConfirmPayRequest, sendResponse) {
|
||||
console.log("confirmPay", JSON.stringify(detail));
|
||||
let tx = db.transaction(['transactions'], 'readwrite');
|
||||
let trans = {
|
||||
contractHash: detail.offer.H_contract,
|
||||
contract: detail.offer.contract,
|
||||
sig: detail.offer
|
||||
}
|
||||
|
||||
let contract = detail.offer.contract;
|
||||
|
||||
//let chosenCoinPromise = chooseCoins(db, contract.max_fee, contract.amount)
|
||||
// .then(x => generateDepositPermissions(db, x))
|
||||
// .then(executePayment);
|
||||
};
|
||||
|
||||
let offer: Offer = detail.offer;
|
||||
getPossibleMintCoins(db,
|
||||
offer.contract.amount,
|
||||
offer.contract.max_fee,
|
||||
offer.contract.mints)
|
||||
.then((mcs) => {
|
||||
if (Object.keys(mcs).length == 0) {
|
||||
sendResponse({error: "Not enough coins."});
|
||||
return;
|
||||
}
|
||||
let mintUrl = Object.keys(mcs)[0];
|
||||
let ds = signDeposit(db, offer, mcs[mintUrl]);
|
||||
return executePay(db, offer, ds, detail.merchantPageUrl, mintUrl);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function confirmReserve(db, detail, sendResponse) {
|
||||
let reservePriv = EddsaPrivateKey.create();
|
||||
let reservePub = reservePriv.getPublicKey();
|
||||
@ -131,8 +309,11 @@ function confirmReserve(db, detail, sendResponse) {
|
||||
sendResponse(resp);
|
||||
var mint;
|
||||
updateMintFromUrl(db, reserveRecord.mint_base_url)
|
||||
.then((m) => { mint = m; return updateReserve(db, reservePub, mint); })
|
||||
.then((reserve) => depleteReserve(db, reserve, mint));
|
||||
.then((m) => {
|
||||
mint = m;
|
||||
return updateReserve(db, reservePub, mint);
|
||||
})
|
||||
.then((reserve) => depleteReserve(db, reserve, mint));
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@ -160,8 +341,8 @@ function rankDenom(denom1: any, denom2: any) {
|
||||
|
||||
|
||||
function withdrawPrepare(db: IDBDatabase,
|
||||
denom: Denomination,
|
||||
reserve): Promise<PreCoin> {
|
||||
denom: Db.Denomination,
|
||||
reserve): Promise<Db.PreCoin> {
|
||||
let reservePriv = new EddsaPrivateKey();
|
||||
reservePriv.loadCrock(reserve.reserve_priv);
|
||||
let reservePub = new EddsaPublicKey();
|
||||
@ -196,7 +377,7 @@ function withdrawPrepare(db: IDBDatabase,
|
||||
|
||||
console.log("crypto done, doing request");
|
||||
|
||||
let preCoin: PreCoin = {
|
||||
let preCoin: Db.PreCoin = {
|
||||
reservePub: reservePub.toCrock(),
|
||||
blindingKey: blindingFactor.toCrock(),
|
||||
coinPub: coinPub.toCrock(),
|
||||
@ -219,57 +400,59 @@ function withdrawPrepare(db: IDBDatabase,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function dbGet(db, store: string, key: any): Promise<any> {
|
||||
let tx = db.transaction([store]);
|
||||
let req = tx.objectStore(store).get(key);
|
||||
return new Promise((resolve, reject) => {
|
||||
req.onsuccess = (e) => resolve(req.result);
|
||||
req.onsuccess = (e) => resolve(req.result);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function withdrawExecute(db, pc: PreCoin): Promise<Coin> {
|
||||
function withdrawExecute(db, pc: Db.PreCoin): Promise<Db.Coin> {
|
||||
return dbGet(db, 'reserves', pc.reservePub)
|
||||
.then((r) => new Promise((resolve, reject) => {
|
||||
console.log("loading precoin", JSON.stringify(pc));
|
||||
let wd: any = {};
|
||||
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);
|
||||
let myRequest = new XMLHttpRequest();
|
||||
console.log("making request to " + reqUrl.href());
|
||||
myRequest.open('post', reqUrl.href());
|
||||
myRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
myRequest.send(JSON.stringify(wd));
|
||||
myRequest.addEventListener('readystatechange', (e) => {
|
||||
if (myRequest.readyState == XMLHttpRequest.DONE) {
|
||||
if (myRequest.status != 200) {
|
||||
console.log("Withdrawal failed, status ", myRequest.status);
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
console.log("Withdrawal successful");
|
||||
console.log(myRequest.responseText);
|
||||
let resp = JSON.parse(myRequest.responseText);
|
||||
let denomSig = rsaUnblind(RsaSignature.fromCrock(resp.coin_ev),
|
||||
RsaBlindingKey.fromCrock(pc.blindingKey),
|
||||
RsaPublicKey.fromCrock(pc.denomPub));
|
||||
let coin: Coin = {
|
||||
coinPub: pc.coinPub,
|
||||
coinPriv: pc.coinPriv,
|
||||
denomPub: pc.denomPub,
|
||||
denomSig: denomSig.encode().toCrock(),
|
||||
currentAmount: pc.coinValue
|
||||
}
|
||||
console.log("unblinded coin");
|
||||
resolve(coin);
|
||||
} else {
|
||||
console.log("ready state change to", myRequest.status);
|
||||
}
|
||||
});
|
||||
}));
|
||||
.then((r) => new Promise((resolve, reject) => {
|
||||
console.log("loading precoin", JSON.stringify(pc));
|
||||
let wd: any = {};
|
||||
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);
|
||||
let myRequest = new XMLHttpRequest();
|
||||
console.log("making request to " + reqUrl.href());
|
||||
myRequest.open('post', reqUrl.href());
|
||||
myRequest.setRequestHeader("Content-Type",
|
||||
"application/json;charset=UTF-8");
|
||||
myRequest.send(JSON.stringify(wd));
|
||||
myRequest.addEventListener('readystatechange', (e) => {
|
||||
if (myRequest.readyState == XMLHttpRequest.DONE) {
|
||||
if (myRequest.status != 200) {
|
||||
console.log("Withdrawal failed, status ", myRequest.status);
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
console.log("Withdrawal successful");
|
||||
console.log(myRequest.responseText);
|
||||
let resp = JSON.parse(myRequest.responseText);
|
||||
let denomSig = rsaUnblind(RsaSignature.fromCrock(resp.ev_sig),
|
||||
RsaBlindingKey.fromCrock(pc.blindingKey),
|
||||
RsaPublicKey.fromCrock(pc.denomPub));
|
||||
let coin: Db.Coin = {
|
||||
coinPub: pc.coinPub,
|
||||
coinPriv: pc.coinPriv,
|
||||
denomPub: pc.denomPub,
|
||||
denomSig: denomSig.encode().toCrock(),
|
||||
currentAmount: pc.coinValue
|
||||
};
|
||||
console.log("unblinded coin");
|
||||
resolve(coin);
|
||||
} else {
|
||||
console.log("ready state change to", myRequest.status);
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@ -284,14 +467,14 @@ function updateBadge(db) {
|
||||
cursor.continue();
|
||||
} else {
|
||||
console.log("badge");
|
||||
chrome.browserAction.setBadgeText({text: ""+n});
|
||||
chrome.browserAction.setBadgeText({text: "" + n});
|
||||
chrome.browserAction.setBadgeBackgroundColor({color: "#0F0"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function storeCoin(db, coin: Coin) {
|
||||
function storeCoin(db, coin: Db.Coin) {
|
||||
let tx = db.transaction(['coins', 'precoins'], 'readwrite');
|
||||
tx.objectStore('precoins').delete(coin.coinPub);
|
||||
tx.objectStore('coins').add(coin);
|
||||
@ -306,8 +489,8 @@ function storeCoin(db, coin: Coin) {
|
||||
|
||||
function withdraw(db, denom, reserve): Promise<void> {
|
||||
return withdrawPrepare(db, denom, reserve)
|
||||
.then((pc) => withdrawExecute(db, pc))
|
||||
.then((c) => storeCoin(db, c));
|
||||
.then((pc) => withdrawExecute(db, pc))
|
||||
.then((c) => storeCoin(db, c));
|
||||
}
|
||||
|
||||
|
||||
@ -344,7 +527,7 @@ function depleteReserve(db, reserve, mint) {
|
||||
console.log("doing work");
|
||||
let d = workList.pop();
|
||||
withdraw(db, d, reserve)
|
||||
.then(() => next());
|
||||
.then(() => next());
|
||||
}
|
||||
|
||||
next();
|
||||
@ -408,7 +591,7 @@ function updateMintFromUrl(db, baseUrl) {
|
||||
console.log("keys invalid");
|
||||
reject();
|
||||
} else {
|
||||
let mint = {
|
||||
let mint: Db.Mint = {
|
||||
baseUrl: baseUrl,
|
||||
keys: mintKeysJson
|
||||
};
|
||||
@ -419,7 +602,7 @@ function updateMintFromUrl(db, baseUrl) {
|
||||
let di = {
|
||||
denomPub: d.denom_pub,
|
||||
value: d.value
|
||||
}
|
||||
};
|
||||
tx.objectStore('denoms').put(di);
|
||||
}
|
||||
tx.oncomplete = (e) => {
|
||||
@ -446,19 +629,21 @@ function dumpDb(db, detail, sendResponse) {
|
||||
console.log("stores: " + JSON.stringify(db.objectStoreNames));
|
||||
let tx = db.transaction(db.objectStoreNames);
|
||||
tx.addEventListener('complete', (e) => {
|
||||
sendResponse(dump);
|
||||
sendResponse(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();
|
||||
}
|
||||
});
|
||||
let store = tx.objectStore(name)
|
||||
.openCursor()
|
||||
.addEventListener('success', (e) => {
|
||||
let cursor = e.target.result;
|
||||
if (cursor) {
|
||||
storeDump[cursor.key] = cursor.value;
|
||||
cursor.continue();
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -513,7 +698,7 @@ chrome.browserAction.setBadgeText({text: ""});
|
||||
openTalerDb().then((db) => {
|
||||
console.log("db loaded");
|
||||
chrome.runtime.onMessage.addListener(
|
||||
function (req, sender, onresponse) {
|
||||
function(req, sender, onresponse) {
|
||||
let dispatch = {
|
||||
"confirm-reserve": confirmReserve,
|
||||
"confirm-pay": confirmPay,
|
||||
@ -524,7 +709,9 @@ openTalerDb().then((db) => {
|
||||
if (req.type in dispatch) {
|
||||
return dispatch[req.type](db, req.detail, onresponse);
|
||||
}
|
||||
console.error(format("Request type {1} unknown, req {0}", JSON.stringify(req), req.type));
|
||||
console.error(format("Request type {1} unknown, req {0}",
|
||||
JSON.stringify(req),
|
||||
req.type));
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
@ -28,3 +28,7 @@ function format(s, ...args) {
|
||||
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; });
|
||||
}
|
||||
|
@ -32,3 +32,8 @@ function format(s: string, ...args: any[]) {
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
function promiseFinally<T>(p: Promise<T>, fn): Promise<T> {
|
||||
return p.then((x) => { fn(); return x; })
|
||||
.catch((e) => {fn(); throw e;});
|
||||
}
|
@ -26,6 +26,10 @@
|
||||
<p />
|
||||
</script>
|
||||
|
||||
<script id="error-template" type="text/x-handlebars-template">
|
||||
Payment was not successful: {{error}}
|
||||
</script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
@ -33,7 +37,9 @@
|
||||
|
||||
<div id="render-contract"></div>
|
||||
|
||||
<button id="confirm-purchase">Confirm Purchase!</button>
|
||||
<button id="confirm-pay">Confirm Pay!</button>
|
||||
|
||||
<div id="status"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -23,16 +23,16 @@ document.addEventListener("DOMContentLoaded", (e) => {
|
||||
let html = template(offer.contract);
|
||||
$_("render-contract").innerHTML = html;
|
||||
document.getElementById("confirm-pay").addEventListener("click", (e) => {
|
||||
let d = clone(query);
|
||||
let d = {
|
||||
offer: JSON.parse(query.offer)
|
||||
};
|
||||
chrome.runtime.sendMessage({ type: 'confirm-pay', detail: d }, (resp) => {
|
||||
if (resp.success === true) {
|
||||
document.location.href = resp.backlink;
|
||||
}
|
||||
else {
|
||||
document.body.innerHTML =
|
||||
`Oops, something went wrong.
|
||||
Here is some more info:
|
||||
<pre>${resp.text}</pre>`;
|
||||
console.log("got response", resp);
|
||||
if ("error" in resp) {
|
||||
let source = $_("error-template").innerHTML;
|
||||
let template = Handlebars.compile(source);
|
||||
$_("status").innerHTML = template(resp);
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -34,15 +34,16 @@ document.addEventListener("DOMContentLoaded", (e) => {
|
||||
$_("render-contract").innerHTML = html;
|
||||
|
||||
document.getElementById("confirm-pay").addEventListener("click", (e) => {
|
||||
let d = clone(query);
|
||||
let d = {
|
||||
offer: JSON.parse(query.offer)
|
||||
};
|
||||
chrome.runtime.sendMessage({type:'confirm-pay', detail: d}, (resp) => {
|
||||
if (resp.success === true) {
|
||||
document.location.href = resp.backlink;
|
||||
} else {
|
||||
document.body.innerHTML =
|
||||
`Oops, something went wrong.
|
||||
Here is some more info:
|
||||
<pre>${resp.text}</pre>`;
|
||||
console.log("got response", resp);
|
||||
if ("error" in resp) {
|
||||
let source = $_("error-template").innerHTML;
|
||||
let template = Handlebars.compile(source);
|
||||
$_("status").innerHTML = template(resp);
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -7,5 +7,6 @@
|
||||
<h1>Debug Pages</h1>
|
||||
<a href="show-db.html">Show DB</a> <br>
|
||||
<a href="../popup/balance-overview.html">Show balance</a>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -20,6 +20,15 @@ document.addEventListener('DOMContentLoaded', (e) => {
|
||||
let context = document.getElementById("balance-template").innerHTML;
|
||||
let template = Handlebars.compile(context);
|
||||
document.getElementById("content").innerHTML = template(wallet);
|
||||
let el = document.getElementById("link-kudos");
|
||||
if (el) {
|
||||
el.onclick = (e) => {
|
||||
let target = e.target;
|
||||
chrome.tabs.create({
|
||||
"url": target.href
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
document.getElementById("debug").addEventListener("click", (e) => {
|
||||
chrome.tabs.create({
|
||||
@ -29,10 +38,4 @@ document.addEventListener('DOMContentLoaded', (e) => {
|
||||
document.getElementById("reset").addEventListener("click", (e) => {
|
||||
chrome.runtime.sendMessage({ type: "reset" });
|
||||
});
|
||||
document.getElementById("link-kudos").addEventListener("click", (e) => {
|
||||
let target = e.target;
|
||||
chrome.tabs.create({
|
||||
"url": target.href
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -16,12 +16,22 @@ let React = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', (e) => {
|
||||
console.log("content loaded");
|
||||
chrome.runtime.sendMessage({type: "balances"}, function(wallet) {
|
||||
let context = document.getElementById("balance-template").innerHTML;
|
||||
let template = Handlebars.compile(context);
|
||||
document.getElementById("content").innerHTML = template(wallet);
|
||||
let el = document.getElementById("link-kudos");
|
||||
if (el) {
|
||||
el.onclick = (e) => {
|
||||
let target: any = e.target;
|
||||
chrome.tabs.create({
|
||||
"url": target.href
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("debug").addEventListener("click", (e) => {
|
||||
@ -32,10 +42,4 @@ document.addEventListener('DOMContentLoaded', (e) => {
|
||||
document.getElementById("reset").addEventListener("click", (e) => {
|
||||
chrome.runtime.sendMessage({type: "reset"});
|
||||
});
|
||||
document.getElementById("link-kudos").addEventListener("click", (e) => {
|
||||
let target: any = e.target;
|
||||
chrome.tabs.create({
|
||||
"url": target.href
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user