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
|
* Return a promise that resolves
|
||||||
* things that are stored in the wallet's
|
* to the taler wallet db.
|
||||||
* database.
|
|
||||||
*/
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
@ -30,6 +30,7 @@ var emsc = {
|
|||||||
amount_add: getEmsc('TALER_amount_add', 'number', ['number', 'number', 'number']),
|
amount_add: getEmsc('TALER_amount_add', 'number', ['number', 'number', 'number']),
|
||||||
amount_subtract: getEmsc('TALER_amount_subtract', 'number', ['number', 'number', 'number']),
|
amount_subtract: getEmsc('TALER_amount_subtract', 'number', ['number', 'number', 'number']),
|
||||||
amount_normalize: getEmsc('TALER_amount_normalize', 'void', ['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_cmp: getEmsc('TALER_amount_cmp', 'number', ['number', 'number']),
|
||||||
amount_hton: getEmsc('TALER_amount_hton', 'void', ['number', 'number']),
|
amount_hton: getEmsc('TALER_amount_hton', 'void', ['number', 'number']),
|
||||||
amount_ntoh: getEmsc('TALER_amount_ntoh', 'void', ['number', 'number']),
|
amount_ntoh: getEmsc('TALER_amount_ntoh', 'void', ['number', 'number']),
|
||||||
@ -62,6 +63,7 @@ var emscAlloc = {
|
|||||||
var SignaturePurpose;
|
var SignaturePurpose;
|
||||||
(function (SignaturePurpose) {
|
(function (SignaturePurpose) {
|
||||||
SignaturePurpose[SignaturePurpose["RESERVE_WITHDRAW"] = 1200] = "RESERVE_WITHDRAW";
|
SignaturePurpose[SignaturePurpose["RESERVE_WITHDRAW"] = 1200] = "RESERVE_WITHDRAW";
|
||||||
|
SignaturePurpose[SignaturePurpose["WALLET_COIN_DEPOSIT"] = 1201] = "WALLET_COIN_DEPOSIT";
|
||||||
})(SignaturePurpose || (SignaturePurpose = {}));
|
})(SignaturePurpose || (SignaturePurpose = {}));
|
||||||
var RandomQuality;
|
var RandomQuality;
|
||||||
(function (RandomQuality) {
|
(function (RandomQuality) {
|
||||||
@ -138,7 +140,7 @@ class SyncArena extends DefaultArena {
|
|||||||
super();
|
super();
|
||||||
let me = this;
|
let me = this;
|
||||||
this.timer = new Worker('background/timerThread.js');
|
this.timer = new Worker('background/timerThread.js');
|
||||||
this.timer.onmessage = (e) => {
|
this.timer.onmessage = () => {
|
||||||
this.destroy();
|
this.destroy();
|
||||||
};
|
};
|
||||||
//this.timer.postMessage({interval: 50});
|
//this.timer.postMessage({interval: 50});
|
||||||
@ -164,6 +166,14 @@ class Amount extends ArenaObject {
|
|||||||
emsc.free(this.nativePtr);
|
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) {
|
toNbo(a) {
|
||||||
let x = new AmountNbo(a);
|
let x = new AmountNbo(a);
|
||||||
x.alloc();
|
x.alloc();
|
||||||
@ -260,7 +270,9 @@ class PackedArenaObject extends ArenaObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
class AmountNbo extends PackedArenaObject {
|
class AmountNbo extends PackedArenaObject {
|
||||||
size() { return 24; }
|
size() {
|
||||||
|
return 24;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class EddsaPrivateKey extends PackedArenaObject {
|
class EddsaPrivateKey extends PackedArenaObject {
|
||||||
static create(a) {
|
static create(a) {
|
||||||
@ -268,30 +280,60 @@ class EddsaPrivateKey extends PackedArenaObject {
|
|||||||
obj.nativePtr = emscAlloc.eddsa_key_create();
|
obj.nativePtr = emscAlloc.eddsa_key_create();
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
size() { return 32; }
|
size() {
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
getPublicKey(a) {
|
getPublicKey(a) {
|
||||||
let obj = new EddsaPublicKey(a);
|
let obj = new EddsaPublicKey(a);
|
||||||
obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
|
obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mixinStatic(EddsaPrivateKey, fromCrock);
|
||||||
|
function fromCrock(s) {
|
||||||
|
let x = new this();
|
||||||
|
x.alloc();
|
||||||
|
x.loadCrock(s);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
function mixin(obj, method, name) {
|
||||||
|
if (!name) {
|
||||||
|
name = method.name;
|
||||||
|
}
|
||||||
|
if (!name) {
|
||||||
|
throw Error("Mixin needs a name.");
|
||||||
|
}
|
||||||
|
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 {
|
class EddsaPublicKey extends PackedArenaObject {
|
||||||
size() { return 32; }
|
size() {
|
||||||
|
return 32;
|
||||||
}
|
}
|
||||||
class RsaBlindingKey extends ArenaObject {
|
|
||||||
static create(len, a) {
|
|
||||||
let o = new RsaBlindingKey(a);
|
|
||||||
o.nativePtr = emscAlloc.rsa_blinding_key_create(len);
|
|
||||||
return o;
|
|
||||||
}
|
}
|
||||||
static fromCrock(s, a) {
|
mixinStatic(EddsaPublicKey, fromCrock);
|
||||||
|
function makeFromCrock(decodeFn) {
|
||||||
|
function fromCrock(s, a) {
|
||||||
let obj = new this(a);
|
let obj = new this(a);
|
||||||
let buf = ByteArray.fromCrock(s);
|
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();
|
buf.destroy();
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
toCrock() {
|
return fromCrock;
|
||||||
|
}
|
||||||
|
function makeToCrock(encodeFn) {
|
||||||
|
function toCrock() {
|
||||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||||
let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr);
|
let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr);
|
||||||
let res = new ByteArray(size, Module.getValue(ptr, '*'));
|
let res = new ByteArray(size, Module.getValue(ptr, '*'));
|
||||||
@ -300,12 +342,27 @@ class RsaBlindingKey extends ArenaObject {
|
|||||||
res.destroy();
|
res.destroy();
|
||||||
return s;
|
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() {
|
destroy() {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mixinStatic(RsaBlindingKey, makeFromCrock(emscAlloc.rsa_blinding_key_decode));
|
||||||
class HashCode extends PackedArenaObject {
|
class HashCode extends PackedArenaObject {
|
||||||
size() { return 64; }
|
size() {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
random(qualStr) {
|
random(qualStr) {
|
||||||
let qual;
|
let qual;
|
||||||
switch (qualStr) {
|
switch (qualStr) {
|
||||||
@ -328,6 +385,7 @@ class HashCode extends PackedArenaObject {
|
|||||||
emsc.hash_create_random(qual, this.nativePtr);
|
emsc.hash_create_random(qual, this.nativePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mixinStatic(HashCode, fromCrock);
|
||||||
class ByteArray extends PackedArenaObject {
|
class ByteArray extends PackedArenaObject {
|
||||||
constructor(desiredSize, init, a) {
|
constructor(desiredSize, init, a) {
|
||||||
super(a);
|
super(a);
|
||||||
@ -339,7 +397,9 @@ class ByteArray extends PackedArenaObject {
|
|||||||
}
|
}
|
||||||
this.allocatedSize = desiredSize;
|
this.allocatedSize = desiredSize;
|
||||||
}
|
}
|
||||||
size() { return this.allocatedSize; }
|
size() {
|
||||||
|
return this.allocatedSize;
|
||||||
|
}
|
||||||
static fromString(s, a) {
|
static fromString(s, a) {
|
||||||
let hstr = emscAlloc.malloc(s.length + 1);
|
let hstr = emscAlloc.malloc(s.length + 1);
|
||||||
Module.writeStringToMemory(s, hstr);
|
Module.writeStringToMemory(s, hstr);
|
||||||
@ -364,7 +424,9 @@ class EccSignaturePurpose extends PackedArenaObject {
|
|||||||
this.nativePtr = emscAlloc.purpose_create(purpose, payload.nativePtr, payload.size());
|
this.nativePtr = emscAlloc.purpose_create(purpose, payload.nativePtr, payload.size());
|
||||||
this.payloadSize = payload.size();
|
this.payloadSize = payload.size();
|
||||||
}
|
}
|
||||||
size() { return this.payloadSize + 8; }
|
size() {
|
||||||
|
return this.payloadSize + 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class SignatureStruct {
|
class SignatureStruct {
|
||||||
constructor(x) {
|
constructor(x) {
|
||||||
@ -393,8 +455,7 @@ class SignatureStruct {
|
|||||||
ptr += size;
|
ptr += size;
|
||||||
}
|
}
|
||||||
let ba = new ByteArray(totalSize, buf, a);
|
let ba = new ByteArray(totalSize, buf, a);
|
||||||
let x = new EccSignaturePurpose(this.purpose(), ba);
|
return new EccSignaturePurpose(this.purpose(), ba);
|
||||||
return x;
|
|
||||||
}
|
}
|
||||||
set(name, value) {
|
set(name, value) {
|
||||||
let typemap = {};
|
let typemap = {};
|
||||||
@ -414,32 +475,68 @@ class WithdrawRequestPS extends SignatureStruct {
|
|||||||
constructor(w) {
|
constructor(w) {
|
||||||
super(w);
|
super(w);
|
||||||
}
|
}
|
||||||
purpose() { return SignaturePurpose.RESERVE_WITHDRAW; }
|
purpose() {
|
||||||
|
return SignaturePurpose.RESERVE_WITHDRAW;
|
||||||
|
}
|
||||||
fieldTypes() {
|
fieldTypes() {
|
||||||
return [
|
return [
|
||||||
["reserve_pub", EddsaPublicKey],
|
["reserve_pub", EddsaPublicKey],
|
||||||
["amount_with_fee", AmountNbo],
|
["amount_with_fee", AmountNbo],
|
||||||
["withdraw_fee", AmountNbo],
|
["withdraw_fee", AmountNbo],
|
||||||
["h_denomination_pub", HashCode],
|
["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 ptr = emscAlloc.malloc(PTR_SIZE);
|
||||||
let len = fn(obj.getNative(), ptr);
|
let len = encodeFn(this.getNative(), ptr);
|
||||||
let res = new ByteArray(len, null, arena);
|
let res = new ByteArray(len, null, arena);
|
||||||
res.setNative(Module.getValue(ptr, '*'));
|
res.setNative(Module.getValue(ptr, '*'));
|
||||||
emsc.free(ptr);
|
emsc.free(ptr);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
class RsaPublicKey extends ArenaObject {
|
return encode;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
class RsaPublicKey extends ArenaObject {
|
||||||
toCrock() {
|
toCrock() {
|
||||||
return this.encode().toCrock();
|
return this.encode().toCrock();
|
||||||
}
|
}
|
||||||
@ -447,38 +544,26 @@ class RsaPublicKey extends ArenaObject {
|
|||||||
emsc.rsa_public_key_free(this.nativePtr);
|
emsc.rsa_public_key_free(this.nativePtr);
|
||||||
this.nativePtr = 0;
|
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 {
|
class EddsaSignature extends PackedArenaObject {
|
||||||
size() { return 64; }
|
size() {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class RsaSignature extends ArenaObject {
|
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() {
|
destroy() {
|
||||||
emsc.rsa_signature_free(this.getNative());
|
emsc.rsa_signature_free(this.getNative());
|
||||||
this.setNative(0);
|
this.setNative(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mixinStatic(RsaSignature, makeFromCrock(emscAlloc.rsa_signature_decode));
|
||||||
|
mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
|
||||||
function rsaBlind(hashCode, blindingKey, pkey, arena) {
|
function rsaBlind(hashCode, blindingKey, pkey, arena) {
|
||||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||||
let s = emscAlloc.rsa_blind(hashCode.nativePtr, blindingKey.nativePtr, pkey.nativePtr, ptr);
|
let s = emscAlloc.rsa_blind(hashCode.nativePtr, blindingKey.nativePtr, pkey.nativePtr, ptr);
|
||||||
let res = new ByteArray(s, Module.getValue(ptr, '*'), arena);
|
return new ByteArray(s, Module.getValue(ptr, '*'), arena);
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
function eddsaSign(purpose, priv, a) {
|
function eddsaSign(purpose, priv, a) {
|
||||||
let sig = new EddsaSignature(a);
|
let sig = new EddsaSignature(a);
|
||||||
|
@ -30,10 +30,18 @@ const GNUNET_SYSERR = -1;
|
|||||||
|
|
||||||
|
|
||||||
interface EmscFunGen {
|
interface EmscFunGen {
|
||||||
(name: string, ret: string, args: string[]): ((...x: (number|string)[]) => any);
|
(name: string,
|
||||||
(name: string, ret: 'number', args: string[]): ((...x: (number|string)[]) => number);
|
ret: string,
|
||||||
(name: string, ret: 'void', args: string[]): ((...x: (number|string)[]) => void);
|
args: string[]): ((...x: (number|string)[]) => any);
|
||||||
(name: string, ret: 'string', args: string[]): ((...x: (number|string)[]) => string);
|
(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);
|
let getEmsc: EmscFunGen = (...args) => Module.cwrap.apply(null, args);
|
||||||
@ -58,6 +66,9 @@ var emsc = {
|
|||||||
amount_normalize: getEmsc('TALER_amount_normalize',
|
amount_normalize: getEmsc('TALER_amount_normalize',
|
||||||
'void',
|
'void',
|
||||||
['number']),
|
['number']),
|
||||||
|
amount_get_zero: getEmsc('TALER_amount_get_zero',
|
||||||
|
'number',
|
||||||
|
['string', 'number']),
|
||||||
amount_cmp: getEmsc('TALER_amount_cmp',
|
amount_cmp: getEmsc('TALER_amount_cmp',
|
||||||
'number',
|
'number',
|
||||||
['number', 'number']),
|
['number', 'number']),
|
||||||
@ -99,7 +110,8 @@ var emscAlloc = {
|
|||||||
['number', 'number', 'number', 'string']),
|
['number', 'number', 'number', 'string']),
|
||||||
eddsa_key_create: getEmsc('GNUNET_CRYPTO_eddsa_key_create',
|
eddsa_key_create: getEmsc('GNUNET_CRYPTO_eddsa_key_create',
|
||||||
'number', []),
|
'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',
|
||||||
['number']),
|
['number']),
|
||||||
data_to_string_alloc: getEmsc('GNUNET_STRINGS_data_to_string_alloc',
|
data_to_string_alloc: getEmsc('GNUNET_STRINGS_data_to_string_alloc',
|
||||||
@ -140,7 +152,8 @@ var emscAlloc = {
|
|||||||
|
|
||||||
|
|
||||||
enum SignaturePurpose {
|
enum SignaturePurpose {
|
||||||
RESERVE_WITHDRAW = 1200
|
RESERVE_WITHDRAW = 1200,
|
||||||
|
WALLET_COIN_DEPOSIT = 1201,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RandomQuality {
|
enum RandomQuality {
|
||||||
@ -153,6 +166,7 @@ enum RandomQuality {
|
|||||||
abstract class ArenaObject {
|
abstract class ArenaObject {
|
||||||
private _nativePtr: number;
|
private _nativePtr: number;
|
||||||
arena: Arena;
|
arena: Arena;
|
||||||
|
|
||||||
abstract destroy(): void;
|
abstract destroy(): void;
|
||||||
|
|
||||||
constructor(arena?: Arena) {
|
constructor(arena?: Arena) {
|
||||||
@ -215,6 +229,7 @@ interface Arena {
|
|||||||
|
|
||||||
class DefaultArena implements Arena {
|
class DefaultArena implements Arena {
|
||||||
heap: Array<ArenaObject>;
|
heap: Array<ArenaObject>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.heap = [];
|
this.heap = [];
|
||||||
}
|
}
|
||||||
@ -238,31 +253,35 @@ class DefaultArena implements Arena {
|
|||||||
*/
|
*/
|
||||||
class SyncArena extends DefaultArena {
|
class SyncArena extends DefaultArena {
|
||||||
timer: Worker;
|
timer: Worker;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
let me = this;
|
let me = this;
|
||||||
this.timer = new Worker('background/timerThread.js');
|
this.timer = new Worker('background/timerThread.js');
|
||||||
this.timer.onmessage = (e) => {
|
this.timer.onmessage = () => {
|
||||||
this.destroy();
|
this.destroy();
|
||||||
};
|
};
|
||||||
//this.timer.postMessage({interval: 50});
|
//this.timer.postMessage({interval: 50});
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
super.destroy();
|
super.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let arenaStack: Arena[] = [];
|
let arenaStack: Arena[] = [];
|
||||||
arenaStack.push(new SyncArena());
|
arenaStack.push(new SyncArena());
|
||||||
|
|
||||||
|
|
||||||
class Amount extends ArenaObject {
|
class Amount extends ArenaObject {
|
||||||
constructor(args?: any, arena?: Arena) {
|
constructor(args?: AmountJson, arena?: Arena) {
|
||||||
super(arena);
|
super(arena);
|
||||||
if (args) {
|
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 {
|
} else {
|
||||||
this.nativePtr = emscAlloc.get_amount(0, 0, 0, "");
|
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 {
|
toNbo(a?: Arena): AmountNbo {
|
||||||
let x = new AmountNbo(a);
|
let x = new AmountNbo(a);
|
||||||
x.alloc();
|
x.alloc();
|
||||||
@ -362,7 +392,10 @@ abstract class PackedArenaObject extends ArenaObject {
|
|||||||
// We need to get the javascript string
|
// We need to get the javascript string
|
||||||
// to the emscripten heap first.
|
// to the emscripten heap first.
|
||||||
let buf = ByteArray.fromString(s);
|
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();
|
buf.destroy();
|
||||||
if (res < 1) {
|
if (res < 1) {
|
||||||
throw {error: "wrong encoding"};
|
throw {error: "wrong encoding"};
|
||||||
@ -390,7 +423,9 @@ abstract class PackedArenaObject extends ArenaObject {
|
|||||||
|
|
||||||
|
|
||||||
class AmountNbo extends PackedArenaObject {
|
class AmountNbo extends PackedArenaObject {
|
||||||
size() { return 24; }
|
size() {
|
||||||
|
return 24;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -401,37 +436,76 @@ class EddsaPrivateKey extends PackedArenaObject {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
size() { return 32; }
|
size() {
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
getPublicKey(a?: Arena): EddsaPublicKey {
|
getPublicKey(a?: Arena): EddsaPublicKey {
|
||||||
let obj = new EddsaPublicKey(a);
|
let obj = new EddsaPublicKey(a);
|
||||||
obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
|
obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
|
||||||
return obj;
|
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 {
|
class EddsaPublicKey extends PackedArenaObject {
|
||||||
size() { return 32; }
|
size() {
|
||||||
|
return 32;
|
||||||
}
|
}
|
||||||
|
static fromCrock: (s: string) => EddsaPublicKey;
|
||||||
|
|
||||||
|
|
||||||
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 {
|
mixinStatic(EddsaPublicKey, fromCrock);
|
||||||
|
|
||||||
|
function makeFromCrock(decodeFn: (p: number, s: number) => number) {
|
||||||
|
function fromCrock(s: string, a?: Arena) {
|
||||||
let obj = new this(a);
|
let obj = new this(a);
|
||||||
let buf = ByteArray.fromCrock(s);
|
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();
|
buf.destroy();
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
toCrock(): string {
|
return fromCrock;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeToCrock(encodeFn: (po: number, ps: number) => number): () => string {
|
||||||
|
function toCrock() {
|
||||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||||
let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr);
|
let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr);
|
||||||
let res = new ByteArray(size, Module.getValue(ptr, '*'));
|
let res = new ByteArray(size, Module.getValue(ptr, '*'));
|
||||||
@ -440,15 +514,32 @@ class RsaBlindingKey extends ArenaObject {
|
|||||||
res.destroy();
|
res.destroy();
|
||||||
return s;
|
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() {
|
destroy() {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mixinStatic(RsaBlindingKey, makeFromCrock(emscAlloc.rsa_blinding_key_decode));
|
||||||
|
|
||||||
|
|
||||||
class HashCode extends PackedArenaObject {
|
class HashCode extends PackedArenaObject {
|
||||||
size() { return 64; }
|
size() {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromCrock: (s: string) => HashCode;
|
||||||
|
|
||||||
random(qualStr: string) {
|
random(qualStr: string) {
|
||||||
let qual: RandomQuality;
|
let qual: RandomQuality;
|
||||||
@ -472,11 +563,15 @@ class HashCode extends PackedArenaObject {
|
|||||||
emsc.hash_create_random(qual, this.nativePtr);
|
emsc.hash_create_random(qual, this.nativePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mixinStatic(HashCode, fromCrock);
|
||||||
|
|
||||||
|
|
||||||
class ByteArray extends PackedArenaObject {
|
class ByteArray extends PackedArenaObject {
|
||||||
private allocatedSize: number;
|
private allocatedSize: number;
|
||||||
size() { return this.allocatedSize; }
|
|
||||||
|
size() {
|
||||||
|
return this.allocatedSize;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(desiredSize: number, init: number, a?: Arena) {
|
constructor(desiredSize: number, init: number, a?: Arena) {
|
||||||
super(a);
|
super(a);
|
||||||
@ -510,11 +605,19 @@ class ByteArray extends PackedArenaObject {
|
|||||||
|
|
||||||
|
|
||||||
class EccSignaturePurpose extends PackedArenaObject {
|
class EccSignaturePurpose extends PackedArenaObject {
|
||||||
size() { return this.payloadSize + 8; }
|
size() {
|
||||||
|
return this.payloadSize + 8;
|
||||||
|
}
|
||||||
|
|
||||||
payloadSize: number;
|
payloadSize: number;
|
||||||
constructor(purpose: SignaturePurpose, payload: PackedArenaObject, a?: Arena) {
|
|
||||||
|
constructor(purpose: SignaturePurpose,
|
||||||
|
payload: PackedArenaObject,
|
||||||
|
a?: Arena) {
|
||||||
super(a);
|
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();
|
this.payloadSize = payload.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -522,7 +625,9 @@ class EccSignaturePurpose extends PackedArenaObject {
|
|||||||
|
|
||||||
abstract class SignatureStruct {
|
abstract class SignatureStruct {
|
||||||
abstract fieldTypes(): Array<any>;
|
abstract fieldTypes(): Array<any>;
|
||||||
|
|
||||||
abstract purpose(): SignaturePurpose;
|
abstract purpose(): SignaturePurpose;
|
||||||
|
|
||||||
private members: any = {};
|
private members: any = {};
|
||||||
|
|
||||||
constructor(x: { [name: string]: any }) {
|
constructor(x: { [name: string]: any }) {
|
||||||
@ -552,12 +657,11 @@ abstract class SignatureStruct {
|
|||||||
ptr += size;
|
ptr += size;
|
||||||
}
|
}
|
||||||
let ba = new ByteArray(totalSize, buf, a);
|
let ba = new ByteArray(totalSize, buf, a);
|
||||||
let x = new EccSignaturePurpose(this.purpose(), ba);
|
return new EccSignaturePurpose(this.purpose(), ba);
|
||||||
return x;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected set(name: string, value: PackedArenaObject) {
|
protected set(name: string, value: PackedArenaObject) {
|
||||||
let typemap: any = {}
|
let typemap: any = {};
|
||||||
for (let f of this.fieldTypes()) {
|
for (let f of this.fieldTypes()) {
|
||||||
typemap[f[0]] = f[1];
|
typemap[f[0]] = f[1];
|
||||||
}
|
}
|
||||||
@ -586,38 +690,104 @@ class WithdrawRequestPS extends SignatureStruct {
|
|||||||
constructor(w: WithdrawRequestPS_Args) {
|
constructor(w: WithdrawRequestPS_Args) {
|
||||||
super(w);
|
super(w);
|
||||||
}
|
}
|
||||||
purpose() { return SignaturePurpose.RESERVE_WITHDRAW; }
|
|
||||||
|
purpose() {
|
||||||
|
return SignaturePurpose.RESERVE_WITHDRAW;
|
||||||
|
}
|
||||||
|
|
||||||
fieldTypes() {
|
fieldTypes() {
|
||||||
return [
|
return [
|
||||||
["reserve_pub", EddsaPublicKey],
|
["reserve_pub", EddsaPublicKey],
|
||||||
["amount_with_fee", AmountNbo],
|
["amount_with_fee", AmountNbo],
|
||||||
["withdraw_fee", AmountNbo],
|
["withdraw_fee", AmountNbo],
|
||||||
["h_denomination_pub", HashCode],
|
["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 ptr = emscAlloc.malloc(PTR_SIZE);
|
||||||
let len = fn(obj.getNative(), ptr);
|
let len = encodeFn(this.getNative(), ptr);
|
||||||
let res = new ByteArray(len, null, arena);
|
let res = new ByteArray(len, null, arena);
|
||||||
res.setNative(Module.getValue(ptr, '*'));
|
res.setNative(Module.getValue(ptr, '*'));
|
||||||
emsc.free(ptr);
|
emsc.free(ptr);
|
||||||
return res;
|
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() {
|
toCrock() {
|
||||||
return this.encode().toCrock();
|
return this.encode().toCrock();
|
||||||
}
|
}
|
||||||
@ -627,57 +797,49 @@ class RsaPublicKey extends ArenaObject {
|
|||||||
this.nativePtr = 0;
|
this.nativePtr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
encode(arena?: Arena): ByteArray {
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
mixinStatic(RsaPublicKey, makeFromCrock(emscAlloc.rsa_public_key_decode));
|
||||||
|
mixin(RsaPublicKey, makeEncode(emscAlloc.rsa_public_key_encode));
|
||||||
|
|
||||||
|
|
||||||
class EddsaSignature extends PackedArenaObject {
|
class EddsaSignature extends PackedArenaObject {
|
||||||
size() { return 64; }
|
size() {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RsaSignature extends ArenaObject {
|
class RsaSignature extends ArenaObject implements Encodeable{
|
||||||
static fromCrock(s: string, a?: Arena): RsaSignature {
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
encode(arena?: Arena): ByteArray {
|
encode: (arena?: Arena) => ByteArray;
|
||||||
return encodeWith(this, emscAlloc.rsa_signature_encode);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
emsc.rsa_signature_free(this.getNative());
|
emsc.rsa_signature_free(this.getNative());
|
||||||
this.setNative(0);
|
this.setNative(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mixinStatic(RsaSignature, makeFromCrock(emscAlloc.rsa_signature_decode));
|
||||||
|
mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
|
||||||
|
|
||||||
|
|
||||||
function rsaBlind(hashCode: HashCode,
|
function rsaBlind(hashCode: HashCode,
|
||||||
blindingKey: RsaBlindingKey,
|
blindingKey: RsaBlindingKey,
|
||||||
pkey: RsaPublicKey,
|
pkey: RsaPublicKey,
|
||||||
arena?: Arena): ByteArray
|
arena?: Arena): ByteArray {
|
||||||
{
|
|
||||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||||
let s = emscAlloc.rsa_blind(hashCode.nativePtr, blindingKey.nativePtr, pkey.nativePtr, ptr);
|
let s = emscAlloc.rsa_blind(hashCode.nativePtr,
|
||||||
let res = new ByteArray(s, Module.getValue(ptr, '*'), arena);
|
blindingKey.nativePtr,
|
||||||
return res;
|
pkey.nativePtr,
|
||||||
|
ptr);
|
||||||
|
return new ByteArray(s, Module.getValue(ptr, '*'), arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function eddsaSign(purpose: EccSignaturePurpose,
|
function eddsaSign(purpose: EccSignaturePurpose,
|
||||||
priv: EddsaPrivateKey,
|
priv: EddsaPrivateKey,
|
||||||
a?: Arena): EddsaSignature
|
a?: Arena): EddsaSignature {
|
||||||
{
|
|
||||||
let sig = new EddsaSignature(a);
|
let sig = new EddsaSignature(a);
|
||||||
sig.alloc();
|
sig.alloc();
|
||||||
let res = emsc.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
|
let res = emsc.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
|
||||||
@ -687,13 +849,14 @@ function eddsaSign(purpose: EccSignaturePurpose,
|
|||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function rsaUnblind(sig: RsaSignature,
|
function rsaUnblind(sig: RsaSignature,
|
||||||
bk: RsaBlindingKey,
|
bk: RsaBlindingKey,
|
||||||
pk: RsaPublicKey,
|
pk: RsaPublicKey,
|
||||||
a?: Arena): RsaSignature {
|
a?: Arena): RsaSignature {
|
||||||
let x = new RsaSignature(a);
|
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;
|
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/urijs/URIjs.d.ts" />
|
||||||
/// <reference path="../decl/chrome/chrome.d.ts" />
|
/// <reference path="../decl/chrome/chrome.d.ts" />
|
||||||
'use strict';
|
'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
|
* See http://api.taler.net/wallet.html#general
|
||||||
*/
|
*/
|
||||||
@ -46,8 +14,141 @@ function canonicalizeBaseUrl(url) {
|
|||||||
x.query();
|
x.query();
|
||||||
return x.href();
|
return x.href();
|
||||||
}
|
}
|
||||||
function grantCoins(db, feeThreshold, paymentAmount, mintBaseUrl) {
|
function signDeposit(db, offer, cds) {
|
||||||
throw "not implemented";
|
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) {
|
function confirmPay(db, detail, sendResponse) {
|
||||||
console.log("confirmPay", JSON.stringify(detail));
|
console.log("confirmPay", JSON.stringify(detail));
|
||||||
@ -57,10 +158,17 @@ function confirmPay(db, detail, sendResponse) {
|
|||||||
contract: detail.offer.contract,
|
contract: detail.offer.contract,
|
||||||
sig: detail.offer
|
sig: detail.offer
|
||||||
};
|
};
|
||||||
let contract = detail.offer.contract;
|
let offer = detail.offer;
|
||||||
//let chosenCoinPromise = chooseCoins(db, contract.max_fee, contract.amount)
|
getPossibleMintCoins(db, offer.contract.amount, offer.contract.max_fee, offer.contract.mints)
|
||||||
// .then(x => generateDepositPermissions(db, x))
|
.then((mcs) => {
|
||||||
// .then(executePayment);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
function confirmReserve(db, detail, sendResponse) {
|
function confirmReserve(db, detail, sendResponse) {
|
||||||
@ -110,7 +218,10 @@ function confirmReserve(db, detail, sendResponse) {
|
|||||||
sendResponse(resp);
|
sendResponse(resp);
|
||||||
var mint;
|
var mint;
|
||||||
updateMintFromUrl(db, reserveRecord.mint_base_url)
|
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));
|
.then((reserve) => depleteReserve(db, reserve, mint));
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@ -213,7 +324,7 @@ function withdrawExecute(db, pc) {
|
|||||||
console.log("Withdrawal successful");
|
console.log("Withdrawal successful");
|
||||||
console.log(myRequest.responseText);
|
console.log(myRequest.responseText);
|
||||||
let resp = JSON.parse(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 = {
|
let coin = {
|
||||||
coinPub: pc.coinPub,
|
coinPub: pc.coinPub,
|
||||||
coinPriv: pc.coinPriv,
|
coinPriv: pc.coinPriv,
|
||||||
@ -397,7 +508,9 @@ function dumpDb(db, detail, sendResponse) {
|
|||||||
let name = db.objectStoreNames[i];
|
let name = db.objectStoreNames[i];
|
||||||
let storeDump = {};
|
let storeDump = {};
|
||||||
dump.stores[name] = 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;
|
let cursor = e.target.result;
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
storeDump[cursor.key] = cursor.value;
|
storeDump[cursor.key] = cursor.value;
|
||||||
|
@ -2,39 +2,11 @@
|
|||||||
/// <reference path="../decl/chrome/chrome.d.ts" />
|
/// <reference path="../decl/chrome/chrome.d.ts" />
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const DB_NAME = "taler";
|
|
||||||
const DB_VERSION = 1;
|
|
||||||
|
|
||||||
|
interface AmountJson {
|
||||||
/**
|
value: number;
|
||||||
* Return a promise that resolves
|
fraction: number;
|
||||||
* to the taler wallet db.
|
currency: string;
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -52,17 +24,214 @@ function canonicalizeBaseUrl(url) {
|
|||||||
return x.href()
|
return x.href()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface ConfirmPayRequest {
|
interface ConfirmPayRequest {
|
||||||
offer: any;
|
merchantPageUrl: string;
|
||||||
selectedMint: any;
|
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,
|
interface CoinPaySig {
|
||||||
feeThreshold: AmountJson,
|
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,
|
paymentAmount: AmountJson,
|
||||||
mintBaseUrl: string): Promise<any> {
|
depositFeeLimit: AmountJson,
|
||||||
throw "not implemented";
|
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,
|
contractHash: detail.offer.H_contract,
|
||||||
contract: detail.offer.contract,
|
contract: detail.offer.contract,
|
||||||
sig: detail.offer
|
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 mintUrl = Object.keys(mcs)[0];
|
||||||
let contract = detail.offer.contract;
|
let ds = signDeposit(db, offer, mcs[mintUrl]);
|
||||||
|
return executePay(db, offer, ds, detail.merchantPageUrl, mintUrl);
|
||||||
//let chosenCoinPromise = chooseCoins(db, contract.max_fee, contract.amount)
|
});
|
||||||
// .then(x => generateDepositPermissions(db, x))
|
|
||||||
// .then(executePayment);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function confirmReserve(db, detail, sendResponse) {
|
function confirmReserve(db, detail, sendResponse) {
|
||||||
let reservePriv = EddsaPrivateKey.create();
|
let reservePriv = EddsaPrivateKey.create();
|
||||||
let reservePub = reservePriv.getPublicKey();
|
let reservePub = reservePriv.getPublicKey();
|
||||||
@ -131,7 +309,10 @@ function confirmReserve(db, detail, sendResponse) {
|
|||||||
sendResponse(resp);
|
sendResponse(resp);
|
||||||
var mint;
|
var mint;
|
||||||
updateMintFromUrl(db, reserveRecord.mint_base_url)
|
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));
|
.then((reserve) => depleteReserve(db, reserve, mint));
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@ -160,8 +341,8 @@ function rankDenom(denom1: any, denom2: any) {
|
|||||||
|
|
||||||
|
|
||||||
function withdrawPrepare(db: IDBDatabase,
|
function withdrawPrepare(db: IDBDatabase,
|
||||||
denom: Denomination,
|
denom: Db.Denomination,
|
||||||
reserve): Promise<PreCoin> {
|
reserve): Promise<Db.PreCoin> {
|
||||||
let reservePriv = new EddsaPrivateKey();
|
let reservePriv = new EddsaPrivateKey();
|
||||||
reservePriv.loadCrock(reserve.reserve_priv);
|
reservePriv.loadCrock(reserve.reserve_priv);
|
||||||
let reservePub = new EddsaPublicKey();
|
let reservePub = new EddsaPublicKey();
|
||||||
@ -196,7 +377,7 @@ function withdrawPrepare(db: IDBDatabase,
|
|||||||
|
|
||||||
console.log("crypto done, doing request");
|
console.log("crypto done, doing request");
|
||||||
|
|
||||||
let preCoin: PreCoin = {
|
let preCoin: Db.PreCoin = {
|
||||||
reservePub: reservePub.toCrock(),
|
reservePub: reservePub.toCrock(),
|
||||||
blindingKey: blindingFactor.toCrock(),
|
blindingKey: blindingFactor.toCrock(),
|
||||||
coinPub: coinPub.toCrock(),
|
coinPub: coinPub.toCrock(),
|
||||||
@ -219,6 +400,7 @@ function withdrawPrepare(db: IDBDatabase,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function dbGet(db, store: string, key: any): Promise<any> {
|
function dbGet(db, store: string, key: any): Promise<any> {
|
||||||
let tx = db.transaction([store]);
|
let tx = db.transaction([store]);
|
||||||
let req = tx.objectStore(store).get(key);
|
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)
|
return dbGet(db, 'reserves', pc.reservePub)
|
||||||
.then((r) => new Promise((resolve, reject) => {
|
.then((r) => new Promise((resolve, reject) => {
|
||||||
console.log("loading precoin", JSON.stringify(pc));
|
console.log("loading precoin", JSON.stringify(pc));
|
||||||
@ -241,7 +423,8 @@ function withdrawExecute(db, pc: PreCoin): Promise<Coin> {
|
|||||||
let myRequest = new XMLHttpRequest();
|
let myRequest = new XMLHttpRequest();
|
||||||
console.log("making request to " + reqUrl.href());
|
console.log("making request to " + reqUrl.href());
|
||||||
myRequest.open('post', 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.send(JSON.stringify(wd));
|
||||||
myRequest.addEventListener('readystatechange', (e) => {
|
myRequest.addEventListener('readystatechange', (e) => {
|
||||||
if (myRequest.readyState == XMLHttpRequest.DONE) {
|
if (myRequest.readyState == XMLHttpRequest.DONE) {
|
||||||
@ -253,16 +436,16 @@ function withdrawExecute(db, pc: PreCoin): Promise<Coin> {
|
|||||||
console.log("Withdrawal successful");
|
console.log("Withdrawal successful");
|
||||||
console.log(myRequest.responseText);
|
console.log(myRequest.responseText);
|
||||||
let resp = JSON.parse(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),
|
RsaBlindingKey.fromCrock(pc.blindingKey),
|
||||||
RsaPublicKey.fromCrock(pc.denomPub));
|
RsaPublicKey.fromCrock(pc.denomPub));
|
||||||
let coin: Coin = {
|
let coin: Db.Coin = {
|
||||||
coinPub: pc.coinPub,
|
coinPub: pc.coinPub,
|
||||||
coinPriv: pc.coinPriv,
|
coinPriv: pc.coinPriv,
|
||||||
denomPub: pc.denomPub,
|
denomPub: pc.denomPub,
|
||||||
denomSig: denomSig.encode().toCrock(),
|
denomSig: denomSig.encode().toCrock(),
|
||||||
currentAmount: pc.coinValue
|
currentAmount: pc.coinValue
|
||||||
}
|
};
|
||||||
console.log("unblinded coin");
|
console.log("unblinded coin");
|
||||||
resolve(coin);
|
resolve(coin);
|
||||||
} else {
|
} else {
|
||||||
@ -291,7 +474,7 @@ function updateBadge(db) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function storeCoin(db, coin: Coin) {
|
function storeCoin(db, coin: Db.Coin) {
|
||||||
let tx = db.transaction(['coins', 'precoins'], 'readwrite');
|
let tx = db.transaction(['coins', 'precoins'], 'readwrite');
|
||||||
tx.objectStore('precoins').delete(coin.coinPub);
|
tx.objectStore('precoins').delete(coin.coinPub);
|
||||||
tx.objectStore('coins').add(coin);
|
tx.objectStore('coins').add(coin);
|
||||||
@ -408,7 +591,7 @@ function updateMintFromUrl(db, baseUrl) {
|
|||||||
console.log("keys invalid");
|
console.log("keys invalid");
|
||||||
reject();
|
reject();
|
||||||
} else {
|
} else {
|
||||||
let mint = {
|
let mint: Db.Mint = {
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
keys: mintKeysJson
|
keys: mintKeysJson
|
||||||
};
|
};
|
||||||
@ -419,7 +602,7 @@ function updateMintFromUrl(db, baseUrl) {
|
|||||||
let di = {
|
let di = {
|
||||||
denomPub: d.denom_pub,
|
denomPub: d.denom_pub,
|
||||||
value: d.value
|
value: d.value
|
||||||
}
|
};
|
||||||
tx.objectStore('denoms').put(di);
|
tx.objectStore('denoms').put(di);
|
||||||
}
|
}
|
||||||
tx.oncomplete = (e) => {
|
tx.oncomplete = (e) => {
|
||||||
@ -452,7 +635,9 @@ function dumpDb(db, detail, sendResponse) {
|
|||||||
let name = db.objectStoreNames[i];
|
let name = db.objectStoreNames[i];
|
||||||
let storeDump = {};
|
let storeDump = {};
|
||||||
dump.stores[name] = 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;
|
let cursor = e.target.result;
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
storeDump[cursor.key] = cursor.value;
|
storeDump[cursor.key] = cursor.value;
|
||||||
@ -524,7 +709,9 @@ openTalerDb().then((db) => {
|
|||||||
if (req.type in dispatch) {
|
if (req.type in dispatch) {
|
||||||
return dispatch[req.type](db, req.detail, onresponse);
|
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;
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -28,3 +28,7 @@ function format(s, ...args) {
|
|||||||
s = s.replace(/{([0-9]+)}/g, r);
|
s = s.replace(/{([0-9]+)}/g, r);
|
||||||
return s;
|
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;
|
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 />
|
<p />
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id="error-template" type="text/x-handlebars-template">
|
||||||
|
Payment was not successful: {{error}}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -33,7 +37,9 @@
|
|||||||
|
|
||||||
<div id="render-contract"></div>
|
<div id="render-contract"></div>
|
||||||
|
|
||||||
<button id="confirm-purchase">Confirm Purchase!</button>
|
<button id="confirm-pay">Confirm Pay!</button>
|
||||||
|
|
||||||
|
<div id="status"></div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -23,16 +23,16 @@ document.addEventListener("DOMContentLoaded", (e) => {
|
|||||||
let html = template(offer.contract);
|
let html = template(offer.contract);
|
||||||
$_("render-contract").innerHTML = html;
|
$_("render-contract").innerHTML = html;
|
||||||
document.getElementById("confirm-pay").addEventListener("click", (e) => {
|
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) => {
|
chrome.runtime.sendMessage({ type: 'confirm-pay', detail: d }, (resp) => {
|
||||||
if (resp.success === true) {
|
console.log("got response", resp);
|
||||||
document.location.href = resp.backlink;
|
if ("error" in resp) {
|
||||||
}
|
let source = $_("error-template").innerHTML;
|
||||||
else {
|
let template = Handlebars.compile(source);
|
||||||
document.body.innerHTML =
|
$_("status").innerHTML = template(resp);
|
||||||
`Oops, something went wrong.
|
return;
|
||||||
Here is some more info:
|
|
||||||
<pre>${resp.text}</pre>`;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -34,15 +34,16 @@ document.addEventListener("DOMContentLoaded", (e) => {
|
|||||||
$_("render-contract").innerHTML = html;
|
$_("render-contract").innerHTML = html;
|
||||||
|
|
||||||
document.getElementById("confirm-pay").addEventListener("click", (e) => {
|
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) => {
|
chrome.runtime.sendMessage({type:'confirm-pay', detail: d}, (resp) => {
|
||||||
if (resp.success === true) {
|
console.log("got response", resp);
|
||||||
document.location.href = resp.backlink;
|
if ("error" in resp) {
|
||||||
} else {
|
let source = $_("error-template").innerHTML;
|
||||||
document.body.innerHTML =
|
let template = Handlebars.compile(source);
|
||||||
`Oops, something went wrong.
|
$_("status").innerHTML = template(resp);
|
||||||
Here is some more info:
|
return;
|
||||||
<pre>${resp.text}</pre>`;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,5 +7,6 @@
|
|||||||
<h1>Debug Pages</h1>
|
<h1>Debug Pages</h1>
|
||||||
<a href="show-db.html">Show DB</a> <br>
|
<a href="show-db.html">Show DB</a> <br>
|
||||||
<a href="../popup/balance-overview.html">Show balance</a>
|
<a href="../popup/balance-overview.html">Show balance</a>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -20,6 +20,15 @@ document.addEventListener('DOMContentLoaded', (e) => {
|
|||||||
let context = document.getElementById("balance-template").innerHTML;
|
let context = document.getElementById("balance-template").innerHTML;
|
||||||
let template = Handlebars.compile(context);
|
let template = Handlebars.compile(context);
|
||||||
document.getElementById("content").innerHTML = template(wallet);
|
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) => {
|
document.getElementById("debug").addEventListener("click", (e) => {
|
||||||
chrome.tabs.create({
|
chrome.tabs.create({
|
||||||
@ -29,10 +38,4 @@ document.addEventListener('DOMContentLoaded', (e) => {
|
|||||||
document.getElementById("reset").addEventListener("click", (e) => {
|
document.getElementById("reset").addEventListener("click", (e) => {
|
||||||
chrome.runtime.sendMessage({ type: "reset" });
|
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) => {
|
document.addEventListener('DOMContentLoaded', (e) => {
|
||||||
console.log("content loaded");
|
console.log("content loaded");
|
||||||
chrome.runtime.sendMessage({type: "balances"}, function(wallet) {
|
chrome.runtime.sendMessage({type: "balances"}, function(wallet) {
|
||||||
let context = document.getElementById("balance-template").innerHTML;
|
let context = document.getElementById("balance-template").innerHTML;
|
||||||
let template = Handlebars.compile(context);
|
let template = Handlebars.compile(context);
|
||||||
document.getElementById("content").innerHTML = template(wallet);
|
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) => {
|
document.getElementById("debug").addEventListener("click", (e) => {
|
||||||
@ -32,10 +42,4 @@ document.addEventListener('DOMContentLoaded', (e) => {
|
|||||||
document.getElementById("reset").addEventListener("click", (e) => {
|
document.getElementById("reset").addEventListener("click", (e) => {
|
||||||
chrome.runtime.sendMessage({type: "reset"});
|
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