derive tipping planchets from seed, implement backup further
This commit is contained in:
parent
f332d61fb6
commit
c09c5bbe62
@ -390,6 +390,25 @@ export function setupRefreshPlanchet(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setupTipPlanchet(
|
||||||
|
secretSeed: Uint8Array,
|
||||||
|
coinNumber: number,
|
||||||
|
): FreshCoin {
|
||||||
|
const info = stringToBytes("taler-tip-coin-derivation");
|
||||||
|
const saltArrBuf = new ArrayBuffer(4);
|
||||||
|
const salt = new Uint8Array(saltArrBuf);
|
||||||
|
const saltDataView = new DataView(saltArrBuf);
|
||||||
|
saltDataView.setUint32(0, coinNumber);
|
||||||
|
const out = kdf(64, secretSeed, salt, info);
|
||||||
|
const coinPriv = out.slice(0, 32);
|
||||||
|
const bks = out.slice(32, 64);
|
||||||
|
return {
|
||||||
|
bks,
|
||||||
|
coinPriv,
|
||||||
|
coinPub: eddsaGetPublic(coinPriv),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function setupRefreshTransferPub(
|
export function setupRefreshTransferPub(
|
||||||
secretSeed: Uint8Array,
|
secretSeed: Uint8Array,
|
||||||
transferPubIndex: number,
|
transferPubIndex: number,
|
||||||
|
@ -22,16 +22,7 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { AmountJson } from "../../util/amounts";
|
import { CoinRecord, DenominationRecord, WireFee } from "../../types/dbTypes";
|
||||||
|
|
||||||
import {
|
|
||||||
CoinRecord,
|
|
||||||
DenominationRecord,
|
|
||||||
RefreshSessionRecord,
|
|
||||||
TipPlanchet,
|
|
||||||
WireFee,
|
|
||||||
DenominationSelectionInfo,
|
|
||||||
} from "../../types/dbTypes";
|
|
||||||
|
|
||||||
import { CryptoWorker } from "./cryptoWorker";
|
import { CryptoWorker } from "./cryptoWorker";
|
||||||
|
|
||||||
@ -49,7 +40,9 @@ import * as timer from "../../util/timer";
|
|||||||
import { Logger } from "../../util/logging";
|
import { Logger } from "../../util/logging";
|
||||||
import {
|
import {
|
||||||
DerivedRefreshSession,
|
DerivedRefreshSession,
|
||||||
|
DerivedTipPlanchet,
|
||||||
DeriveRefreshSessionRequest,
|
DeriveRefreshSessionRequest,
|
||||||
|
DeriveTipRequest,
|
||||||
} from "../../types/cryptoTypes";
|
} from "../../types/cryptoTypes";
|
||||||
|
|
||||||
const logger = new Logger("cryptoApi.ts");
|
const logger = new Logger("cryptoApi.ts");
|
||||||
@ -329,8 +322,8 @@ export class CryptoApi {
|
|||||||
return this.doRpc<PlanchetCreationResult>("createPlanchet", 1, req);
|
return this.doRpc<PlanchetCreationResult>("createPlanchet", 1, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
createTipPlanchet(denom: DenominationRecord): Promise<TipPlanchet> {
|
createTipPlanchet(req: DeriveTipRequest): Promise<DerivedTipPlanchet> {
|
||||||
return this.doRpc<TipPlanchet>("createTipPlanchet", 1, denom);
|
return this.doRpc<DerivedTipPlanchet>("createTipPlanchet", 1, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
hashString(str: string): Promise<string> {
|
hashString(str: string): Promise<string> {
|
||||||
|
@ -30,11 +30,8 @@ import {
|
|||||||
CoinRecord,
|
CoinRecord,
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
RefreshPlanchet,
|
RefreshPlanchet,
|
||||||
RefreshSessionRecord,
|
|
||||||
TipPlanchet,
|
|
||||||
WireFee,
|
WireFee,
|
||||||
CoinSourceType,
|
CoinSourceType,
|
||||||
DenominationSelectionInfo,
|
|
||||||
} from "../../types/dbTypes";
|
} from "../../types/dbTypes";
|
||||||
|
|
||||||
import { CoinDepositPermission, RecoupRequest } from "../../types/talerTypes";
|
import { CoinDepositPermission, RecoupRequest } from "../../types/talerTypes";
|
||||||
@ -59,25 +56,25 @@ import {
|
|||||||
rsaUnblind,
|
rsaUnblind,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
createHashContext,
|
createHashContext,
|
||||||
createEcdheKeyPair,
|
|
||||||
keyExchangeEcdheEddsa,
|
keyExchangeEcdheEddsa,
|
||||||
setupRefreshPlanchet,
|
setupRefreshPlanchet,
|
||||||
rsaVerify,
|
rsaVerify,
|
||||||
getRandomBytes,
|
|
||||||
setupRefreshTransferPub,
|
setupRefreshTransferPub,
|
||||||
|
setupTipPlanchet,
|
||||||
} 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";
|
||||||
import {
|
import {
|
||||||
Timestamp,
|
Timestamp,
|
||||||
getTimestampNow,
|
|
||||||
timestampTruncateToSecond,
|
timestampTruncateToSecond,
|
||||||
} from "../../util/time";
|
} from "../../util/time";
|
||||||
|
|
||||||
import { Logger } from "../../util/logging";
|
import { Logger } from "../../util/logging";
|
||||||
import {
|
import {
|
||||||
DerivedRefreshSession,
|
DerivedRefreshSession,
|
||||||
|
DerivedTipPlanchet,
|
||||||
DeriveRefreshSessionRequest,
|
DeriveRefreshSessionRequest,
|
||||||
|
DeriveTipRequest,
|
||||||
} from "../../types/cryptoTypes";
|
} from "../../types/cryptoTypes";
|
||||||
|
|
||||||
const logger = new Logger("cryptoImplementation.ts");
|
const logger = new Logger("cryptoImplementation.ts");
|
||||||
@ -199,21 +196,18 @@ export class CryptoImplementation {
|
|||||||
/**
|
/**
|
||||||
* Create a planchet used for tipping, including the private keys.
|
* Create a planchet used for tipping, including the private keys.
|
||||||
*/
|
*/
|
||||||
createTipPlanchet(denom: DenominationRecord): TipPlanchet {
|
createTipPlanchet(req: DeriveTipRequest): DerivedTipPlanchet {
|
||||||
const denomPub = decodeCrock(denom.denomPub);
|
const fc = setupTipPlanchet(decodeCrock(req.secretSeed), req.planchetIndex);
|
||||||
const coinKeyPair = createEddsaKeyPair();
|
const denomPub = decodeCrock(req.denomPub);
|
||||||
const blindingFactor = createBlindingKeySecret();
|
const blindingFactor = createBlindingKeySecret();
|
||||||
const coinPubHash = hash(coinKeyPair.eddsaPub);
|
const coinPubHash = hash(fc.coinPub);
|
||||||
const ev = rsaBlind(coinPubHash, blindingFactor, denomPub);
|
const ev = rsaBlind(coinPubHash, blindingFactor, denomPub);
|
||||||
|
|
||||||
const tipPlanchet: TipPlanchet = {
|
const tipPlanchet: DerivedTipPlanchet = {
|
||||||
blindingKey: encodeCrock(blindingFactor),
|
blindingKey: encodeCrock(blindingFactor),
|
||||||
coinEv: encodeCrock(ev),
|
coinEv: encodeCrock(ev),
|
||||||
coinPriv: encodeCrock(coinKeyPair.eddsaPriv),
|
coinPriv: encodeCrock(fc.coinPriv),
|
||||||
coinPub: encodeCrock(coinKeyPair.eddsaPub),
|
coinPub: encodeCrock(fc.coinPub),
|
||||||
coinValue: denom.value,
|
|
||||||
denomPub: encodeCrock(denomPub),
|
|
||||||
denomPubHash: encodeCrock(hash(denomPub)),
|
|
||||||
};
|
};
|
||||||
return tipPlanchet;
|
return tipPlanchet;
|
||||||
}
|
}
|
||||||
|
@ -26,20 +26,34 @@
|
|||||||
*/
|
*/
|
||||||
import { InternalWalletState } from "./state";
|
import { InternalWalletState } from "./state";
|
||||||
import {
|
import {
|
||||||
|
BackupBackupProvider,
|
||||||
BackupCoin,
|
BackupCoin,
|
||||||
BackupCoinSource,
|
BackupCoinSource,
|
||||||
BackupCoinSourceType,
|
BackupCoinSourceType,
|
||||||
BackupDenomination,
|
BackupDenomination,
|
||||||
BackupExchange,
|
BackupExchange,
|
||||||
BackupExchangeWireFee,
|
BackupExchangeWireFee,
|
||||||
|
BackupProposal,
|
||||||
|
BackupProposalStatus,
|
||||||
|
BackupPurchase,
|
||||||
|
BackupRecoupGroup,
|
||||||
|
BackupRefreshGroup,
|
||||||
|
BackupRefreshOldCoin,
|
||||||
|
BackupRefreshSession,
|
||||||
|
BackupRefundItem,
|
||||||
|
BackupRefundState,
|
||||||
BackupReserve,
|
BackupReserve,
|
||||||
|
BackupTip,
|
||||||
WalletBackupContentV1,
|
WalletBackupContentV1,
|
||||||
} from "../types/backupTypes";
|
} from "../types/backupTypes";
|
||||||
import { TransactionHandle } from "../util/query";
|
import { TransactionHandle } from "../util/query";
|
||||||
import {
|
import {
|
||||||
|
AbortStatus,
|
||||||
CoinSourceType,
|
CoinSourceType,
|
||||||
CoinStatus,
|
CoinStatus,
|
||||||
ConfigRecord,
|
ConfigRecord,
|
||||||
|
ProposalStatus,
|
||||||
|
RefundState,
|
||||||
Stores,
|
Stores,
|
||||||
} from "../types/dbTypes";
|
} from "../types/dbTypes";
|
||||||
import { checkDbInvariant } from "../util/invariants";
|
import { checkDbInvariant } from "../util/invariants";
|
||||||
@ -56,7 +70,7 @@ import {
|
|||||||
import { canonicalizeBaseUrl, canonicalJson, j2s } from "../util/helpers";
|
import { canonicalizeBaseUrl, canonicalJson, j2s } from "../util/helpers";
|
||||||
import { getTimestampNow, Timestamp } from "../util/time";
|
import { getTimestampNow, Timestamp } from "../util/time";
|
||||||
import { URL } from "../util/url";
|
import { URL } from "../util/url";
|
||||||
import { AmountString } from "../types/talerTypes";
|
import { AmountString, TipResponse } from "../types/talerTypes";
|
||||||
import {
|
import {
|
||||||
buildCodecForObject,
|
buildCodecForObject,
|
||||||
Codec,
|
Codec,
|
||||||
@ -146,16 +160,80 @@ export async function exportBackup(
|
|||||||
): Promise<WalletBackupContentV1> {
|
): Promise<WalletBackupContentV1> {
|
||||||
await provideBackupState(ws);
|
await provideBackupState(ws);
|
||||||
return ws.db.runWithWriteTransaction(
|
return ws.db.runWithWriteTransaction(
|
||||||
[Stores.config, Stores.exchanges, Stores.coins, Stores.denominations],
|
[
|
||||||
|
Stores.config,
|
||||||
|
Stores.exchanges,
|
||||||
|
Stores.coins,
|
||||||
|
Stores.denominations,
|
||||||
|
Stores.purchases,
|
||||||
|
Stores.proposals,
|
||||||
|
Stores.refreshGroups,
|
||||||
|
Stores.backupProviders,
|
||||||
|
Stores.tips,
|
||||||
|
Stores.recoupGroups,
|
||||||
|
Stores.reserves,
|
||||||
|
],
|
||||||
async (tx) => {
|
async (tx) => {
|
||||||
const bs = await getWalletBackupState(ws, tx);
|
const bs = await getWalletBackupState(ws, tx);
|
||||||
|
|
||||||
const exchanges: BackupExchange[] = [];
|
const backupExchanges: BackupExchange[] = [];
|
||||||
const coinsByDenom: { [dph: string]: BackupCoin[] } = {};
|
const backupCoinsByDenom: { [dph: string]: BackupCoin[] } = {};
|
||||||
const denominationsByExchange: {
|
const backupDenominationsByExchange: {
|
||||||
[url: string]: BackupDenomination[];
|
[url: string]: BackupDenomination[];
|
||||||
} = {};
|
} = {};
|
||||||
const reservesByExchange: { [url: string]: BackupReserve[] } = {};
|
const backupReservesByExchange: { [url: string]: BackupReserve[] } = {};
|
||||||
|
const backupPurchases: BackupPurchase[] = [];
|
||||||
|
const backupProposals: BackupProposal[] = [];
|
||||||
|
const backupRefreshGroups: BackupRefreshGroup[] = [];
|
||||||
|
const backupBackupProviders: BackupBackupProvider[] = [];
|
||||||
|
const backupTips: BackupTip[] = [];
|
||||||
|
const backupRecoupGroups: BackupRecoupGroup[] = [];
|
||||||
|
|
||||||
|
await tx.iter(Stores.reserves).forEach((reserve) => {
|
||||||
|
// FIXME: implement
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.iter(Stores.tips).forEach((tip) => {
|
||||||
|
backupTips.push({
|
||||||
|
exchange_base_url: tip.exchangeBaseUrl,
|
||||||
|
merchant_base_url: tip.merchantBaseUrl,
|
||||||
|
merchant_tip_id: tip.merchantTipId,
|
||||||
|
wallet_tip_id: tip.walletTipId,
|
||||||
|
secret_seed: tip.secretSeed,
|
||||||
|
selected_denoms: tip.denomsSel.selectedDenoms.map((x) => ({
|
||||||
|
count: x.count,
|
||||||
|
denom_pub_hash: x.denomPubHash,
|
||||||
|
})),
|
||||||
|
timestam_picked_up: tip.pickedUpTimestamp,
|
||||||
|
timestamp_accepted: tip.acceptedTimestamp,
|
||||||
|
timestamp_created: tip.createdTimestamp,
|
||||||
|
timestamp_expiration: tip.tipExpiration,
|
||||||
|
tip_amount_raw: Amounts.stringify(tip.tipAmountRaw),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.iter(Stores.recoupGroups).forEach((recoupGroup) => {
|
||||||
|
backupRecoupGroups.push({
|
||||||
|
recoup_group_id: recoupGroup.recoupGroupId,
|
||||||
|
timestamp_started: recoupGroup.timestampStarted,
|
||||||
|
timestamp_finished: recoupGroup.timestampFinished,
|
||||||
|
coins: recoupGroup.coinPubs.map((x, i) => ({
|
||||||
|
coin_pub: x,
|
||||||
|
recoup_finished: recoupGroup.recoupFinishedPerCoin[i],
|
||||||
|
old_amount: Amounts.stringify(recoupGroup.oldAmountPerCoin[i]),
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.iter(Stores.backupProviders).forEach((bp) => {
|
||||||
|
backupBackupProviders.push({
|
||||||
|
annual_fee: Amounts.stringify(bp.annualFee),
|
||||||
|
base_url: canonicalizeBaseUrl(bp.baseUrl),
|
||||||
|
pay_proposal_ids: [],
|
||||||
|
storage_limit_in_megabytes: bp.storageLimitInMegabytes,
|
||||||
|
supported_protocol_version: bp.supportedProtocolVersion,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
await tx.iter(Stores.coins).forEach((coin) => {
|
await tx.iter(Stores.coins).forEach((coin) => {
|
||||||
let bcs: BackupCoinSource;
|
let bcs: BackupCoinSource;
|
||||||
@ -183,7 +261,7 @@ export async function exportBackup(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const coins = (coinsByDenom[coin.denomPubHash] ??= []);
|
const coins = (backupCoinsByDenom[coin.denomPubHash] ??= []);
|
||||||
coins.push({
|
coins.push({
|
||||||
blinding_key: coin.blindingKey,
|
blinding_key: coin.blindingKey,
|
||||||
coin_priv: coin.coinPriv,
|
coin_priv: coin.coinPriv,
|
||||||
@ -195,11 +273,11 @@ export async function exportBackup(
|
|||||||
});
|
});
|
||||||
|
|
||||||
await tx.iter(Stores.denominations).forEach((denom) => {
|
await tx.iter(Stores.denominations).forEach((denom) => {
|
||||||
const backupDenoms = (denominationsByExchange[
|
const backupDenoms = (backupDenominationsByExchange[
|
||||||
denom.exchangeBaseUrl
|
denom.exchangeBaseUrl
|
||||||
] ??= []);
|
] ??= []);
|
||||||
backupDenoms.push({
|
backupDenoms.push({
|
||||||
coins: coinsByDenom[denom.denomPubHash] ?? [],
|
coins: backupCoinsByDenom[denom.denomPubHash] ?? [],
|
||||||
denom_pub: denom.denomPub,
|
denom_pub: denom.denomPub,
|
||||||
fee_deposit: Amounts.stringify(denom.feeDeposit),
|
fee_deposit: Amounts.stringify(denom.feeDeposit),
|
||||||
fee_refresh: Amounts.stringify(denom.feeRefresh),
|
fee_refresh: Amounts.stringify(denom.feeRefresh),
|
||||||
@ -247,7 +325,7 @@ export async function exportBackup(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
exchanges.push({
|
backupExchanges.push({
|
||||||
base_url: ex.baseUrl,
|
base_url: ex.baseUrl,
|
||||||
accounts: ex.wireInfo.accounts.map((x) => ({
|
accounts: ex.wireInfo.accounts.map((x) => ({
|
||||||
payto_uri: x.payto_uri,
|
payto_uri: x.payto_uri,
|
||||||
@ -271,8 +349,132 @@ export async function exportBackup(
|
|||||||
})),
|
})),
|
||||||
tos_etag_accepted: ex.termsOfServiceAcceptedEtag,
|
tos_etag_accepted: ex.termsOfServiceAcceptedEtag,
|
||||||
tos_etag_last: ex.termsOfServiceLastEtag,
|
tos_etag_last: ex.termsOfServiceLastEtag,
|
||||||
denominations: denominationsByExchange[ex.baseUrl] ?? [],
|
denominations: backupDenominationsByExchange[ex.baseUrl] ?? [],
|
||||||
reserves: reservesByExchange[ex.baseUrl] ?? [],
|
reserves: backupReservesByExchange[ex.baseUrl] ?? [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const purchaseProposalIdSet = new Set<string>();
|
||||||
|
|
||||||
|
await tx.iter(Stores.purchases).forEach((purch) => {
|
||||||
|
const refunds: BackupRefundItem[] = [];
|
||||||
|
purchaseProposalIdSet.add(purch.proposalId);
|
||||||
|
for (const refundKey of Object.keys(purch.refunds)) {
|
||||||
|
const ri = purch.refunds[refundKey];
|
||||||
|
const common = {
|
||||||
|
coin_pub: ri.coinPub,
|
||||||
|
execution_time: ri.executionTime,
|
||||||
|
obtained_time: ri.obtainedTime,
|
||||||
|
refund_amount: Amounts.stringify(ri.refundAmount),
|
||||||
|
rtransaction_id: ri.rtransactionId,
|
||||||
|
total_refresh_cost_bound: Amounts.stringify(
|
||||||
|
ri.totalRefreshCostBound,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
switch (ri.type) {
|
||||||
|
case RefundState.Applied:
|
||||||
|
refunds.push({ type: BackupRefundState.Applied, ...common });
|
||||||
|
break;
|
||||||
|
case RefundState.Failed:
|
||||||
|
refunds.push({ type: BackupRefundState.Failed, ...common });
|
||||||
|
break;
|
||||||
|
case RefundState.Pending:
|
||||||
|
refunds.push({ type: BackupRefundState.Pending, ...common });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backupPurchases.push({
|
||||||
|
clock_created: 1,
|
||||||
|
contract_terms_raw: purch.contractTermsRaw,
|
||||||
|
auto_refund_deadline: purch.autoRefundDeadline,
|
||||||
|
merchant_pay_sig: purch.merchantPaySig,
|
||||||
|
pay_coins: purch.payCoinSelection.coinPubs.map((x, i) => ({
|
||||||
|
coin_pub: x,
|
||||||
|
contribution: Amounts.stringify(
|
||||||
|
purch.payCoinSelection.coinContributions[i],
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
proposal_id: purch.proposalId,
|
||||||
|
refunds,
|
||||||
|
timestamp_accept: purch.timestampAccept,
|
||||||
|
timestamp_first_successful_pay: purch.timestampFirstSuccessfulPay,
|
||||||
|
timestamp_last_refund_status: purch.timestampLastRefundStatus,
|
||||||
|
abort_status:
|
||||||
|
purch.abortStatus === AbortStatus.None
|
||||||
|
? undefined
|
||||||
|
: purch.abortStatus,
|
||||||
|
nonce_priv: purch.noncePriv,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.iter(Stores.proposals).forEach((prop) => {
|
||||||
|
if (purchaseProposalIdSet.has(prop.proposalId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let propStatus: BackupProposalStatus;
|
||||||
|
switch (prop.proposalStatus) {
|
||||||
|
case ProposalStatus.ACCEPTED:
|
||||||
|
return;
|
||||||
|
case ProposalStatus.DOWNLOADING:
|
||||||
|
case ProposalStatus.PROPOSED:
|
||||||
|
propStatus = BackupProposalStatus.Proposed;
|
||||||
|
break;
|
||||||
|
case ProposalStatus.PERMANENTLY_FAILED:
|
||||||
|
propStatus = BackupProposalStatus.PermanentlyFailed;
|
||||||
|
break;
|
||||||
|
case ProposalStatus.REFUSED:
|
||||||
|
propStatus = BackupProposalStatus.Refused;
|
||||||
|
break;
|
||||||
|
case ProposalStatus.REPURCHASE:
|
||||||
|
propStatus = BackupProposalStatus.Repurchase;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
backupProposals.push({
|
||||||
|
claim_token: prop.claimToken,
|
||||||
|
nonce_priv: prop.noncePriv,
|
||||||
|
proposal_id: prop.noncePriv,
|
||||||
|
proposal_status: propStatus,
|
||||||
|
repurchase_proposal_id: prop.repurchaseProposalId,
|
||||||
|
timestamp: prop.timestamp,
|
||||||
|
contract_terms_raw: prop.download?.contractTermsRaw,
|
||||||
|
download_session_id: prop.downloadSessionId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.iter(Stores.refreshGroups).forEach((rg) => {
|
||||||
|
const oldCoins: BackupRefreshOldCoin[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < rg.oldCoinPubs.length; i++) {
|
||||||
|
let refreshSession: BackupRefreshSession | undefined;
|
||||||
|
const s = rg.refreshSessionPerCoin[i];
|
||||||
|
if (s) {
|
||||||
|
refreshSession = {
|
||||||
|
new_denoms: s.newDenoms.map((x) => ({
|
||||||
|
count: x.count,
|
||||||
|
denom_pub_hash: x.denomPubHash,
|
||||||
|
})),
|
||||||
|
session_secret_seed: s.sessionSecretSeed,
|
||||||
|
noreveal_index: s.norevealIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
oldCoins.push({
|
||||||
|
coin_pub: rg.oldCoinPubs[i],
|
||||||
|
estimated_output_amount: Amounts.stringify(
|
||||||
|
rg.estimatedOutputPerCoin[i],
|
||||||
|
),
|
||||||
|
finished: rg.finishedPerCoin[i],
|
||||||
|
input_amount: Amounts.stringify(rg.inputPerCoin[i]),
|
||||||
|
refresh_session: refreshSession,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
backupRefreshGroups.push({
|
||||||
|
reason: rg.reason as any,
|
||||||
|
refresh_group_id: rg.refreshGroupId,
|
||||||
|
timestamp_started: rg.timestampCreated,
|
||||||
|
timestamp_finished: rg.timestampFinished,
|
||||||
|
old_coins: oldCoins,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -284,16 +486,16 @@ export async function exportBackup(
|
|||||||
schema_id: "gnu-taler-wallet-backup-content",
|
schema_id: "gnu-taler-wallet-backup-content",
|
||||||
schema_version: 1,
|
schema_version: 1,
|
||||||
clocks: bs.clocks,
|
clocks: bs.clocks,
|
||||||
exchanges: exchanges,
|
exchanges: backupExchanges,
|
||||||
wallet_root_pub: bs.walletRootPub,
|
wallet_root_pub: bs.walletRootPub,
|
||||||
backup_providers: [],
|
backup_providers: backupBackupProviders,
|
||||||
current_device_id: bs.deviceId,
|
current_device_id: bs.deviceId,
|
||||||
proposals: [],
|
proposals: backupProposals,
|
||||||
purchase_tombstones: [],
|
purchase_tombstones: [],
|
||||||
purchases: [],
|
purchases: backupPurchases,
|
||||||
recoup_groups: [],
|
recoup_groups: backupRecoupGroups,
|
||||||
refresh_groups: [],
|
refresh_groups: backupRefreshGroups,
|
||||||
tips: [],
|
tips: backupTips,
|
||||||
timestamp: bs.lastBackupTimestamp,
|
timestamp: bs.lastBackupTimestamp,
|
||||||
trusted_auditors: {},
|
trusted_auditors: {},
|
||||||
trusted_exchanges: {},
|
trusted_exchanges: {},
|
||||||
|
@ -711,6 +711,7 @@ export async function createRefreshGroup(
|
|||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
inputPerCoin,
|
inputPerCoin,
|
||||||
estimatedOutputPerCoin,
|
estimatedOutputPerCoin,
|
||||||
|
timestampCreated: getTimestampNow(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (oldCoinPubs.length == 0) {
|
if (oldCoinPubs.length == 0) {
|
||||||
|
@ -158,6 +158,8 @@ async function applySuccessfulRefund(
|
|||||||
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
||||||
refundFee: denom.feeRefund,
|
refundFee: denom.feeRefund,
|
||||||
totalRefreshCostBound,
|
totalRefreshCostBound,
|
||||||
|
coinPub: r.coin_pub,
|
||||||
|
rtransactionId: r.rtransaction_id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,6 +210,8 @@ async function storePendingRefund(
|
|||||||
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
||||||
refundFee: denom.feeRefund,
|
refundFee: denom.feeRefund,
|
||||||
totalRefreshCostBound,
|
totalRefreshCostBound,
|
||||||
|
coinPub: r.coin_pub,
|
||||||
|
rtransactionId: r.rtransaction_id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +263,8 @@ async function storeFailedRefund(
|
|||||||
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
||||||
refundFee: denom.feeRefund,
|
refundFee: denom.feeRefund,
|
||||||
totalRefreshCostBound,
|
totalRefreshCostBound,
|
||||||
|
coinPub: r.coin_pub,
|
||||||
|
rtransactionId: r.rtransaction_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (p.abortStatus === AbortStatus.AbortRefund) {
|
if (p.abortStatus === AbortStatus.AbortRefund) {
|
||||||
|
@ -25,10 +25,10 @@ import {
|
|||||||
import * as Amounts from "../util/amounts";
|
import * as Amounts from "../util/amounts";
|
||||||
import {
|
import {
|
||||||
Stores,
|
Stores,
|
||||||
TipPlanchet,
|
|
||||||
CoinRecord,
|
CoinRecord,
|
||||||
CoinSourceType,
|
CoinSourceType,
|
||||||
CoinStatus,
|
CoinStatus,
|
||||||
|
DenominationRecord,
|
||||||
} from "../types/dbTypes";
|
} from "../types/dbTypes";
|
||||||
import {
|
import {
|
||||||
getExchangeWithdrawalInfo,
|
getExchangeWithdrawalInfo,
|
||||||
@ -50,6 +50,7 @@ import { checkDbInvariant } from "../util/invariants";
|
|||||||
import { TalerErrorCode } from "../TalerErrorCode";
|
import { TalerErrorCode } from "../TalerErrorCode";
|
||||||
import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries";
|
import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries";
|
||||||
import { j2s } from "../util/helpers";
|
import { j2s } from "../util/helpers";
|
||||||
|
import { DerivedTipPlanchet } from '../types/cryptoTypes';
|
||||||
|
|
||||||
const logger = new Logger("operations/tip.ts");
|
const logger = new Logger("operations/tip.ts");
|
||||||
|
|
||||||
@ -201,39 +202,34 @@ async function processTipImpl(
|
|||||||
|
|
||||||
const denomsForWithdraw = tipRecord.denomsSel;
|
const denomsForWithdraw = tipRecord.denomsSel;
|
||||||
|
|
||||||
if (!tipRecord.planchets) {
|
|
||||||
const planchets: TipPlanchet[] = [];
|
|
||||||
|
|
||||||
for (const sd of denomsForWithdraw.selectedDenoms) {
|
|
||||||
const denom = await ws.db.get(Stores.denominations, [
|
|
||||||
tipRecord.exchangeBaseUrl,
|
|
||||||
sd.denomPubHash,
|
|
||||||
]);
|
|
||||||
if (!denom) {
|
|
||||||
throw Error("denom does not exist anymore");
|
|
||||||
}
|
|
||||||
for (let i = 0; i < sd.count; i++) {
|
|
||||||
const r = await ws.cryptoApi.createTipPlanchet(denom);
|
|
||||||
planchets.push(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await ws.db.mutate(Stores.tips, walletTipId, (r) => {
|
|
||||||
if (!r.planchets) {
|
|
||||||
r.planchets = planchets;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
tipRecord = await ws.db.get(Stores.tips, walletTipId);
|
tipRecord = await ws.db.get(Stores.tips, walletTipId);
|
||||||
checkDbInvariant(!!tipRecord, "tip record should be in database");
|
checkDbInvariant(!!tipRecord, "tip record should be in database");
|
||||||
checkDbInvariant(!!tipRecord.planchets, "tip record should have planchets");
|
|
||||||
|
|
||||||
|
const planchets: DerivedTipPlanchet[] = [];
|
||||||
// Planchets in the form that the merchant expects
|
// Planchets in the form that the merchant expects
|
||||||
const planchetsDetail: TipPlanchetDetail[] = tipRecord.planchets.map((p) => ({
|
const planchetsDetail: TipPlanchetDetail[] = [];
|
||||||
coin_ev: p.coinEv,
|
const denomForPlanchet: { [index: number]: DenominationRecord} = [];
|
||||||
denom_pub_hash: p.denomPubHash,
|
|
||||||
}));
|
for (const dh of denomsForWithdraw.selectedDenoms) {
|
||||||
|
const denom = await ws.db.get(Stores.denominations, [
|
||||||
|
tipRecord.exchangeBaseUrl,
|
||||||
|
dh.denomPubHash,
|
||||||
|
]);
|
||||||
|
checkDbInvariant(!!denom, "denomination should be in database");
|
||||||
|
denomForPlanchet[planchets.length] = denom;
|
||||||
|
for (let i = 0; i < dh.count; i++) {
|
||||||
|
const p = await ws.cryptoApi.createTipPlanchet({
|
||||||
|
denomPub: dh.denomPubHash,
|
||||||
|
planchetIndex: planchets.length,
|
||||||
|
secretSeed: tipRecord.secretSeed,
|
||||||
|
});
|
||||||
|
planchets.push(p);
|
||||||
|
planchetsDetail.push({
|
||||||
|
coin_ev: p.coinEv,
|
||||||
|
denom_pub_hash: denom.denomPubHash,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const tipStatusUrl = new URL(
|
const tipStatusUrl = new URL(
|
||||||
`tips/${tipRecord.merchantTipId}/pickup`,
|
`tips/${tipRecord.merchantTipId}/pickup`,
|
||||||
@ -264,7 +260,7 @@ async function processTipImpl(
|
|||||||
codecForTipResponse(),
|
codecForTipResponse(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.blind_sigs.length !== tipRecord.planchets.length) {
|
if (response.blind_sigs.length !== planchets.length) {
|
||||||
throw Error("number of tip responses does not match requested planchets");
|
throw Error("number of tip responses does not match requested planchets");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,18 +269,19 @@ async function processTipImpl(
|
|||||||
for (let i = 0; i < response.blind_sigs.length; i++) {
|
for (let i = 0; i < response.blind_sigs.length; i++) {
|
||||||
const blindedSig = response.blind_sigs[i].blind_sig;
|
const blindedSig = response.blind_sigs[i].blind_sig;
|
||||||
|
|
||||||
const planchet = tipRecord.planchets[i];
|
const denom = denomForPlanchet[i];
|
||||||
|
const planchet = planchets[i];
|
||||||
|
|
||||||
const denomSig = await ws.cryptoApi.rsaUnblind(
|
const denomSig = await ws.cryptoApi.rsaUnblind(
|
||||||
blindedSig,
|
blindedSig,
|
||||||
planchet.blindingKey,
|
planchet.blindingKey,
|
||||||
planchet.denomPub,
|
denom.denomPub,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isValid = await ws.cryptoApi.rsaVerify(
|
const isValid = await ws.cryptoApi.rsaVerify(
|
||||||
planchet.coinPub,
|
planchet.coinPub,
|
||||||
denomSig,
|
denomSig,
|
||||||
planchet.denomPub,
|
denom.denomPub,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
@ -312,9 +309,9 @@ async function processTipImpl(
|
|||||||
coinIndex: i,
|
coinIndex: i,
|
||||||
walletTipId: walletTipId,
|
walletTipId: walletTipId,
|
||||||
},
|
},
|
||||||
currentAmount: planchet.coinValue,
|
currentAmount: denom.value,
|
||||||
denomPub: planchet.denomPub,
|
denomPub: denom.denomPub,
|
||||||
denomPubHash: planchet.denomPubHash,
|
denomPubHash: denom.denomPubHash,
|
||||||
denomSig: denomSig,
|
denomSig: denomSig,
|
||||||
exchangeBaseUrl: tipRecord.exchangeBaseUrl,
|
exchangeBaseUrl: tipRecord.exchangeBaseUrl,
|
||||||
status: CoinStatus.Fresh,
|
status: CoinStatus.Fresh,
|
||||||
|
@ -92,7 +92,6 @@ export async function getTransactions(
|
|||||||
Stores.purchases,
|
Stores.purchases,
|
||||||
Stores.refreshGroups,
|
Stores.refreshGroups,
|
||||||
Stores.reserves,
|
Stores.reserves,
|
||||||
Stores.reserveHistory,
|
|
||||||
Stores.tips,
|
Stores.tips,
|
||||||
Stores.withdrawalGroups,
|
Stores.withdrawalGroups,
|
||||||
Stores.planchets,
|
Stores.planchets,
|
||||||
|
@ -24,6 +24,20 @@
|
|||||||
* 1. Exchange/auditor trust isn't exported yet
|
* 1. Exchange/auditor trust isn't exported yet
|
||||||
* (see https://bugs.gnunet.org/view.php?id=6448)
|
* (see https://bugs.gnunet.org/view.php?id=6448)
|
||||||
* 2. Reports to the auditor (cryptographic proofs and/or diagnostics) aren't exported yet
|
* 2. Reports to the auditor (cryptographic proofs and/or diagnostics) aren't exported yet
|
||||||
|
* 3. "Ghost spends", where a coin is spent unexpectedly by another wallet
|
||||||
|
* and a corresponding transaction (that is missing some details!) should
|
||||||
|
* be added to the transaction history, aren't implemented yet.
|
||||||
|
* 4. Clocks for denom/coin selections aren't properly modeled yet.
|
||||||
|
* (Needed for re-denomination of withdrawal / re-selection of coins)
|
||||||
|
* 5. Preferences about how currencies are to be displayed
|
||||||
|
* aren't exported yet (and not even implemented in wallet-core).
|
||||||
|
* 6. Returning money to own bank account isn't supported/exported yet.
|
||||||
|
* 7. Peer-to-peer payments aren't supported yet.
|
||||||
|
*
|
||||||
|
* Questions:
|
||||||
|
* 1. What happens when two backups are merged that have
|
||||||
|
* the same coin in different refresh groups?
|
||||||
|
* => Both are added, one will eventually fail
|
||||||
*
|
*
|
||||||
* General considerations / decisions:
|
* General considerations / decisions:
|
||||||
* 1. Information about previously occurring errors and
|
* 1. Information about previously occurring errors and
|
||||||
@ -318,6 +332,9 @@ export interface BackupRecoupGroup {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp when the recoup finished.
|
* Timestamp when the recoup finished.
|
||||||
|
*
|
||||||
|
* (That means all coins have been recouped and coins to
|
||||||
|
* be refreshed have been put in a refresh group.)
|
||||||
*/
|
*/
|
||||||
timestamp_finished: Timestamp | undefined;
|
timestamp_finished: Timestamp | undefined;
|
||||||
|
|
||||||
@ -326,15 +343,9 @@ export interface BackupRecoupGroup {
|
|||||||
*/
|
*/
|
||||||
coins: {
|
coins: {
|
||||||
coin_pub: string;
|
coin_pub: string;
|
||||||
finished: boolean;
|
recoup_finished: boolean;
|
||||||
old_amount: BackupAmountString;
|
old_amount: BackupAmountString;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
/**
|
|
||||||
* Public keys of coins that should be scheduled for refreshing
|
|
||||||
* after all individual recoups are done.
|
|
||||||
*/
|
|
||||||
recoup_refresh_coins: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -465,6 +476,11 @@ export interface BackupTip {
|
|||||||
*/
|
*/
|
||||||
merchant_tip_id: string;
|
merchant_tip_id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Secret seed used for the tipping planchets.
|
||||||
|
*/
|
||||||
|
secret_seed: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has the user accepted the tip? Only after the tip has been accepted coins
|
* Has the user accepted the tip? Only after the tip has been accepted coins
|
||||||
* withdrawn from the tip may be used.
|
* withdrawn from the tip may be used.
|
||||||
@ -502,15 +518,6 @@ export interface BackupTip {
|
|||||||
*/
|
*/
|
||||||
merchant_base_url: string;
|
merchant_base_url: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Planchets, the members included in TipPlanchetDetail will be sent to the
|
|
||||||
* merchant.
|
|
||||||
*/
|
|
||||||
planchets?: {
|
|
||||||
blinding_key: string;
|
|
||||||
coin_priv: string;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selected denominations. Determines the effective tip amount.
|
* Selected denominations. Determines the effective tip amount.
|
||||||
*/
|
*/
|
||||||
@ -543,7 +550,10 @@ export interface BackupRefreshSession {
|
|||||||
/**
|
/**
|
||||||
* Hased denominations of the newly requested coins.
|
* Hased denominations of the newly requested coins.
|
||||||
*/
|
*/
|
||||||
new_denom_hashes: string[];
|
new_denoms: {
|
||||||
|
count: number;
|
||||||
|
denom_pub_hash: string;
|
||||||
|
}[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Seed used to derive the planchets and
|
* Seed used to derive the planchets and
|
||||||
@ -557,6 +567,39 @@ export interface BackupRefreshSession {
|
|||||||
noreveal_index?: number;
|
noreveal_index?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh session for one coin inside a refresh group.
|
||||||
|
*/
|
||||||
|
export interface BackupRefreshOldCoin {
|
||||||
|
/**
|
||||||
|
* Public key of the old coin,
|
||||||
|
*/
|
||||||
|
coin_pub: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requested amount to refresh. Must be subtracted from the coin's remaining
|
||||||
|
* amount as soon as the coin is added to the refresh group.
|
||||||
|
*/
|
||||||
|
input_amount: BackupAmountString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimated output (may change if it takes a long time to create the
|
||||||
|
* actual session).
|
||||||
|
*/
|
||||||
|
estimated_output_amount: BackupAmountString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Did the refresh session finish (or was it unnecessary/impossible to create
|
||||||
|
* one)
|
||||||
|
*/
|
||||||
|
finished: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh session (if created) or undefined it not created yet.
|
||||||
|
*/
|
||||||
|
refresh_session: BackupRefreshSession | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about one refresh group.
|
* Information about one refresh group.
|
||||||
*
|
*
|
||||||
@ -570,35 +613,9 @@ export interface BackupRefreshGroup {
|
|||||||
/**
|
/**
|
||||||
* Details per old coin.
|
* Details per old coin.
|
||||||
*/
|
*/
|
||||||
old_coins: {
|
old_coins: BackupRefreshOldCoin[];
|
||||||
/**
|
|
||||||
* Public key of the old coin,
|
|
||||||
*/
|
|
||||||
coin_pub: string;
|
|
||||||
|
|
||||||
/**
|
timestamp_started: Timestamp;
|
||||||
* Requested amount to refresh. Must be subtracted from the coin's remaining
|
|
||||||
* amount as soon as the coin is added to the refresh group.
|
|
||||||
*/
|
|
||||||
input_amount: BackupAmountString;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Estimated output (may change if it takes a long time to create the
|
|
||||||
* actual session).
|
|
||||||
*/
|
|
||||||
estimated_output_amount: BackupAmountString;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Did the refresh session finish (or was it unnecessary/impossible to create
|
|
||||||
* one)
|
|
||||||
*/
|
|
||||||
finished: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh session (if created) or undefined it not created yet.
|
|
||||||
*/
|
|
||||||
refresh_session: BackupRefreshSession | undefined;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp when the refresh group finished.
|
* Timestamp when the refresh group finished.
|
||||||
@ -741,22 +758,23 @@ export interface BackupPurchase {
|
|||||||
*/
|
*/
|
||||||
contract_terms_raw: string;
|
contract_terms_raw: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private key for the nonce. Might eventually be used
|
||||||
|
* to prove ownership of the contract.
|
||||||
|
*/
|
||||||
|
nonce_priv: string;
|
||||||
|
|
||||||
pay_coins: {
|
pay_coins: {
|
||||||
/**
|
/**
|
||||||
* Public keys of the coins that were selected.
|
* Public keys of the coins that were selected.
|
||||||
*/
|
*/
|
||||||
coin_pubs: string[];
|
coin_pub: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Deposit permission signature of each coin.
|
|
||||||
*/
|
|
||||||
coin_sigs: string[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount that each coin contributes.
|
* Amount that each coin contributes.
|
||||||
*/
|
*/
|
||||||
contribution: BackupAmountString;
|
contribution: BackupAmountString;
|
||||||
};
|
}[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp of the first time that sending a payment to the merchant
|
* Timestamp of the first time that sending a payment to the merchant
|
||||||
@ -1132,6 +1150,9 @@ export interface BackupReserveHistoryCreditItem {
|
|||||||
matched_exchange_transaction?: ReserveCreditTransaction;
|
matched_exchange_transaction?: ReserveCreditTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserve history item for a withdrawal
|
||||||
|
*/
|
||||||
export interface BackupReserveHistoryWithdrawItem {
|
export interface BackupReserveHistoryWithdrawItem {
|
||||||
type: WalletReserveHistoryItemType.Withdraw;
|
type: WalletReserveHistoryItemType.Withdraw;
|
||||||
|
|
||||||
@ -1141,7 +1162,7 @@ export interface BackupReserveHistoryWithdrawItem {
|
|||||||
* Hash of the blinded coin.
|
* Hash of the blinded coin.
|
||||||
*
|
*
|
||||||
* When this value is set, it indicates that a withdrawal is active
|
* When this value is set, it indicates that a withdrawal is active
|
||||||
* in the wallet for the
|
* in the wallet for the reserve.
|
||||||
*/
|
*/
|
||||||
expected_coin_ev_hash?: string;
|
expected_coin_ev_hash?: string;
|
||||||
|
|
||||||
@ -1183,13 +1204,11 @@ export type BackupReserveHistoryItem =
|
|||||||
| BackupReserveHistoryRecoupItem
|
| BackupReserveHistoryRecoupItem
|
||||||
| BackupReserveHistoryClosingItem;
|
| BackupReserveHistoryClosingItem;
|
||||||
|
|
||||||
export enum ProposalStatus {
|
export enum BackupProposalStatus {
|
||||||
/**
|
/**
|
||||||
* Not downloaded yet.
|
* Proposed (and either downloaded or not,
|
||||||
*/
|
* depending on whether contract terms are present),
|
||||||
Downloading = "downloading",
|
* but the user needs to accept/reject it.
|
||||||
/**
|
|
||||||
* Proposal downloaded, but the user needs to accept/reject it.
|
|
||||||
*/
|
*/
|
||||||
Proposed = "proposed",
|
Proposed = "proposed",
|
||||||
/**
|
/**
|
||||||
@ -1202,6 +1221,8 @@ export enum ProposalStatus {
|
|||||||
Refused = "refused",
|
Refused = "refused",
|
||||||
/**
|
/**
|
||||||
* Downloading or processing the proposal has failed permanently.
|
* Downloading or processing the proposal has failed permanently.
|
||||||
|
*
|
||||||
|
* FIXME: Should this be modeled as a "misbehavior report" instead?
|
||||||
*/
|
*/
|
||||||
PermanentlyFailed = "permanently-failed",
|
PermanentlyFailed = "permanently-failed",
|
||||||
/**
|
/**
|
||||||
@ -1235,11 +1256,6 @@ export interface BackupProposal {
|
|||||||
*/
|
*/
|
||||||
nonce_priv: string;
|
nonce_priv: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Public key for the nonce.
|
|
||||||
*/
|
|
||||||
nonce_pub: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Claim token initially given by the merchant.
|
* Claim token initially given by the merchant.
|
||||||
*/
|
*/
|
||||||
@ -1248,7 +1264,7 @@ export interface BackupProposal {
|
|||||||
/**
|
/**
|
||||||
* Status of the proposal.
|
* Status of the proposal.
|
||||||
*/
|
*/
|
||||||
proposal_status: ProposalStatus;
|
proposal_status: BackupProposalStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proposal that this one got "redirected" to as part of
|
* Proposal that this one got "redirected" to as part of
|
||||||
|
@ -109,3 +109,19 @@ export interface DerivedRefreshSession {
|
|||||||
*/
|
*/
|
||||||
meltValueWithFee: AmountJson;
|
meltValueWithFee: AmountJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DeriveTipRequest {
|
||||||
|
secretSeed: string;
|
||||||
|
denomPub: string;
|
||||||
|
planchetIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tipping planchet stored in the database.
|
||||||
|
*/
|
||||||
|
export interface DerivedTipPlanchet {
|
||||||
|
blindingKey: string;
|
||||||
|
coinEv: string;
|
||||||
|
coinPriv: string;
|
||||||
|
coinPub: string;
|
||||||
|
}
|
||||||
|
@ -109,14 +109,6 @@ export interface WalletReserveHistoryCreditItem {
|
|||||||
export interface WalletReserveHistoryWithdrawItem {
|
export interface WalletReserveHistoryWithdrawItem {
|
||||||
expectedAmount?: AmountJson;
|
expectedAmount?: AmountJson;
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash of the blinded coin.
|
|
||||||
*
|
|
||||||
* When this value is set, it indicates that a withdrawal is active
|
|
||||||
* in the wallet for the
|
|
||||||
*/
|
|
||||||
expectedCoinEvHash?: string;
|
|
||||||
|
|
||||||
type: WalletReserveHistoryItemType.Withdraw;
|
type: WalletReserveHistoryItemType.Withdraw;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -921,11 +913,9 @@ export interface TipRecord {
|
|||||||
merchantBaseUrl: string;
|
merchantBaseUrl: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Planchets, the members included in TipPlanchetDetail will be sent to the
|
* Denomination selection made by the wallet for picking up
|
||||||
* merchant.
|
* this tip.
|
||||||
*/
|
*/
|
||||||
planchets?: TipPlanchet[];
|
|
||||||
|
|
||||||
denomsSel: DenomSelectionState;
|
denomsSel: DenomSelectionState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -933,6 +923,11 @@ export interface TipRecord {
|
|||||||
*/
|
*/
|
||||||
walletTipId: string;
|
walletTipId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Secret seed used to derive planchets for this tip.
|
||||||
|
*/
|
||||||
|
secretSeed: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The merchant's identifier for this tip.
|
* The merchant's identifier for this tip.
|
||||||
*/
|
*/
|
||||||
@ -984,6 +979,8 @@ export interface RefreshGroupRecord {
|
|||||||
*/
|
*/
|
||||||
finishedPerCoin: boolean[];
|
finishedPerCoin: boolean[];
|
||||||
|
|
||||||
|
timestampCreated: Timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp when the refresh session finished.
|
* Timestamp when the refresh session finished.
|
||||||
*/
|
*/
|
||||||
@ -1023,19 +1020,6 @@ export interface RefreshSessionRecord {
|
|||||||
norevealIndex?: number;
|
norevealIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tipping planchet stored in the database.
|
|
||||||
*/
|
|
||||||
export interface TipPlanchet {
|
|
||||||
blindingKey: string;
|
|
||||||
coinEv: string;
|
|
||||||
coinPriv: string;
|
|
||||||
coinPub: string;
|
|
||||||
coinValue: AmountJson;
|
|
||||||
denomPubHash: string;
|
|
||||||
denomPub: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wire fee for one wire method as stored in the
|
* Wire fee for one wire method as stored in the
|
||||||
* wallet's database.
|
* wallet's database.
|
||||||
@ -1106,6 +1090,7 @@ export interface WalletRefundItemCommon {
|
|||||||
obtainedTime: Timestamp;
|
obtainedTime: Timestamp;
|
||||||
|
|
||||||
refundAmount: AmountJson;
|
refundAmount: AmountJson;
|
||||||
|
|
||||||
refundFee: AmountJson;
|
refundFee: AmountJson;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1116,6 +1101,10 @@ export interface WalletRefundItemCommon {
|
|||||||
* coin are refreshed in the same refresh operation.
|
* coin are refreshed in the same refresh operation.
|
||||||
*/
|
*/
|
||||||
totalRefreshCostBound: AmountJson;
|
totalRefreshCostBound: AmountJson;
|
||||||
|
|
||||||
|
coinPub: string;
|
||||||
|
|
||||||
|
rtransactionId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1266,11 +1255,24 @@ export interface PurchaseRecord {
|
|||||||
*/
|
*/
|
||||||
proposalId: string;
|
proposalId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private key for the nonce.
|
||||||
|
*/
|
||||||
|
noncePriv: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public key for the nonce.
|
||||||
|
*/
|
||||||
|
noncePub: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract terms we got from the merchant.
|
* Contract terms we got from the merchant.
|
||||||
*/
|
*/
|
||||||
contractTermsRaw: string;
|
contractTermsRaw: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsed contract terms.
|
||||||
|
*/
|
||||||
contractData: WalletContractData;
|
contractData: WalletContractData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +14,12 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers for dealing with reserve histories.
|
||||||
|
*
|
||||||
|
* @author Florian Dold <dold@taler.net>
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
@ -31,11 +37,8 @@ import { deepCopy } from "./helpers";
|
|||||||
import { AmountJson } from "../util/amounts";
|
import { AmountJson } from "../util/amounts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helpers for dealing with reserve histories.
|
* Result of a reserve reconciliation.
|
||||||
*
|
|
||||||
* @author Florian Dold <dold@taler.net>
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface ReserveReconciliationResult {
|
export interface ReserveReconciliationResult {
|
||||||
/**
|
/**
|
||||||
* The wallet's local history reconciled with the exchange's reserve history.
|
* The wallet's local history reconciled with the exchange's reserve history.
|
||||||
|
Loading…
Reference in New Issue
Block a user