worker refactoring / sync worker
This commit is contained in:
parent
78f885db3d
commit
ea2fb677d0
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "taler-wallet",
|
"name": "taler-wallet",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "dist/node/index.js",
|
"main": "dist/node/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -48,7 +48,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.19.0",
|
||||||
"commander": "^2.20.0",
|
"commander": "^2.20.0",
|
||||||
"idb-bridge": "^0.0.1",
|
"idb-bridge": "0.0.2",
|
||||||
"source-map-support": "^0.5.12",
|
"source-map-support": "^0.5.12",
|
||||||
"urijs": "^1.18.10"
|
"urijs": "^1.18.10"
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,6 @@ import { ContractTerms, PaybackRequest } from "../talerTypes";
|
|||||||
import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
|
import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
|
||||||
|
|
||||||
import * as timer from "../timer";
|
import * as timer from "../timer";
|
||||||
import { string } from "prop-types";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* State of a crypto worker.
|
* State of a crypto worker.
|
||||||
@ -103,33 +102,31 @@ export interface CryptoWorkerFactory {
|
|||||||
* Query the number of workers that should be
|
* Query the number of workers that should be
|
||||||
* run at the same time.
|
* run at the same time.
|
||||||
*/
|
*/
|
||||||
getConcurrency(): number
|
getConcurrency(): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class NodeCryptoWorkerFactory implements CryptoWorkerFactory {
|
export class NodeCryptoWorkerFactory implements CryptoWorkerFactory {
|
||||||
startWorker(): CryptoWorker {
|
startWorker(): CryptoWorker {
|
||||||
if (typeof require === "undefined") {
|
if (typeof require === "undefined") {
|
||||||
throw Error("cannot make worker, require(...) not defined");
|
throw Error("cannot make worker, require(...) not defined");
|
||||||
}
|
}
|
||||||
const workerCtor = require("./nodeWorker").Worker;
|
const workerCtor = require("./nodeProcessWorker").Worker;
|
||||||
const workerPath = __dirname + "/cryptoWorker.js";
|
const workerPath = __dirname + "/cryptoWorker.js";
|
||||||
return new workerCtor(workerPath);
|
return new workerCtor(workerPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConcurrency(): number {
|
getConcurrency(): number {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class BrowserCryptoWorkerFactory implements CryptoWorkerFactory {
|
export class BrowserCryptoWorkerFactory implements CryptoWorkerFactory {
|
||||||
startWorker(): CryptoWorker {
|
startWorker(): CryptoWorker {
|
||||||
const workerCtor = Worker;
|
const workerCtor = Worker;
|
||||||
const workerPath = "/dist/cryptoWorker-bundle.js";
|
const workerPath = "/dist/cryptoWorker-bundle.js";
|
||||||
return new workerCtor(workerPath) as CryptoWorker;
|
return new workerCtor(workerPath) as CryptoWorker;
|
||||||
}
|
}
|
||||||
|
|
||||||
getConcurrency(): number {
|
getConcurrency(): number {
|
||||||
let concurrency = 2;
|
let concurrency = 2;
|
||||||
try {
|
try {
|
||||||
@ -144,6 +141,23 @@ export class BrowserCryptoWorkerFactory implements CryptoWorkerFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The synchronous crypto worker produced by this factory doesn't run in the
|
||||||
|
* background, but actually blocks the caller until the operation is done.
|
||||||
|
*/
|
||||||
|
export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
|
||||||
|
startWorker(): CryptoWorker {
|
||||||
|
if (typeof require === "undefined") {
|
||||||
|
throw Error("cannot make worker, require(...) not defined");
|
||||||
|
}
|
||||||
|
const workerCtor = require("./synchronousWorker").SynchronousCryptoWorker;
|
||||||
|
return new workerCtor();
|
||||||
|
}
|
||||||
|
|
||||||
|
getConcurrency(): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crypto API that interfaces manages a background crypto thread
|
* Crypto API that interfaces manages a background crypto thread
|
||||||
@ -166,8 +180,7 @@ export class CryptoApi {
|
|||||||
*/
|
*/
|
||||||
private stopped: boolean = false;
|
private stopped: boolean = false;
|
||||||
|
|
||||||
public enableTracing = false;
|
public enableTracing = true;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminate all worker threads.
|
* Terminate all worker threads.
|
||||||
@ -295,10 +308,11 @@ export class CryptoApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.enableTracing && console.log(
|
this.enableTracing &&
|
||||||
`rpc ${currentWorkItem.operation} took ${timer.performanceNow() -
|
console.log(
|
||||||
currentWorkItem.startTime}ms`,
|
`rpc ${currentWorkItem.operation} took ${timer.performanceNow() -
|
||||||
);
|
currentWorkItem.startTime}ms`,
|
||||||
|
);
|
||||||
currentWorkItem.resolve(msg.data.result);
|
currentWorkItem.resolve(msg.data.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
754
src/crypto/cryptoImplementation.ts
Normal file
754
src/crypto/cryptoImplementation.ts
Normal file
@ -0,0 +1,754 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2019 GNUnet e.V.
|
||||||
|
|
||||||
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 3, or (at your option) any later version.
|
||||||
|
|
||||||
|
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronous implementation of crypto-related functions for the wallet.
|
||||||
|
*
|
||||||
|
* The functionality is parameterized over an Emscripten environment.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
CoinRecord,
|
||||||
|
CoinStatus,
|
||||||
|
DenominationRecord,
|
||||||
|
PreCoinRecord,
|
||||||
|
RefreshPreCoinRecord,
|
||||||
|
RefreshSessionRecord,
|
||||||
|
ReserveRecord,
|
||||||
|
TipPlanchet,
|
||||||
|
WireFee,
|
||||||
|
} from "../dbTypes";
|
||||||
|
|
||||||
|
import { CoinPaySig, ContractTerms, PaybackRequest } from "../talerTypes";
|
||||||
|
import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
|
||||||
|
import { canonicalJson } from "../helpers";
|
||||||
|
import { EmscEnvironment } from "./emscInterface";
|
||||||
|
import * as native from "./emscInterface";
|
||||||
|
import { AmountJson } from "../amounts";
|
||||||
|
import * as Amounts from "../amounts";
|
||||||
|
import * as timer from "../timer";
|
||||||
|
|
||||||
|
export class CryptoImplementation {
|
||||||
|
static enableTracing: boolean = false;
|
||||||
|
|
||||||
|
constructor(private emsc: EmscEnvironment) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a pre-coin of the given denomination to be withdrawn from then given
|
||||||
|
* reserve.
|
||||||
|
*/
|
||||||
|
createPreCoin(
|
||||||
|
denom: DenominationRecord,
|
||||||
|
reserve: ReserveRecord,
|
||||||
|
): PreCoinRecord {
|
||||||
|
const reservePriv = new native.EddsaPrivateKey(this.emsc);
|
||||||
|
reservePriv.loadCrock(reserve.reserve_priv);
|
||||||
|
const reservePub = new native.EddsaPublicKey(this.emsc);
|
||||||
|
reservePub.loadCrock(reserve.reserve_pub);
|
||||||
|
const denomPub = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
|
||||||
|
const coinPriv = native.EddsaPrivateKey.create(this.emsc);
|
||||||
|
const coinPub = coinPriv.getPublicKey();
|
||||||
|
const blindingFactor = native.RsaBlindingKeySecret.create(this.emsc);
|
||||||
|
const pubHash: native.HashCode = coinPub.hash();
|
||||||
|
const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
|
||||||
|
|
||||||
|
if (!ev) {
|
||||||
|
throw Error("couldn't blind (malicious exchange key?)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!denom.feeWithdraw) {
|
||||||
|
throw Error("Field fee_withdraw missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
const amountWithFee = new native.Amount(this.emsc, denom.value);
|
||||||
|
amountWithFee.add(new native.Amount(this.emsc, denom.feeWithdraw));
|
||||||
|
const withdrawFee = new native.Amount(this.emsc, denom.feeWithdraw);
|
||||||
|
|
||||||
|
const denomPubHash = denomPub.encode().hash();
|
||||||
|
|
||||||
|
// Signature
|
||||||
|
const withdrawRequest = new native.WithdrawRequestPS(this.emsc, {
|
||||||
|
amount_with_fee: amountWithFee.toNbo(),
|
||||||
|
h_coin_envelope: ev.hash(),
|
||||||
|
h_denomination_pub: denomPubHash,
|
||||||
|
reserve_pub: reservePub,
|
||||||
|
withdraw_fee: withdrawFee.toNbo(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const sig = native.eddsaSign(withdrawRequest.toPurpose(), reservePriv);
|
||||||
|
|
||||||
|
const preCoin: PreCoinRecord = {
|
||||||
|
blindingKey: blindingFactor.toCrock(),
|
||||||
|
coinEv: ev.toCrock(),
|
||||||
|
coinPriv: coinPriv.toCrock(),
|
||||||
|
coinPub: coinPub.toCrock(),
|
||||||
|
coinValue: denom.value,
|
||||||
|
denomPub: denomPub.toCrock(),
|
||||||
|
denomPubHash: denomPubHash.toCrock(),
|
||||||
|
exchangeBaseUrl: reserve.exchange_base_url,
|
||||||
|
isFromTip: false,
|
||||||
|
reservePub: reservePub.toCrock(),
|
||||||
|
withdrawSig: sig.toCrock(),
|
||||||
|
};
|
||||||
|
return preCoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a planchet used for tipping, including the private keys.
|
||||||
|
*/
|
||||||
|
createTipPlanchet(denom: DenominationRecord): TipPlanchet {
|
||||||
|
const denomPub = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
|
||||||
|
const coinPriv = native.EddsaPrivateKey.create(this.emsc);
|
||||||
|
const coinPub = coinPriv.getPublicKey();
|
||||||
|
const blindingFactor = native.RsaBlindingKeySecret.create(this.emsc);
|
||||||
|
const pubHash: native.HashCode = coinPub.hash();
|
||||||
|
const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
|
||||||
|
|
||||||
|
if (!ev) {
|
||||||
|
throw Error("couldn't blind (malicious exchange key?)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!denom.feeWithdraw) {
|
||||||
|
throw Error("Field fee_withdraw missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
const tipPlanchet: TipPlanchet = {
|
||||||
|
blindingKey: blindingFactor.toCrock(),
|
||||||
|
coinEv: ev.toCrock(),
|
||||||
|
coinPriv: coinPriv.toCrock(),
|
||||||
|
coinPub: coinPub.toCrock(),
|
||||||
|
coinValue: denom.value,
|
||||||
|
denomPub: denomPub.encode().toCrock(),
|
||||||
|
denomPubHash: denomPub
|
||||||
|
.encode()
|
||||||
|
.hash()
|
||||||
|
.toCrock(),
|
||||||
|
};
|
||||||
|
return tipPlanchet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and sign a message to request payback for a coin.
|
||||||
|
*/
|
||||||
|
createPaybackRequest(coin: CoinRecord): PaybackRequest {
|
||||||
|
const p = new native.PaybackRequestPS(this.emsc, {
|
||||||
|
coin_blind: native.RsaBlindingKeySecret.fromCrock(
|
||||||
|
this.emsc,
|
||||||
|
coin.blindingKey,
|
||||||
|
),
|
||||||
|
coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, coin.coinPub),
|
||||||
|
h_denom_pub: native.RsaPublicKey.fromCrock(this.emsc, coin.denomPub)
|
||||||
|
.encode()
|
||||||
|
.hash(),
|
||||||
|
});
|
||||||
|
const coinPriv = native.EddsaPrivateKey.fromCrock(this.emsc, coin.coinPriv);
|
||||||
|
const coinSig = native.eddsaSign(p.toPurpose(), coinPriv);
|
||||||
|
const paybackRequest: PaybackRequest = {
|
||||||
|
coin_blind_key_secret: coin.blindingKey,
|
||||||
|
coin_pub: coin.coinPub,
|
||||||
|
coin_sig: coinSig.toCrock(),
|
||||||
|
denom_pub: coin.denomPub,
|
||||||
|
denom_sig: coin.denomSig,
|
||||||
|
};
|
||||||
|
return paybackRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a payment signature is valid.
|
||||||
|
*/
|
||||||
|
isValidPaymentSignature(
|
||||||
|
sig: string,
|
||||||
|
contractHash: string,
|
||||||
|
merchantPub: string,
|
||||||
|
): boolean {
|
||||||
|
const p = new native.PaymentSignaturePS(this.emsc, {
|
||||||
|
contract_hash: native.HashCode.fromCrock(this.emsc, contractHash),
|
||||||
|
});
|
||||||
|
const nativeSig = new native.EddsaSignature(this.emsc);
|
||||||
|
nativeSig.loadCrock(sig);
|
||||||
|
const nativePub = native.EddsaPublicKey.fromCrock(this.emsc, merchantPub);
|
||||||
|
return native.eddsaVerify(
|
||||||
|
native.SignaturePurpose.MERCHANT_PAYMENT_OK,
|
||||||
|
p.toPurpose(),
|
||||||
|
nativeSig,
|
||||||
|
nativePub,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a wire fee is correctly signed.
|
||||||
|
*/
|
||||||
|
isValidWireFee(type: string, wf: WireFee, masterPub: string): boolean {
|
||||||
|
const p = new native.MasterWireFeePS(this.emsc, {
|
||||||
|
closing_fee: new native.Amount(this.emsc, wf.closingFee).toNbo(),
|
||||||
|
end_date: native.AbsoluteTimeNbo.fromStampSeconds(this.emsc, wf.endStamp),
|
||||||
|
h_wire_method: native.ByteArray.fromStringWithNull(
|
||||||
|
this.emsc,
|
||||||
|
type,
|
||||||
|
).hash(),
|
||||||
|
start_date: native.AbsoluteTimeNbo.fromStampSeconds(
|
||||||
|
this.emsc,
|
||||||
|
wf.startStamp,
|
||||||
|
),
|
||||||
|
wire_fee: new native.Amount(this.emsc, wf.wireFee).toNbo(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const nativeSig = new native.EddsaSignature(this.emsc);
|
||||||
|
nativeSig.loadCrock(wf.sig);
|
||||||
|
const nativePub = native.EddsaPublicKey.fromCrock(this.emsc, masterPub);
|
||||||
|
|
||||||
|
return native.eddsaVerify(
|
||||||
|
native.SignaturePurpose.MASTER_WIRE_FEES,
|
||||||
|
p.toPurpose(),
|
||||||
|
nativeSig,
|
||||||
|
nativePub,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the signature of a denomination is valid.
|
||||||
|
*/
|
||||||
|
isValidDenom(denom: DenominationRecord, masterPub: string): boolean {
|
||||||
|
const p = new native.DenominationKeyValidityPS(this.emsc, {
|
||||||
|
denom_hash: native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub)
|
||||||
|
.encode()
|
||||||
|
.hash(),
|
||||||
|
expire_legal: native.AbsoluteTimeNbo.fromTalerString(
|
||||||
|
this.emsc,
|
||||||
|
denom.stampExpireLegal,
|
||||||
|
),
|
||||||
|
expire_spend: native.AbsoluteTimeNbo.fromTalerString(
|
||||||
|
this.emsc,
|
||||||
|
denom.stampExpireDeposit,
|
||||||
|
),
|
||||||
|
expire_withdraw: native.AbsoluteTimeNbo.fromTalerString(
|
||||||
|
this.emsc,
|
||||||
|
denom.stampExpireWithdraw,
|
||||||
|
),
|
||||||
|
fee_deposit: new native.Amount(this.emsc, denom.feeDeposit).toNbo(),
|
||||||
|
fee_refresh: new native.Amount(this.emsc, denom.feeRefresh).toNbo(),
|
||||||
|
fee_refund: new native.Amount(this.emsc, denom.feeRefund).toNbo(),
|
||||||
|
fee_withdraw: new native.Amount(this.emsc, denom.feeWithdraw).toNbo(),
|
||||||
|
master: native.EddsaPublicKey.fromCrock(this.emsc, masterPub),
|
||||||
|
start: native.AbsoluteTimeNbo.fromTalerString(
|
||||||
|
this.emsc,
|
||||||
|
denom.stampStart,
|
||||||
|
),
|
||||||
|
value: new native.Amount(this.emsc, denom.value).toNbo(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const nativeSig = new native.EddsaSignature(this.emsc);
|
||||||
|
nativeSig.loadCrock(denom.masterSig);
|
||||||
|
|
||||||
|
const nativePub = native.EddsaPublicKey.fromCrock(this.emsc, masterPub);
|
||||||
|
|
||||||
|
return native.eddsaVerify(
|
||||||
|
native.SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY,
|
||||||
|
p.toPurpose(),
|
||||||
|
nativeSig,
|
||||||
|
nativePub,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new EdDSA key pair.
|
||||||
|
*/
|
||||||
|
createEddsaKeypair(): { priv: string; pub: string } {
|
||||||
|
const priv = native.EddsaPrivateKey.create(this.emsc);
|
||||||
|
const pub = priv.getPublicKey();
|
||||||
|
return { priv: priv.toCrock(), pub: pub.toCrock() };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblind a blindly signed value.
|
||||||
|
*/
|
||||||
|
rsaUnblind(sig: string, bk: string, pk: string): string {
|
||||||
|
const denomSig = native.rsaUnblind(
|
||||||
|
native.RsaSignature.fromCrock(this.emsc, sig),
|
||||||
|
native.RsaBlindingKeySecret.fromCrock(this.emsc, bk),
|
||||||
|
native.RsaPublicKey.fromCrock(this.emsc, pk),
|
||||||
|
);
|
||||||
|
return denomSig.encode().toCrock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate updated coins (to store in the database)
|
||||||
|
* and deposit permissions for each given coin.
|
||||||
|
*/
|
||||||
|
signDeposit(
|
||||||
|
contractTerms: ContractTerms,
|
||||||
|
cds: CoinWithDenom[],
|
||||||
|
totalAmount: AmountJson,
|
||||||
|
): PayCoinInfo {
|
||||||
|
const ret: PayCoinInfo = {
|
||||||
|
originalCoins: [],
|
||||||
|
sigs: [],
|
||||||
|
updatedCoins: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const contractTermsHash = this.hashString(canonicalJson(contractTerms));
|
||||||
|
|
||||||
|
const feeList: AmountJson[] = cds.map(x => x.denom.feeDeposit);
|
||||||
|
let fees = Amounts.add(Amounts.getZero(feeList[0].currency), ...feeList)
|
||||||
|
.amount;
|
||||||
|
// okay if saturates
|
||||||
|
fees = Amounts.sub(fees, Amounts.parseOrThrow(contractTerms.max_fee))
|
||||||
|
.amount;
|
||||||
|
const total = Amounts.add(fees, totalAmount).amount;
|
||||||
|
|
||||||
|
const amountSpent = native.Amount.getZero(
|
||||||
|
this.emsc,
|
||||||
|
cds[0].coin.currentAmount.currency,
|
||||||
|
);
|
||||||
|
const amountRemaining = new native.Amount(this.emsc, total);
|
||||||
|
for (const cd of cds) {
|
||||||
|
let coinSpend: native.Amount;
|
||||||
|
const originalCoin = { ...cd.coin };
|
||||||
|
|
||||||
|
if (amountRemaining.value === 0 && amountRemaining.fraction === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
amountRemaining.cmp(
|
||||||
|
new native.Amount(this.emsc, cd.coin.currentAmount),
|
||||||
|
) < 0
|
||||||
|
) {
|
||||||
|
coinSpend = new native.Amount(this.emsc, amountRemaining.toJson());
|
||||||
|
} else {
|
||||||
|
coinSpend = new native.Amount(this.emsc, cd.coin.currentAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
amountSpent.add(coinSpend);
|
||||||
|
amountRemaining.sub(coinSpend);
|
||||||
|
|
||||||
|
const feeDeposit: native.Amount = new native.Amount(
|
||||||
|
this.emsc,
|
||||||
|
cd.denom.feeDeposit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Give the merchant at least the deposit fee, otherwise it'll reject
|
||||||
|
// the coin.
|
||||||
|
if (coinSpend.cmp(feeDeposit) < 0) {
|
||||||
|
coinSpend = feeDeposit;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAmount = new native.Amount(this.emsc, cd.coin.currentAmount);
|
||||||
|
newAmount.sub(coinSpend);
|
||||||
|
cd.coin.currentAmount = newAmount.toJson();
|
||||||
|
cd.coin.status = CoinStatus.PurchasePending;
|
||||||
|
|
||||||
|
const d = new native.DepositRequestPS(this.emsc, {
|
||||||
|
amount_with_fee: coinSpend.toNbo(),
|
||||||
|
coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, cd.coin.coinPub),
|
||||||
|
deposit_fee: new native.Amount(this.emsc, cd.denom.feeDeposit).toNbo(),
|
||||||
|
h_contract: native.HashCode.fromCrock(this.emsc, contractTermsHash),
|
||||||
|
h_wire: native.HashCode.fromCrock(this.emsc, contractTerms.H_wire),
|
||||||
|
merchant: native.EddsaPublicKey.fromCrock(
|
||||||
|
this.emsc,
|
||||||
|
contractTerms.merchant_pub,
|
||||||
|
),
|
||||||
|
refund_deadline: native.AbsoluteTimeNbo.fromTalerString(
|
||||||
|
this.emsc,
|
||||||
|
contractTerms.refund_deadline,
|
||||||
|
),
|
||||||
|
timestamp: native.AbsoluteTimeNbo.fromTalerString(
|
||||||
|
this.emsc,
|
||||||
|
contractTerms.timestamp,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const coinSig = native
|
||||||
|
.eddsaSign(
|
||||||
|
d.toPurpose(),
|
||||||
|
native.EddsaPrivateKey.fromCrock(this.emsc, cd.coin.coinPriv),
|
||||||
|
)
|
||||||
|
.toCrock();
|
||||||
|
|
||||||
|
const s: CoinPaySig = {
|
||||||
|
coin_pub: cd.coin.coinPub,
|
||||||
|
coin_sig: coinSig,
|
||||||
|
contribution: Amounts.toString(coinSpend.toJson()),
|
||||||
|
denom_pub: cd.coin.denomPub,
|
||||||
|
exchange_url: cd.denom.exchangeBaseUrl,
|
||||||
|
ub_sig: cd.coin.denomSig,
|
||||||
|
};
|
||||||
|
ret.sigs.push(s);
|
||||||
|
ret.updatedCoins.push(cd.coin);
|
||||||
|
ret.originalCoins.push(originalCoin);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new refresh session.
|
||||||
|
*/
|
||||||
|
createRefreshSession(
|
||||||
|
exchangeBaseUrl: string,
|
||||||
|
kappa: number,
|
||||||
|
meltCoin: CoinRecord,
|
||||||
|
newCoinDenoms: DenominationRecord[],
|
||||||
|
meltFee: AmountJson,
|
||||||
|
): RefreshSessionRecord {
|
||||||
|
let valueWithFee = Amounts.getZero(newCoinDenoms[0].value.currency);
|
||||||
|
|
||||||
|
for (const ncd of newCoinDenoms) {
|
||||||
|
valueWithFee = Amounts.add(valueWithFee, ncd.value, ncd.feeWithdraw)
|
||||||
|
.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// melt fee
|
||||||
|
valueWithFee = Amounts.add(valueWithFee, meltFee).amount;
|
||||||
|
|
||||||
|
const sessionHc = new native.HashContext(this.emsc);
|
||||||
|
|
||||||
|
const transferPubs: string[] = [];
|
||||||
|
const transferPrivs: string[] = [];
|
||||||
|
|
||||||
|
const preCoinsForGammas: RefreshPreCoinRecord[][] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < kappa; i++) {
|
||||||
|
const t = native.EcdhePrivateKey.create(this.emsc);
|
||||||
|
const pub = t.getPublicKey();
|
||||||
|
sessionHc.read(pub);
|
||||||
|
transferPrivs.push(t.toCrock());
|
||||||
|
transferPubs.push(pub.toCrock());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const denom of newCoinDenoms) {
|
||||||
|
const r = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
|
||||||
|
sessionHc.read(r.encode());
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionHc.read(
|
||||||
|
native.EddsaPublicKey.fromCrock(this.emsc, meltCoin.coinPub),
|
||||||
|
);
|
||||||
|
sessionHc.read(new native.Amount(this.emsc, valueWithFee).toNbo());
|
||||||
|
|
||||||
|
for (let i = 0; i < kappa; i++) {
|
||||||
|
const preCoins: RefreshPreCoinRecord[] = [];
|
||||||
|
for (let j = 0; j < newCoinDenoms.length; j++) {
|
||||||
|
const transferPriv = native.EcdhePrivateKey.fromCrock(
|
||||||
|
this.emsc,
|
||||||
|
transferPrivs[i],
|
||||||
|
);
|
||||||
|
const oldCoinPub = native.EddsaPublicKey.fromCrock(
|
||||||
|
this.emsc,
|
||||||
|
meltCoin.coinPub,
|
||||||
|
);
|
||||||
|
const transferSecret = native.ecdhEddsa(transferPriv, oldCoinPub);
|
||||||
|
|
||||||
|
const fresh = native.setupFreshCoin(transferSecret, j);
|
||||||
|
|
||||||
|
const coinPriv = fresh.priv;
|
||||||
|
const coinPub = coinPriv.getPublicKey();
|
||||||
|
const blindingFactor = fresh.blindingKey;
|
||||||
|
const pubHash: native.HashCode = coinPub.hash();
|
||||||
|
const denomPub = native.RsaPublicKey.fromCrock(
|
||||||
|
this.emsc,
|
||||||
|
newCoinDenoms[j].denomPub,
|
||||||
|
);
|
||||||
|
const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
|
||||||
|
if (!ev) {
|
||||||
|
throw Error("couldn't blind (malicious exchange key?)");
|
||||||
|
}
|
||||||
|
const preCoin: RefreshPreCoinRecord = {
|
||||||
|
blindingKey: blindingFactor.toCrock(),
|
||||||
|
coinEv: ev.toCrock(),
|
||||||
|
privateKey: coinPriv.toCrock(),
|
||||||
|
publicKey: coinPub.toCrock(),
|
||||||
|
};
|
||||||
|
preCoins.push(preCoin);
|
||||||
|
sessionHc.read(ev);
|
||||||
|
}
|
||||||
|
preCoinsForGammas.push(preCoins);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionHash = new native.HashCode(this.emsc);
|
||||||
|
sessionHash.alloc();
|
||||||
|
sessionHc.finish(sessionHash);
|
||||||
|
|
||||||
|
const confirmData = new native.RefreshMeltCoinAffirmationPS(this.emsc, {
|
||||||
|
amount_with_fee: new native.Amount(this.emsc, valueWithFee).toNbo(),
|
||||||
|
coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, meltCoin.coinPub),
|
||||||
|
melt_fee: new native.Amount(this.emsc, meltFee).toNbo(),
|
||||||
|
session_hash: sessionHash,
|
||||||
|
});
|
||||||
|
|
||||||
|
const confirmSig: string = native
|
||||||
|
.eddsaSign(
|
||||||
|
confirmData.toPurpose(),
|
||||||
|
native.EddsaPrivateKey.fromCrock(this.emsc, meltCoin.coinPriv),
|
||||||
|
)
|
||||||
|
.toCrock();
|
||||||
|
|
||||||
|
let valueOutput = Amounts.getZero(newCoinDenoms[0].value.currency);
|
||||||
|
for (const denom of newCoinDenoms) {
|
||||||
|
valueOutput = Amounts.add(valueOutput, denom.value).amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshSession: RefreshSessionRecord = {
|
||||||
|
confirmSig,
|
||||||
|
exchangeBaseUrl,
|
||||||
|
finished: false,
|
||||||
|
hash: sessionHash.toCrock(),
|
||||||
|
meltCoinPub: meltCoin.coinPub,
|
||||||
|
newDenomHashes: newCoinDenoms.map(d => d.denomPubHash),
|
||||||
|
newDenoms: newCoinDenoms.map(d => d.denomPub),
|
||||||
|
norevealIndex: undefined,
|
||||||
|
preCoinsForGammas,
|
||||||
|
transferPrivs,
|
||||||
|
transferPubs,
|
||||||
|
valueOutput,
|
||||||
|
valueWithFee,
|
||||||
|
};
|
||||||
|
|
||||||
|
return refreshSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash a string including the zero terminator.
|
||||||
|
*/
|
||||||
|
hashString(str: string): string {
|
||||||
|
const b = native.ByteArray.fromStringWithNull(this.emsc, str);
|
||||||
|
return b.hash().toCrock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash a denomination public key.
|
||||||
|
*/
|
||||||
|
hashDenomPub(denomPub: string): string {
|
||||||
|
return native.RsaPublicKey.fromCrock(this.emsc, denomPub)
|
||||||
|
.encode()
|
||||||
|
.hash()
|
||||||
|
.toCrock();
|
||||||
|
}
|
||||||
|
|
||||||
|
signCoinLink(
|
||||||
|
oldCoinPriv: string,
|
||||||
|
newDenomHash: string,
|
||||||
|
oldCoinPub: string,
|
||||||
|
transferPub: string,
|
||||||
|
coinEv: string,
|
||||||
|
): string {
|
||||||
|
const coinEvHash = native.ByteArray.fromCrock(this.emsc, coinEv).hash();
|
||||||
|
|
||||||
|
const coinLink = new native.CoinLinkSignaturePS(this.emsc, {
|
||||||
|
coin_envelope_hash: coinEvHash,
|
||||||
|
h_denom_pub: native.HashCode.fromCrock(this.emsc, newDenomHash),
|
||||||
|
old_coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, oldCoinPub),
|
||||||
|
transfer_pub: native.EcdhePublicKey.fromCrock(this.emsc, transferPub),
|
||||||
|
});
|
||||||
|
|
||||||
|
const coinPriv = native.EddsaPrivateKey.fromCrock(this.emsc, oldCoinPriv);
|
||||||
|
|
||||||
|
const sig = native.eddsaSign(coinLink.toPurpose(), coinPriv);
|
||||||
|
|
||||||
|
return sig.toCrock();
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmark(repetitions: number): BenchmarkResult {
|
||||||
|
let time_hash = 0;
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
this.hashString("hello world");
|
||||||
|
time_hash += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
let time_hash_big = 0;
|
||||||
|
const ba = new native.ByteArray(this.emsc, 4096);
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
ba.randomize(native.RandomQuality.WEAK);
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
ba.hash();
|
||||||
|
time_hash_big += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
let time_eddsa_create = 0;
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
const priv: native.EddsaPrivateKey = native.EddsaPrivateKey.create(
|
||||||
|
this.emsc,
|
||||||
|
);
|
||||||
|
time_eddsa_create += timer.performanceNow() - start;
|
||||||
|
priv.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
let time_eddsa_sign = 0;
|
||||||
|
const eddsaPriv: native.EddsaPrivateKey = native.EddsaPrivateKey.create(
|
||||||
|
this.emsc,
|
||||||
|
);
|
||||||
|
const eddsaPub: native.EddsaPublicKey = eddsaPriv.getPublicKey();
|
||||||
|
const h: native.HashCode = new native.HashCode(this.emsc);
|
||||||
|
h.alloc();
|
||||||
|
h.random(native.RandomQuality.WEAK);
|
||||||
|
|
||||||
|
const ps = new native.PaymentSignaturePS(this.emsc, {
|
||||||
|
contract_hash: h,
|
||||||
|
});
|
||||||
|
|
||||||
|
const p = ps.toPurpose();
|
||||||
|
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
native.eddsaSign(p, eddsaPriv);
|
||||||
|
time_eddsa_sign += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eddsaSig = native.eddsaSign(p, eddsaPriv);
|
||||||
|
|
||||||
|
let time_ecdsa_create = 0;
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
const priv: native.EcdsaPrivateKey = native.EcdsaPrivateKey.create(
|
||||||
|
this.emsc,
|
||||||
|
);
|
||||||
|
time_ecdsa_create += timer.performanceNow() - start;
|
||||||
|
priv.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
let time_eddsa_verify = 0;
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
native.eddsaVerify(
|
||||||
|
native.SignaturePurpose.MERCHANT_PAYMENT_OK,
|
||||||
|
p,
|
||||||
|
eddsaSig,
|
||||||
|
eddsaPub,
|
||||||
|
);
|
||||||
|
time_eddsa_verify += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rsa 2048 */
|
||||||
|
|
||||||
|
let time_rsa_2048_blind = 0;
|
||||||
|
const rsaPriv2048: native.RsaPrivateKey = native.RsaPrivateKey.create(
|
||||||
|
this.emsc,
|
||||||
|
2048,
|
||||||
|
);
|
||||||
|
const rsaPub2048 = rsaPriv2048.getPublicKey();
|
||||||
|
const blindingSecret2048 = native.RsaBlindingKeySecret.create(this.emsc);
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
native.rsaBlind(h, blindingSecret2048, rsaPub2048);
|
||||||
|
time_rsa_2048_blind += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blindedMessage2048 = native.rsaBlind(
|
||||||
|
h,
|
||||||
|
blindingSecret2048,
|
||||||
|
rsaPub2048,
|
||||||
|
);
|
||||||
|
if (!blindedMessage2048) {
|
||||||
|
throw Error("should not happen");
|
||||||
|
}
|
||||||
|
const rsaBlindSig2048 = native.rsaSignBlinded(
|
||||||
|
rsaPriv2048,
|
||||||
|
blindedMessage2048,
|
||||||
|
);
|
||||||
|
|
||||||
|
let time_rsa_2048_unblind = 0;
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
native.rsaUnblind(rsaBlindSig2048, blindingSecret2048, rsaPub2048);
|
||||||
|
time_rsa_2048_unblind += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unblindedSig2048 = native.rsaUnblind(
|
||||||
|
rsaBlindSig2048,
|
||||||
|
blindingSecret2048,
|
||||||
|
rsaPub2048,
|
||||||
|
);
|
||||||
|
|
||||||
|
let time_rsa_2048_verify = 0;
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
native.rsaVerify(h, unblindedSig2048, rsaPub2048);
|
||||||
|
time_rsa_2048_verify += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rsa 4096 */
|
||||||
|
|
||||||
|
let time_rsa_4096_blind = 0;
|
||||||
|
const rsaPriv4096: native.RsaPrivateKey = native.RsaPrivateKey.create(
|
||||||
|
this.emsc,
|
||||||
|
4096,
|
||||||
|
);
|
||||||
|
const rsaPub4096 = rsaPriv4096.getPublicKey();
|
||||||
|
const blindingSecret4096 = native.RsaBlindingKeySecret.create(this.emsc);
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
native.rsaBlind(h, blindingSecret4096, rsaPub4096);
|
||||||
|
time_rsa_4096_blind += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blindedMessage4096 = native.rsaBlind(
|
||||||
|
h,
|
||||||
|
blindingSecret4096,
|
||||||
|
rsaPub4096,
|
||||||
|
);
|
||||||
|
if (!blindedMessage4096) {
|
||||||
|
throw Error("should not happen");
|
||||||
|
}
|
||||||
|
const rsaBlindSig4096 = native.rsaSignBlinded(
|
||||||
|
rsaPriv4096,
|
||||||
|
blindedMessage4096,
|
||||||
|
);
|
||||||
|
|
||||||
|
let time_rsa_4096_unblind = 0;
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
native.rsaUnblind(rsaBlindSig4096, blindingSecret4096, rsaPub4096);
|
||||||
|
time_rsa_4096_unblind += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unblindedSig4096 = native.rsaUnblind(
|
||||||
|
rsaBlindSig4096,
|
||||||
|
blindingSecret4096,
|
||||||
|
rsaPub4096,
|
||||||
|
);
|
||||||
|
|
||||||
|
let time_rsa_4096_verify = 0;
|
||||||
|
for (let i = 0; i < repetitions; i++) {
|
||||||
|
const start = timer.performanceNow();
|
||||||
|
native.rsaVerify(h, unblindedSig4096, rsaPub4096);
|
||||||
|
time_rsa_4096_verify += timer.performanceNow() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
repetitions,
|
||||||
|
time: {
|
||||||
|
hash_small: time_hash,
|
||||||
|
hash_big: time_hash_big,
|
||||||
|
eddsa_create: time_eddsa_create,
|
||||||
|
eddsa_sign: time_eddsa_sign,
|
||||||
|
eddsa_verify: time_eddsa_verify,
|
||||||
|
ecdsa_create: time_ecdsa_create,
|
||||||
|
rsa_2048_blind: time_rsa_2048_blind,
|
||||||
|
rsa_2048_unblind: time_rsa_2048_unblind,
|
||||||
|
rsa_2048_verify: time_rsa_2048_verify,
|
||||||
|
rsa_4096_blind: time_rsa_4096_blind,
|
||||||
|
rsa_4096_unblind: time_rsa_4096_unblind,
|
||||||
|
rsa_4096_verify: time_rsa_4096_verify,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -21,742 +21,57 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import * as Amounts from "../amounts";
|
|
||||||
import { AmountJson } from "../amounts";
|
|
||||||
|
|
||||||
import * as timer from "../timer";
|
|
||||||
|
|
||||||
import {
|
|
||||||
CoinRecord,
|
|
||||||
CoinStatus,
|
|
||||||
DenominationRecord,
|
|
||||||
PreCoinRecord,
|
|
||||||
RefreshPreCoinRecord,
|
|
||||||
RefreshSessionRecord,
|
|
||||||
ReserveRecord,
|
|
||||||
TipPlanchet,
|
|
||||||
WireFee,
|
|
||||||
} from "../dbTypes";
|
|
||||||
|
|
||||||
import { CoinPaySig, ContractTerms, PaybackRequest } from "../talerTypes";
|
|
||||||
|
|
||||||
import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
|
|
||||||
|
|
||||||
import { canonicalJson } from "../helpers";
|
|
||||||
|
|
||||||
import * as emscLoader from "./emscLoader";
|
import * as emscLoader from "./emscLoader";
|
||||||
|
|
||||||
import {
|
import { CryptoImplementation } from "./cryptoImplementation";
|
||||||
Amount,
|
import { EmscEnvironment } from "./emscInterface";
|
||||||
EddsaPublicKey,
|
|
||||||
HashCode,
|
|
||||||
HashContext,
|
|
||||||
RefreshMeltCoinAffirmationPS,
|
|
||||||
} from "./emscInterface";
|
|
||||||
import * as native from "./emscInterface";
|
|
||||||
|
|
||||||
namespace RpcFunctions {
|
|
||||||
|
|
||||||
export let enableTracing: boolean = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a pre-coin of the given denomination to be withdrawn from then given
|
|
||||||
* reserve.
|
|
||||||
*/
|
|
||||||
export function createPreCoin(
|
|
||||||
denom: DenominationRecord,
|
|
||||||
reserve: ReserveRecord,
|
|
||||||
): PreCoinRecord {
|
|
||||||
const reservePriv = new native.EddsaPrivateKey();
|
|
||||||
reservePriv.loadCrock(reserve.reserve_priv);
|
|
||||||
const reservePub = new native.EddsaPublicKey();
|
|
||||||
reservePub.loadCrock(reserve.reserve_pub);
|
|
||||||
const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
|
|
||||||
const coinPriv = native.EddsaPrivateKey.create();
|
|
||||||
const coinPub = coinPriv.getPublicKey();
|
|
||||||
const blindingFactor = native.RsaBlindingKeySecret.create();
|
|
||||||
const pubHash: native.HashCode = coinPub.hash();
|
|
||||||
const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
|
|
||||||
|
|
||||||
if (!ev) {
|
|
||||||
throw Error("couldn't blind (malicious exchange key?)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!denom.feeWithdraw) {
|
|
||||||
throw Error("Field fee_withdraw missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
const amountWithFee = new native.Amount(denom.value);
|
|
||||||
amountWithFee.add(new native.Amount(denom.feeWithdraw));
|
|
||||||
const withdrawFee = new native.Amount(denom.feeWithdraw);
|
|
||||||
|
|
||||||
const denomPubHash = denomPub.encode().hash();
|
|
||||||
|
|
||||||
// Signature
|
|
||||||
const withdrawRequest = new native.WithdrawRequestPS({
|
|
||||||
amount_with_fee: amountWithFee.toNbo(),
|
|
||||||
h_coin_envelope: ev.hash(),
|
|
||||||
h_denomination_pub: denomPubHash,
|
|
||||||
reserve_pub: reservePub,
|
|
||||||
withdraw_fee: withdrawFee.toNbo(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const sig = native.eddsaSign(withdrawRequest.toPurpose(), reservePriv);
|
|
||||||
|
|
||||||
const preCoin: PreCoinRecord = {
|
|
||||||
blindingKey: blindingFactor.toCrock(),
|
|
||||||
coinEv: ev.toCrock(),
|
|
||||||
coinPriv: coinPriv.toCrock(),
|
|
||||||
coinPub: coinPub.toCrock(),
|
|
||||||
coinValue: denom.value,
|
|
||||||
denomPub: denomPub.toCrock(),
|
|
||||||
denomPubHash: denomPubHash.toCrock(),
|
|
||||||
exchangeBaseUrl: reserve.exchange_base_url,
|
|
||||||
isFromTip: false,
|
|
||||||
reservePub: reservePub.toCrock(),
|
|
||||||
withdrawSig: sig.toCrock(),
|
|
||||||
};
|
|
||||||
return preCoin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a planchet used for tipping, including the private keys.
|
|
||||||
*/
|
|
||||||
export function createTipPlanchet(denom: DenominationRecord): TipPlanchet {
|
|
||||||
const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
|
|
||||||
const coinPriv = native.EddsaPrivateKey.create();
|
|
||||||
const coinPub = coinPriv.getPublicKey();
|
|
||||||
const blindingFactor = native.RsaBlindingKeySecret.create();
|
|
||||||
const pubHash: native.HashCode = coinPub.hash();
|
|
||||||
const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
|
|
||||||
|
|
||||||
if (!ev) {
|
|
||||||
throw Error("couldn't blind (malicious exchange key?)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!denom.feeWithdraw) {
|
|
||||||
throw Error("Field fee_withdraw missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
const tipPlanchet: TipPlanchet = {
|
|
||||||
blindingKey: blindingFactor.toCrock(),
|
|
||||||
coinEv: ev.toCrock(),
|
|
||||||
coinPriv: coinPriv.toCrock(),
|
|
||||||
coinPub: coinPub.toCrock(),
|
|
||||||
coinValue: denom.value,
|
|
||||||
denomPub: denomPub.encode().toCrock(),
|
|
||||||
denomPubHash: denomPub
|
|
||||||
.encode()
|
|
||||||
.hash()
|
|
||||||
.toCrock(),
|
|
||||||
};
|
|
||||||
return tipPlanchet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and sign a message to request payback for a coin.
|
|
||||||
*/
|
|
||||||
export function createPaybackRequest(coin: CoinRecord): PaybackRequest {
|
|
||||||
const p = new native.PaybackRequestPS({
|
|
||||||
coin_blind: native.RsaBlindingKeySecret.fromCrock(coin.blindingKey),
|
|
||||||
coin_pub: native.EddsaPublicKey.fromCrock(coin.coinPub),
|
|
||||||
h_denom_pub: native.RsaPublicKey.fromCrock(coin.denomPub)
|
|
||||||
.encode()
|
|
||||||
.hash(),
|
|
||||||
});
|
|
||||||
const coinPriv = native.EddsaPrivateKey.fromCrock(coin.coinPriv);
|
|
||||||
const coinSig = native.eddsaSign(p.toPurpose(), coinPriv);
|
|
||||||
const paybackRequest: PaybackRequest = {
|
|
||||||
coin_blind_key_secret: coin.blindingKey,
|
|
||||||
coin_pub: coin.coinPub,
|
|
||||||
coin_sig: coinSig.toCrock(),
|
|
||||||
denom_pub: coin.denomPub,
|
|
||||||
denom_sig: coin.denomSig,
|
|
||||||
};
|
|
||||||
return paybackRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a payment signature is valid.
|
|
||||||
*/
|
|
||||||
export function isValidPaymentSignature(
|
|
||||||
sig: string,
|
|
||||||
contractHash: string,
|
|
||||||
merchantPub: string,
|
|
||||||
): boolean {
|
|
||||||
const p = new native.PaymentSignaturePS({
|
|
||||||
contract_hash: native.HashCode.fromCrock(contractHash),
|
|
||||||
});
|
|
||||||
const nativeSig = new native.EddsaSignature();
|
|
||||||
nativeSig.loadCrock(sig);
|
|
||||||
const nativePub = native.EddsaPublicKey.fromCrock(merchantPub);
|
|
||||||
return native.eddsaVerify(
|
|
||||||
native.SignaturePurpose.MERCHANT_PAYMENT_OK,
|
|
||||||
p.toPurpose(),
|
|
||||||
nativeSig,
|
|
||||||
nativePub,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a wire fee is correctly signed.
|
|
||||||
*/
|
|
||||||
export function isValidWireFee(
|
|
||||||
type: string,
|
|
||||||
wf: WireFee,
|
|
||||||
masterPub: string,
|
|
||||||
): boolean {
|
|
||||||
const p = new native.MasterWireFeePS({
|
|
||||||
closing_fee: new native.Amount(wf.closingFee).toNbo(),
|
|
||||||
end_date: native.AbsoluteTimeNbo.fromStampSeconds(wf.endStamp),
|
|
||||||
h_wire_method: native.ByteArray.fromStringWithNull(type).hash(),
|
|
||||||
start_date: native.AbsoluteTimeNbo.fromStampSeconds(wf.startStamp),
|
|
||||||
wire_fee: new native.Amount(wf.wireFee).toNbo(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const nativeSig = new native.EddsaSignature();
|
|
||||||
nativeSig.loadCrock(wf.sig);
|
|
||||||
const nativePub = native.EddsaPublicKey.fromCrock(masterPub);
|
|
||||||
|
|
||||||
return native.eddsaVerify(
|
|
||||||
native.SignaturePurpose.MASTER_WIRE_FEES,
|
|
||||||
p.toPurpose(),
|
|
||||||
nativeSig,
|
|
||||||
nativePub,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the signature of a denomination is valid.
|
|
||||||
*/
|
|
||||||
export function isValidDenom(
|
|
||||||
denom: DenominationRecord,
|
|
||||||
masterPub: string,
|
|
||||||
): boolean {
|
|
||||||
const p = new native.DenominationKeyValidityPS({
|
|
||||||
denom_hash: native.RsaPublicKey.fromCrock(denom.denomPub)
|
|
||||||
.encode()
|
|
||||||
.hash(),
|
|
||||||
expire_legal: native.AbsoluteTimeNbo.fromTalerString(
|
|
||||||
denom.stampExpireLegal,
|
|
||||||
),
|
|
||||||
expire_spend: native.AbsoluteTimeNbo.fromTalerString(
|
|
||||||
denom.stampExpireDeposit,
|
|
||||||
),
|
|
||||||
expire_withdraw: native.AbsoluteTimeNbo.fromTalerString(
|
|
||||||
denom.stampExpireWithdraw,
|
|
||||||
),
|
|
||||||
fee_deposit: new native.Amount(denom.feeDeposit).toNbo(),
|
|
||||||
fee_refresh: new native.Amount(denom.feeRefresh).toNbo(),
|
|
||||||
fee_refund: new native.Amount(denom.feeRefund).toNbo(),
|
|
||||||
fee_withdraw: new native.Amount(denom.feeWithdraw).toNbo(),
|
|
||||||
master: native.EddsaPublicKey.fromCrock(masterPub),
|
|
||||||
start: native.AbsoluteTimeNbo.fromTalerString(denom.stampStart),
|
|
||||||
value: new native.Amount(denom.value).toNbo(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const nativeSig = new native.EddsaSignature();
|
|
||||||
nativeSig.loadCrock(denom.masterSig);
|
|
||||||
|
|
||||||
const nativePub = native.EddsaPublicKey.fromCrock(masterPub);
|
|
||||||
|
|
||||||
return native.eddsaVerify(
|
|
||||||
native.SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY,
|
|
||||||
p.toPurpose(),
|
|
||||||
nativeSig,
|
|
||||||
nativePub,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new EdDSA key pair.
|
|
||||||
*/
|
|
||||||
export function createEddsaKeypair(): { priv: string; pub: string } {
|
|
||||||
const priv = native.EddsaPrivateKey.create();
|
|
||||||
const pub = priv.getPublicKey();
|
|
||||||
return { priv: priv.toCrock(), pub: pub.toCrock() };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unblind a blindly signed value.
|
|
||||||
*/
|
|
||||||
export function rsaUnblind(sig: string, bk: string, pk: string): string {
|
|
||||||
const denomSig = native.rsaUnblind(
|
|
||||||
native.RsaSignature.fromCrock(sig),
|
|
||||||
native.RsaBlindingKeySecret.fromCrock(bk),
|
|
||||||
native.RsaPublicKey.fromCrock(pk),
|
|
||||||
);
|
|
||||||
return denomSig.encode().toCrock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate updated coins (to store in the database)
|
|
||||||
* and deposit permissions for each given coin.
|
|
||||||
*/
|
|
||||||
export function signDeposit(
|
|
||||||
contractTerms: ContractTerms,
|
|
||||||
cds: CoinWithDenom[],
|
|
||||||
totalAmount: AmountJson,
|
|
||||||
): PayCoinInfo {
|
|
||||||
const ret: PayCoinInfo = {
|
|
||||||
originalCoins: [],
|
|
||||||
sigs: [],
|
|
||||||
updatedCoins: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const contractTermsHash = hashString(canonicalJson(contractTerms));
|
|
||||||
|
|
||||||
const feeList: AmountJson[] = cds.map(x => x.denom.feeDeposit);
|
|
||||||
let fees = Amounts.add(Amounts.getZero(feeList[0].currency), ...feeList)
|
|
||||||
.amount;
|
|
||||||
// okay if saturates
|
|
||||||
fees = Amounts.sub(fees, Amounts.parseOrThrow(contractTerms.max_fee))
|
|
||||||
.amount;
|
|
||||||
const total = Amounts.add(fees, totalAmount).amount;
|
|
||||||
|
|
||||||
const amountSpent = native.Amount.getZero(
|
|
||||||
cds[0].coin.currentAmount.currency,
|
|
||||||
);
|
|
||||||
const amountRemaining = new native.Amount(total);
|
|
||||||
for (const cd of cds) {
|
|
||||||
let coinSpend: Amount;
|
|
||||||
const originalCoin = { ...cd.coin };
|
|
||||||
|
|
||||||
if (amountRemaining.value === 0 && amountRemaining.fraction === 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (amountRemaining.cmp(new native.Amount(cd.coin.currentAmount)) < 0) {
|
|
||||||
coinSpend = new native.Amount(amountRemaining.toJson());
|
|
||||||
} else {
|
|
||||||
coinSpend = new native.Amount(cd.coin.currentAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
amountSpent.add(coinSpend);
|
|
||||||
amountRemaining.sub(coinSpend);
|
|
||||||
|
|
||||||
const feeDeposit: Amount = new native.Amount(cd.denom.feeDeposit);
|
|
||||||
|
|
||||||
// Give the merchant at least the deposit fee, otherwise it'll reject
|
|
||||||
// the coin.
|
|
||||||
if (coinSpend.cmp(feeDeposit) < 0) {
|
|
||||||
coinSpend = feeDeposit;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newAmount = new native.Amount(cd.coin.currentAmount);
|
|
||||||
newAmount.sub(coinSpend);
|
|
||||||
cd.coin.currentAmount = newAmount.toJson();
|
|
||||||
cd.coin.status = CoinStatus.PurchasePending;
|
|
||||||
|
|
||||||
const d = new native.DepositRequestPS({
|
|
||||||
amount_with_fee: coinSpend.toNbo(),
|
|
||||||
coin_pub: native.EddsaPublicKey.fromCrock(cd.coin.coinPub),
|
|
||||||
deposit_fee: new native.Amount(cd.denom.feeDeposit).toNbo(),
|
|
||||||
h_contract: native.HashCode.fromCrock(contractTermsHash),
|
|
||||||
h_wire: native.HashCode.fromCrock(contractTerms.H_wire),
|
|
||||||
merchant: native.EddsaPublicKey.fromCrock(contractTerms.merchant_pub),
|
|
||||||
refund_deadline: native.AbsoluteTimeNbo.fromTalerString(
|
|
||||||
contractTerms.refund_deadline,
|
|
||||||
),
|
|
||||||
timestamp: native.AbsoluteTimeNbo.fromTalerString(
|
|
||||||
contractTerms.timestamp,
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
const coinSig = native
|
|
||||||
.eddsaSign(
|
|
||||||
d.toPurpose(),
|
|
||||||
native.EddsaPrivateKey.fromCrock(cd.coin.coinPriv),
|
|
||||||
)
|
|
||||||
.toCrock();
|
|
||||||
|
|
||||||
const s: CoinPaySig = {
|
|
||||||
coin_pub: cd.coin.coinPub,
|
|
||||||
coin_sig: coinSig,
|
|
||||||
contribution: Amounts.toString(coinSpend.toJson()),
|
|
||||||
denom_pub: cd.coin.denomPub,
|
|
||||||
exchange_url: cd.denom.exchangeBaseUrl,
|
|
||||||
ub_sig: cd.coin.denomSig,
|
|
||||||
};
|
|
||||||
ret.sigs.push(s);
|
|
||||||
ret.updatedCoins.push(cd.coin);
|
|
||||||
ret.originalCoins.push(originalCoin);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new refresh session.
|
|
||||||
*/
|
|
||||||
export function createRefreshSession(
|
|
||||||
exchangeBaseUrl: string,
|
|
||||||
kappa: number,
|
|
||||||
meltCoin: CoinRecord,
|
|
||||||
newCoinDenoms: DenominationRecord[],
|
|
||||||
meltFee: AmountJson,
|
|
||||||
): RefreshSessionRecord {
|
|
||||||
let valueWithFee = Amounts.getZero(newCoinDenoms[0].value.currency);
|
|
||||||
|
|
||||||
for (const ncd of newCoinDenoms) {
|
|
||||||
valueWithFee = Amounts.add(valueWithFee, ncd.value, ncd.feeWithdraw)
|
|
||||||
.amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// melt fee
|
|
||||||
valueWithFee = Amounts.add(valueWithFee, meltFee).amount;
|
|
||||||
|
|
||||||
const sessionHc = new HashContext();
|
|
||||||
|
|
||||||
const transferPubs: string[] = [];
|
|
||||||
const transferPrivs: string[] = [];
|
|
||||||
|
|
||||||
const preCoinsForGammas: RefreshPreCoinRecord[][] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < kappa; i++) {
|
|
||||||
const t = native.EcdhePrivateKey.create();
|
|
||||||
const pub = t.getPublicKey();
|
|
||||||
sessionHc.read(pub);
|
|
||||||
transferPrivs.push(t.toCrock());
|
|
||||||
transferPubs.push(pub.toCrock());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const denom of newCoinDenoms) {
|
|
||||||
const r = native.RsaPublicKey.fromCrock(denom.denomPub);
|
|
||||||
sessionHc.read(r.encode());
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionHc.read(native.EddsaPublicKey.fromCrock(meltCoin.coinPub));
|
|
||||||
sessionHc.read(new native.Amount(valueWithFee).toNbo());
|
|
||||||
|
|
||||||
for (let i = 0; i < kappa; i++) {
|
|
||||||
const preCoins: RefreshPreCoinRecord[] = [];
|
|
||||||
for (let j = 0; j < newCoinDenoms.length; j++) {
|
|
||||||
const transferPriv = native.EcdhePrivateKey.fromCrock(transferPrivs[i]);
|
|
||||||
const oldCoinPub = native.EddsaPublicKey.fromCrock(meltCoin.coinPub);
|
|
||||||
const transferSecret = native.ecdhEddsa(transferPriv, oldCoinPub);
|
|
||||||
|
|
||||||
const fresh = native.setupFreshCoin(transferSecret, j);
|
|
||||||
|
|
||||||
const coinPriv = fresh.priv;
|
|
||||||
const coinPub = coinPriv.getPublicKey();
|
|
||||||
const blindingFactor = fresh.blindingKey;
|
|
||||||
const pubHash: native.HashCode = coinPub.hash();
|
|
||||||
const denomPub = native.RsaPublicKey.fromCrock(
|
|
||||||
newCoinDenoms[j].denomPub,
|
|
||||||
);
|
|
||||||
const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
|
|
||||||
if (!ev) {
|
|
||||||
throw Error("couldn't blind (malicious exchange key?)");
|
|
||||||
}
|
|
||||||
const preCoin: RefreshPreCoinRecord = {
|
|
||||||
blindingKey: blindingFactor.toCrock(),
|
|
||||||
coinEv: ev.toCrock(),
|
|
||||||
privateKey: coinPriv.toCrock(),
|
|
||||||
publicKey: coinPub.toCrock(),
|
|
||||||
};
|
|
||||||
preCoins.push(preCoin);
|
|
||||||
sessionHc.read(ev);
|
|
||||||
}
|
|
||||||
preCoinsForGammas.push(preCoins);
|
|
||||||
}
|
|
||||||
|
|
||||||
const sessionHash = new HashCode();
|
|
||||||
sessionHash.alloc();
|
|
||||||
sessionHc.finish(sessionHash);
|
|
||||||
|
|
||||||
const confirmData = new RefreshMeltCoinAffirmationPS({
|
|
||||||
amount_with_fee: new Amount(valueWithFee).toNbo(),
|
|
||||||
coin_pub: EddsaPublicKey.fromCrock(meltCoin.coinPub),
|
|
||||||
melt_fee: new Amount(meltFee).toNbo(),
|
|
||||||
session_hash: sessionHash,
|
|
||||||
});
|
|
||||||
|
|
||||||
const confirmSig: string = native
|
|
||||||
.eddsaSign(
|
|
||||||
confirmData.toPurpose(),
|
|
||||||
native.EddsaPrivateKey.fromCrock(meltCoin.coinPriv),
|
|
||||||
)
|
|
||||||
.toCrock();
|
|
||||||
|
|
||||||
let valueOutput = Amounts.getZero(newCoinDenoms[0].value.currency);
|
|
||||||
for (const denom of newCoinDenoms) {
|
|
||||||
valueOutput = Amounts.add(valueOutput, denom.value).amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
const refreshSession: RefreshSessionRecord = {
|
|
||||||
confirmSig,
|
|
||||||
exchangeBaseUrl,
|
|
||||||
finished: false,
|
|
||||||
hash: sessionHash.toCrock(),
|
|
||||||
meltCoinPub: meltCoin.coinPub,
|
|
||||||
newDenomHashes: newCoinDenoms.map(d => d.denomPubHash),
|
|
||||||
newDenoms: newCoinDenoms.map(d => d.denomPub),
|
|
||||||
norevealIndex: undefined,
|
|
||||||
preCoinsForGammas,
|
|
||||||
transferPrivs,
|
|
||||||
transferPubs,
|
|
||||||
valueOutput,
|
|
||||||
valueWithFee,
|
|
||||||
};
|
|
||||||
|
|
||||||
return refreshSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash a string including the zero terminator.
|
|
||||||
*/
|
|
||||||
export function hashString(str: string): string {
|
|
||||||
const b = native.ByteArray.fromStringWithNull(str);
|
|
||||||
return b.hash().toCrock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash a denomination public key.
|
|
||||||
*/
|
|
||||||
export function hashDenomPub(denomPub: string): string {
|
|
||||||
return native.RsaPublicKey.fromCrock(denomPub)
|
|
||||||
.encode()
|
|
||||||
.hash()
|
|
||||||
.toCrock();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function signCoinLink(
|
|
||||||
oldCoinPriv: string,
|
|
||||||
newDenomHash: string,
|
|
||||||
oldCoinPub: string,
|
|
||||||
transferPub: string,
|
|
||||||
coinEv: string,
|
|
||||||
): string {
|
|
||||||
const coinEvHash = native.ByteArray.fromCrock(coinEv).hash();
|
|
||||||
|
|
||||||
const coinLink = new native.CoinLinkSignaturePS({
|
|
||||||
coin_envelope_hash: coinEvHash,
|
|
||||||
h_denom_pub: native.HashCode.fromCrock(newDenomHash),
|
|
||||||
old_coin_pub: native.EddsaPublicKey.fromCrock(oldCoinPub),
|
|
||||||
transfer_pub: native.EcdhePublicKey.fromCrock(transferPub),
|
|
||||||
});
|
|
||||||
|
|
||||||
const coinPriv = native.EddsaPrivateKey.fromCrock(oldCoinPriv);
|
|
||||||
|
|
||||||
const sig = native.eddsaSign(coinLink.toPurpose(), coinPriv);
|
|
||||||
|
|
||||||
return sig.toCrock();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function benchmark(repetitions: number): BenchmarkResult {
|
|
||||||
let time_hash = 0;
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
hashString("hello world");
|
|
||||||
time_hash += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
let time_hash_big = 0;
|
|
||||||
const ba = new native.ByteArray(4096);
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
ba.randomize(native.RandomQuality.WEAK);
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
ba.hash();
|
|
||||||
time_hash_big += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
let time_eddsa_create = 0;
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
const priv: native.EddsaPrivateKey = native.EddsaPrivateKey.create();
|
|
||||||
time_eddsa_create += timer.performanceNow() - start;
|
|
||||||
priv.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
let time_eddsa_sign = 0;
|
|
||||||
const eddsaPriv: native.EddsaPrivateKey = native.EddsaPrivateKey.create();
|
|
||||||
const eddsaPub: native.EddsaPublicKey = eddsaPriv.getPublicKey();
|
|
||||||
const h: native.HashCode = new native.HashCode();
|
|
||||||
h.alloc();
|
|
||||||
h.random(native.RandomQuality.WEAK);
|
|
||||||
|
|
||||||
const ps = new native.PaymentSignaturePS({
|
|
||||||
contract_hash: h,
|
|
||||||
});
|
|
||||||
|
|
||||||
const p = ps.toPurpose();
|
|
||||||
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
native.eddsaSign(p, eddsaPriv);
|
|
||||||
time_eddsa_sign += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
const eddsaSig = native.eddsaSign(p, eddsaPriv);
|
|
||||||
|
|
||||||
let time_ecdsa_create = 0;
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
const priv: native.EcdsaPrivateKey = native.EcdsaPrivateKey.create();
|
|
||||||
time_ecdsa_create += timer.performanceNow() - start;
|
|
||||||
priv.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
let time_eddsa_verify = 0;
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
native.eddsaVerify(
|
|
||||||
native.SignaturePurpose.MERCHANT_PAYMENT_OK,
|
|
||||||
p,
|
|
||||||
eddsaSig,
|
|
||||||
eddsaPub,
|
|
||||||
);
|
|
||||||
time_eddsa_verify += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* rsa 2048 */
|
|
||||||
|
|
||||||
let time_rsa_2048_blind = 0;
|
|
||||||
const rsaPriv2048: native.RsaPrivateKey = native.RsaPrivateKey.create(2048);
|
|
||||||
const rsaPub2048 = rsaPriv2048.getPublicKey();
|
|
||||||
const blindingSecret2048 = native.RsaBlindingKeySecret.create();
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
native.rsaBlind(h, blindingSecret2048, rsaPub2048);
|
|
||||||
time_rsa_2048_blind += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blindedMessage2048 = native.rsaBlind(
|
|
||||||
h,
|
|
||||||
blindingSecret2048,
|
|
||||||
rsaPub2048,
|
|
||||||
);
|
|
||||||
if (!blindedMessage2048) {
|
|
||||||
throw Error("should not happen");
|
|
||||||
}
|
|
||||||
const rsaBlindSig2048 = native.rsaSignBlinded(
|
|
||||||
rsaPriv2048,
|
|
||||||
blindedMessage2048,
|
|
||||||
);
|
|
||||||
|
|
||||||
let time_rsa_2048_unblind = 0;
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
native.rsaUnblind(rsaBlindSig2048, blindingSecret2048, rsaPub2048);
|
|
||||||
time_rsa_2048_unblind += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unblindedSig2048 = native.rsaUnblind(
|
|
||||||
rsaBlindSig2048,
|
|
||||||
blindingSecret2048,
|
|
||||||
rsaPub2048,
|
|
||||||
);
|
|
||||||
|
|
||||||
let time_rsa_2048_verify = 0;
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
native.rsaVerify(h, unblindedSig2048, rsaPub2048);
|
|
||||||
time_rsa_2048_verify += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* rsa 4096 */
|
|
||||||
|
|
||||||
let time_rsa_4096_blind = 0;
|
|
||||||
const rsaPriv4096: native.RsaPrivateKey = native.RsaPrivateKey.create(4096);
|
|
||||||
const rsaPub4096 = rsaPriv4096.getPublicKey();
|
|
||||||
const blindingSecret4096 = native.RsaBlindingKeySecret.create();
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
native.rsaBlind(h, blindingSecret4096, rsaPub4096);
|
|
||||||
time_rsa_4096_blind += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blindedMessage4096 = native.rsaBlind(
|
|
||||||
h,
|
|
||||||
blindingSecret4096,
|
|
||||||
rsaPub4096,
|
|
||||||
);
|
|
||||||
if (!blindedMessage4096) {
|
|
||||||
throw Error("should not happen");
|
|
||||||
}
|
|
||||||
const rsaBlindSig4096 = native.rsaSignBlinded(
|
|
||||||
rsaPriv4096,
|
|
||||||
blindedMessage4096,
|
|
||||||
);
|
|
||||||
|
|
||||||
let time_rsa_4096_unblind = 0;
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
native.rsaUnblind(rsaBlindSig4096, blindingSecret4096, rsaPub4096);
|
|
||||||
time_rsa_4096_unblind += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unblindedSig4096 = native.rsaUnblind(
|
|
||||||
rsaBlindSig4096,
|
|
||||||
blindingSecret4096,
|
|
||||||
rsaPub4096,
|
|
||||||
);
|
|
||||||
|
|
||||||
let time_rsa_4096_verify = 0;
|
|
||||||
for (let i = 0; i < repetitions; i++) {
|
|
||||||
const start = timer.performanceNow();
|
|
||||||
native.rsaVerify(h, unblindedSig4096, rsaPub4096);
|
|
||||||
time_rsa_4096_verify += timer.performanceNow() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
repetitions,
|
|
||||||
time: {
|
|
||||||
hash_small: time_hash,
|
|
||||||
hash_big: time_hash_big,
|
|
||||||
eddsa_create: time_eddsa_create,
|
|
||||||
eddsa_sign: time_eddsa_sign,
|
|
||||||
eddsa_verify: time_eddsa_verify,
|
|
||||||
ecdsa_create: time_ecdsa_create,
|
|
||||||
rsa_2048_blind: time_rsa_2048_blind,
|
|
||||||
rsa_2048_unblind: time_rsa_2048_unblind,
|
|
||||||
rsa_2048_verify: time_rsa_2048_verify,
|
|
||||||
rsa_4096_blind: time_rsa_4096_blind,
|
|
||||||
rsa_4096_unblind: time_rsa_4096_unblind,
|
|
||||||
rsa_4096_verify: time_rsa_4096_verify,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const worker: Worker = (self as any) as Worker;
|
const worker: Worker = (self as any) as Worker;
|
||||||
|
|
||||||
|
let impl: CryptoImplementation | undefined;
|
||||||
|
|
||||||
|
|
||||||
worker.onmessage = (msg: MessageEvent) => {
|
worker.onmessage = (msg: MessageEvent) => {
|
||||||
if (!Array.isArray(msg.data.args)) {
|
const args = msg.data.args;
|
||||||
|
if (!Array.isArray(args)) {
|
||||||
console.error("args must be array");
|
console.error("args must be array");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof msg.data.id !== "number") {
|
const id = msg.data.id;
|
||||||
|
if (typeof id !== "number") {
|
||||||
console.error("RPC id must be number");
|
console.error("RPC id must be number");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (typeof msg.data.operation !== "string") {
|
const operation = msg.data.operation;
|
||||||
|
if (typeof operation !== "string") {
|
||||||
console.error("RPC operation must be string");
|
console.error("RPC operation must be string");
|
||||||
}
|
|
||||||
const f = (RpcFunctions as any)[msg.data.operation];
|
|
||||||
if (!f) {
|
|
||||||
console.error(`unknown operation: '${msg.data.operation}'`);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RpcFunctions.enableTracing) {
|
if (CryptoImplementation.enableTracing) {
|
||||||
console.log("onmessage with", msg.data.operation);
|
console.log("onmessage with", operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
emscLoader.getLib().then(p => {
|
emscLoader.getLib().then(p => {
|
||||||
const lib = p.lib;
|
const lib = p.lib;
|
||||||
if (!native.isInitialized()) {
|
const emsc = new EmscEnvironment(lib);
|
||||||
if (RpcFunctions.enableTracing) {
|
const impl = new CryptoImplementation(emsc);
|
||||||
console.log("initializing emscripten for then first time with lib");
|
|
||||||
}
|
if (!(operation in impl)) {
|
||||||
native.initialize(lib);
|
console.error(`unknown operation: '${operation}'`);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (RpcFunctions.enableTracing) {
|
|
||||||
console.log("about to execute", msg.data.operation);
|
if (CryptoImplementation.enableTracing) {
|
||||||
|
console.log("about to execute", operation);
|
||||||
}
|
}
|
||||||
const res = f(...msg.data.args);
|
|
||||||
if (RpcFunctions.enableTracing) {
|
const result = (impl as any)[operation](...args);
|
||||||
console.log("finished executing", msg.data.operation);
|
|
||||||
|
if (CryptoImplementation.enableTracing) {
|
||||||
|
console.log("finished executing", operation);
|
||||||
}
|
}
|
||||||
worker.postMessage({ result: res, id: msg.data.id });
|
worker.postMessage({ result, id });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -20,13 +20,12 @@ import test from "ava";
|
|||||||
import * as emscLoader from "./emscLoader";
|
import * as emscLoader from "./emscLoader";
|
||||||
import * as native from "./emscInterface";
|
import * as native from "./emscInterface";
|
||||||
|
|
||||||
test.before(async () => {
|
|
||||||
const { lib } = await emscLoader.getLib();
|
|
||||||
native.initialize(lib);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("string hashing", (t) => {
|
test("string hashing", async (t) => {
|
||||||
const x = native.ByteArray.fromStringWithNull("hello taler");
|
const { lib } = await emscLoader.getLib();
|
||||||
|
const emsc = new native.EmscEnvironment(lib);
|
||||||
|
|
||||||
|
const x = native.ByteArray.fromStringWithNull(emsc, "hello taler");
|
||||||
const h = "8RDMADB3YNF3QZBS3V467YZVJAMC2QAQX0TZGVZ6Q5PFRRAJFT70HHN0QF661QR9QWKYMMC7YEMPD679D2RADXCYK8Y669A2A5MKQFR";
|
const h = "8RDMADB3YNF3QZBS3V467YZVJAMC2QAQX0TZGVZ6Q5PFRRAJFT70HHN0QF661QR9QWKYMMC7YEMPD679D2RADXCYK8Y669A2A5MKQFR";
|
||||||
const hc = x.hash().toCrock();
|
const hc = x.hash().toCrock();
|
||||||
console.log(`# hc ${hc}`);
|
console.log(`# hc ${hc}`);
|
||||||
@ -35,28 +34,34 @@ test("string hashing", (t) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test("signing", (t) => {
|
test("signing", async (t) => {
|
||||||
const x = native.ByteArray.fromStringWithNull("hello taler");
|
const { lib } = await emscLoader.getLib();
|
||||||
const priv = native.EddsaPrivateKey.create();
|
const emsc = new native.EmscEnvironment(lib);
|
||||||
|
|
||||||
|
const x = native.ByteArray.fromStringWithNull(emsc, "hello taler");
|
||||||
|
const priv = native.EddsaPrivateKey.create(emsc, );
|
||||||
const pub = priv.getPublicKey();
|
const pub = priv.getPublicKey();
|
||||||
const purpose = new native.EccSignaturePurpose(native.SignaturePurpose.TEST, x);
|
const purpose = new native.EccSignaturePurpose(emsc, native.SignaturePurpose.TEST, x);
|
||||||
const sig = native.eddsaSign(purpose, priv);
|
const sig = native.eddsaSign(purpose, priv);
|
||||||
t.true(native.eddsaVerify(native.SignaturePurpose.TEST, purpose, sig, pub));
|
t.true(native.eddsaVerify(native.SignaturePurpose.TEST, purpose, sig, pub));
|
||||||
t.pass();
|
t.pass();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test("signing-fixed-data", (t) => {
|
test("signing-fixed-data", async (t) => {
|
||||||
const x = native.ByteArray.fromStringWithNull("hello taler");
|
const { lib } = await emscLoader.getLib();
|
||||||
const purpose = new native.EccSignaturePurpose(native.SignaturePurpose.TEST, x);
|
const emsc = new native.EmscEnvironment(lib);
|
||||||
|
|
||||||
|
const x = native.ByteArray.fromStringWithNull(emsc, "hello taler");
|
||||||
|
const purpose = new native.EccSignaturePurpose(emsc, native.SignaturePurpose.TEST, x);
|
||||||
const privStr = "G9R8KRRCAFKPD0KW7PW48CC2T03VQ8K2AN9J6J6K2YW27J5MHN90";
|
const privStr = "G9R8KRRCAFKPD0KW7PW48CC2T03VQ8K2AN9J6J6K2YW27J5MHN90";
|
||||||
const pubStr = "YHCZB442FQFJ0ET20MWA8YJ53M61EZGJ6QKV1KTJZMRNXDY45WT0";
|
const pubStr = "YHCZB442FQFJ0ET20MWA8YJ53M61EZGJ6QKV1KTJZMRNXDY45WT0";
|
||||||
const sigStr = "7V6XY4QGC1406GPMT305MZQ1HDCR7R0S5BP02GTGDQFPSXB6YD2YDN5ZS7NJQCNP61Y39MRHXNXQ1Z15JY4CJY4CPDA6CKQ3313WG38";
|
const sigStr = "7V6XY4QGC1406GPMT305MZQ1HDCR7R0S5BP02GTGDQFPSXB6YD2YDN5ZS7NJQCNP61Y39MRHXNXQ1Z15JY4CJY4CPDA6CKQ3313WG38";
|
||||||
const priv = native.EddsaPrivateKey.fromCrock(privStr);
|
const priv = native.EddsaPrivateKey.fromCrock(emsc, privStr);
|
||||||
t.true(privStr === priv.toCrock());
|
t.true(privStr === priv.toCrock());
|
||||||
const pub = priv.getPublicKey();
|
const pub = priv.getPublicKey();
|
||||||
t.true(pubStr === pub.toCrock());
|
t.true(pubStr === pub.toCrock());
|
||||||
const sig = native.EddsaSignature.fromCrock(sigStr);
|
const sig = native.EddsaSignature.fromCrock(emsc, sigStr);
|
||||||
t.true(sigStr === sig.toCrock());
|
t.true(sigStr === sig.toCrock());
|
||||||
const sig2 = native.eddsaSign(purpose, priv);
|
const sig2 = native.eddsaSign(purpose, priv);
|
||||||
t.true(sig.toCrock() === sig2.toCrock());
|
t.true(sig.toCrock() === sig2.toCrock());
|
||||||
@ -68,27 +73,33 @@ test("signing-fixed-data", (t) => {
|
|||||||
const denomPubStr1 = "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30G9R64VK6HHS6MW42DSN8MVKJGHK6WR3CGT18MWMCDSM75138E1K8S0MADSQ68W34DHH6MW4CHA270W4CG9J6GW48DHG8MVK4E9S7523GEA56H0K4E1Q891KCCSG752KGC1M88VMCDSQ6D23CHHG8H33AGHG6MSK8GT26CRKAC1M64V3JCJ56CVKCC228MWMCHA26MS30H1J8MVKEDHJ70TMADHK892KJC1H60TKJDHM710KGGT584T38H9K851KCDHG60W30HJ28CT4CC1G8CR3JGJ28H236DJ28H330H9S890M2D9S8S14AGA369344GA36S248CHS70RKEDSS6MWKGDJ26D136GT465348CSS8S232CHM6GS34C9N8CS3GD9H60W36H1R8MSK2GSQ8MSM6C9R70SKCHHN6MW3ACJ28N0K2CA58RS3GCA26MV42G9P891KAG9Q8N0KGD9M850KEHJ16S130CA27124AE1G852KJCHR6S1KGDSJ8RTKED1S8RR3CCHP68W4CH9Q6GT34GT18GS36EA46N24AGSP6933GCHM60VMAE1S8GV3EHHN74W3GC1J651KEH9N8MSK0CSG6S2KEEA460R32C1M8D144GSR6RWKEC218S0KEGJ4611KEEA36CSKJC2564TM4CSJ6H230E1N74TM8C1P61342CSG60WKCGHH64VK2G9S8CRKAHHK88W30HJ388R3CH1Q6X2K2DHK8GSM4D1Q74WM4HA461146H9S6D33JDJ26D234C9Q6923ECSS60RM6CT46CSKCH1M6S13EH9J8S33GCSN4CMGM81051JJ08SG64R30C1H4CMGM81054520A8A00";
|
const denomPubStr1 = "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30G9R64VK6HHS6MW42DSN8MVKJGHK6WR3CGT18MWMCDSM75138E1K8S0MADSQ68W34DHH6MW4CHA270W4CG9J6GW48DHG8MVK4E9S7523GEA56H0K4E1Q891KCCSG752KGC1M88VMCDSQ6D23CHHG8H33AGHG6MSK8GT26CRKAC1M64V3JCJ56CVKCC228MWMCHA26MS30H1J8MVKEDHJ70TMADHK892KJC1H60TKJDHM710KGGT584T38H9K851KCDHG60W30HJ28CT4CC1G8CR3JGJ28H236DJ28H330H9S890M2D9S8S14AGA369344GA36S248CHS70RKEDSS6MWKGDJ26D136GT465348CSS8S232CHM6GS34C9N8CS3GD9H60W36H1R8MSK2GSQ8MSM6C9R70SKCHHN6MW3ACJ28N0K2CA58RS3GCA26MV42G9P891KAG9Q8N0KGD9M850KEHJ16S130CA27124AE1G852KJCHR6S1KGDSJ8RTKED1S8RR3CCHP68W4CH9Q6GT34GT18GS36EA46N24AGSP6933GCHM60VMAE1S8GV3EHHN74W3GC1J651KEH9N8MSK0CSG6S2KEEA460R32C1M8D144GSR6RWKEC218S0KEGJ4611KEEA36CSKJC2564TM4CSJ6H230E1N74TM8C1P61342CSG60WKCGHH64VK2G9S8CRKAHHK88W30HJ388R3CH1Q6X2K2DHK8GSM4D1Q74WM4HA461146H9S6D33JDJ26D234C9Q6923ECSS60RM6CT46CSKCH1M6S13EH9J8S33GCSN4CMGM81051JJ08SG64R30C1H4CMGM81054520A8A00";
|
||||||
|
|
||||||
|
|
||||||
test("rsa-encode", (t) => {
|
test("rsa-encode", async (t) => {
|
||||||
|
const { lib } = await emscLoader.getLib();
|
||||||
|
const emsc = new native.EmscEnvironment(lib);
|
||||||
|
|
||||||
const pubHashStr = "JM63YM5X7X547164QJ3MGJZ4WDD47GEQR5DW5SH35G4JFZXEJBHE5JBNZM5K8XN5C4BRW25BE6GSVAYBF790G2BZZ13VW91D41S4DS0";
|
const pubHashStr = "JM63YM5X7X547164QJ3MGJZ4WDD47GEQR5DW5SH35G4JFZXEJBHE5JBNZM5K8XN5C4BRW25BE6GSVAYBF790G2BZZ13VW91D41S4DS0";
|
||||||
const denomPub = native.RsaPublicKey.fromCrock(denomPubStr1);
|
const denomPub = native.RsaPublicKey.fromCrock(emsc, denomPubStr1);
|
||||||
const pubHash = denomPub.encode().hash();
|
const pubHash = denomPub.encode().hash();
|
||||||
t.true(pubHashStr === pubHash.toCrock());
|
t.true(pubHashStr === pubHash.toCrock());
|
||||||
t.pass();
|
t.pass();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test("withdraw-request", (t) => {
|
test("withdraw-request", async (t) => {
|
||||||
|
const { lib } = await emscLoader.getLib();
|
||||||
|
const emsc = new native.EmscEnvironment(lib);
|
||||||
|
|
||||||
const reservePrivStr = "G9R8KRRCAFKPD0KW7PW48CC2T03VQ8K2AN9J6J6K2YW27J5MHN90";
|
const reservePrivStr = "G9R8KRRCAFKPD0KW7PW48CC2T03VQ8K2AN9J6J6K2YW27J5MHN90";
|
||||||
const reservePriv = native.EddsaPrivateKey.fromCrock(reservePrivStr);
|
const reservePriv = native.EddsaPrivateKey.fromCrock(emsc, reservePrivStr);
|
||||||
const reservePub = reservePriv.getPublicKey();
|
const reservePub = reservePriv.getPublicKey();
|
||||||
const amountWithFee = new native.Amount({currency: "KUDOS", value: 1, fraction: 10000});
|
const amountWithFee = new native.Amount(emsc, {currency: "KUDOS", value: 1, fraction: 10000});
|
||||||
amountWithFee.add(new native.Amount({currency: "KUDOS", value: 0, fraction: 20000}));
|
amountWithFee.add(new native.Amount(emsc, {currency: "KUDOS", value: 0, fraction: 20000}));
|
||||||
const withdrawFee = new native.Amount({currency: "KUDOS", value: 0, fraction: 20000});
|
const withdrawFee = new native.Amount(emsc, {currency: "KUDOS", value: 0, fraction: 20000});
|
||||||
const denomPub = native.RsaPublicKey.fromCrock(denomPubStr1);
|
const denomPub = native.RsaPublicKey.fromCrock(emsc, denomPubStr1);
|
||||||
const ev = native.ByteArray.fromStringWithNull("hello, world");
|
const ev = native.ByteArray.fromStringWithNull(emsc, "hello, world");
|
||||||
|
|
||||||
// Signature
|
// Signature
|
||||||
const withdrawRequest = new native.WithdrawRequestPS({
|
const withdrawRequest = new native.WithdrawRequestPS(emsc, {
|
||||||
amount_with_fee: amountWithFee.toNbo(),
|
amount_with_fee: amountWithFee.toNbo(),
|
||||||
h_coin_envelope: ev.hash(),
|
h_coin_envelope: ev.hash(),
|
||||||
h_denomination_pub: denomPub.encode().hash(),
|
h_denomination_pub: denomPub.encode().hash(),
|
||||||
@ -105,9 +116,13 @@ test("withdraw-request", (t) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test("currency-conversion", (t) => {
|
test("currency-conversion", async (t) => {
|
||||||
const a1 = new native.Amount({currency: "KUDOS", value: 1, fraction: 50000000});
|
|
||||||
const a2 = new native.Amount({currency: "KUDOS", value: 1, fraction: 50000000});
|
const { lib } = await emscLoader.getLib();
|
||||||
|
const emsc = new native.EmscEnvironment(lib);
|
||||||
|
|
||||||
|
const a1 = new native.Amount(emsc, {currency: "KUDOS", value: 1, fraction: 50000000});
|
||||||
|
const a2 = new native.Amount(emsc, {currency: "KUDOS", value: 1, fraction: 50000000});
|
||||||
a1.add(a2);
|
a1.add(a2);
|
||||||
const x = a1.toJson();
|
const x = a1.toJson();
|
||||||
t.true(x.currency === "KUDOS");
|
t.true(x.currency === "KUDOS");
|
||||||
@ -117,8 +132,11 @@ test("currency-conversion", (t) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test("ecdsa", (t) => {
|
test("ecdsa", async (t) => {
|
||||||
const priv = native.EcdsaPrivateKey.create();
|
const { lib } = await emscLoader.getLib();
|
||||||
|
const emsc = new native.EmscEnvironment(lib);
|
||||||
|
|
||||||
|
const priv = native.EcdsaPrivateKey.create(emsc);
|
||||||
const pub1 = priv.getPublicKey();
|
const pub1 = priv.getPublicKey();
|
||||||
t.truthy(priv);
|
t.truthy(priv);
|
||||||
t.truthy(pub1);
|
t.truthy(pub1);
|
||||||
@ -126,8 +144,11 @@ test("ecdsa", (t) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test("ecdhe", (t) => {
|
test("ecdhe", async (t) => {
|
||||||
const priv = native.EcdhePrivateKey.create();
|
const { lib } = await emscLoader.getLib();
|
||||||
|
const emsc = new native.EmscEnvironment(lib);
|
||||||
|
|
||||||
|
const priv = native.EcdhePrivateKey.create(emsc);
|
||||||
const pub = priv.getPublicKey();
|
const pub = priv.getPublicKey();
|
||||||
t.truthy(priv);
|
t.truthy(priv);
|
||||||
t.truthy(pub);
|
t.truthy(pub);
|
||||||
|
@ -30,37 +30,6 @@ import { AmountJson } from "../amounts";
|
|||||||
|
|
||||||
import { EmscFunGen, EmscLib } from "./emscLoader";
|
import { EmscFunGen, EmscLib } from "./emscLoader";
|
||||||
|
|
||||||
|
|
||||||
// Will be set only after initialization.
|
|
||||||
let maybeEmscEnv: EmscEnvironment | undefined = undefined;
|
|
||||||
|
|
||||||
export function isInitialized() {
|
|
||||||
return !!maybeEmscEnv;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function initialize(lib: EmscLib) {
|
|
||||||
if (!lib) {
|
|
||||||
throw Error("library must be object");
|
|
||||||
}
|
|
||||||
if (!lib.ccall) {
|
|
||||||
throw Error("sanity check failed: EmscLib does not have 'ccall'");
|
|
||||||
}
|
|
||||||
if (maybeEmscEnv) {
|
|
||||||
throw Error("emsc lib already initialized");
|
|
||||||
}
|
|
||||||
maybeEmscEnv = new EmscEnvironment(lib);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function emsc() {
|
|
||||||
if (maybeEmscEnv) {
|
|
||||||
return maybeEmscEnv;
|
|
||||||
}
|
|
||||||
throw Error("cannot use taler emscripten before initialization");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Size of a native pointer. Must match the size
|
* Size of a native pointer. Must match the size
|
||||||
* use when compiling via emscripten.
|
* use when compiling via emscripten.
|
||||||
@ -132,7 +101,7 @@ interface EmscAllocFunctions {
|
|||||||
rsa_unblind(a1: number, a2: number, a3: number): number;
|
rsa_unblind(a1: number, a2: number, a3: number): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class EmscEnvironment {
|
export class EmscEnvironment {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emscripten functions that don't do any memory allocations.
|
* Emscripten functions that don't do any memory allocations.
|
||||||
@ -254,8 +223,8 @@ interface ArenaObject {
|
|||||||
export class HashContext implements ArenaObject {
|
export class HashContext implements ArenaObject {
|
||||||
private hashContextPtr: number | undefined;
|
private hashContextPtr: number | undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor(private emsc: EmscEnvironment) {
|
||||||
this.hashContextPtr = emsc().allocFuncs.hash_context_start();
|
this.hashContextPtr = emsc.allocFuncs.hash_context_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -265,7 +234,7 @@ export class HashContext implements ArenaObject {
|
|||||||
if (!this.hashContextPtr) {
|
if (!this.hashContextPtr) {
|
||||||
throw Error("assertion failed");
|
throw Error("assertion failed");
|
||||||
}
|
}
|
||||||
emsc().funcs.hash_context_read(this.hashContextPtr, obj.nativePtr, obj.size());
|
this.emsc.funcs.hash_context_read(this.hashContextPtr, obj.nativePtr, obj.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -276,7 +245,7 @@ export class HashContext implements ArenaObject {
|
|||||||
throw Error("assertion failed");
|
throw Error("assertion failed");
|
||||||
}
|
}
|
||||||
h.alloc();
|
h.alloc();
|
||||||
emsc().funcs.hash_context_finish(this.hashContextPtr, h.nativePtr);
|
this.emsc.funcs.hash_context_finish(this.hashContextPtr, h.nativePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -284,7 +253,7 @@ export class HashContext implements ArenaObject {
|
|||||||
*/
|
*/
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
if (this.hashContextPtr) {
|
if (this.hashContextPtr) {
|
||||||
emsc().funcs.hash_context_abort(this.hashContextPtr);
|
this.emsc.funcs.hash_context_abort(this.hashContextPtr);
|
||||||
}
|
}
|
||||||
this.hashContextPtr = undefined;
|
this.hashContextPtr = undefined;
|
||||||
}
|
}
|
||||||
@ -304,12 +273,12 @@ abstract class MallocArenaObject implements ArenaObject {
|
|||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
if (this._nativePtr && !this.isWeak) {
|
if (this._nativePtr && !this.isWeak) {
|
||||||
emsc().funcs.free(this.nativePtr);
|
this.emsc.funcs.free(this.nativePtr);
|
||||||
this._nativePtr = undefined;
|
this._nativePtr = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(arena?: Arena) {
|
constructor(public emsc: EmscEnvironment, arena?: Arena) {
|
||||||
if (!arena) {
|
if (!arena) {
|
||||||
if (arenaStack.length === 0) {
|
if (arenaStack.length === 0) {
|
||||||
throw Error("No arena available");
|
throw Error("No arena available");
|
||||||
@ -323,7 +292,7 @@ abstract class MallocArenaObject implements ArenaObject {
|
|||||||
if (this._nativePtr !== undefined) {
|
if (this._nativePtr !== undefined) {
|
||||||
throw Error("Double allocation");
|
throw Error("Double allocation");
|
||||||
}
|
}
|
||||||
this.nativePtr = emsc().allocFuncs.malloc(size);
|
this.nativePtr = this.emsc.allocFuncs.malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
set nativePtr(v: number) {
|
set nativePtr(v: number) {
|
||||||
@ -414,21 +383,21 @@ arenaStack.push(new SyncArena());
|
|||||||
* Representation of monetary value in a given currency.
|
* Representation of monetary value in a given currency.
|
||||||
*/
|
*/
|
||||||
export class Amount extends MallocArenaObject {
|
export class Amount extends MallocArenaObject {
|
||||||
constructor(args?: AmountJson, arena?: Arena) {
|
constructor(emsc: EmscEnvironment, args?: AmountJson, arena?: Arena) {
|
||||||
super(arena);
|
super(emsc, arena);
|
||||||
if (args) {
|
if (args) {
|
||||||
this.nativePtr = emsc().allocFuncs.get_amount(args.value,
|
this.nativePtr = emsc.allocFuncs.get_amount(args.value,
|
||||||
0,
|
0,
|
||||||
args.fraction,
|
args.fraction,
|
||||||
args.currency);
|
args.currency);
|
||||||
} else {
|
} else {
|
||||||
this.nativePtr = emsc().allocFuncs.get_amount(0, 0, 0, "");
|
this.nativePtr = emsc.allocFuncs.get_amount(0, 0, 0, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getZero(currency: string, a?: Arena): Amount {
|
static getZero(emsc: EmscEnvironment, currency: string, a?: Arena): Amount {
|
||||||
const am = new Amount(undefined, a);
|
const am = new Amount(emsc, undefined, a);
|
||||||
const r = emsc().funcs.amount_get_zero(currency, am.nativePtr);
|
const r = emsc.funcs.amount_get_zero(currency, am.nativePtr);
|
||||||
if (r !== GNUNET_OK) {
|
if (r !== GNUNET_OK) {
|
||||||
throw Error("invalid currency");
|
throw Error("invalid currency");
|
||||||
}
|
}
|
||||||
@ -437,33 +406,33 @@ export class Amount extends MallocArenaObject {
|
|||||||
|
|
||||||
|
|
||||||
toNbo(a?: Arena): AmountNbo {
|
toNbo(a?: Arena): AmountNbo {
|
||||||
const x = new AmountNbo(a);
|
const x = new AmountNbo(this.emsc, a);
|
||||||
x.alloc();
|
x.alloc();
|
||||||
emsc().funcs.amount_hton(x.nativePtr, this.nativePtr);
|
this.emsc.funcs.amount_hton(x.nativePtr, this.nativePtr);
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
fromNbo(nbo: AmountNbo): void {
|
fromNbo(nbo: AmountNbo): void {
|
||||||
emsc().funcs.amount_ntoh(this.nativePtr, nbo.nativePtr);
|
this.emsc.funcs.amount_ntoh(this.nativePtr, nbo.nativePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return emsc().funcs.get_value(this.nativePtr);
|
return this.emsc.funcs.get_value(this.nativePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
get fraction() {
|
get fraction() {
|
||||||
return emsc().funcs.get_fraction(this.nativePtr);
|
return this.emsc.funcs.get_fraction(this.nativePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
get currency(): string {
|
get currency(): string {
|
||||||
return emsc().funcs.get_currency(this.nativePtr);
|
return this.emsc.funcs.get_currency(this.nativePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): AmountJson {
|
toJson(): AmountJson {
|
||||||
return {
|
return {
|
||||||
currency: emsc().funcs.get_currency(this.nativePtr),
|
currency: this.emsc.funcs.get_currency(this.nativePtr),
|
||||||
fraction: emsc().funcs.get_fraction(this.nativePtr),
|
fraction: this.emsc.funcs.get_fraction(this.nativePtr),
|
||||||
value: emsc().funcs.get_value(this.nativePtr),
|
value: this.emsc.funcs.get_value(this.nativePtr),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,7 +440,7 @@ export class Amount extends MallocArenaObject {
|
|||||||
* Add an amount to this amount.
|
* Add an amount to this amount.
|
||||||
*/
|
*/
|
||||||
add(a: Amount) {
|
add(a: Amount) {
|
||||||
const res = emsc().funcs.amount_add(this.nativePtr, a.nativePtr, this.nativePtr);
|
const res = this.emsc.funcs.amount_add(this.nativePtr, a.nativePtr, this.nativePtr);
|
||||||
if (res < 1) {
|
if (res < 1) {
|
||||||
// Overflow
|
// Overflow
|
||||||
return false;
|
return false;
|
||||||
@ -484,7 +453,7 @@ export class Amount extends MallocArenaObject {
|
|||||||
*/
|
*/
|
||||||
sub(a: Amount) {
|
sub(a: Amount) {
|
||||||
// this = this - a
|
// this = this - a
|
||||||
const res = emsc().funcs.amount_subtract(this.nativePtr, this.nativePtr, a.nativePtr);
|
const res = this.emsc.funcs.amount_subtract(this.nativePtr, this.nativePtr, a.nativePtr);
|
||||||
if (res === 0) {
|
if (res === 0) {
|
||||||
// Underflow
|
// Underflow
|
||||||
return false;
|
return false;
|
||||||
@ -500,11 +469,11 @@ export class Amount extends MallocArenaObject {
|
|||||||
if (this.currency !== a.currency) {
|
if (this.currency !== a.currency) {
|
||||||
throw Error(`incomparable currencies (${this.currency} and ${a.currency})`);
|
throw Error(`incomparable currencies (${this.currency} and ${a.currency})`);
|
||||||
}
|
}
|
||||||
return emsc().funcs.amount_cmp(this.nativePtr, a.nativePtr);
|
return this.emsc.funcs.amount_cmp(this.nativePtr, a.nativePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
normalize() {
|
normalize() {
|
||||||
emsc().funcs.amount_normalize(this.nativePtr);
|
this.emsc.funcs.amount_normalize(this.nativePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,18 +510,18 @@ function countUtf8Bytes(str: string): number {
|
|||||||
abstract class PackedArenaObject extends MallocArenaObject {
|
abstract class PackedArenaObject extends MallocArenaObject {
|
||||||
abstract size(): number;
|
abstract size(): number;
|
||||||
|
|
||||||
constructor(a?: Arena) {
|
constructor(emsc: EmscEnvironment, a?: Arena) {
|
||||||
super(a);
|
super(emsc, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
randomize(qual: RandomQuality = RandomQuality.STRONG): void {
|
randomize(qual: RandomQuality = RandomQuality.STRONG): void {
|
||||||
emsc().funcs.random_block(qual, this.nativePtr, this.size());
|
this.emsc.funcs.random_block(qual, this.nativePtr, this.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
toCrock(): string {
|
toCrock(): string {
|
||||||
const d = emsc().allocFuncs.data_to_string_alloc(this.nativePtr, this.size());
|
const d = this.emsc.allocFuncs.data_to_string_alloc(this.nativePtr, this.size());
|
||||||
const s = emsc().lib.Pointer_stringify(d);
|
const s = this.emsc.lib.Pointer_stringify(d);
|
||||||
emsc().funcs.free(d);
|
this.emsc.funcs.free(d);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,8 +536,8 @@ abstract class PackedArenaObject extends MallocArenaObject {
|
|||||||
this.alloc();
|
this.alloc();
|
||||||
// We need to get the javascript string
|
// We need to get the javascript string
|
||||||
// to the emscripten heap first.
|
// to the emscripten heap first.
|
||||||
const buf = ByteArray.fromStringWithNull(s);
|
const buf = ByteArray.fromStringWithNull(this.emsc, s);
|
||||||
const res = emsc().funcs.string_to_data(buf.nativePtr,
|
const res = this.emsc.funcs.string_to_data(buf.nativePtr,
|
||||||
s.length,
|
s.length,
|
||||||
this.nativePtr,
|
this.nativePtr,
|
||||||
this.size());
|
this.size());
|
||||||
@ -581,21 +550,21 @@ abstract class PackedArenaObject extends MallocArenaObject {
|
|||||||
alloc() {
|
alloc() {
|
||||||
// FIXME: should the client be allowed to call alloc multiple times?
|
// FIXME: should the client be allowed to call alloc multiple times?
|
||||||
if (!this._nativePtr) {
|
if (!this._nativePtr) {
|
||||||
this.nativePtr = emsc().allocFuncs.malloc(this.size());
|
this.nativePtr = this.emsc.allocFuncs.malloc(this.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hash(): HashCode {
|
hash(): HashCode {
|
||||||
const x = new HashCode();
|
const x = new HashCode(this.emsc);
|
||||||
x.alloc();
|
x.alloc();
|
||||||
emsc().funcs.hash(this.nativePtr, this.size(), x.nativePtr);
|
this.emsc.funcs.hash(this.nativePtr, this.size(), x.nativePtr);
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
hexdump() {
|
hexdump() {
|
||||||
const bytes: string[] = [];
|
const bytes: string[] = [];
|
||||||
for (let i = 0; i < this.size(); i++) {
|
for (let i = 0; i < this.size(); i++) {
|
||||||
let b = emsc().lib.getValue(this.nativePtr + i, "i8");
|
let b = this.emsc.lib.getValue(this.nativePtr + i, "i8");
|
||||||
b = (b + 256) % 256;
|
b = (b + 256) % 256;
|
||||||
bytes.push("0".concat(b.toString(16)).slice(-2));
|
bytes.push("0".concat(b.toString(16)).slice(-2));
|
||||||
}
|
}
|
||||||
@ -618,7 +587,7 @@ export class AmountNbo extends PackedArenaObject {
|
|||||||
|
|
||||||
toJson(): any {
|
toJson(): any {
|
||||||
const a = new SimpleArena();
|
const a = new SimpleArena();
|
||||||
const am = new Amount(undefined, a);
|
const am = new Amount(this.emsc, undefined, a);
|
||||||
am.fromNbo(this);
|
am.fromNbo(this);
|
||||||
const json = am.toJson();
|
const json = am.toJson();
|
||||||
a.destroy();
|
a.destroy();
|
||||||
@ -630,8 +599,8 @@ export class AmountNbo extends PackedArenaObject {
|
|||||||
/**
|
/**
|
||||||
* Create a packed arena object from the base32 crockford encoding.
|
* Create a packed arena object from the base32 crockford encoding.
|
||||||
*/
|
*/
|
||||||
function fromCrock<T extends PackedArenaObject>(s: string, ctor: Ctor<T>): T {
|
function fromCrock<T extends PackedArenaObject>(emsc: EmscEnvironment, s: string, ctor: Ctor<T>): T {
|
||||||
const x: T = new ctor();
|
const x: T = new ctor(emsc);
|
||||||
x.alloc();
|
x.alloc();
|
||||||
x.loadCrock(s);
|
x.loadCrock(s);
|
||||||
return x;
|
return x;
|
||||||
@ -642,11 +611,11 @@ function fromCrock<T extends PackedArenaObject>(s: string, ctor: Ctor<T>): T {
|
|||||||
* Create a packed arena object from the base32 crockford encoding for objects
|
* Create a packed arena object from the base32 crockford encoding for objects
|
||||||
* that have a special decoding function.
|
* that have a special decoding function.
|
||||||
*/
|
*/
|
||||||
function fromCrockDecoded<T extends MallocArenaObject>(s: string,
|
function fromCrockDecoded<T extends MallocArenaObject>(emsc: EmscEnvironment, s: string,
|
||||||
ctor: Ctor<T>,
|
ctor: Ctor<T>,
|
||||||
decodeFn: (p: number, s: number) => number): T {
|
decodeFn: (p: number, s: number) => number): T {
|
||||||
const obj = new ctor();
|
const obj = new ctor(emsc);
|
||||||
const buf = ByteArray.fromCrock(s);
|
const buf = ByteArray.fromCrock(emsc, s);
|
||||||
obj.nativePtr = decodeFn(buf.nativePtr, buf.size());
|
obj.nativePtr = decodeFn(buf.nativePtr, buf.size());
|
||||||
buf.destroy();
|
buf.destroy();
|
||||||
return obj;
|
return obj;
|
||||||
@ -657,11 +626,11 @@ function fromCrockDecoded<T extends MallocArenaObject>(s: string,
|
|||||||
* Encode an object using a special encoding function.
|
* Encode an object using a special encoding function.
|
||||||
*/
|
*/
|
||||||
function encode<T extends MallocArenaObject>(obj: T, encodeFn: any, arena?: Arena): ByteArray {
|
function encode<T extends MallocArenaObject>(obj: T, encodeFn: any, arena?: Arena): ByteArray {
|
||||||
const ptr = emsc().allocFuncs.malloc(PTR_SIZE);
|
const ptr = obj.emsc.allocFuncs.malloc(PTR_SIZE);
|
||||||
const len = encodeFn(obj.nativePtr, ptr);
|
const len = encodeFn(obj.nativePtr, ptr);
|
||||||
const res = new ByteArray(len, undefined, arena);
|
const res = new ByteArray(obj.emsc, len, undefined, arena);
|
||||||
res.nativePtr = emsc().lib.getValue(ptr, "*");
|
res.nativePtr = obj.emsc.lib.getValue(ptr, "*");
|
||||||
emsc().funcs.free(ptr);
|
obj.emsc.funcs.free(ptr);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,9 +639,9 @@ function encode<T extends MallocArenaObject>(obj: T, encodeFn: any, arena?: Aren
|
|||||||
* Private EdDSA key.
|
* Private EdDSA key.
|
||||||
*/
|
*/
|
||||||
export class EddsaPrivateKey extends PackedArenaObject {
|
export class EddsaPrivateKey extends PackedArenaObject {
|
||||||
static create(a?: Arena): EddsaPrivateKey {
|
static create(emsc: EmscEnvironment, a?: Arena): EddsaPrivateKey {
|
||||||
const obj = new EddsaPrivateKey(a);
|
const obj = new EddsaPrivateKey(emsc, a);
|
||||||
obj.nativePtr = emsc().allocFuncs.eddsa_key_create();
|
obj.nativePtr = emsc.allocFuncs.eddsa_key_create();
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,13 +650,13 @@ export class EddsaPrivateKey extends PackedArenaObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPublicKey(a?: Arena): EddsaPublicKey {
|
getPublicKey(a?: Arena): EddsaPublicKey {
|
||||||
const obj = new EddsaPublicKey(a);
|
const obj = new EddsaPublicKey(this.emsc, a);
|
||||||
obj.nativePtr = emsc().allocFuncs.eddsa_public_key_from_private(this.nativePtr);
|
obj.nativePtr = this.emsc.allocFuncs.eddsa_public_key_from_private(this.nativePtr);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock(s: string): EddsaPrivateKey {
|
static fromCrock(emsc: EmscEnvironment, s: string): EddsaPrivateKey {
|
||||||
return fromCrock(s, this);
|
return fromCrock(emsc, s, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,9 +665,9 @@ export class EddsaPrivateKey extends PackedArenaObject {
|
|||||||
* Low-level handle to an EdDSA private key.
|
* Low-level handle to an EdDSA private key.
|
||||||
*/
|
*/
|
||||||
export class EcdsaPrivateKey extends PackedArenaObject {
|
export class EcdsaPrivateKey extends PackedArenaObject {
|
||||||
static create(a?: Arena): EcdsaPrivateKey {
|
static create(emsc: EmscEnvironment, a?: Arena): EcdsaPrivateKey {
|
||||||
const obj = new EcdsaPrivateKey(a);
|
const obj = new EcdsaPrivateKey(emsc, a);
|
||||||
obj.nativePtr = emsc().allocFuncs.ecdsa_key_create();
|
obj.nativePtr = emsc.allocFuncs.ecdsa_key_create();
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,13 +676,13 @@ export class EcdsaPrivateKey extends PackedArenaObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPublicKey(a?: Arena): EcdsaPublicKey {
|
getPublicKey(a?: Arena): EcdsaPublicKey {
|
||||||
const obj = new EcdsaPublicKey(a);
|
const obj = new EcdsaPublicKey(this.emsc, a);
|
||||||
obj.nativePtr = emsc().allocFuncs.ecdsa_public_key_from_private(this.nativePtr);
|
obj.nativePtr = this.emsc.allocFuncs.ecdsa_public_key_from_private(this.nativePtr);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock(s: string): EcdsaPrivateKey {
|
static fromCrock(emsc: EmscEnvironment, s: string): EcdsaPrivateKey {
|
||||||
return fromCrock(s, this);
|
return fromCrock(emsc, s, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -722,9 +691,9 @@ export class EcdsaPrivateKey extends PackedArenaObject {
|
|||||||
* Low-level handle to an ECDHE private key.
|
* Low-level handle to an ECDHE private key.
|
||||||
*/
|
*/
|
||||||
export class EcdhePrivateKey extends PackedArenaObject {
|
export class EcdhePrivateKey extends PackedArenaObject {
|
||||||
static create(a?: Arena): EcdhePrivateKey {
|
static create(emsc: EmscEnvironment, a?: Arena): EcdhePrivateKey {
|
||||||
const obj = new EcdhePrivateKey(a);
|
const obj = new EcdhePrivateKey(emsc, a);
|
||||||
obj.nativePtr = emsc().allocFuncs.ecdhe_key_create();
|
obj.nativePtr = emsc.allocFuncs.ecdhe_key_create();
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,13 +702,13 @@ export class EcdhePrivateKey extends PackedArenaObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPublicKey(a?: Arena): EcdhePublicKey {
|
getPublicKey(a?: Arena): EcdhePublicKey {
|
||||||
const obj = new EcdhePublicKey(a);
|
const obj = new EcdhePublicKey(this.emsc, a);
|
||||||
obj.nativePtr = emsc().allocFuncs.ecdhe_public_key_from_private(this.nativePtr);
|
obj.nativePtr = this.emsc.allocFuncs.ecdhe_public_key_from_private(this.nativePtr);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock(s: string): EcdhePrivateKey {
|
static fromCrock(emsc: EmscEnvironment, s: string): EcdhePrivateKey {
|
||||||
return fromCrock(s, this);
|
return fromCrock(emsc, s, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -748,7 +717,7 @@ export class EcdhePrivateKey extends PackedArenaObject {
|
|||||||
* Constructor for a given type.
|
* Constructor for a given type.
|
||||||
*/
|
*/
|
||||||
interface Ctor<T> {
|
interface Ctor<T> {
|
||||||
new(): T;
|
new(emsc: EmscEnvironment): T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -760,8 +729,8 @@ export class EddsaPublicKey extends PackedArenaObject {
|
|||||||
return 32;
|
return 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock(s: string): EddsaPublicKey {
|
static fromCrock(emsc: EmscEnvironment, s: string): EddsaPublicKey {
|
||||||
return fromCrock(s, this);
|
return fromCrock(emsc, s, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,8 +742,8 @@ export class EcdsaPublicKey extends PackedArenaObject {
|
|||||||
return 32;
|
return 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock(s: string): EcdsaPublicKey {
|
static fromCrock(emsc: EmscEnvironment, s: string): EcdsaPublicKey {
|
||||||
return fromCrock(s, this);
|
return fromCrock(emsc, s, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -787,8 +756,8 @@ export class EcdhePublicKey extends PackedArenaObject {
|
|||||||
return 32;
|
return 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock(s: string): EcdhePublicKey {
|
static fromCrock(emsc: EmscEnvironment, s: string): EcdhePublicKey {
|
||||||
return fromCrock(s, this);
|
return fromCrock(emsc, s, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,15 +773,15 @@ export class RsaBlindingKeySecret extends PackedArenaObject {
|
|||||||
/**
|
/**
|
||||||
* Create a random blinding key secret.
|
* Create a random blinding key secret.
|
||||||
*/
|
*/
|
||||||
static create(a?: Arena): RsaBlindingKeySecret {
|
static create(emsc: EmscEnvironment, a?: Arena): RsaBlindingKeySecret {
|
||||||
const o = new RsaBlindingKeySecret(a);
|
const o = new RsaBlindingKeySecret(emsc, a);
|
||||||
o.alloc();
|
o.alloc();
|
||||||
o.randomize();
|
o.randomize();
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock(s: string): RsaBlindingKeySecret {
|
static fromCrock(emsc: EmscEnvironment, s: string): RsaBlindingKeySecret {
|
||||||
return fromCrock(s, this);
|
return fromCrock(emsc, s, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -825,13 +794,13 @@ export class HashCode extends PackedArenaObject {
|
|||||||
return 64;
|
return 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock(s: string): HashCode {
|
static fromCrock(emsc: EmscEnvironment, s: string): HashCode {
|
||||||
return fromCrock(s, this);
|
return fromCrock(emsc, s, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
random(qual: RandomQuality = RandomQuality.STRONG) {
|
random(qual: RandomQuality = RandomQuality.STRONG) {
|
||||||
this.alloc();
|
this.alloc();
|
||||||
emsc().funcs.hash_create_random(qual, this.nativePtr);
|
this.emsc.funcs.hash_create_random(qual, this.nativePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,42 +815,42 @@ export class ByteArray extends PackedArenaObject {
|
|||||||
return this.allocatedSize;
|
return this.allocatedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(desiredSize: number, init?: number, a?: Arena) {
|
constructor(public emsc: EmscEnvironment, desiredSize: number, init?: number, a?: Arena) {
|
||||||
super(a);
|
super(emsc, a);
|
||||||
if (init === undefined) {
|
if (init === undefined) {
|
||||||
this.nativePtr = emsc().allocFuncs.malloc(desiredSize);
|
this.nativePtr = this.emsc.allocFuncs.malloc(desiredSize);
|
||||||
} else {
|
} else {
|
||||||
this.nativePtr = init;
|
this.nativePtr = init;
|
||||||
}
|
}
|
||||||
this.allocatedSize = desiredSize;
|
this.allocatedSize = desiredSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromStringWithoutNull(s: string, a?: Arena): ByteArray {
|
static fromStringWithoutNull(emsc: EmscEnvironment, s: string, a?: Arena): ByteArray {
|
||||||
// UTF-8 bytes, including 0-terminator
|
// UTF-8 bytes, including 0-terminator
|
||||||
const terminatedByteLength = countUtf8Bytes(s) + 1;
|
const terminatedByteLength = countUtf8Bytes(s) + 1;
|
||||||
const hstr = emsc().allocFuncs.malloc(terminatedByteLength);
|
const hstr = emsc.allocFuncs.malloc(terminatedByteLength);
|
||||||
emsc().lib.stringToUTF8(s, hstr, terminatedByteLength);
|
emsc.lib.stringToUTF8(s, hstr, terminatedByteLength);
|
||||||
return new ByteArray(terminatedByteLength - 1, hstr, a);
|
return new ByteArray(emsc, terminatedByteLength - 1, hstr, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromStringWithNull(s: string, a?: Arena): ByteArray {
|
static fromStringWithNull(emsc: EmscEnvironment, s: string, a?: Arena): ByteArray {
|
||||||
// UTF-8 bytes, including 0-terminator
|
// UTF-8 bytes, including 0-terminator
|
||||||
const terminatedByteLength = countUtf8Bytes(s) + 1;
|
const terminatedByteLength = countUtf8Bytes(s) + 1;
|
||||||
const hstr = emsc().allocFuncs.malloc(terminatedByteLength);
|
const hstr = emsc.allocFuncs.malloc(terminatedByteLength);
|
||||||
emsc().lib.stringToUTF8(s, hstr, terminatedByteLength);
|
emsc.lib.stringToUTF8(s, hstr, terminatedByteLength);
|
||||||
return new ByteArray(terminatedByteLength, hstr, a);
|
return new ByteArray(emsc, terminatedByteLength, hstr, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock(s: string, a?: Arena): ByteArray {
|
static fromCrock(emsc: EmscEnvironment, s: string, a?: Arena): ByteArray {
|
||||||
// this one is a bit more complicated than the other fromCrock functions,
|
// this one is a bit more complicated than the other fromCrock functions,
|
||||||
// since we don't have a fixed size
|
// since we don't have a fixed size
|
||||||
const byteLength = countUtf8Bytes(s);
|
const byteLength = countUtf8Bytes(s);
|
||||||
const hstr = emsc().allocFuncs.malloc(byteLength + 1);
|
const hstr = emsc.allocFuncs.malloc(byteLength + 1);
|
||||||
emsc().lib.stringToUTF8(s, hstr, byteLength + 1);
|
emsc.lib.stringToUTF8(s, hstr, byteLength + 1);
|
||||||
const decodedLen = Math.floor((byteLength * 5) / 8);
|
const decodedLen = Math.floor((byteLength * 5) / 8);
|
||||||
const ba = new ByteArray(decodedLen, undefined, a);
|
const ba = new ByteArray(emsc, decodedLen, undefined, a);
|
||||||
const res = emsc().funcs.string_to_data(hstr, byteLength, ba.nativePtr, decodedLen);
|
const res = emsc.funcs.string_to_data(hstr, byteLength, ba.nativePtr, decodedLen);
|
||||||
emsc().funcs.free(hstr);
|
emsc.funcs.free(hstr);
|
||||||
if (res !== GNUNET_OK) {
|
if (res !== GNUNET_OK) {
|
||||||
throw Error("decoding failed");
|
throw Error("decoding failed");
|
||||||
}
|
}
|
||||||
@ -901,11 +870,12 @@ export class EccSignaturePurpose extends PackedArenaObject {
|
|||||||
|
|
||||||
private payloadSize: number;
|
private payloadSize: number;
|
||||||
|
|
||||||
constructor(purpose: SignaturePurpose,
|
constructor(emsc: EmscEnvironment,
|
||||||
|
purpose: SignaturePurpose,
|
||||||
payload: PackedArenaObject,
|
payload: PackedArenaObject,
|
||||||
a?: Arena) {
|
a?: Arena) {
|
||||||
super(a);
|
super(emsc, a);
|
||||||
this.nativePtr = emsc().allocFuncs.purpose_create(purpose,
|
this.nativePtr = emsc.allocFuncs.purpose_create(purpose,
|
||||||
payload.nativePtr,
|
payload.nativePtr,
|
||||||
payload.size());
|
payload.size());
|
||||||
this.payloadSize = payload.size();
|
this.payloadSize = payload.size();
|
||||||
@ -920,7 +890,7 @@ abstract class SignatureStruct {
|
|||||||
|
|
||||||
private members: any = {};
|
private members: any = {};
|
||||||
|
|
||||||
constructor(x: { [name: string]: any }) {
|
constructor(public emsc: EmscEnvironment, x: { [name: string]: any }) {
|
||||||
for (const k in x) {
|
for (const k in x) {
|
||||||
this.set(k, x[k]);
|
this.set(k, x[k]);
|
||||||
}
|
}
|
||||||
@ -937,17 +907,17 @@ abstract class SignatureStruct {
|
|||||||
totalSize += member.size();
|
totalSize += member.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const buf = emsc().allocFuncs.malloc(totalSize);
|
const buf = this.emsc.allocFuncs.malloc(totalSize);
|
||||||
let ptr = buf;
|
let ptr = buf;
|
||||||
for (const f of this.fieldTypes()) {
|
for (const f of this.fieldTypes()) {
|
||||||
const name = f[0];
|
const name = f[0];
|
||||||
const member = this.members[name];
|
const member = this.members[name];
|
||||||
const size = member.size();
|
const size = member.size();
|
||||||
emsc().funcs.memmove(ptr, member.nativePtr, size);
|
this.emsc.funcs.memmove(ptr, member.nativePtr, size);
|
||||||
ptr += size;
|
ptr += size;
|
||||||
}
|
}
|
||||||
const ba = new ByteArray(totalSize, buf, a);
|
const ba = new ByteArray(this.emsc, totalSize, buf, a);
|
||||||
return new EccSignaturePurpose(this.purpose(), ba);
|
return new EccSignaturePurpose(this.emsc, this.purpose(), ba);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1012,8 +982,8 @@ export interface WithdrawRequestPS_Args {
|
|||||||
* Low-level handle to a WithdrawRequest signature structure.
|
* Low-level handle to a WithdrawRequest signature structure.
|
||||||
*/
|
*/
|
||||||
export class WithdrawRequestPS extends SignatureStruct {
|
export class WithdrawRequestPS extends SignatureStruct {
|
||||||
constructor(w: WithdrawRequestPS_Args) {
|
constructor(emsc: EmscEnvironment, w: WithdrawRequestPS_Args) {
|
||||||
super(w);
|
super(emsc, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
purpose() {
|
purpose() {
|
||||||
@ -1046,8 +1016,8 @@ export interface PaybackRequestPS_args {
|
|||||||
* Low-level handle to a PaybackRequest signature structure.
|
* Low-level handle to a PaybackRequest signature structure.
|
||||||
*/
|
*/
|
||||||
export class PaybackRequestPS extends SignatureStruct {
|
export class PaybackRequestPS extends SignatureStruct {
|
||||||
constructor(w: PaybackRequestPS_args) {
|
constructor(emsc: EmscEnvironment, w: PaybackRequestPS_args) {
|
||||||
super(w);
|
super(emsc, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
purpose() {
|
purpose() {
|
||||||
@ -1078,8 +1048,8 @@ interface RefreshMeltCoinAffirmationPS_Args {
|
|||||||
* Low-level handle to a RefreshMeltCoinAffirmationPS signature structure.
|
* Low-level handle to a RefreshMeltCoinAffirmationPS signature structure.
|
||||||
*/
|
*/
|
||||||
export class RefreshMeltCoinAffirmationPS extends SignatureStruct {
|
export class RefreshMeltCoinAffirmationPS extends SignatureStruct {
|
||||||
constructor(w: RefreshMeltCoinAffirmationPS_Args) {
|
constructor(emsc: EmscEnvironment, w: RefreshMeltCoinAffirmationPS_Args) {
|
||||||
super(w);
|
super(emsc, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
purpose() {
|
purpose() {
|
||||||
@ -1128,8 +1098,8 @@ interface MasterWireFeePS_Args {
|
|||||||
* Low-level handle to a structure being signed over.
|
* Low-level handle to a structure being signed over.
|
||||||
*/
|
*/
|
||||||
export class MasterWireFeePS extends SignatureStruct {
|
export class MasterWireFeePS extends SignatureStruct {
|
||||||
constructor(w: MasterWireFeePS_Args) {
|
constructor(emsc: EmscEnvironment, w: MasterWireFeePS_Args) {
|
||||||
super(w);
|
super(emsc, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
purpose() {
|
purpose() {
|
||||||
@ -1152,8 +1122,8 @@ export class MasterWireFeePS extends SignatureStruct {
|
|||||||
* Low-level handle to an absolute time in network byte order (NBO).
|
* Low-level handle to an absolute time in network byte order (NBO).
|
||||||
*/
|
*/
|
||||||
export class AbsoluteTimeNbo extends PackedArenaObject {
|
export class AbsoluteTimeNbo extends PackedArenaObject {
|
||||||
static fromTalerString(s: string): AbsoluteTimeNbo {
|
static fromTalerString(emsc: EmscEnvironment, s: string): AbsoluteTimeNbo {
|
||||||
const x = new AbsoluteTimeNbo();
|
const x = new AbsoluteTimeNbo(emsc);
|
||||||
x.alloc();
|
x.alloc();
|
||||||
const r = /Date\(([0-9]+)\)/;
|
const r = /Date\(([0-9]+)\)/;
|
||||||
const m = r.exec(s);
|
const m = r.exec(s);
|
||||||
@ -1162,15 +1132,15 @@ export class AbsoluteTimeNbo extends PackedArenaObject {
|
|||||||
}
|
}
|
||||||
const n = parseInt(m[1], 10) * 1000000;
|
const n = parseInt(m[1], 10) * 1000000;
|
||||||
// XXX: This only works up to 54 bit numbers.
|
// XXX: This only works up to 54 bit numbers.
|
||||||
set64(x.nativePtr, n);
|
set64(emsc, x.nativePtr, n);
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromStampSeconds(stamp: number): AbsoluteTimeNbo {
|
static fromStampSeconds(emsc: EmscEnvironment, stamp: number): AbsoluteTimeNbo {
|
||||||
const x = new AbsoluteTimeNbo();
|
const x = new AbsoluteTimeNbo(emsc);
|
||||||
x.alloc();
|
x.alloc();
|
||||||
// XXX: This only works up to 54 bit numbers.
|
// XXX: This only works up to 54 bit numbers.
|
||||||
set64(x.nativePtr, stamp * 1000000);
|
set64(emsc, x.nativePtr, stamp * 1000000);
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1182,17 +1152,17 @@ export class AbsoluteTimeNbo extends PackedArenaObject {
|
|||||||
|
|
||||||
|
|
||||||
// XXX: This only works up to 54 bit numbers.
|
// XXX: This only works up to 54 bit numbers.
|
||||||
function set64(p: number, n: number) {
|
function set64(emsc: EmscEnvironment, p: number, n: number) {
|
||||||
for (let i = 0; i < 8; ++i) {
|
for (let i = 0; i < 8; ++i) {
|
||||||
emsc().lib.setValue(p + (7 - i), n & 0xFF, "i8");
|
emsc.lib.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.
|
// XXX: This only works up to 54 bit numbers.
|
||||||
function set32(p: number, n: number) {
|
function set32(emsc: EmscEnvironment, p: number, n: number) {
|
||||||
for (let i = 0; i < 4; ++i) {
|
for (let i = 0; i < 4; ++i) {
|
||||||
emsc().lib.setValue(p + (3 - i), n & 0xFF, "i8");
|
emsc.lib.setValue(p + (3 - i), n & 0xFF, "i8");
|
||||||
n = Math.floor(n / 256);
|
n = Math.floor(n / 256);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1202,10 +1172,10 @@ function set32(p: number, n: number) {
|
|||||||
* Low-level handle to an unsigned 64-bit value.
|
* Low-level handle to an unsigned 64-bit value.
|
||||||
*/
|
*/
|
||||||
export class UInt64 extends PackedArenaObject {
|
export class UInt64 extends PackedArenaObject {
|
||||||
static fromNumber(n: number): UInt64 {
|
static fromNumber(emsc: EmscEnvironment, n: number): UInt64 {
|
||||||
const x = new UInt64();
|
const x = new UInt64(emsc);
|
||||||
x.alloc();
|
x.alloc();
|
||||||
set64(x.nativePtr, n);
|
set64(emsc, x.nativePtr, n);
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1219,10 +1189,10 @@ export class UInt64 extends PackedArenaObject {
|
|||||||
* Low-level handle to an unsigned 32-bit value.
|
* Low-level handle to an unsigned 32-bit value.
|
||||||
*/
|
*/
|
||||||
export class UInt32 extends PackedArenaObject {
|
export class UInt32 extends PackedArenaObject {
|
||||||
static fromNumber(n: number): UInt32 {
|
static fromNumber(emsc: EmscEnvironment, n: number): UInt32 {
|
||||||
const x = new UInt32();
|
const x = new UInt32(emsc);
|
||||||
x.alloc();
|
x.alloc();
|
||||||
set32(x.nativePtr, n);
|
set32(emsc, x.nativePtr, n);
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1275,8 +1245,8 @@ export interface DepositRequestPS_Args {
|
|||||||
* Low-level handle to a struct being signed over.
|
* Low-level handle to a struct being signed over.
|
||||||
*/
|
*/
|
||||||
export class DepositRequestPS extends SignatureStruct {
|
export class DepositRequestPS extends SignatureStruct {
|
||||||
constructor(w: DepositRequestPS_Args) {
|
constructor(emsc: EmscEnvironment, w: DepositRequestPS_Args) {
|
||||||
super(w);
|
super(emsc, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
purpose() {
|
purpose() {
|
||||||
@ -1307,8 +1277,8 @@ interface CoinLinkSignaturePS_args {
|
|||||||
|
|
||||||
|
|
||||||
export class CoinLinkSignaturePS extends SignatureStruct {
|
export class CoinLinkSignaturePS extends SignatureStruct {
|
||||||
constructor(w: CoinLinkSignaturePS_args) {
|
constructor(emsc: EmscEnvironment, w: CoinLinkSignaturePS_args) {
|
||||||
super(w);
|
super(emsc, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
purpose() {
|
purpose() {
|
||||||
@ -1348,8 +1318,8 @@ export interface DenominationKeyValidityPS_args {
|
|||||||
* Low-level handle to a structure being signed over.
|
* Low-level handle to a structure being signed over.
|
||||||
*/
|
*/
|
||||||
export class DenominationKeyValidityPS extends SignatureStruct {
|
export class DenominationKeyValidityPS extends SignatureStruct {
|
||||||
constructor(w: DenominationKeyValidityPS_args) {
|
constructor(emsc: EmscEnvironment, w: DenominationKeyValidityPS_args) {
|
||||||
super(w);
|
super(emsc, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
purpose() {
|
purpose() {
|
||||||
@ -1388,8 +1358,8 @@ export interface PaymentSignaturePS_args {
|
|||||||
* Low-level handle to a structure being signed over.
|
* Low-level handle to a structure being signed over.
|
||||||
*/
|
*/
|
||||||
export class PaymentSignaturePS extends SignatureStruct {
|
export class PaymentSignaturePS extends SignatureStruct {
|
||||||
constructor(w: PaymentSignaturePS_args) {
|
constructor(emsc: EmscEnvironment, w: PaymentSignaturePS_args) {
|
||||||
super(w);
|
super(emsc, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
purpose() {
|
purpose() {
|
||||||
@ -1408,13 +1378,13 @@ export class PaymentSignaturePS extends SignatureStruct {
|
|||||||
* Low-level handle to an RsaPrivateKey.
|
* Low-level handle to an RsaPrivateKey.
|
||||||
*/
|
*/
|
||||||
export class RsaPrivateKey extends MallocArenaObject {
|
export class RsaPrivateKey extends MallocArenaObject {
|
||||||
static fromCrock(s: string): RsaPrivateKey {
|
static fromCrock(emsc: EmscEnvironment, s: string): RsaPrivateKey {
|
||||||
return fromCrockDecoded(s, this, emsc().allocFuncs.rsa_private_key_decode);
|
return fromCrockDecoded(emsc, s, this, emsc.allocFuncs.rsa_private_key_decode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(bitLen: number, a?: Arena): RsaPrivateKey {
|
static create(emsc: EmscEnvironment, bitLen: number, a?: Arena): RsaPrivateKey {
|
||||||
const obj = new RsaPrivateKey(a);
|
const obj = new RsaPrivateKey(emsc, a);
|
||||||
obj.nativePtr = emsc().allocFuncs.rsa_private_key_create(bitLen);
|
obj.nativePtr = emsc.allocFuncs.rsa_private_key_create(bitLen);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1424,18 +1394,18 @@ export class RsaPrivateKey extends MallocArenaObject {
|
|||||||
|
|
||||||
|
|
||||||
getPublicKey(a?: Arena): RsaPublicKey {
|
getPublicKey(a?: Arena): RsaPublicKey {
|
||||||
const obj = new RsaPublicKey(a);
|
const obj = new RsaPublicKey(this.emsc, a);
|
||||||
obj.nativePtr = emsc().allocFuncs.rsa_private_key_get_public(this.nativePtr);
|
obj.nativePtr = this.emsc.allocFuncs.rsa_private_key_get_public(this.nativePtr);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
emsc().funcs.rsa_public_key_free(this.nativePtr);
|
this.emsc.funcs.rsa_public_key_free(this.nativePtr);
|
||||||
this.nativePtr = 0;
|
this.nativePtr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
encode(arena?: Arena): ByteArray {
|
encode(arena?: Arena): ByteArray {
|
||||||
return encode(this, emsc().allocFuncs.rsa_private_key_encode);
|
return encode(this, this.emsc.allocFuncs.rsa_private_key_encode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1444,8 +1414,8 @@ export class RsaPrivateKey extends MallocArenaObject {
|
|||||||
* Low-level handle to an RsaPublicKey.
|
* Low-level handle to an RsaPublicKey.
|
||||||
*/
|
*/
|
||||||
export class RsaPublicKey extends MallocArenaObject {
|
export class RsaPublicKey extends MallocArenaObject {
|
||||||
static fromCrock(s: string): RsaPublicKey {
|
static fromCrock(emsc: EmscEnvironment, s: string): RsaPublicKey {
|
||||||
return fromCrockDecoded(s, this, emsc().allocFuncs.rsa_public_key_decode);
|
return fromCrockDecoded(emsc, s, this, emsc.allocFuncs.rsa_public_key_decode);
|
||||||
}
|
}
|
||||||
|
|
||||||
toCrock() {
|
toCrock() {
|
||||||
@ -1453,12 +1423,12 @@ export class RsaPublicKey extends MallocArenaObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
emsc().funcs.rsa_public_key_free(this.nativePtr);
|
this.emsc.funcs.rsa_public_key_free(this.nativePtr);
|
||||||
this.nativePtr = 0;
|
this.nativePtr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
encode(arena?: Arena): ByteArray {
|
encode(arena?: Arena): ByteArray {
|
||||||
return encode(this, emsc().allocFuncs.rsa_public_key_encode);
|
return encode(this, this.emsc.allocFuncs.rsa_public_key_encode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1470,8 +1440,8 @@ export class EddsaSignature extends PackedArenaObject {
|
|||||||
size() {
|
size() {
|
||||||
return 64;
|
return 64;
|
||||||
}
|
}
|
||||||
static fromCrock(s: string): EddsaSignature {
|
static fromCrock(emsc: EmscEnvironment, s: string): EddsaSignature {
|
||||||
return fromCrock(s, this);
|
return fromCrock(emsc, s, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1480,16 +1450,16 @@ export class EddsaSignature extends PackedArenaObject {
|
|||||||
* Low-level handle to an RsaSignature.
|
* Low-level handle to an RsaSignature.
|
||||||
*/
|
*/
|
||||||
export class RsaSignature extends MallocArenaObject {
|
export class RsaSignature extends MallocArenaObject {
|
||||||
static fromCrock(s: string, a?: Arena) {
|
static fromCrock(emsc: EmscEnvironment, s: string, a?: Arena) {
|
||||||
return fromCrockDecoded(s, this, emsc().allocFuncs.rsa_signature_decode);
|
return fromCrockDecoded(emsc, s, this, emsc.allocFuncs.rsa_signature_decode);
|
||||||
}
|
}
|
||||||
|
|
||||||
encode(arena?: Arena): ByteArray {
|
encode(arena?: Arena): ByteArray {
|
||||||
return encode(this, emsc().allocFuncs.rsa_signature_encode);
|
return encode(this, this.emsc.allocFuncs.rsa_signature_encode);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
emsc().funcs.rsa_signature_free(this.nativePtr);
|
this.emsc.funcs.rsa_signature_free(this.nativePtr);
|
||||||
this.nativePtr = 0;
|
this.nativePtr = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1502,22 +1472,23 @@ export function rsaBlind(hashCode: HashCode,
|
|||||||
blindingKey: RsaBlindingKeySecret,
|
blindingKey: RsaBlindingKeySecret,
|
||||||
pkey: RsaPublicKey,
|
pkey: RsaPublicKey,
|
||||||
arena?: Arena): ByteArray|null {
|
arena?: Arena): ByteArray|null {
|
||||||
const buf_ptr_out = emsc().allocFuncs.malloc(PTR_SIZE);
|
const emsc: EmscEnvironment = hashCode.emsc;
|
||||||
const buf_size_out = emsc().allocFuncs.malloc(PTR_SIZE);
|
const buf_ptr_out = emsc.allocFuncs.malloc(PTR_SIZE);
|
||||||
const res = emsc().allocFuncs.rsa_blind(hashCode.nativePtr,
|
const buf_size_out = emsc.allocFuncs.malloc(PTR_SIZE);
|
||||||
|
const res = emsc.allocFuncs.rsa_blind(hashCode.nativePtr,
|
||||||
blindingKey.nativePtr,
|
blindingKey.nativePtr,
|
||||||
pkey.nativePtr,
|
pkey.nativePtr,
|
||||||
buf_ptr_out,
|
buf_ptr_out,
|
||||||
buf_size_out);
|
buf_size_out);
|
||||||
const buf_ptr = emsc().lib.getValue(buf_ptr_out, "*");
|
const buf_ptr = emsc.lib.getValue(buf_ptr_out, "*");
|
||||||
const buf_size = emsc().lib.getValue(buf_size_out, "*");
|
const buf_size = emsc.lib.getValue(buf_size_out, "*");
|
||||||
emsc().funcs.free(buf_ptr_out);
|
emsc.funcs.free(buf_ptr_out);
|
||||||
emsc().funcs.free(buf_size_out);
|
emsc.funcs.free(buf_size_out);
|
||||||
if (res !== GNUNET_OK) {
|
if (res !== GNUNET_OK) {
|
||||||
// malicious key
|
// malicious key
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new ByteArray(buf_size, buf_ptr, arena);
|
return new ByteArray(emsc, buf_size, buf_ptr, arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1527,9 +1498,9 @@ export function rsaBlind(hashCode: HashCode,
|
|||||||
export function eddsaSign(purpose: EccSignaturePurpose,
|
export function eddsaSign(purpose: EccSignaturePurpose,
|
||||||
priv: EddsaPrivateKey,
|
priv: EddsaPrivateKey,
|
||||||
a?: Arena): EddsaSignature {
|
a?: Arena): EddsaSignature {
|
||||||
const sig = new EddsaSignature(a);
|
const sig = new EddsaSignature(purpose.emsc, a);
|
||||||
sig.alloc();
|
sig.alloc();
|
||||||
const res = emsc().funcs.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
|
const res = purpose.emsc.funcs.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
|
||||||
if (res < 1) {
|
if (res < 1) {
|
||||||
throw Error("EdDSA signing failed");
|
throw Error("EdDSA signing failed");
|
||||||
}
|
}
|
||||||
@ -1545,7 +1516,7 @@ export function eddsaVerify(purposeNum: number,
|
|||||||
sig: EddsaSignature,
|
sig: EddsaSignature,
|
||||||
pub: EddsaPublicKey,
|
pub: EddsaPublicKey,
|
||||||
a?: Arena): boolean {
|
a?: Arena): boolean {
|
||||||
const r = emsc().funcs.eddsa_verify(purposeNum,
|
const r = verify.emsc.funcs.eddsa_verify(purposeNum,
|
||||||
verify.nativePtr,
|
verify.nativePtr,
|
||||||
sig.nativePtr,
|
sig.nativePtr,
|
||||||
pub.nativePtr);
|
pub.nativePtr);
|
||||||
@ -1556,7 +1527,7 @@ export function eddsaVerify(purposeNum: number,
|
|||||||
export function rsaVerify(h: HashCode,
|
export function rsaVerify(h: HashCode,
|
||||||
sig: RsaSignature,
|
sig: RsaSignature,
|
||||||
pub: RsaPublicKey) {
|
pub: RsaPublicKey) {
|
||||||
const r = emsc().funcs.rsa_verify(h.nativePtr,
|
const r = h.emsc.funcs.rsa_verify(h.nativePtr,
|
||||||
sig.nativePtr,
|
sig.nativePtr,
|
||||||
pub.nativePtr);
|
pub.nativePtr);
|
||||||
return r === GNUNET_OK;
|
return r === GNUNET_OK;
|
||||||
@ -1570,8 +1541,8 @@ export function rsaUnblind(sig: RsaSignature,
|
|||||||
bk: RsaBlindingKeySecret,
|
bk: RsaBlindingKeySecret,
|
||||||
pk: RsaPublicKey,
|
pk: RsaPublicKey,
|
||||||
a?: Arena): RsaSignature {
|
a?: Arena): RsaSignature {
|
||||||
const x = new RsaSignature(a);
|
const x = new RsaSignature(sig.emsc, a);
|
||||||
x.nativePtr = emsc().allocFuncs.rsa_unblind(sig.nativePtr,
|
x.nativePtr = sig.emsc.allocFuncs.rsa_unblind(sig.nativePtr,
|
||||||
bk.nativePtr,
|
bk.nativePtr,
|
||||||
pk.nativePtr);
|
pk.nativePtr);
|
||||||
return x;
|
return x;
|
||||||
@ -1600,9 +1571,9 @@ export interface FreshCoin {
|
|||||||
*/
|
*/
|
||||||
export function ecdhEddsa(priv: EcdhePrivateKey,
|
export function ecdhEddsa(priv: EcdhePrivateKey,
|
||||||
pub: EddsaPublicKey): HashCode {
|
pub: EddsaPublicKey): HashCode {
|
||||||
const h = new HashCode();
|
const h = new HashCode(priv.emsc);
|
||||||
h.alloc();
|
h.alloc();
|
||||||
const res = emsc().funcs.ecdh_eddsa(priv.nativePtr, pub.nativePtr, h.nativePtr);
|
const res = priv.emsc.funcs.ecdh_eddsa(priv.nativePtr, pub.nativePtr, h.nativePtr);
|
||||||
if (res !== GNUNET_OK) {
|
if (res !== GNUNET_OK) {
|
||||||
throw Error("ecdh_eddsa failed");
|
throw Error("ecdh_eddsa failed");
|
||||||
}
|
}
|
||||||
@ -1611,8 +1582,8 @@ export function ecdhEddsa(priv: EcdhePrivateKey,
|
|||||||
|
|
||||||
export function rsaSignBlinded(priv: RsaPrivateKey,
|
export function rsaSignBlinded(priv: RsaPrivateKey,
|
||||||
msg: ByteArray): RsaSignature {
|
msg: ByteArray): RsaSignature {
|
||||||
const sig = new RsaSignature();
|
const sig = new RsaSignature(priv.emsc);
|
||||||
sig.nativePtr = emsc().allocFuncs.rsa_sign_blinded (priv.nativePtr,
|
sig.nativePtr = priv.emsc.allocFuncs.rsa_sign_blinded (priv.nativePtr,
|
||||||
msg.nativePtr,
|
msg.nativePtr,
|
||||||
msg.size());
|
msg.size());
|
||||||
return sig;
|
return sig;
|
||||||
@ -1625,13 +1596,14 @@ export function rsaSignBlinded(priv: RsaPrivateKey,
|
|||||||
*/
|
*/
|
||||||
export function setupFreshCoin(secretSeed: TransferSecretP,
|
export function setupFreshCoin(secretSeed: TransferSecretP,
|
||||||
coinIndex: number): FreshCoin {
|
coinIndex: number): FreshCoin {
|
||||||
const priv = new EddsaPrivateKey();
|
const emsc: EmscEnvironment = secretSeed.emsc;
|
||||||
|
const priv = new EddsaPrivateKey(emsc);
|
||||||
priv.isWeak = true;
|
priv.isWeak = true;
|
||||||
const blindingKey = new RsaBlindingKeySecret();
|
const blindingKey = new RsaBlindingKeySecret(emsc);
|
||||||
blindingKey.isWeak = true;
|
blindingKey.isWeak = true;
|
||||||
const buf = new ByteArray(priv.size() + blindingKey.size());
|
const buf = new ByteArray(emsc, priv.size() + blindingKey.size());
|
||||||
|
|
||||||
emsc().funcs.setup_fresh_coin(secretSeed.nativePtr, coinIndex, buf.nativePtr);
|
emsc.funcs.setup_fresh_coin(secretSeed.nativePtr, coinIndex, buf.nativePtr);
|
||||||
|
|
||||||
priv.nativePtr = buf.nativePtr;
|
priv.nativePtr = buf.nativePtr;
|
||||||
blindingKey.nativePtr = buf.nativePtr + priv.size();
|
blindingKey.nativePtr = buf.nativePtr + priv.size();
|
||||||
|
162
src/crypto/synchronousWorker.ts
Normal file
162
src/crypto/synchronousWorker.ts
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2019 GNUnet e.V.
|
||||||
|
|
||||||
|
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 3, or (at your option) any later version.
|
||||||
|
|
||||||
|
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { EmscEnvironment } from "./emscInterface";
|
||||||
|
import { CryptoImplementation } from "./cryptoImplementation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Worker implementation that uses node subprocesses.
|
||||||
|
*/
|
||||||
|
export class SynchronousCryptoWorker {
|
||||||
|
private cachedEmscEnvironment: EmscEnvironment | undefined = undefined;
|
||||||
|
private cachedEmscEnvironmentPromise: Promise<EmscEnvironment> | undefined = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to be called when we receive a message from the worker thread.
|
||||||
|
*/
|
||||||
|
onmessage: undefined | ((m: any) => void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to be called when we receive an error from the worker thread.
|
||||||
|
*/
|
||||||
|
onerror: undefined | ((m: any) => void);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.onerror = undefined;
|
||||||
|
this.onmessage = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an event listener for either an "error" or "message" event.
|
||||||
|
*/
|
||||||
|
addEventListener(event: "message" | "error", fn: (x: any) => void): void {
|
||||||
|
switch (event) {
|
||||||
|
case "message":
|
||||||
|
this.onmessage = fn;
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
this.onerror = fn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getEmscriptenEnvironment(): Promise<EmscEnvironment> {
|
||||||
|
if (this.cachedEmscEnvironment) {
|
||||||
|
return Promise.resolve(this.cachedEmscEnvironment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cachedEmscEnvironmentPromise) {
|
||||||
|
return this.cachedEmscEnvironmentPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that TypeScript doesn't try
|
||||||
|
// to check the taler-emscripten-lib.
|
||||||
|
const indirectRequire = require;
|
||||||
|
|
||||||
|
const g = global;
|
||||||
|
|
||||||
|
// unavoidable hack, so that emscripten detects
|
||||||
|
// the environment as node even though importScripts
|
||||||
|
// is present.
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const savedImportScripts = g.importScripts;
|
||||||
|
// @ts-ignore
|
||||||
|
delete g.importScripts;
|
||||||
|
|
||||||
|
// Assume that the code is run from the build/ directory.
|
||||||
|
const libFn = indirectRequire(
|
||||||
|
"../../../emscripten/taler-emscripten-lib.js",
|
||||||
|
);
|
||||||
|
const lib = libFn();
|
||||||
|
// @ts-ignore
|
||||||
|
g.importScripts = savedImportScripts;
|
||||||
|
|
||||||
|
if (!lib) {
|
||||||
|
throw Error("could not load taler-emscripten-lib.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lib.ccall) {
|
||||||
|
throw Error(
|
||||||
|
"sanity check failed: taler-emscripten lib does not have 'ccall'",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cachedEmscEnvironmentPromise = new Promise((resolve, reject) => {
|
||||||
|
lib.onRuntimeInitialized = () => {
|
||||||
|
this.cachedEmscEnvironmentPromise = undefined;
|
||||||
|
this.cachedEmscEnvironment = new EmscEnvironment(lib);
|
||||||
|
resolve(this.cachedEmscEnvironment);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return this.cachedEmscEnvironmentPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
private dispatchMessage(msg: any) {
|
||||||
|
if (this.onmessage) {
|
||||||
|
this.onmessage({ data: msg });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleRequest(operation: string, id: number, args: string[]) {
|
||||||
|
let emsc = await this.getEmscriptenEnvironment();
|
||||||
|
|
||||||
|
const impl = new CryptoImplementation(emsc);
|
||||||
|
|
||||||
|
if (!(operation in impl)) {
|
||||||
|
console.error(`crypto operation '${operation}' not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = (impl as any)[operation](...args);
|
||||||
|
this.dispatchMessage({ result, id });
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error during operation", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to the worker thread.
|
||||||
|
*/
|
||||||
|
postMessage(msg: any) {
|
||||||
|
const args = msg.args;
|
||||||
|
if (!Array.isArray(args)) {
|
||||||
|
console.error("args must be array");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const id = msg.id;
|
||||||
|
if (typeof id !== "number") {
|
||||||
|
console.error("RPC id must be number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const operation = msg.operation;
|
||||||
|
if (typeof operation !== "string") {
|
||||||
|
console.error("RPC operation must be string");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleRequest(operation, id, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcibly terminate the worker thread.
|
||||||
|
*/
|
||||||
|
terminate() {
|
||||||
|
console.log("terminating synchronous worker (no-op)");
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ import URI = require("urijs");
|
|||||||
|
|
||||||
import querystring = require("querystring");
|
import querystring = require("querystring");
|
||||||
import { CheckPaymentResponse } from "../talerTypes";
|
import { CheckPaymentResponse } from "../talerTypes";
|
||||||
import { NodeCryptoWorkerFactory } from "../crypto/cryptoApi";
|
import { NodeCryptoWorkerFactory, SynchronousCryptoWorkerFactory } from "../crypto/cryptoApi";
|
||||||
|
|
||||||
const enableTracing = false;
|
const enableTracing = false;
|
||||||
|
|
||||||
@ -269,7 +269,8 @@ export async function main() {
|
|||||||
myUnsupportedUpgrade,
|
myUnsupportedUpgrade,
|
||||||
);
|
);
|
||||||
|
|
||||||
const myWallet = new Wallet(myDb, myHttpLib, myBadge, myNotifier, new NodeCryptoWorkerFactory());
|
const myWallet = new Wallet(myDb, myHttpLib, myBadge, myNotifier, new SynchronousCryptoWorkerFactory());
|
||||||
|
//const myWallet = new Wallet(myDb, myHttpLib, myBadge, myNotifier, new NodeCryptoWorkerFactory());
|
||||||
|
|
||||||
const reserveResponse = await myWallet.createReserve({
|
const reserveResponse = await myWallet.createReserve({
|
||||||
amount: amounts.parseOrThrow("TESTKUDOS:10.0"),
|
amount: amounts.parseOrThrow("TESTKUDOS:10.0"),
|
||||||
|
@ -242,7 +242,7 @@ export function selectPayCoins(
|
|||||||
) >= 0;
|
) >= 0;
|
||||||
const isBelowFee = Amounts.cmp(accDepositFee, depositFeeLimit) <= 0;
|
const isBelowFee = Amounts.cmp(accDepositFee, depositFeeLimit) <= 0;
|
||||||
|
|
||||||
console.log("coin selection", {
|
console.log("candidate coin selection", {
|
||||||
coversAmount,
|
coversAmount,
|
||||||
isBelowFee,
|
isBelowFee,
|
||||||
accDepositFee,
|
accDepositFee,
|
||||||
@ -536,8 +536,8 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get exchanges and associated coins that are still spendable,
|
* Get exchanges and associated coins that are still spendable, but only
|
||||||
* but only if the sum the coins' remaining value exceeds the payment amount.
|
* if the sum the coins' remaining value covers the payment amount and fees.
|
||||||
*/
|
*/
|
||||||
private async getCoinsForPayment(
|
private async getCoinsForPayment(
|
||||||
args: CoinsForPaymentArgs,
|
args: CoinsForPaymentArgs,
|
||||||
@ -592,6 +592,7 @@ export class Wallet {
|
|||||||
const coins: CoinRecord[] = await this.q()
|
const coins: CoinRecord[] = await this.q()
|
||||||
.iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
|
.iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
|
||||||
.toArray();
|
.toArray();
|
||||||
|
|
||||||
const denoms = await this.q()
|
const denoms = await this.q()
|
||||||
.iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
|
.iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
|
||||||
.toArray();
|
.toArray();
|
||||||
@ -644,12 +645,6 @@ export class Wallet {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("payment coins: wireFeeLimit", wireFeeLimit);
|
|
||||||
console.log("payment coins: wireFeeAmortization", wireFeeAmortization);
|
|
||||||
console.log("payment coins: fees", fees);
|
|
||||||
console.log("payment coins: wireMethod", wireMethod);
|
|
||||||
console.log("payment coins: wireFeeTime", wireFeeTime);
|
|
||||||
|
|
||||||
let totalFees = Amounts.getZero(currency);
|
let totalFees = Amounts.getZero(currency);
|
||||||
let wireFee: AmountJson | undefined;
|
let wireFee: AmountJson | undefined;
|
||||||
for (const fee of fees.feesForType[wireMethod] || []) {
|
for (const fee of fees.feesForType[wireMethod] || []) {
|
||||||
@ -659,8 +654,6 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("payment coins: current wire fees", wireFee);
|
|
||||||
|
|
||||||
if (wireFee) {
|
if (wireFee) {
|
||||||
const amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization);
|
const amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization);
|
||||||
if (Amounts.cmp(wireFeeLimit, amortizedWireFee) < 0) {
|
if (Amounts.cmp(wireFeeLimit, amortizedWireFee) < 0) {
|
||||||
@ -671,6 +664,7 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = selectPayCoins(denoms, cds, remainingAmount, depositFeeLimit);
|
const res = selectPayCoins(denoms, cds, remainingAmount, depositFeeLimit);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
totalFees = Amounts.add(totalFees, res.totalFees).amount;
|
totalFees = Amounts.add(totalFees, res.totalFees).amount;
|
||||||
return {
|
return {
|
||||||
@ -748,7 +742,6 @@ export class Wallet {
|
|||||||
console.log("contract download failed", e);
|
console.log("contract download failed", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
console.log("got response", resp);
|
|
||||||
|
|
||||||
const proposal = Proposal.checked(resp.data);
|
const proposal = Proposal.checked(resp.data);
|
||||||
|
|
||||||
@ -919,7 +912,6 @@ export class Wallet {
|
|||||||
wireMethod: proposal.contractTerms.wire_method,
|
wireMethod: proposal.contractTerms.wire_method,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("max_fee", proposal.contractTerms.max_fee);
|
|
||||||
console.log("coin selection result", res);
|
console.log("coin selection result", res);
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
@ -1033,6 +1025,8 @@ export class Wallet {
|
|||||||
return { status: "insufficient-balance" };
|
return { status: "insufficient-balance" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("checkPay: payment possible!");
|
||||||
|
|
||||||
// Only create speculative signature if we don't already have one for this proposal
|
// Only create speculative signature if we don't already have one for this proposal
|
||||||
if (
|
if (
|
||||||
!this.speculativePayData ||
|
!this.speculativePayData ||
|
||||||
@ -1051,6 +1045,7 @@ export class Wallet {
|
|||||||
proposal,
|
proposal,
|
||||||
proposalId,
|
proposalId,
|
||||||
};
|
};
|
||||||
|
console.log("created speculative pay data for payment");
|
||||||
}
|
}
|
||||||
|
|
||||||
return { status: "payment-possible", coinSelection: res };
|
return { status: "payment-possible", coinSelection: res };
|
||||||
@ -1156,7 +1151,6 @@ export class Wallet {
|
|||||||
return op.promise;
|
return op.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log("executing processPreCoin", preCoin);
|
|
||||||
this.processPreCoinConcurrent++;
|
this.processPreCoinConcurrent++;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -29,14 +29,15 @@
|
|||||||
"src/checkable.ts",
|
"src/checkable.ts",
|
||||||
"src/crypto/cryptoApi-test.ts",
|
"src/crypto/cryptoApi-test.ts",
|
||||||
"src/crypto/cryptoApi.ts",
|
"src/crypto/cryptoApi.ts",
|
||||||
|
"src/crypto/cryptoImplementation.ts",
|
||||||
"src/crypto/cryptoWorker.ts",
|
"src/crypto/cryptoWorker.ts",
|
||||||
"src/crypto/emscInterface-test.ts",
|
"src/crypto/emscInterface-test.ts",
|
||||||
"src/crypto/emscInterface.ts",
|
"src/crypto/emscInterface.ts",
|
||||||
"src/crypto/emscLoader.d.ts",
|
"src/crypto/emscLoader.d.ts",
|
||||||
"src/crypto/emscLoader.js",
|
"src/crypto/emscLoader.js",
|
||||||
"src/crypto/nodeWorker.ts",
|
"src/crypto/nodeProcessWorker.ts",
|
||||||
"src/crypto/nodeWorkerEntry.ts",
|
"src/crypto/nodeWorkerEntry.ts",
|
||||||
"src/crypto/startWorker.js",
|
"src/crypto/synchronousWorker.ts",
|
||||||
"src/db.ts",
|
"src/db.ts",
|
||||||
"src/dbTypes.ts",
|
"src/dbTypes.ts",
|
||||||
"src/headless/taler-wallet-cli.ts",
|
"src/headless/taler-wallet-cli.ts",
|
||||||
|
@ -3216,10 +3216,10 @@ iconv-lite@^0.4.4, iconv-lite@~0.4.13:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
safer-buffer ">= 2.1.2 < 3"
|
||||||
|
|
||||||
idb-bridge@^0.0.1:
|
idb-bridge@0.0.2:
|
||||||
version "0.0.1"
|
version "0.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.1.tgz#4498704b79f354dcd3a628825656967939003614"
|
resolved "https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.2.tgz#daa46d75060bd6a116b26155c314446bea355570"
|
||||||
integrity sha512-GTFqRkjFk4C98zvPA65cKpB1JnBt+bFftn+Kkwucoy+hLmxVfdmbwZ6hj2ZieBHOppMtRs0il3zbzSzLofWrDg==
|
integrity sha512-PEfZmdbIQUV4vxJRSSXhan7niclJDJGPGUSJ2WlHCYCgdFK6n25UD8z/lsLoqWKfcp+xPuL+9MI+h9Ql8XFzkw==
|
||||||
|
|
||||||
ieee754@^1.1.4:
|
ieee754@^1.1.4:
|
||||||
version "1.1.13"
|
version "1.1.13"
|
||||||
|
Loading…
Reference in New Issue
Block a user