crypto for refreshing

This commit is contained in:
Florian Dold 2016-10-12 23:30:10 +02:00
parent d4be3906e3
commit 122e069d91
6 changed files with 351 additions and 124 deletions

View File

@ -1,6 +1,6 @@
// Place your settings in this file to overwrite default and user settings. // Place your settings in this file to overwrite default and user settings.
{ {
// Use latest language services // Use latest language servicesu
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
// Defines space handling after a comma delimiter // Defines space handling after a comma delimiter
"typescript.format.insertSpaceAfterCommaDelimiter": true, "typescript.format.insertSpaceAfterCommaDelimiter": true,
@ -33,5 +33,6 @@
"when": "$(basename).tsx" "when": "$(basename).tsx"
}, },
"**/*.js.map": true "**/*.js.map": true
} },
"editor.wrappingIndent": "same"
} }

View File

@ -33,6 +33,8 @@ export interface EmscFunGen {
export declare namespace Module { export declare namespace Module {
var cwrap: EmscFunGen; var cwrap: EmscFunGen;
function ccall(name: string, ret:"number"|"string", argTypes: any[], args: any[]): any
function stringToUTF8(s: string, addr: number, maxLength: number): void function stringToUTF8(s: string, addr: number, maxLength: number): void
function _free(ptr: number): void; function _free(ptr: number): void;

View File

@ -14,13 +14,13 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import {AmountJson} from "./types"; import { AmountJson } from "./types";
import * as EmscWrapper from "../emscripten/emsc"; import * as EmscWrapper from "../emscripten/emsc";
/** /**
* High-level interface to emscripten-compiled modules used * High-level interface to emscripten-compiled modules used
* by the wallet. * by the wallet.
* @module EmscriptIf *
* @author Florian Dold * @author Florian Dold
*/ */
@ -43,110 +43,122 @@ let getEmsc: EmscWrapper.EmscFunGen = (...args: any[]) => Module.cwrap.apply(
var emsc = { var emsc = {
free: (ptr: number) => Module._free(ptr), free: (ptr: number) => Module._free(ptr),
get_value: getEmsc('TALER_WR_get_value', get_value: getEmsc('TALER_WR_get_value',
'number', 'number',
['number']), ['number']),
get_fraction: getEmsc('TALER_WR_get_fraction', get_fraction: getEmsc('TALER_WR_get_fraction',
'number', 'number',
['number']), ['number']),
get_currency: getEmsc('TALER_WR_get_currency', get_currency: getEmsc('TALER_WR_get_currency',
'string', 'string',
['number']), ['number']),
amount_add: getEmsc('TALER_amount_add', amount_add: getEmsc('TALER_amount_add',
'number', 'number',
['number', 'number', 'number']), ['number', 'number', 'number']),
amount_subtract: getEmsc('TALER_amount_subtract', amount_subtract: getEmsc('TALER_amount_subtract',
'number', 'number',
['number', 'number', 'number']), ['number', 'number', 'number']),
amount_normalize: getEmsc('TALER_amount_normalize', amount_normalize: getEmsc('TALER_amount_normalize',
'void', 'void',
['number']), ['number']),
amount_get_zero: getEmsc('TALER_amount_get_zero', amount_get_zero: getEmsc('TALER_amount_get_zero',
'number', 'number',
['string', 'number']), ['string', 'number']),
amount_cmp: getEmsc('TALER_amount_cmp', amount_cmp: getEmsc('TALER_amount_cmp',
'number', 'number',
['number', 'number']), ['number', 'number']),
amount_hton: getEmsc('TALER_amount_hton', amount_hton: getEmsc('TALER_amount_hton',
'void', 'void',
['number', 'number']), ['number', 'number']),
amount_ntoh: getEmsc('TALER_amount_ntoh', amount_ntoh: getEmsc('TALER_amount_ntoh',
'void', 'void',
['number', 'number']), ['number', 'number']),
hash: getEmsc('GNUNET_CRYPTO_hash', hash: getEmsc('GNUNET_CRYPTO_hash',
'void', 'void',
['number', 'number', 'number']), ['number', 'number', 'number']),
memmove: getEmsc('memmove', memmove: getEmsc('memmove',
'number', 'number',
['number', 'number', 'number']), ['number', 'number', 'number']),
rsa_public_key_free: getEmsc('GNUNET_CRYPTO_rsa_public_key_free', rsa_public_key_free: getEmsc('GNUNET_CRYPTO_rsa_public_key_free',
'void', 'void',
['number']), ['number']),
rsa_signature_free: getEmsc('GNUNET_CRYPTO_rsa_signature_free', rsa_signature_free: getEmsc('GNUNET_CRYPTO_rsa_signature_free',
'void', 'void',
['number']), ['number']),
string_to_data: getEmsc('GNUNET_STRINGS_string_to_data', string_to_data: getEmsc('GNUNET_STRINGS_string_to_data',
'number', 'number',
['number', 'number', 'number', 'number']), ['number', 'number', 'number', 'number']),
eddsa_sign: getEmsc('GNUNET_CRYPTO_eddsa_sign', eddsa_sign: getEmsc('GNUNET_CRYPTO_eddsa_sign',
'number', 'number',
['number', 'number', 'number']), ['number', 'number', 'number']),
eddsa_verify: getEmsc('GNUNET_CRYPTO_eddsa_verify', eddsa_verify: getEmsc('GNUNET_CRYPTO_eddsa_verify',
'number', 'number',
['number', 'number', 'number', 'number']), ['number', 'number', 'number', 'number']),
hash_create_random: getEmsc('GNUNET_CRYPTO_hash_create_random', hash_create_random: getEmsc('GNUNET_CRYPTO_hash_create_random',
'void', 'void',
['number', 'number']), ['number', 'number']),
rsa_blinding_key_destroy: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_free', rsa_blinding_key_destroy: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_free',
'void', 'void',
['number']), ['number']),
random_block: getEmsc('GNUNET_CRYPTO_random_block', random_block: getEmsc('GNUNET_CRYPTO_random_block',
'void', 'void',
['number', 'number', 'number']), ['number', 'number', 'number']),
hash_context_abort: getEmsc('GNUNET_CRYPTO_hash_context_abort',
'void',
['number']),
hash_context_read: getEmsc('GNUNET_CRYPTO_hash_context_read',
'void',
['number', 'number', 'number']),
hash_context_finish: getEmsc('GNUNET_CRYPTO_hash_context_finish',
'void',
['number', 'number']),
}; };
var emscAlloc = { var emscAlloc = {
get_amount: getEmsc('TALER_WRALL_get_amount', get_amount: getEmsc('TALER_WRALL_get_amount',
'number', 'number',
['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( eddsa_public_key_from_private: getEmsc(
'TALER_WRALL_eddsa_public_key_from_private', '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',
'number', 'number',
['number', 'number']), ['number', 'number']),
purpose_create: getEmsc('TALER_WRALL_purpose_create', purpose_create: getEmsc('TALER_WRALL_purpose_create',
'number', 'number',
['number', 'number', 'number']), ['number', 'number', 'number']),
rsa_blind: getEmsc('GNUNET_CRYPTO_rsa_blind', rsa_blind: getEmsc('GNUNET_CRYPTO_rsa_blind',
'number', 'number',
['number', 'number', 'number', 'number']), ['number', 'number', 'number', 'number']),
rsa_blinding_key_create: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_create', rsa_blinding_key_create: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_create',
'number', 'number',
['number']), ['number']),
rsa_blinding_key_encode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_encode', rsa_blinding_key_encode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_encode',
'number', 'number',
['number', 'number']), ['number', 'number']),
rsa_signature_encode: getEmsc('GNUNET_CRYPTO_rsa_signature_encode', rsa_signature_encode: getEmsc('GNUNET_CRYPTO_rsa_signature_encode',
'number', 'number',
['number', 'number']), ['number', 'number']),
rsa_blinding_key_decode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_decode', rsa_blinding_key_decode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_decode',
'number', 'number',
['number', 'number']), ['number', 'number']),
rsa_public_key_decode: getEmsc('GNUNET_CRYPTO_rsa_public_key_decode', rsa_public_key_decode: getEmsc('GNUNET_CRYPTO_rsa_public_key_decode',
'number', 'number',
['number', 'number']), ['number', 'number']),
rsa_signature_decode: getEmsc('GNUNET_CRYPTO_rsa_signature_decode', rsa_signature_decode: getEmsc('GNUNET_CRYPTO_rsa_signature_decode',
'number', 'number',
['number', 'number']), ['number', 'number']),
rsa_public_key_encode: getEmsc('GNUNET_CRYPTO_rsa_public_key_encode', rsa_public_key_encode: getEmsc('GNUNET_CRYPTO_rsa_public_key_encode',
'number', 'number',
['number', 'number']), ['number', 'number']),
rsa_unblind: getEmsc('GNUNET_CRYPTO_rsa_unblind', rsa_unblind: getEmsc('GNUNET_CRYPTO_rsa_unblind',
'number', 'number',
['number', 'number', 'number']), ['number', 'number', 'number']),
hash_context_start: getEmsc('GNUNET_CRYPTO_hash_context_start',
'number',
[]),
malloc: (size: number) => Module._malloc(size), malloc: (size: number) => Module._malloc(size),
}; };
@ -155,6 +167,7 @@ export enum SignaturePurpose {
RESERVE_WITHDRAW = 1200, RESERVE_WITHDRAW = 1200,
WALLET_COIN_DEPOSIT = 1201, WALLET_COIN_DEPOSIT = 1201,
MASTER_DENOMINATION_KEY_VALIDITY = 1025, MASTER_DENOMINATION_KEY_VALIDITY = 1025,
WALLET_COIN_MELT = 1202,
} }
enum RandomQuality { enum RandomQuality {
@ -163,9 +176,49 @@ enum RandomQuality {
NONCE = 2 NONCE = 2
} }
interface ArenaObject {
destroy(): void;
}
abstract class ArenaObject {
class HashContext implements ArenaObject {
private hashContextPtr: number | undefined;
constructor() {
this.hashContextPtr = emscAlloc.hash_context_start();
}
read(obj: PackedArenaObject): void {
if (!this.hashContextPtr) {
throw Error("assertion failed");
}
emsc.hash_context_read(this.hashContextPtr, obj.getNative(), obj.size());
}
finish(h: HashCode) {
if (!this.hashContextPtr) {
throw Error("assertion failed");
}
h.alloc();
emsc.hash_context_finish(this.hashContextPtr, h.getNative());
}
destroy(): void {
if (this.hashContextPtr) {
emsc.hash_context_abort(this.hashContextPtr);
}
this.hashContextPtr = undefined;
}
}
abstract class MallocArenaObject implements ArenaObject {
protected _nativePtr: number | undefined = undefined; protected _nativePtr: number | undefined = undefined;
/**
* Is this a weak reference to the underlying memory?
*/
isWeak = false;
arena: Arena; arena: Arena;
abstract destroy(): void; abstract destroy(): void;
@ -192,7 +245,7 @@ abstract class ArenaObject {
} }
free() { free() {
if (this.nativePtr) { if (this.nativePtr && !this.isWeak) {
emsc.free(this.nativePtr); emsc.free(this.nativePtr);
this._nativePtr = undefined; this._nativePtr = undefined;
} }
@ -270,7 +323,7 @@ class SyncArena extends DefaultArena {
super(); super();
} }
pub(obj: ArenaObject) { pub(obj: MallocArenaObject) {
super.put(obj); super.put(obj);
if (!this.isScheduled) { if (!this.isScheduled) {
this.schedule(); this.schedule();
@ -295,14 +348,14 @@ let arenaStack: Arena[] = [];
arenaStack.push(new SyncArena()); arenaStack.push(new SyncArena());
export class Amount extends ArenaObject { export class Amount extends MallocArenaObject {
constructor(args?: AmountJson, arena?: Arena) { constructor(args?: AmountJson, arena?: Arena) {
super(arena); super(arena);
if (args) { if (args) {
this.nativePtr = emscAlloc.get_amount(args.value, this.nativePtr = emscAlloc.get_amount(args.value,
0, 0,
args.fraction, args.fraction,
args.currency); args.currency);
} else { } else {
this.nativePtr = emscAlloc.get_amount(0, 0, 0, ""); this.nativePtr = emscAlloc.get_amount(0, 0, 0, "");
} }
@ -399,7 +452,7 @@ export class Amount extends ArenaObject {
/** /**
* Count the UTF-8 characters in a JavaScript string. * Count the UTF-8 characters in a JavaScript string.
*/ */
function countBytes(str: string): number { function countUtf8Bytes(str: string): number {
var s = str.length; var s = str.length;
// JavaScript strings are UTF-16 arrays // JavaScript strings are UTF-16 arrays
for (let i = str.length - 1; i >= 0; i--) { for (let i = str.length - 1; i >= 0; i--) {
@ -424,7 +477,7 @@ function countBytes(str: string): number {
* Managed reference to a contiguous block of memory in the Emscripten heap. * Managed reference to a contiguous block of memory in the Emscripten heap.
* Should contain only data, not pointers. * Should contain only data, not pointers.
*/ */
abstract class PackedArenaObject extends ArenaObject { abstract class PackedArenaObject extends MallocArenaObject {
abstract size(): number; abstract size(): number;
constructor(a?: Arena) { constructor(a?: Arena) {
@ -455,12 +508,12 @@ abstract class PackedArenaObject extends ArenaObject {
// 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, let res = emsc.string_to_data(buf.nativePtr,
s.length, s.length,
this.nativePtr, this.nativePtr,
this.size()); this.size());
buf.destroy(); buf.destroy();
if (res < 1) { if (res < 1) {
throw {error: "wrong encoding"}; throw { error: "wrong encoding" };
} }
} }
@ -581,7 +634,7 @@ function makeFromCrock(decodeFn: (p: number, s: number) => number) {
let obj = new this(a); let obj = new this(a);
let buf = ByteArray.fromCrock(s); let buf = ByteArray.fromCrock(s);
obj.setNative(decodeFn(buf.getNative(), obj.setNative(decodeFn(buf.getNative(),
buf.size())); buf.size()));
buf.destroy(); buf.destroy();
return obj; return obj;
} }
@ -590,7 +643,7 @@ function makeFromCrock(decodeFn: (p: number, s: number) => number) {
} }
function makeToCrock(encodeFn: (po: number, function makeToCrock(encodeFn: (po: number,
ps: number) => number): () => string { ps: number) => number): () => string {
function toCrock() { 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);
@ -658,14 +711,14 @@ export class ByteArray extends PackedArenaObject {
static fromString(s: string, a?: Arena): ByteArray { static fromString(s: string, a?: Arena): ByteArray {
// UTF-8 bytes, including 0-terminator // UTF-8 bytes, including 0-terminator
let terminatedByteLength = countBytes(s) + 1; let terminatedByteLength = countUtf8Bytes(s) + 1;
let hstr = emscAlloc.malloc(terminatedByteLength); let hstr = emscAlloc.malloc(terminatedByteLength);
Module.stringToUTF8(s, hstr, terminatedByteLength); Module.stringToUTF8(s, hstr, terminatedByteLength);
return new ByteArray(terminatedByteLength, hstr, a); return new ByteArray(terminatedByteLength, hstr, a);
} }
static fromCrock(s: string, a?: Arena): ByteArray { static fromCrock(s: string, a?: Arena): ByteArray {
let byteLength = countBytes(s); let byteLength = countUtf8Bytes(s);
let hstr = emscAlloc.malloc(byteLength + 1); let hstr = emscAlloc.malloc(byteLength + 1);
Module.stringToUTF8(s, hstr, byteLength + 1); Module.stringToUTF8(s, hstr, byteLength + 1);
let decodedLen = Math.floor((byteLength * 5) / 8); let decodedLen = Math.floor((byteLength * 5) / 8);
@ -688,12 +741,12 @@ export class EccSignaturePurpose extends PackedArenaObject {
payloadSize: number; payloadSize: number;
constructor(purpose: SignaturePurpose, constructor(purpose: SignaturePurpose,
payload: PackedArenaObject, payload: PackedArenaObject,
a?: Arena) { a?: Arena) {
super(a); super(a);
this.nativePtr = emscAlloc.purpose_create(purpose, this.nativePtr = emscAlloc.purpose_create(purpose,
payload.nativePtr, payload.nativePtr,
payload.size()); payload.size());
this.payloadSize = payload.size(); this.payloadSize = payload.size();
} }
} }
@ -798,6 +851,34 @@ export class WithdrawRequestPS extends SignatureStruct {
} }
interface RefreshMeltCoinAffirmationPS_Args {
session_hash: HashCode;
amount_with_fee: AmountNbo;
melt_fee: AmountNbo;
coin_pub: EddsaPublicKey;
}
export class RefreshMeltCoinAffirmationPS extends SignatureStruct {
constructor(w: RefreshMeltCoinAffirmationPS_Args) {
super(w);
}
purpose() {
return SignaturePurpose.WALLET_COIN_MELT;
}
fieldTypes() {
return [
["session_hash", HashCode],
["amount_with_fee", AmountNbo],
["melt_fee", AmountNbo],
["coin_pub", EddsaPublicKey]
];
}
}
export class AbsoluteTimeNbo extends PackedArenaObject { export class AbsoluteTimeNbo extends PackedArenaObject {
static fromTalerString(s: string): AbsoluteTimeNbo { static fromTalerString(s: string): AbsoluteTimeNbo {
let x = new AbsoluteTimeNbo(); let x = new AbsoluteTimeNbo();
@ -825,7 +906,14 @@ function set64(p: number, n: number) {
Module.setValue(p + (7 - i), n & 0xFF, "i8"); Module.setValue(p + (7 - i), n & 0xFF, "i8");
n = Math.floor(n / 256); n = Math.floor(n / 256);
} }
}
// XXX: This only works up to 54 bit numbers.
function set32(p: number, n: number) {
for (let i = 0; i < 4; ++i) {
Module.setValue(p + (3 - i), n & 0xFF, "i8");
n = Math.floor(n / 256);
}
} }
@ -843,6 +931,20 @@ export class UInt64 extends PackedArenaObject {
} }
export class UInt32 extends PackedArenaObject {
static fromNumber(n: number): UInt64 {
let x = new UInt32();
x.alloc();
set32(x.getNative(), n);
return x;
}
size() {
return 8;
}
}
// It's redundant, but more type safe. // It's redundant, but more type safe.
export interface DepositRequestPS_Args { export interface DepositRequestPS_Args {
h_contract: HashCode; h_contract: HashCode;
@ -940,7 +1042,7 @@ function makeEncode(encodeFn: any) {
} }
export class RsaPublicKey extends ArenaObject implements Encodeable { export class RsaPublicKey extends MallocArenaObject implements Encodeable {
static fromCrock: (s: string, a?: Arena) => RsaPublicKey; static fromCrock: (s: string, a?: Arena) => RsaPublicKey;
toCrock() { toCrock() {
@ -965,7 +1067,7 @@ export class EddsaSignature extends PackedArenaObject {
} }
export class RsaSignature extends ArenaObject implements Encodeable { export class RsaSignature extends MallocArenaObject implements Encodeable {
static fromCrock: (s: string, a?: Arena) => RsaSignature; static fromCrock: (s: string, a?: Arena) => RsaSignature;
encode: (arena?: Arena) => ByteArray; encode: (arena?: Arena) => ByteArray;
@ -980,21 +1082,21 @@ mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
export function rsaBlind(hashCode: HashCode, export function rsaBlind(hashCode: HashCode,
blindingKey: RsaBlindingKeySecret, blindingKey: RsaBlindingKeySecret,
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, let s = emscAlloc.rsa_blind(hashCode.nativePtr,
blindingKey.nativePtr, blindingKey.nativePtr,
pkey.nativePtr, pkey.nativePtr,
ptr); ptr);
return new ByteArray(s, Module.getValue(ptr, '*'), arena); return new ByteArray(s, Module.getValue(ptr, '*'), arena);
} }
export function eddsaSign(purpose: EccSignaturePurpose, export 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);
@ -1006,14 +1108,14 @@ export function eddsaSign(purpose: EccSignaturePurpose,
export function eddsaVerify(purposeNum: number, export function eddsaVerify(purposeNum: number,
verify: EccSignaturePurpose, verify: EccSignaturePurpose,
sig: EddsaSignature, sig: EddsaSignature,
pub: EddsaPublicKey, pub: EddsaPublicKey,
a?: Arena): boolean { a?: Arena): boolean {
let r = emsc.eddsa_verify(purposeNum, let r = emsc.eddsa_verify(purposeNum,
verify.nativePtr, verify.nativePtr,
sig.nativePtr, sig.nativePtr,
pub.nativePtr); pub.nativePtr);
if (r === GNUNET_OK) { if (r === GNUNET_OK) {
return true; return true;
} }
@ -1022,12 +1124,61 @@ export function eddsaVerify(purposeNum: number,
export function rsaUnblind(sig: RsaSignature, export function rsaUnblind(sig: RsaSignature,
bk: RsaBlindingKeySecret, bk: RsaBlindingKeySecret,
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, x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr,
bk.nativePtr, bk.nativePtr,
pk.nativePtr); pk.nativePtr);
return x; return x;
} }
type TransferSecretP = HashCode;
export function kdf(outLength: number,
salt: PackedArenaObject,
skm: PackedArenaObject,
...contextChunks: PackedArenaObject[]): ByteArray {
const args: number[] = [];
let out = new ByteArray(outLength);
args.push(out.nativePtr, outLength);
args.push(salt.nativePtr, salt.size());
args.push(skm.nativePtr, skm.size());
for (let chunk of contextChunks) {
args.push(chunk.nativePtr, chunk.size());
}
// end terminator (it's varargs)
args.push(0);
args.push(0);
let argTypes = args.map(() => "number");
const res = Module.ccall("GNUNET_CRYPTO_kdf", "number", argTypes, args);
if (res != GNUNET_OK) {
throw Error("fatal: kdf failed");
}
return out;
}
export interface FreshCoin {
priv: EddsaPrivateKey;
blindingKey: RsaBlindingKeySecret;
}
export function setupFreshCoin(secretSeed: TransferSecretP, coinIndex: number): FreshCoin {
let priv = new EddsaPrivateKey();
priv.isWeak = true;
let blindingKey = new RsaBlindingKeySecret();
blindingKey.isWeak = true;
let buf = kdf(priv.size() + blindingKey.size(), UInt32.fromNumber(coinIndex), ByteArray.fromString("taler-coin-derivation"));
priv.nativePtr = buf.nativePtr;
blindingKey.nativePtr = buf.nativePtr + priv.size();
return { priv, blindingKey };
}

View File

@ -14,7 +14,6 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* Smaller helper functions that do not depend * Smaller helper functions that do not depend
* on the emscripten machinery. * on the emscripten machinery.
@ -22,7 +21,10 @@
* @author Florian Dold * @author Florian Dold
*/ */
/// <reference path="../decl/urijs/URIjs.d.ts" />
import {AmountJson} from "./types"; import {AmountJson} from "./types";
import URI = uri.URI;
export function substituteFulfillmentUrl(url: string, vars: any) { export function substituteFulfillmentUrl(url: string, vars: any) {
url = url.replace("${H_contract}", vars.H_contract); url = url.replace("${H_contract}", vars.H_contract);
@ -43,7 +45,7 @@ export function amountToPretty(amount: AmountJson): string {
* See http://api.taler.net/wallet.html#general * See http://api.taler.net/wallet.html#general
*/ */
export function canonicalizeBaseUrl(url: string) { export function canonicalizeBaseUrl(url: string) {
let x = new URI(url); let x: URI = new URI(url);
if (!x.protocol()) { if (!x.protocol()) {
x.protocol("https"); x.protocol("https");
} }

View File

@ -25,7 +25,7 @@
* @author Florian Dold * @author Florian Dold
*/ */
import {Checkable} from "./checkable"; import { Checkable } from "./checkable";
@Checkable.Class @Checkable.Class
export class AmountJson { export class AmountJson {
@ -120,7 +120,7 @@ export interface IExchangeInfo {
} }
export interface WireInfo { export interface WireInfo {
[type: string]: any; [type: string]: any;
} }
export interface ReserveCreationInfo { export interface ReserveCreationInfo {
@ -148,6 +148,53 @@ export interface PreCoin {
} }
/**
* Ongoing refresh
*/
export interface RefreshSession {
/**
* Public key that's being melted in this session.
*/
meltCoinPub: string;
/**
* How much of the coin's value is melted away
* with this refresh session?
*/
valueWithFee: AmountJson
/**
* Signature to confirm the melting.
*/
confirmSig: string;
/**
* Denominations of the newly requested coins
*/
newDenoms: string[];
/**
* Blinded public keys for the requested coins.
*/
newCoinBlanks: string[][];
/**
* Blinding factors for the new coins.
*/
newCoinBlindingFactors: string[][];
/**
* Private keys for the requested coins.
*/
newCoinPrivs: string[][];
/**
* The transfer keys, kappa of them.
*/
transferPubs: string[];
}
export interface Reserve { export interface Reserve {
exchange_base_url: string exchange_base_url: string
reserve_priv: string; reserve_priv: string;
@ -165,7 +212,6 @@ export interface CoinPaySig {
f: AmountJson; f: AmountJson;
} }
/** /**
* Coin as stored in the "coins" data store * Coin as stored in the "coins" data store
* of the wallet database. * of the wallet database.
@ -291,7 +337,7 @@ export namespace Amounts {
return { return {
currency, currency,
value: Number.MAX_SAFE_INTEGER, value: Number.MAX_SAFE_INTEGER,
fraction: 2**32, fraction: 2 ** 32,
} }
} }
@ -307,7 +353,7 @@ export namespace Amounts {
let currency = first.currency; let currency = first.currency;
let value = first.value + Math.floor(first.fraction / 1e6); let value = first.value + Math.floor(first.fraction / 1e6);
if (value > Number.MAX_SAFE_INTEGER) { if (value > Number.MAX_SAFE_INTEGER) {
return {amount: getMaxAmount(currency), saturated: true}; return { amount: getMaxAmount(currency), saturated: true };
} }
let fraction = first.fraction % 1e6; let fraction = first.fraction % 1e6;
for (let x of rest) { for (let x of rest) {
@ -318,10 +364,10 @@ export namespace Amounts {
value = value + x.value + Math.floor((fraction + x.fraction) / 1e6); value = value + x.value + Math.floor((fraction + x.fraction) / 1e6);
fraction = (fraction + x.fraction) % 1e6; fraction = (fraction + x.fraction) % 1e6;
if (value > Number.MAX_SAFE_INTEGER) { if (value > Number.MAX_SAFE_INTEGER) {
return {amount: getMaxAmount(currency), saturated: true}; return { amount: getMaxAmount(currency), saturated: true };
} }
} }
return {amount: {currency, value, fraction}, saturated: false}; return { amount: { currency, value, fraction }, saturated: false };
} }
@ -334,7 +380,7 @@ export namespace Amounts {
let fraction = a.fraction; let fraction = a.fraction;
if (fraction < b.fraction) { if (fraction < b.fraction) {
if (value < 1) { if (value < 1) {
return {amount: {currency, value: 0, fraction: 0}, saturated: true}; return { amount: { currency, value: 0, fraction: 0 }, saturated: true };
} }
value--; value--;
fraction += 1e6; fraction += 1e6;
@ -342,10 +388,10 @@ export namespace Amounts {
console.assert(fraction >= b.fraction); console.assert(fraction >= b.fraction);
fraction -= b.fraction; fraction -= b.fraction;
if (value < b.value) { if (value < b.value) {
return {amount: {currency, value: 0, fraction: 0}, saturated: true}; return { amount: { currency, value: 0, fraction: 0 }, saturated: true };
} }
value -= b.value; value -= b.value;
return {amount: {currency, value, fraction}, saturated: false}; return { amount: { currency, value, fraction }, saturated: false };
} }
export function cmp(a: AmountJson, b: AmountJson): number { export function cmp(a: AmountJson, b: AmountJson): number {

View File

@ -84,6 +84,30 @@ interface CoinViewProps {
coin: Coin; coin: Coin;
} }
interface RefreshDialogProps {
coin: Coin;
}
class RefreshDialog extends ImplicitStateComponent<RefreshDialogProps> {
refreshRequested = this.makeState<boolean>(false);
render(): JSX.Element {
if (!this.refreshRequested()) {
return (
<div style="display:inline;">
<button onClick={() => this.refreshRequested(true)}>refresh</button>
</div>
);
}
return (
<div>
Refresh amount: <input type="text" size={10} />
<button>ok</button>
<button onClick={() => this.refreshRequested(false)}>cancel</button>
</div>
);
}
}
class CoinView extends preact.Component<CoinViewProps, void> { class CoinView extends preact.Component<CoinViewProps, void> {
render() { render() {
let c = this.props.coin; let c = this.props.coin;
@ -94,6 +118,7 @@ class CoinView extends preact.Component<CoinViewProps, void> {
<li>Current amount: {prettyAmount(c.currentAmount)}</li> <li>Current amount: {prettyAmount(c.currentAmount)}</li>
<li>Denomination: {abbrev(c.denomPub, 20)}</li> <li>Denomination: {abbrev(c.denomPub, 20)}</li>
<li>Suspended: {(c.suspended || false).toString()}</li> <li>Suspended: {(c.suspended || false).toString()}</li>
<li><RefreshDialog coin={c} /></li>
</ul> </ul>
</div> </div>
); );