derive refresh info from secret seed

This commit is contained in:
Florian Dold 2020-12-14 16:44:42 +01:00
parent 80a0fab126
commit 12234083ec
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
6 changed files with 350 additions and 208 deletions

View File

@ -389,3 +389,19 @@ export function setupRefreshPlanchet(
coinPub: eddsaGetPublic(coinPriv), coinPub: eddsaGetPublic(coinPriv),
}; };
} }
export function setupRefreshTransferPub(
secretSeed: Uint8Array,
transferPubIndex: number,
): EcdheKeyPair {
const info = stringToBytes("taler-transfer-pub-derivation");
const saltArrBuf = new ArrayBuffer(4);
const salt = new Uint8Array(saltArrBuf);
const saltDataView = new DataView(saltArrBuf);
saltDataView.setUint32(0, transferPubIndex);
const out = kdf(32, secretSeed, salt, info);
return {
ecdhePriv: out,
ecdhePub: ecdheGetPublic(out),
};
}

View File

@ -47,6 +47,10 @@ import {
import * as timer from "../../util/timer"; import * as timer from "../../util/timer";
import { Logger } from "../../util/logging"; import { Logger } from "../../util/logging";
import {
DerivedRefreshSession,
DeriveRefreshSessionRequest,
} from "../../types/cryptoTypes";
const logger = new Logger("cryptoApi.ts"); const logger = new Logger("cryptoApi.ts");
@ -417,22 +421,10 @@ export class CryptoApi {
return this.doRpc<RecoupRequest>("createRecoupRequest", 1, coin); return this.doRpc<RecoupRequest>("createRecoupRequest", 1, coin);
} }
createRefreshSession( deriveRefreshSession(
exchangeBaseUrl: string, req: DeriveRefreshSessionRequest,
kappa: number, ): Promise<DerivedRefreshSession> {
meltCoin: CoinRecord, return this.doRpc<DerivedRefreshSession>("deriveRefreshSession", 4, req);
newCoinDenoms: DenominationSelectionInfo,
meltFee: AmountJson,
): Promise<RefreshSessionRecord> {
return this.doRpc<RefreshSessionRecord>(
"createRefreshSession",
4,
exchangeBaseUrl,
kappa,
meltCoin,
newCoinDenoms,
meltFee,
);
} }
signCoinLink( signCoinLink(

View File

@ -63,6 +63,8 @@ import {
keyExchangeEcdheEddsa, keyExchangeEcdheEddsa,
setupRefreshPlanchet, setupRefreshPlanchet,
rsaVerify, rsaVerify,
getRandomBytes,
setupRefreshTransferPub,
} from "../talerCrypto"; } from "../talerCrypto";
import { randomBytes } from "../primitives/nacl-fast"; import { randomBytes } from "../primitives/nacl-fast";
import { kdf } from "../primitives/kdf"; import { kdf } from "../primitives/kdf";
@ -73,6 +75,10 @@ import {
} from "../../util/time"; } from "../../util/time";
import { Logger } from "../../util/logging"; import { Logger } from "../../util/logging";
import {
DerivedRefreshSession,
DeriveRefreshSessionRequest,
} from "../../types/cryptoTypes";
const logger = new Logger("cryptoImplementation.ts"); const logger = new Logger("cryptoImplementation.ts");
@ -375,21 +381,24 @@ export class CryptoImplementation {
return s; return s;
} }
/** deriveRefreshSession(
* Create a new refresh session. req: DeriveRefreshSessionRequest,
*/ ): DerivedRefreshSession {
createRefreshSession( const {
exchangeBaseUrl: string, newCoinDenoms,
kappa: number, feeRefresh: meltFee,
meltCoin: CoinRecord, kappa,
newCoinDenoms: DenominationSelectionInfo, meltCoinDenomPubHash,
meltFee: AmountJson, meltCoinPriv,
): RefreshSessionRecord { meltCoinPub,
const currency = newCoinDenoms.selectedDenoms[0].denom.value.currency; sessionSecretSeed: refreshSessionSecretSeed,
} = req;
const currency = newCoinDenoms[0].value.currency;
let valueWithFee = Amounts.getZero(currency); let valueWithFee = Amounts.getZero(currency);
for (const ncd of newCoinDenoms.selectedDenoms) { for (const ncd of newCoinDenoms) {
const t = Amounts.add(ncd.denom.value, ncd.denom.feeWithdraw).amount; const t = Amounts.add(ncd.value, ncd.feeWithdraw).amount;
valueWithFee = Amounts.add( valueWithFee = Amounts.add(
valueWithFee, valueWithFee,
Amounts.mult(t, ncd.count).amount, Amounts.mult(t, ncd.count).amount,
@ -409,7 +418,10 @@ export class CryptoImplementation {
logger.trace("starting RC computation"); logger.trace("starting RC computation");
for (let i = 0; i < kappa; i++) { for (let i = 0; i < kappa; i++) {
const transferKeyPair = createEcdheKeyPair(); const transferKeyPair = setupRefreshTransferPub(
decodeCrock(refreshSessionSecretSeed),
i,
);
sessionHc.update(transferKeyPair.ecdhePub); sessionHc.update(transferKeyPair.ecdhePub);
logger.trace( logger.trace(
`HASH transfer_pub ${encodeCrock(transferKeyPair.ecdhePub)}`, `HASH transfer_pub ${encodeCrock(transferKeyPair.ecdhePub)}`,
@ -418,16 +430,16 @@ export class CryptoImplementation {
transferPubs.push(encodeCrock(transferKeyPair.ecdhePub)); transferPubs.push(encodeCrock(transferKeyPair.ecdhePub));
} }
for (const denomSel of newCoinDenoms.selectedDenoms) { for (const denomSel of newCoinDenoms) {
for (let i = 0; i < denomSel.count; i++) { for (let i = 0; i < denomSel.count; i++) {
const r = decodeCrock(denomSel.denom.denomPub); const r = decodeCrock(denomSel.denomPub);
sessionHc.update(r); sessionHc.update(r);
logger.trace(`HASH new_coins ${encodeCrock(r)}`); logger.trace(`HASH new_coins ${encodeCrock(r)}`);
} }
} }
sessionHc.update(decodeCrock(meltCoin.coinPub)); sessionHc.update(decodeCrock(meltCoinPub));
logger.trace(`HASH coin_pub ${meltCoin.coinPub}`); logger.trace(`HASH coin_pub ${meltCoinPub}`);
sessionHc.update(amountToBuffer(valueWithFee)); sessionHc.update(amountToBuffer(valueWithFee));
logger.trace( logger.trace(
`HASH melt_amount ${encodeCrock(amountToBuffer(valueWithFee))}`, `HASH melt_amount ${encodeCrock(amountToBuffer(valueWithFee))}`,
@ -435,12 +447,12 @@ export class CryptoImplementation {
for (let i = 0; i < kappa; i++) { for (let i = 0; i < kappa; i++) {
const planchets: RefreshPlanchet[] = []; const planchets: RefreshPlanchet[] = [];
for (let j = 0; j < newCoinDenoms.selectedDenoms.length; j++) { for (let j = 0; j < newCoinDenoms.length; j++) {
const denomSel = newCoinDenoms.selectedDenoms[j]; const denomSel = newCoinDenoms[j];
for (let k = 0; k < denomSel.count; k++) { for (let k = 0; k < denomSel.count; k++) {
const coinNumber = planchets.length; const coinNumber = planchets.length;
const transferPriv = decodeCrock(transferPrivs[i]); const transferPriv = decodeCrock(transferPrivs[i]);
const oldCoinPub = decodeCrock(meltCoin.coinPub); const oldCoinPub = decodeCrock(meltCoinPub);
const transferSecret = keyExchangeEcdheEddsa( const transferSecret = keyExchangeEcdheEddsa(
transferPriv, transferPriv,
oldCoinPub, oldCoinPub,
@ -450,7 +462,7 @@ export class CryptoImplementation {
const coinPub = fresh.coinPub; const coinPub = fresh.coinPub;
const blindingFactor = fresh.bks; const blindingFactor = fresh.bks;
const pubHash = hash(coinPub); const pubHash = hash(coinPub);
const denomPub = decodeCrock(denomSel.denom.denomPub); const denomPub = decodeCrock(denomSel.denomPub);
const ev = rsaBlind(pubHash, blindingFactor, denomPub); const ev = rsaBlind(pubHash, blindingFactor, denomPub);
const planchet: RefreshPlanchet = { const planchet: RefreshPlanchet = {
blindingKey: encodeCrock(blindingFactor), blindingKey: encodeCrock(blindingFactor),
@ -481,49 +493,22 @@ export class CryptoImplementation {
const confirmData = buildSigPS(SignaturePurpose.WALLET_COIN_MELT) const confirmData = buildSigPS(SignaturePurpose.WALLET_COIN_MELT)
.put(sessionHash) .put(sessionHash)
.put(decodeCrock(meltCoin.denomPubHash)) .put(decodeCrock(meltCoinDenomPubHash))
.put(amountToBuffer(valueWithFee)) .put(amountToBuffer(valueWithFee))
.put(amountToBuffer(meltFee)) .put(amountToBuffer(meltFee))
.put(decodeCrock(meltCoin.coinPub)) .put(decodeCrock(meltCoinPub))
.build(); .build();
const confirmSig = eddsaSign(confirmData, decodeCrock(meltCoin.coinPriv)); const confirmSig = eddsaSign(confirmData, decodeCrock(meltCoinPriv));
let valueOutput = Amounts.getZero(currency); const refreshSession: DerivedRefreshSession = {
for (const denomSel of newCoinDenoms.selectedDenoms) {
const denom = denomSel.denom;
for (let i = 0; i < denomSel.count; i++) {
valueOutput = Amounts.add(valueOutput, denom.value).amount;
}
}
const newDenoms: string[] = [];
const newDenomHashes: string[] = [];
for (const denomSel of newCoinDenoms.selectedDenoms) {
const denom = denomSel.denom;
for (let i = 0; i < denomSel.count; i++) {
newDenoms.push(denom.denomPub);
newDenomHashes.push(denom.denomPubHash);
}
}
const refreshSession: RefreshSessionRecord = {
confirmSig: encodeCrock(confirmSig), confirmSig: encodeCrock(confirmSig),
exchangeBaseUrl,
hash: encodeCrock(sessionHash), hash: encodeCrock(sessionHash),
meltCoinPub: meltCoin.coinPub, meltCoinPub: meltCoinPub,
newDenomHashes,
newDenoms,
norevealIndex: undefined,
planchetsForGammas: planchetsForGammas, planchetsForGammas: planchetsForGammas,
transferPrivs, transferPrivs,
transferPubs, transferPubs,
amountRefreshOutput: valueOutput, meltValueWithFee: valueWithFee,
amountRefreshInput: valueWithFee,
timestampCreated: getTimestampNow(),
finishedTimestamp: undefined,
lastError: undefined,
}; };
return refreshSession; return refreshSession;

View File

@ -60,6 +60,8 @@ import {
import { URL } from "../util/url"; import { URL } from "../util/url";
import { checkDbInvariant } from "../util/invariants"; import { checkDbInvariant } from "../util/invariants";
import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries"; import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries";
import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "./versions";
import { RefreshNewDenomInfo } from "../types/cryptoTypes";
const logger = new Logger("refresh.ts"); const logger = new Logger("refresh.ts");
@ -182,13 +184,7 @@ async function refreshCreateSession(
return; return;
} }
const refreshSession: RefreshSessionRecord = await ws.cryptoApi.createRefreshSession( const sessionSecretSeed = encodeCrock(getRandomBytes(64));
exchange.baseUrl,
3,
coin,
newCoinDenoms,
oldDenom.feeRefresh,
);
// Store refresh session for this coin in the database. // Store refresh session for this coin in the database.
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
@ -201,7 +197,15 @@ async function refreshCreateSession(
if (rg.refreshSessionPerCoin[coinIndex]) { if (rg.refreshSessionPerCoin[coinIndex]) {
return; return;
} }
rg.refreshSessionPerCoin[coinIndex] = refreshSession; rg.refreshSessionPerCoin[coinIndex] = {
norevealIndex: undefined,
sessionSecretSeed: sessionSecretSeed,
newDenoms: newCoinDenoms.selectedDenoms.map((x) => ({
count: x.count,
denomPubHash: x.denom.denomPubHash,
})),
amountRefreshOutput: newCoinDenoms.totalCoinValue,
};
await tx.put(Stores.refreshGroups, rg); await tx.put(Stores.refreshGroups, rg);
}, },
); );
@ -232,24 +236,57 @@ async function refreshMelt(
return; return;
} }
const coin = await ws.db.get(Stores.coins, refreshSession.meltCoinPub); const oldCoin = await ws.db.get(
Stores.coins,
refreshGroup.oldCoinPubs[coinIndex],
);
checkDbInvariant(!!oldCoin, "melt coin doesn't exist");
const oldDenom = await ws.db.get(Stores.denominations, [
oldCoin.exchangeBaseUrl,
oldCoin.denomPubHash,
]);
checkDbInvariant(!!oldDenom, "denomination for melted coin doesn't exist");
if (!coin) { const newCoinDenoms: RefreshNewDenomInfo[] = [];
console.error("can't melt coin, it does not exist");
return; for (const dh of refreshSession.newDenoms) {
const newDenom = await ws.db.get(Stores.denominations, [
oldCoin.exchangeBaseUrl,
dh.denomPubHash,
]);
checkDbInvariant(
!!newDenom,
"new denomination for refresh not in database",
);
newCoinDenoms.push({
count: dh.count,
denomPub: newDenom.denomPub,
feeWithdraw: newDenom.feeWithdraw,
value: newDenom.value,
});
} }
const derived = await ws.cryptoApi.deriveRefreshSession({
kappa: 3,
meltCoinDenomPubHash: oldCoin.denomPubHash,
meltCoinPriv: oldCoin.coinPriv,
meltCoinPub: oldCoin.coinPub,
feeRefresh: oldDenom.feeRefresh,
newCoinDenoms,
sessionSecretSeed: refreshSession.sessionSecretSeed,
});
const reqUrl = new URL( const reqUrl = new URL(
`coins/${coin.coinPub}/melt`, `coins/${oldCoin.coinPub}/melt`,
refreshSession.exchangeBaseUrl, oldCoin.exchangeBaseUrl,
); );
const meltReq = { const meltReq = {
coin_pub: coin.coinPub, coin_pub: oldCoin.coinPub,
confirm_sig: refreshSession.confirmSig, confirm_sig: derived.confirmSig,
denom_pub_hash: coin.denomPubHash, denom_pub_hash: oldCoin.denomPubHash,
denom_sig: coin.denomSig, denom_sig: oldCoin.denomSig,
rc: refreshSession.hash, rc: derived.hash,
value_with_fee: Amounts.stringify(refreshSession.amountRefreshInput), value_with_fee: Amounts.stringify(derived.meltValueWithFee),
}; };
logger.trace(`melt request for coin:`, meltReq); logger.trace(`melt request for coin:`, meltReq);
@ -270,15 +307,15 @@ async function refreshMelt(
await ws.db.mutate(Stores.refreshGroups, refreshGroupId, (rg) => { await ws.db.mutate(Stores.refreshGroups, refreshGroupId, (rg) => {
const rs = rg.refreshSessionPerCoin[coinIndex]; const rs = rg.refreshSessionPerCoin[coinIndex];
if (rg.timestampFinished) {
return;
}
if (!rs) { if (!rs) {
return; return;
} }
if (rs.norevealIndex !== undefined) { if (rs.norevealIndex !== undefined) {
return; return;
} }
if (rs.finishedTimestamp) {
return;
}
rs.norevealIndex = norevealIndex; rs.norevealIndex = norevealIndex;
return rg; return rg;
}); });
@ -305,48 +342,95 @@ async function refreshReveal(
if (norevealIndex === undefined) { if (norevealIndex === undefined) {
throw Error("can't reveal without melting first"); throw Error("can't reveal without melting first");
} }
const privs = Array.from(refreshSession.transferPrivs);
const oldCoin = await ws.db.get(
Stores.coins,
refreshGroup.oldCoinPubs[coinIndex],
);
checkDbInvariant(!!oldCoin, "melt coin doesn't exist");
const oldDenom = await ws.db.get(Stores.denominations, [
oldCoin.exchangeBaseUrl,
oldCoin.denomPubHash,
]);
checkDbInvariant(!!oldDenom, "denomination for melted coin doesn't exist");
const newCoinDenoms: RefreshNewDenomInfo[] = [];
for (const dh of refreshSession.newDenoms) {
const newDenom = await ws.db.get(Stores.denominations, [
oldCoin.exchangeBaseUrl,
dh.denomPubHash,
]);
checkDbInvariant(
!!newDenom,
"new denomination for refresh not in database",
);
newCoinDenoms.push({
count: dh.count,
denomPub: newDenom.denomPub,
feeWithdraw: newDenom.feeWithdraw,
value: newDenom.value,
});
}
const derived = await ws.cryptoApi.deriveRefreshSession({
kappa: 3,
meltCoinDenomPubHash: oldCoin.denomPubHash,
meltCoinPriv: oldCoin.coinPriv,
meltCoinPub: oldCoin.coinPub,
feeRefresh: oldDenom.feeRefresh,
newCoinDenoms,
sessionSecretSeed: refreshSession.sessionSecretSeed,
});
const privs = Array.from(derived.transferPrivs);
privs.splice(norevealIndex, 1); privs.splice(norevealIndex, 1);
const planchets = refreshSession.planchetsForGammas[norevealIndex]; const planchets = derived.planchetsForGammas[norevealIndex];
if (!planchets) { if (!planchets) {
throw Error("refresh index error"); throw Error("refresh index error");
} }
const meltCoinRecord = await ws.db.get( const meltCoinRecord = await ws.db.get(
Stores.coins, Stores.coins,
refreshSession.meltCoinPub, refreshGroup.oldCoinPubs[coinIndex],
); );
if (!meltCoinRecord) { if (!meltCoinRecord) {
throw Error("inconsistent database"); throw Error("inconsistent database");
} }
const evs = planchets.map((x: RefreshPlanchet) => x.coinEv); const evs = planchets.map((x: RefreshPlanchet) => x.coinEv);
const newDenomsFlat: string[] = [];
const linkSigs: string[] = []; const linkSigs: string[] = [];
for (let i = 0; i < refreshSession.newDenoms.length; i++) { for (let i = 0; i < refreshSession.newDenoms.length; i++) {
const linkSig = await ws.cryptoApi.signCoinLink( const dsel = refreshSession.newDenoms[i];
meltCoinRecord.coinPriv, for (let j = 0; j < dsel.count; j++) {
refreshSession.newDenomHashes[i], const newCoinIndex = linkSigs.length;
refreshSession.meltCoinPub, const linkSig = await ws.cryptoApi.signCoinLink(
refreshSession.transferPubs[norevealIndex], meltCoinRecord.coinPriv,
planchets[i].coinEv, dsel.denomPubHash,
); meltCoinRecord.coinPub,
linkSigs.push(linkSig); derived.transferPubs[norevealIndex],
planchets[newCoinIndex].coinEv,
);
linkSigs.push(linkSig);
newDenomsFlat.push(dsel.denomPubHash);
}
} }
const req = { const req = {
coin_evs: evs, coin_evs: evs,
new_denoms_h: refreshSession.newDenomHashes, new_denoms_h: newDenomsFlat,
rc: refreshSession.hash, rc: derived.hash,
transfer_privs: privs, transfer_privs: privs,
transfer_pub: refreshSession.transferPubs[norevealIndex], transfer_pub: derived.transferPubs[norevealIndex],
link_sigs: linkSigs, link_sigs: linkSigs,
}; };
const reqUrl = new URL( const reqUrl = new URL(
`refreshes/${refreshSession.hash}/reveal`, `refreshes/${derived.hash}/reveal`,
refreshSession.exchangeBaseUrl, oldCoin.exchangeBaseUrl,
); );
const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], async () => { const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], async () => {
@ -362,39 +446,42 @@ async function refreshReveal(
const coins: CoinRecord[] = []; const coins: CoinRecord[] = [];
for (let i = 0; i < reveal.ev_sigs.length; i++) { for (let i = 0; i < refreshSession.newDenoms.length; i++) {
const denom = await ws.db.get(Stores.denominations, [ for (let j = 0; j < refreshSession.newDenoms[i].count; j++) {
refreshSession.exchangeBaseUrl, const newCoinIndex = coins.length;
refreshSession.newDenomHashes[i], const denom = await ws.db.get(Stores.denominations, [
]); oldCoin.exchangeBaseUrl,
if (!denom) { refreshSession.newDenoms[i].denomPubHash,
console.error("denom not found"); ]);
continue; if (!denom) {
} console.error("denom not found");
const pc = refreshSession.planchetsForGammas[norevealIndex][i]; continue;
const denomSig = await ws.cryptoApi.rsaUnblind( }
reveal.ev_sigs[i].ev_sig, const pc = derived.planchetsForGammas[norevealIndex][newCoinIndex];
pc.blindingKey, const denomSig = await ws.cryptoApi.rsaUnblind(
denom.denomPub, reveal.ev_sigs[newCoinIndex].ev_sig,
); pc.blindingKey,
const coin: CoinRecord = { denom.denomPub,
blindingKey: pc.blindingKey, );
coinPriv: pc.privateKey, const coin: CoinRecord = {
coinPub: pc.publicKey, blindingKey: pc.blindingKey,
currentAmount: denom.value, coinPriv: pc.privateKey,
denomPub: denom.denomPub, coinPub: pc.publicKey,
denomPubHash: denom.denomPubHash, currentAmount: denom.value,
denomSig, denomPub: denom.denomPub,
exchangeBaseUrl: refreshSession.exchangeBaseUrl, denomPubHash: denom.denomPubHash,
status: CoinStatus.Fresh, denomSig,
coinSource: { exchangeBaseUrl: oldCoin.exchangeBaseUrl,
type: CoinSourceType.Refresh, status: CoinStatus.Fresh,
oldCoinPub: refreshSession.meltCoinPub, coinSource: {
}, type: CoinSourceType.Refresh,
suspended: false, oldCoinPub: refreshGroup.oldCoinPubs[coinIndex],
}; },
suspended: false,
};
coins.push(coin); coins.push(coin);
}
} }
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
@ -409,11 +496,6 @@ async function refreshReveal(
if (!rs) { if (!rs) {
return; return;
} }
if (rs.finishedTimestamp) {
logger.warn("refresh session already finished");
return;
}
rs.finishedTimestamp = getTimestampNow();
rg.finishedPerCoin[coinIndex] = true; rg.finishedPerCoin[coinIndex] = true;
let allDone = true; let allDone = true;
for (const f of rg.finishedPerCoin) { for (const f of rg.finishedPerCoin) {

View File

@ -0,0 +1,111 @@
/*
This file is part of GNU Taler
(C) 2020 Taler Systems SA
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/>
*/
/**
* Types used by the wallet crypto worker.
*
* These types are defined in a separate file make tree shaking easier, since
* some components use these types (via RPC) but do not depend on the wallet
* code directly.
*
* @author Florian Dold <dold@taler.net>
*/
/**
* Imports.
*/
import { AmountJson } from "../util/amounts";
export interface RefreshNewDenomInfo {
count: number;
value: AmountJson;
feeWithdraw: AmountJson;
denomPub: string;
}
/**
* Request to derive a refresh session from the refresh session
* secret seed.
*/
export interface DeriveRefreshSessionRequest {
sessionSecretSeed: string;
kappa: number;
meltCoinPub: string;
meltCoinPriv: string;
meltCoinDenomPubHash: string;
newCoinDenoms: RefreshNewDenomInfo[];
feeRefresh: AmountJson;
}
/**
*
*/
export interface DerivedRefreshSession {
/**
* Public key that's being melted in this session.
*/
meltCoinPub: string;
/**
* Signature to confirm the melting.
*/
confirmSig: string;
/**
* Planchets for each cut-and-choose instance.
*/
planchetsForGammas: {
/**
* Public key for the coin.
*/
publicKey: string;
/**
* Private key for the coin.
*/
privateKey: string;
/**
* Blinded public key.
*/
coinEv: string;
/**
* Blinding key used.
*/
blindingKey: string;
}[][];
/**
* The transfer keys, kappa of them.
*/
transferPubs: string[];
/**
* Private keys for the transfer public keys.
*/
transferPrivs: string[];
/**
* Hash of the session.
*/
hash: string;
/**
* Exact value that is being melted.
*/
meltValueWithFee: AmountJson;
}

View File

@ -557,7 +557,7 @@ export interface ExchangeRecord {
/** /**
* Terms of service text or undefined if not downloaded yet. * Terms of service text or undefined if not downloaded yet.
* *
* This is just used as a cache of the last downloaded ToS. * This is just used as a cache of the last downloaded ToS.
*/ */
termsOfServiceText: string | undefined; termsOfServiceText: string | undefined;
@ -664,14 +664,17 @@ export interface RefreshPlanchet {
* Public key for the coin. * Public key for the coin.
*/ */
publicKey: string; publicKey: string;
/** /**
* Private key for the coin. * Private key for the coin.
*/ */
privateKey: string; privateKey: string;
/** /**
* Blinded public key. * Blinded public key.
*/ */
coinEv: string; coinEv: string;
/** /**
* Blinding key used. * Blinding key used.
*/ */
@ -991,18 +994,14 @@ export interface RefreshGroupRecord {
* Ongoing refresh * Ongoing refresh
*/ */
export interface RefreshSessionRecord { export interface RefreshSessionRecord {
lastError: TalerErrorDetails | undefined;
/** /**
* Public key that's being melted in this session. * 512-bit secret that can be used to derive
* the other cryptographic material for the refresh session.
*
* FIXME: We currently store the derived material, but
* should always derive it.
*/ */
meltCoinPub: string; sessionSecretSeed: string;
/**
* How much of the coin's value is melted away
* with this refresh session?
*/
amountRefreshInput: AmountJson;
/** /**
* Sum of the value of denominations we want * Sum of the value of denominations we want
@ -1011,59 +1010,17 @@ export interface RefreshSessionRecord {
amountRefreshOutput: AmountJson; amountRefreshOutput: AmountJson;
/** /**
* Signature to confirm the melting. * Hashed denominations of the newly requested coins.
*/ */
confirmSig: string; newDenoms: {
denomPubHash: string;
/** count: number;
* Hased denominations of the newly requested coins. }[];
*/
newDenomHashes: string[];
/**
* Denominations of the newly requested coins.
*/
newDenoms: string[];
/**
* Planchets for each cut-and-choose instance.
*/
planchetsForGammas: RefreshPlanchet[][];
/**
* The transfer keys, kappa of them.
*/
transferPubs: string[];
/**
* Private keys for the transfer public keys.
*/
transferPrivs: string[];
/** /**
* The no-reveal-index after we've done the melting. * The no-reveal-index after we've done the melting.
*/ */
norevealIndex?: number; norevealIndex?: number;
/**
* Hash of the session.
*/
hash: string;
/**
* Timestamp when the refresh session finished.
*/
finishedTimestamp: Timestamp | undefined;
/**
* When has this refresh session been created?
*/
timestampCreated: Timestamp;
/**
* Base URL for the exchange we're doing the refresh with.
*/
exchangeBaseUrl: string;
} }
/** /**
@ -1602,7 +1559,7 @@ class PurchasesStore extends Store<"purchases", PurchaseRecord> {
string, string,
PurchaseRecord PurchaseRecord
>(this, "fulfillmentUrlIndex", "contractData.fulfillmentUrl"); >(this, "fulfillmentUrlIndex", "contractData.fulfillmentUrl");
orderIdIndex = new Index<"purchases", "orderIdIndex", string, PurchaseRecord>( orderIdIndex = new Index<"purchases", "orderIdIndex", string, PurchaseRecord>(
this, this,
"orderIdIndex", "orderIdIndex",
@ -1712,7 +1669,6 @@ class BankWithdrawUrisStore extends Store<
} }
} }
/** /**
*/ */
class BackupProvidersStore extends Store< class BackupProvidersStore extends Store<