Towards payment.

This commit is contained in:
Florian Dold 2015-12-17 22:56:24 +01:00
parent 5f907c13fc
commit 38c947d771
15 changed files with 1135 additions and 450 deletions

View File

@ -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;
}
};
});
}

View 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;
}
};
});
}

View File

@ -13,7 +13,7 @@
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) {
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 = fn(obj.getNative(), ptr);
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);

View File

@ -13,11 +13,11 @@
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,6 +66,9 @@ 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']),
@ -99,7 +110,8 @@ 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',
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',
@ -140,7 +152,8 @@ var emscAlloc = {
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) {
@ -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,37 +690,103 @@ 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]];
["h_coin_envelope", HashCode]
];
}
}
class AbsoluteTimeNbo extends PackedArenaObject {
static fromTalerString(s: string): AbsoluteTimeNbo {
throw Error();
}
function encodeWith(obj, fn, arena?: Arena) {
size() {
return 8;
}
}
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 = fn(obj.getNative(), ptr);
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: 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 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,
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

View File

@ -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;

View File

@ -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,17 +24,214 @@ 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,
interface CoinPaySig {
coin_sig: string;
coin_pub: string;
ub_sig: string;
denom_pub: string;
f: AmountJson;
}
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,
mintBaseUrl: string): Promise<any> {
throw "not implemented";
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()
}
});
});
}
@ -73,17 +242,26 @@ function confirmPay(db, detail: ConfirmPayRequest, sendResponse) {
contractHash: detail.offer.H_contract,
contract: detail.offer.contract,
sig: detail.offer
};
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 contract = detail.offer.contract;
//let chosenCoinPromise = chooseCoins(db, contract.max_fee, contract.amount)
// .then(x => generateDepositPermissions(db, x))
// .then(executePayment);
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,7 +309,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;
@ -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,6 +400,7 @@ 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);
@ -228,7 +410,7 @@ function dbGet(db, store: string, key: any): Promise<any> {
}
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));
@ -241,7 +423,8 @@ function withdrawExecute(db, pc: PreCoin): Promise<Coin> {
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.setRequestHeader("Content-Type",
"application/json;charset=UTF-8");
myRequest.send(JSON.stringify(wd));
myRequest.addEventListener('readystatechange', (e) => {
if (myRequest.readyState == XMLHttpRequest.DONE) {
@ -253,16 +436,16 @@ function withdrawExecute(db, pc: PreCoin): Promise<Coin> {
console.log("Withdrawal successful");
console.log(myRequest.responseText);
let resp = JSON.parse(myRequest.responseText);
let denomSig = rsaUnblind(RsaSignature.fromCrock(resp.coin_ev),
let denomSig = rsaUnblind(RsaSignature.fromCrock(resp.ev_sig),
RsaBlindingKey.fromCrock(pc.blindingKey),
RsaPublicKey.fromCrock(pc.denomPub));
let coin: Coin = {
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 {
@ -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);
@ -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) => {
@ -452,7 +635,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;
@ -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;
});
});

View File

@ -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; });
}

View File

@ -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;});
}

View File

@ -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>

View File

@ -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;
}
});
});

View File

@ -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;
}
});
});

View File

@ -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>

View File

@ -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
});
});
});

View File

@ -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
});
});
});