add link signature to /refresh/reveal
This commit is contained in:
parent
420a5ba23a
commit
cfa1df7343
@ -14,7 +14,6 @@
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* API to access the Taler crypto worker thread.
|
||||
* @author Florian Dold
|
||||
@ -35,22 +34,14 @@ import {
|
||||
WireFee,
|
||||
} from "../dbTypes";
|
||||
|
||||
import {
|
||||
ContractTerms,
|
||||
PaybackRequest,
|
||||
} from "../talerTypes";
|
||||
import { ContractTerms, PaybackRequest } from "../talerTypes";
|
||||
|
||||
import {
|
||||
BenchmarkResult,
|
||||
CoinWithDenom,
|
||||
PayCoinInfo,
|
||||
} from "../walletTypes";
|
||||
import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
|
||||
|
||||
import * as timer from "../timer";
|
||||
|
||||
import { startWorker } from "./startWorker";
|
||||
|
||||
|
||||
/**
|
||||
* State of a crypto worker.
|
||||
*/
|
||||
@ -58,17 +49,17 @@ interface WorkerState {
|
||||
/**
|
||||
* The actual worker thread.
|
||||
*/
|
||||
w: Worker|null;
|
||||
w: Worker | null;
|
||||
|
||||
/**
|
||||
* Work we're currently executing or null if not busy.
|
||||
*/
|
||||
currentWorkItem: WorkItem|null;
|
||||
currentWorkItem: WorkItem | null;
|
||||
|
||||
/**
|
||||
* Timer to terminate the worker if it's not busy enough.
|
||||
*/
|
||||
terminationTimerHandle: timer.TimerHandle|null;
|
||||
terminationTimerHandle: timer.TimerHandle | null;
|
||||
}
|
||||
|
||||
interface WorkItem {
|
||||
@ -88,7 +79,6 @@ interface WorkItem {
|
||||
startTime: number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Number of different priorities. Each priority p
|
||||
* must be 0 <= p < NUM_PRIO.
|
||||
@ -151,8 +141,10 @@ export class CryptoApi {
|
||||
|
||||
handleWorkerError(ws: WorkerState, e: ErrorEvent) {
|
||||
if (ws.currentWorkItem) {
|
||||
console.error(`error in worker during ${ws.currentWorkItem!.operation}`,
|
||||
e);
|
||||
console.error(
|
||||
`error in worker during ${ws.currentWorkItem!.operation}`,
|
||||
e,
|
||||
);
|
||||
} else {
|
||||
console.error("error in worker", e);
|
||||
}
|
||||
@ -201,7 +193,10 @@ export class CryptoApi {
|
||||
console.error(`RPC with id ${id} has no registry entry`);
|
||||
return;
|
||||
}
|
||||
console.log(`rpc ${currentWorkItem.operation} took ${timer.performanceNow() - currentWorkItem.startTime}ms`);
|
||||
console.log(
|
||||
`rpc ${currentWorkItem.operation} took ${timer.performanceNow() -
|
||||
currentWorkItem.startTime}ms`,
|
||||
);
|
||||
currentWorkItem.resolve(msg.data.result);
|
||||
}
|
||||
|
||||
@ -230,12 +225,21 @@ export class CryptoApi {
|
||||
}
|
||||
}
|
||||
|
||||
private doRpc<T>(operation: string, priority: number,
|
||||
...args: any[]): Promise<T> {
|
||||
|
||||
private doRpc<T>(
|
||||
operation: string,
|
||||
priority: number,
|
||||
...args: any[]
|
||||
): Promise<T> {
|
||||
const p: Promise<T> = new Promise<T>((resolve, reject) => {
|
||||
const rpcId = this.nextRpcId++;
|
||||
const workItem: WorkItem = {operation, args, resolve, reject, rpcId, startTime: 0};
|
||||
const workItem: WorkItem = {
|
||||
operation,
|
||||
args,
|
||||
resolve,
|
||||
reject,
|
||||
rpcId,
|
||||
startTime: 0,
|
||||
};
|
||||
|
||||
if (this.numBusy === this.workers.length) {
|
||||
const q = this.workQueues[priority];
|
||||
@ -263,8 +267,10 @@ export class CryptoApi {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
createPreCoin(denom: DenominationRecord, reserve: ReserveRecord): Promise<PreCoinRecord> {
|
||||
createPreCoin(
|
||||
denom: DenominationRecord,
|
||||
reserve: ReserveRecord,
|
||||
): Promise<PreCoinRecord> {
|
||||
return this.doRpc<PreCoinRecord>("createPreCoin", 1, denom, reserve);
|
||||
}
|
||||
|
||||
@ -280,27 +286,48 @@ export class CryptoApi {
|
||||
return this.doRpc<string>("hashDenomPub", 1, denomPub);
|
||||
}
|
||||
|
||||
isValidDenom(denom: DenominationRecord,
|
||||
masterPub: string): Promise<boolean> {
|
||||
isValidDenom(denom: DenominationRecord, masterPub: string): Promise<boolean> {
|
||||
return this.doRpc<boolean>("isValidDenom", 2, denom, masterPub);
|
||||
}
|
||||
|
||||
isValidWireFee(type: string, wf: WireFee, masterPub: string): Promise<boolean> {
|
||||
isValidWireFee(
|
||||
type: string,
|
||||
wf: WireFee,
|
||||
masterPub: string,
|
||||
): Promise<boolean> {
|
||||
return this.doRpc<boolean>("isValidWireFee", 2, type, wf, masterPub);
|
||||
}
|
||||
|
||||
isValidPaymentSignature(sig: string, contractHash: string, merchantPub: string): Promise<boolean> {
|
||||
return this.doRpc<boolean>("isValidPaymentSignature", 1, sig, contractHash, merchantPub);
|
||||
isValidPaymentSignature(
|
||||
sig: string,
|
||||
contractHash: string,
|
||||
merchantPub: string,
|
||||
): Promise<boolean> {
|
||||
return this.doRpc<boolean>(
|
||||
"isValidPaymentSignature",
|
||||
1,
|
||||
sig,
|
||||
contractHash,
|
||||
merchantPub,
|
||||
);
|
||||
}
|
||||
|
||||
signDeposit(contractTerms: ContractTerms,
|
||||
cds: CoinWithDenom[],
|
||||
totalAmount: AmountJson): Promise<PayCoinInfo> {
|
||||
return this.doRpc<PayCoinInfo>("signDeposit", 3, contractTerms, cds, totalAmount);
|
||||
signDeposit(
|
||||
contractTerms: ContractTerms,
|
||||
cds: CoinWithDenom[],
|
||||
totalAmount: AmountJson,
|
||||
): Promise<PayCoinInfo> {
|
||||
return this.doRpc<PayCoinInfo>(
|
||||
"signDeposit",
|
||||
3,
|
||||
contractTerms,
|
||||
cds,
|
||||
totalAmount,
|
||||
);
|
||||
}
|
||||
|
||||
createEddsaKeypair(): Promise<{priv: string, pub: string}> {
|
||||
return this.doRpc<{priv: string, pub: string}>("createEddsaKeypair", 1);
|
||||
createEddsaKeypair(): Promise<{ priv: string; pub: string }> {
|
||||
return this.doRpc<{ priv: string; pub: string }>("createEddsaKeypair", 1);
|
||||
}
|
||||
|
||||
rsaUnblind(sig: string, bk: string, pk: string): Promise<string> {
|
||||
@ -311,23 +338,43 @@ export class CryptoApi {
|
||||
return this.doRpc<PaybackRequest>("createPaybackRequest", 1, coin);
|
||||
}
|
||||
|
||||
createRefreshSession(exchangeBaseUrl: string,
|
||||
kappa: number,
|
||||
meltCoin: CoinRecord,
|
||||
newCoinDenoms: DenominationRecord[],
|
||||
meltFee: AmountJson): Promise<RefreshSessionRecord> {
|
||||
return this.doRpc<RefreshSessionRecord>("createRefreshSession",
|
||||
4,
|
||||
exchangeBaseUrl,
|
||||
kappa,
|
||||
meltCoin,
|
||||
newCoinDenoms,
|
||||
meltFee);
|
||||
createRefreshSession(
|
||||
exchangeBaseUrl: string,
|
||||
kappa: number,
|
||||
meltCoin: CoinRecord,
|
||||
newCoinDenoms: DenominationRecord[],
|
||||
meltFee: AmountJson,
|
||||
): Promise<RefreshSessionRecord> {
|
||||
return this.doRpc<RefreshSessionRecord>(
|
||||
"createRefreshSession",
|
||||
4,
|
||||
exchangeBaseUrl,
|
||||
kappa,
|
||||
meltCoin,
|
||||
newCoinDenoms,
|
||||
meltFee,
|
||||
);
|
||||
}
|
||||
|
||||
signCoinLink(
|
||||
oldCoinPriv: string,
|
||||
newDenomHash: string,
|
||||
oldCoinPub: string,
|
||||
transferPub: string,
|
||||
coinEv: string,
|
||||
): Promise<string> {
|
||||
return this.doRpc<string>(
|
||||
"signCoinLink",
|
||||
4,
|
||||
oldCoinPriv,
|
||||
newDenomHash,
|
||||
oldCoinPub,
|
||||
transferPub,
|
||||
coinEv,
|
||||
);
|
||||
}
|
||||
|
||||
benchmark(repetitions: number): Promise<BenchmarkResult> {
|
||||
return this.doRpc<BenchmarkResult>("benchmark",
|
||||
1,
|
||||
repetitions);
|
||||
return this.doRpc<BenchmarkResult>("benchmark", 1, repetitions);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
* Web worker for crypto operations.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
@ -39,17 +38,9 @@ import {
|
||||
WireFee,
|
||||
} from "../dbTypes";
|
||||
|
||||
import {
|
||||
CoinPaySig,
|
||||
ContractTerms,
|
||||
PaybackRequest,
|
||||
} from "../talerTypes";
|
||||
import { CoinPaySig, ContractTerms, PaybackRequest } from "../talerTypes";
|
||||
|
||||
import {
|
||||
BenchmarkResult,
|
||||
CoinWithDenom,
|
||||
PayCoinInfo,
|
||||
} from "../walletTypes";
|
||||
import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
|
||||
|
||||
import { canonicalJson } from "../helpers";
|
||||
|
||||
@ -64,15 +55,15 @@ import {
|
||||
} from "./emscInterface";
|
||||
import * as native from "./emscInterface";
|
||||
|
||||
|
||||
namespace RpcFunctions {
|
||||
|
||||
/**
|
||||
* Create a pre-coin of the given denomination to be withdrawn from then given
|
||||
* reserve.
|
||||
*/
|
||||
export function createPreCoin(denom: DenominationRecord,
|
||||
reserve: ReserveRecord): PreCoinRecord {
|
||||
export function createPreCoin(
|
||||
denom: DenominationRecord,
|
||||
reserve: ReserveRecord,
|
||||
): PreCoinRecord {
|
||||
const reservePriv = new native.EddsaPrivateKey();
|
||||
reservePriv.loadCrock(reserve.reserve_priv);
|
||||
const reservePub = new native.EddsaPublicKey();
|
||||
@ -125,7 +116,6 @@ namespace RpcFunctions {
|
||||
return preCoin;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a planchet used for tipping, including the private keys.
|
||||
*/
|
||||
@ -152,12 +142,14 @@ namespace RpcFunctions {
|
||||
coinPub: coinPub.toCrock(),
|
||||
coinValue: denom.value,
|
||||
denomPub: denomPub.encode().toCrock(),
|
||||
denomPubHash: denomPub.encode().hash().toCrock(),
|
||||
denomPubHash: denomPub
|
||||
.encode()
|
||||
.hash()
|
||||
.toCrock(),
|
||||
};
|
||||
return tipPlanchet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create and sign a message to request payback for a coin.
|
||||
*/
|
||||
@ -165,7 +157,9 @@ namespace RpcFunctions {
|
||||
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(),
|
||||
h_denom_pub: native.RsaPublicKey.fromCrock(coin.denomPub)
|
||||
.encode()
|
||||
.hash(),
|
||||
});
|
||||
const coinPriv = native.EddsaPrivateKey.fromCrock(coin.coinPriv);
|
||||
const coinSig = native.eddsaSign(p.toPurpose(), coinPriv);
|
||||
@ -179,63 +173,83 @@ namespace RpcFunctions {
|
||||
return paybackRequest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a payment signature is valid.
|
||||
*/
|
||||
export function isValidPaymentSignature(sig: string, contractHash: string, merchantPub: string): boolean {
|
||||
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);
|
||||
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 {
|
||||
export function isValidWireFee(
|
||||
type: string,
|
||||
wf: WireFee,
|
||||
masterPub: string,
|
||||
): boolean {
|
||||
const p = new native.MasterWireFeePS({
|
||||
closing_fee: (new native.Amount(wf.closingFee)).toNbo(),
|
||||
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(),
|
||||
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);
|
||||
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 {
|
||||
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(),
|
||||
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(),
|
||||
value: new native.Amount(denom.value).toNbo(),
|
||||
});
|
||||
|
||||
const nativeSig = new native.EddsaSignature();
|
||||
@ -243,42 +257,44 @@ namespace RpcFunctions {
|
||||
|
||||
const nativePub = native.EddsaPublicKey.fromCrock(masterPub);
|
||||
|
||||
return native.eddsaVerify(native.SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY,
|
||||
p.toPurpose(),
|
||||
nativeSig,
|
||||
nativePub);
|
||||
|
||||
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} {
|
||||
export function createEddsaKeypair(): { priv: string; pub: string } {
|
||||
const priv = native.EddsaPrivateKey.create();
|
||||
const pub = priv.getPublicKey();
|
||||
return {priv: priv.toCrock(), pub: pub.toCrock()};
|
||||
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));
|
||||
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 {
|
||||
export function signDeposit(
|
||||
contractTerms: ContractTerms,
|
||||
cds: CoinWithDenom[],
|
||||
totalAmount: AmountJson,
|
||||
): PayCoinInfo {
|
||||
const ret: PayCoinInfo = {
|
||||
originalCoins: [],
|
||||
sigs: [],
|
||||
@ -287,17 +303,21 @@ namespace RpcFunctions {
|
||||
|
||||
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;
|
||||
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;
|
||||
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 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) };
|
||||
const originalCoin = { ...cd.coin };
|
||||
|
||||
if (amountRemaining.value === 0 && amountRemaining.fraction === 0) {
|
||||
break;
|
||||
@ -332,13 +352,20 @@ namespace RpcFunctions {
|
||||
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),
|
||||
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 coinSig = native
|
||||
.eddsaSign(
|
||||
d.toPurpose(),
|
||||
native.EddsaPrivateKey.fromCrock(cd.coin.coinPriv),
|
||||
)
|
||||
.toCrock();
|
||||
|
||||
const s: CoinPaySig = {
|
||||
coin_pub: cd.coin.coinPub,
|
||||
@ -355,22 +382,21 @@ namespace RpcFunctions {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new refresh session.
|
||||
*/
|
||||
export function createRefreshSession(exchangeBaseUrl: string,
|
||||
kappa: number,
|
||||
meltCoin: CoinRecord,
|
||||
newCoinDenoms: DenominationRecord[],
|
||||
meltFee: AmountJson): RefreshSessionRecord {
|
||||
|
||||
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;
|
||||
valueWithFee = Amounts.add(valueWithFee, ncd.value, ncd.feeWithdraw)
|
||||
.amount;
|
||||
}
|
||||
|
||||
// melt fee
|
||||
@ -397,12 +423,11 @@ namespace RpcFunctions {
|
||||
}
|
||||
|
||||
sessionHc.read(native.EddsaPublicKey.fromCrock(meltCoin.coinPub));
|
||||
sessionHc.read((new native.Amount(valueWithFee)).toNbo());
|
||||
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);
|
||||
@ -413,10 +438,10 @@ namespace RpcFunctions {
|
||||
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);
|
||||
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?)");
|
||||
}
|
||||
@ -437,16 +462,18 @@ namespace RpcFunctions {
|
||||
sessionHc.finish(sessionHash);
|
||||
|
||||
const confirmData = new RefreshMeltCoinAffirmationPS({
|
||||
amount_with_fee: (new Amount(valueWithFee)).toNbo(),
|
||||
amount_with_fee: new Amount(valueWithFee).toNbo(),
|
||||
coin_pub: EddsaPublicKey.fromCrock(meltCoin.coinPub),
|
||||
melt_fee: (new Amount(meltFee)).toNbo(),
|
||||
melt_fee: new Amount(meltFee).toNbo(),
|
||||
session_hash: sessionHash,
|
||||
});
|
||||
|
||||
|
||||
const confirmSig: string = native.eddsaSign(confirmData.toPurpose(),
|
||||
native.EddsaPrivateKey.fromCrock(
|
||||
meltCoin.coinPriv)).toCrock();
|
||||
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) {
|
||||
@ -459,8 +486,8 @@ namespace RpcFunctions {
|
||||
finished: false,
|
||||
hash: sessionHash.toCrock(),
|
||||
meltCoinPub: meltCoin.coinPub,
|
||||
newDenomHashes: newCoinDenoms.map((d) => d.denomPubHash),
|
||||
newDenoms: newCoinDenoms.map((d) => d.denomPub),
|
||||
newDenomHashes: newCoinDenoms.map(d => d.denomPubHash),
|
||||
newDenoms: newCoinDenoms.map(d => d.denomPub),
|
||||
norevealIndex: undefined,
|
||||
preCoinsForGammas,
|
||||
transferPrivs,
|
||||
@ -484,7 +511,33 @@ namespace RpcFunctions {
|
||||
* Hash a denomination public key.
|
||||
*/
|
||||
export function hashDenomPub(denomPub: string): string {
|
||||
return native.RsaPublicKey.fromCrock(denomPub).encode().hash().toCrock();
|
||||
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 {
|
||||
@ -500,7 +553,7 @@ namespace RpcFunctions {
|
||||
for (let i = 0; i < repetitions; i++) {
|
||||
ba.randomize(native.RandomQuality.WEAK);
|
||||
const start = timer.performanceNow();
|
||||
ba.hash();
|
||||
ba.hash();
|
||||
time_hash_big += timer.performanceNow() - start;
|
||||
}
|
||||
|
||||
@ -508,7 +561,7 @@ namespace RpcFunctions {
|
||||
for (let i = 0; i < repetitions; i++) {
|
||||
const start = timer.performanceNow();
|
||||
const priv: native.EddsaPrivateKey = native.EddsaPrivateKey.create();
|
||||
time_eddsa_create += timer.performanceNow() - start;
|
||||
time_eddsa_create += timer.performanceNow() - start;
|
||||
priv.destroy();
|
||||
}
|
||||
|
||||
@ -541,14 +594,15 @@ namespace RpcFunctions {
|
||||
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);
|
||||
native.eddsaVerify(
|
||||
native.SignaturePurpose.MERCHANT_PAYMENT_OK,
|
||||
p,
|
||||
eddsaSig,
|
||||
eddsaPub,
|
||||
);
|
||||
time_eddsa_verify += timer.performanceNow() - start;
|
||||
}
|
||||
|
||||
@ -564,11 +618,18 @@ namespace RpcFunctions {
|
||||
time_rsa_2048_blind += timer.performanceNow() - start;
|
||||
}
|
||||
|
||||
const blindedMessage2048 = native.rsaBlind(h, blindingSecret2048, rsaPub2048);
|
||||
const blindedMessage2048 = native.rsaBlind(
|
||||
h,
|
||||
blindingSecret2048,
|
||||
rsaPub2048,
|
||||
);
|
||||
if (!blindedMessage2048) {
|
||||
throw Error("should not happen");
|
||||
}
|
||||
const rsaBlindSig2048 = native.rsaSignBlinded(rsaPriv2048, blindedMessage2048);
|
||||
const rsaBlindSig2048 = native.rsaSignBlinded(
|
||||
rsaPriv2048,
|
||||
blindedMessage2048,
|
||||
);
|
||||
|
||||
let time_rsa_2048_unblind = 0;
|
||||
for (let i = 0; i < repetitions; i++) {
|
||||
@ -577,7 +638,11 @@ namespace RpcFunctions {
|
||||
time_rsa_2048_unblind += timer.performanceNow() - start;
|
||||
}
|
||||
|
||||
const unblindedSig2048 = native.rsaUnblind(rsaBlindSig2048, blindingSecret2048, rsaPub2048);
|
||||
const unblindedSig2048 = native.rsaUnblind(
|
||||
rsaBlindSig2048,
|
||||
blindingSecret2048,
|
||||
rsaPub2048,
|
||||
);
|
||||
|
||||
let time_rsa_2048_verify = 0;
|
||||
for (let i = 0; i < repetitions; i++) {
|
||||
@ -586,7 +651,6 @@ namespace RpcFunctions {
|
||||
time_rsa_2048_verify += timer.performanceNow() - start;
|
||||
}
|
||||
|
||||
|
||||
/* rsa 4096 */
|
||||
|
||||
let time_rsa_4096_blind = 0;
|
||||
@ -599,11 +663,18 @@ namespace RpcFunctions {
|
||||
time_rsa_4096_blind += timer.performanceNow() - start;
|
||||
}
|
||||
|
||||
const blindedMessage4096 = native.rsaBlind(h, blindingSecret4096, rsaPub4096);
|
||||
const blindedMessage4096 = native.rsaBlind(
|
||||
h,
|
||||
blindingSecret4096,
|
||||
rsaPub4096,
|
||||
);
|
||||
if (!blindedMessage4096) {
|
||||
throw Error("should not happen");
|
||||
}
|
||||
const rsaBlindSig4096 = native.rsaSignBlinded(rsaPriv4096, blindedMessage4096);
|
||||
const rsaBlindSig4096 = native.rsaSignBlinded(
|
||||
rsaPriv4096,
|
||||
blindedMessage4096,
|
||||
);
|
||||
|
||||
let time_rsa_4096_unblind = 0;
|
||||
for (let i = 0; i < repetitions; i++) {
|
||||
@ -612,7 +683,11 @@ namespace RpcFunctions {
|
||||
time_rsa_4096_unblind += timer.performanceNow() - start;
|
||||
}
|
||||
|
||||
const unblindedSig4096 = native.rsaUnblind(rsaBlindSig4096, blindingSecret4096, rsaPub4096);
|
||||
const unblindedSig4096 = native.rsaUnblind(
|
||||
rsaBlindSig4096,
|
||||
blindingSecret4096,
|
||||
rsaPub4096,
|
||||
);
|
||||
|
||||
let time_rsa_4096_verify = 0;
|
||||
for (let i = 0; i < repetitions; i++) {
|
||||
@ -621,7 +696,6 @@ namespace RpcFunctions {
|
||||
time_rsa_4096_verify += timer.performanceNow() - start;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
repetitions,
|
||||
time: {
|
||||
@ -637,12 +711,11 @@ namespace RpcFunctions {
|
||||
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;
|
||||
|
||||
worker.onmessage = (msg: MessageEvent) => {
|
||||
@ -665,7 +738,7 @@ worker.onmessage = (msg: MessageEvent) => {
|
||||
console.log("onmessage with", msg.data.operation);
|
||||
console.log("foo");
|
||||
|
||||
emscLoader.getLib().then((p) => {
|
||||
emscLoader.getLib().then(p => {
|
||||
const lib = p.lib;
|
||||
if (!native.isInitialized()) {
|
||||
console.log("initializing emscripten for then first time with lib");
|
||||
|
@ -223,6 +223,7 @@ export enum SignaturePurpose {
|
||||
MERCHANT_PAYMENT_OK = 1104,
|
||||
MASTER_WIRE_FEES = 1028,
|
||||
WALLET_COIN_PAYBACK = 1203,
|
||||
WALLET_COIN_LINK = 1204,
|
||||
}
|
||||
|
||||
|
||||
@ -970,7 +971,7 @@ abstract class SignatureStruct {
|
||||
throw Error(`Key ${name} not found`);
|
||||
}
|
||||
if (!(value instanceof typemap[name])) {
|
||||
throw Error("Wrong type for ${name}");
|
||||
throw Error(`Wrong type for ${name}`);
|
||||
}
|
||||
this.members[name] = value;
|
||||
}
|
||||
@ -1293,6 +1294,35 @@ export class DepositRequestPS extends SignatureStruct {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface CoinLinkSignaturePS_args {
|
||||
h_denom_pub: HashCode;
|
||||
old_coin_pub: EddsaPublicKey;
|
||||
transfer_pub: EcdhePublicKey;
|
||||
coin_envelope_hash: HashCode;
|
||||
}
|
||||
|
||||
|
||||
export class CoinLinkSignaturePS extends SignatureStruct {
|
||||
constructor(w: CoinLinkSignaturePS_args) {
|
||||
super(w);
|
||||
}
|
||||
|
||||
purpose() {
|
||||
return SignaturePurpose.WALLET_COIN_LINK;
|
||||
}
|
||||
|
||||
fieldTypes() {
|
||||
return [
|
||||
["h_denom_pub", HashCode],
|
||||
["old_coin_pub", EddsaPublicKey],
|
||||
["transfer_pub", EcdhePublicKey],
|
||||
["coin_envelope_hash", HashCode],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Arguments for constuctor of [[DenominationKeyValidityPS]].
|
||||
*/
|
||||
|
1434
src/wallet.ts
1434
src/wallet.ts
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user