wallet-core: towards DD37 for deposits

This commit is contained in:
Florian Dold 2023-04-22 14:17:49 +02:00
parent e331012c9f
commit 15feebecfe
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
12 changed files with 314 additions and 111 deletions

View File

@ -970,6 +970,19 @@ export class ExchangeService implements ExchangeServiceInterface {
);
}
async runAggregatorOnceWithTimetravel(opts: {
timetravelMicroseconds: number;
}) {
let timetravelArgArr = [];
timetravelArgArr.push(`--timetravel=${opts.timetravelMicroseconds}`);
await runCommand(
this.globalState,
`exchange-${this.name}-aggregator-once`,
"taler-exchange-aggregator",
[...timetravelArgArr, "-c", this.configFilename, "-t"],
);
}
async runAggregatorOnce() {
try {
await runCommand(
@ -1147,6 +1160,9 @@ export class ExchangeService implements ExchangeServiceInterface {
exchangeHttpProc: ProcessWrapper | undefined;
exchangeWirewatchProc: ProcessWrapper | undefined;
exchangeTransferProc: ProcessWrapper | undefined;
exchangeAggregatorProc: ProcessWrapper | undefined;
helperCryptoRsaProc: ProcessWrapper | undefined;
helperCryptoEddsaProc: ProcessWrapper | undefined;
helperCryptoCsProc: ProcessWrapper | undefined;
@ -1200,6 +1216,18 @@ export class ExchangeService implements ExchangeServiceInterface {
await wirewatch.wait();
this.exchangeWirewatchProc = undefined;
}
const aggregatorProc = this.exchangeAggregatorProc;
if (aggregatorProc) {
aggregatorProc.proc.kill("SIGTERM");
await aggregatorProc.wait();
this.exchangeAggregatorProc = undefined;
}
const transferProc = this.exchangeTransferProc;
if (transferProc) {
transferProc.proc.kill("SIGTERM");
await transferProc.wait();
this.exchangeTransferProc = undefined;
}
const httpd = this.exchangeHttpProc;
if (httpd) {
httpd.proc.kill("SIGTERM");
@ -1369,6 +1397,22 @@ export class ExchangeService implements ExchangeServiceInterface {
);
}
private internalCreateAggregatorProc() {
this.exchangeAggregatorProc = this.globalState.spawnService(
"taler-exchange-aggregator",
["-c", this.configFilename, ...this.timetravelArgArr],
`exchange-aggregator-${this.name}`,
);
}
private internalCreateTransferProc() {
this.exchangeTransferProc = this.globalState.spawnService(
"taler-exchange-transfer",
["-c", this.configFilename, ...this.timetravelArgArr],
`exchange-transfer-${this.name}`,
);
}
async start(): Promise<void> {
if (this.isRunning()) {
throw Error("exchange is already running");
@ -1398,6 +1442,8 @@ export class ExchangeService implements ExchangeServiceInterface {
);
this.internalCreateWirewatchProc();
this.internalCreateTransferProc();
this.internalCreateAggregatorProc();
this.exchangeHttpProc = this.globalState.spawnService(
"taler-exchange-httpd",
@ -2062,7 +2108,7 @@ export class WalletService {
[
"--wallet-db",
dbPath,
"-LDEBUG", // FIXME: Make this configurable?
"-LTRACE", // FIXME: Make this configurable?
"--no-throttle", // FIXME: Optionally do throttling for some tests?
"advanced",
"serve",

View File

@ -47,7 +47,14 @@ import { lintExchangeDeployment } from "./lint.js";
import { runEnvFull } from "./env-full.js";
import { clk } from "@gnu-taler/taler-util/clk";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import { BankAccessApiClient } from "@gnu-taler/taler-wallet-core";
import {
BankAccessApiClient,
checkReserve,
CryptoDispatcher,
downloadExchangeInfo,
SynchronousCryptoWorkerFactoryPlain,
topupReserveWithDemobank,
} from "@gnu-taler/taler-wallet-core";
const logger = new Logger("taler-harness:index.ts");
@ -162,7 +169,6 @@ advancedCli
await runTestWithState(testState, runEnv1, "env1", true);
});
const sandcastleCli = testingCli.subcommand("sandcastleArgs", "sandcastle", {
help: "Subcommands for handling GNU Taler sandcastle deployments.",
});
@ -260,6 +266,66 @@ deploymentCli
// FIXME: Now delete reserves that are not filled yet
});
deploymentCli
.subcommand("testTalerdotnetDemo", "test-demo-talerdotnet")
.action(async (args) => {
const http = createPlatformHttpLib();
const cryptiDisp = new CryptoDispatcher(
new SynchronousCryptoWorkerFactoryPlain(),
);
const cryptoApi = cryptiDisp.cryptoApi;
const reserveKeyPair = await cryptoApi.createEddsaKeypair({});
const exchangeBaseUrl = "https://exchange.demo.taler.net/";
const exchangeInfo = await downloadExchangeInfo(exchangeBaseUrl, http);
await topupReserveWithDemobank({
amount: "KUDOS:10",
bankAccessApiBaseUrl:
"https://bank.demo.taler.net/demobanks/default/access-api/",
bankBaseUrl: "",
exchangeInfo,
http,
reservePub: reserveKeyPair.pub,
});
let reserveUrl = new URL(`reserves/${reserveKeyPair.pub}`, exchangeBaseUrl);
reserveUrl.searchParams.set("timeout_ms", "30000");
console.log("requesting", reserveUrl.href);
const longpollReq = http.fetch(reserveUrl.href, {
method: "GET",
});
const reserveStatusResp = await longpollReq;
console.log("reserve status", reserveStatusResp.status);
});
deploymentCli
.subcommand("testLocalhostDemo", "test-demo-localhost")
.action(async (args) => {
// Run checks against the "env-full" demo deployment on localhost
const http = createPlatformHttpLib();
const cryptiDisp = new CryptoDispatcher(
new SynchronousCryptoWorkerFactoryPlain(),
);
const cryptoApi = cryptiDisp.cryptoApi;
const reserveKeyPair = await cryptoApi.createEddsaKeypair({});
const exchangeBaseUrl = "http://localhost:8081/";
const exchangeInfo = await downloadExchangeInfo(exchangeBaseUrl, http);
await topupReserveWithDemobank({
amount: "TESTKUDOS:10",
bankAccessApiBaseUrl: "http://localhost:8082/taler-bank-access/",
bankBaseUrl: "",
exchangeInfo,
http,
reservePub: reserveKeyPair.pub,
});
let reserveUrl = new URL(`reserves/${reserveKeyPair.pub}`, exchangeBaseUrl);
reserveUrl.searchParams.set("timeout_ms", "30000");
console.log("requesting", reserveUrl.href);
const longpollReq = http.fetch(reserveUrl.href, {
method: "GET",
});
const reserveStatusResp = await longpollReq;
console.log("reserve status", reserveStatusResp.status);
});
deploymentCli
.subcommand("tipStatus", "tip-status")
.requiredOption("merchantBaseUrl", ["--merchant-url"], clk.STRING)

View File

@ -17,7 +17,11 @@
/**
* Imports.
*/
import { NotificationType, TransactionState } from "@gnu-taler/taler-util";
import {
NotificationType,
TransactionMajorState,
TransactionMinorState,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, getPayto } from "../harness/harness.js";
import {
@ -52,11 +56,19 @@ export async function runDepositTest(t: GlobalTestState) {
const depositTxId = dgIdResp.transactionId;
const depositTrack = walletClient.waitForNotificationCond(
(n) =>
n.type == NotificationType.TransactionStateTransition &&
n.transactionId == depositTxId &&
n.newTxState.major == TransactionMajorState.Pending &&
n.newTxState.minor == TransactionMinorState.Track,
);
const depositDone = walletClient.waitForNotificationCond(
(n) =>
n.type == NotificationType.TransactionStateTransition &&
n.transactionId == depositTxId &&
n.newTxState == TransactionState.Done,
n.newTxState.major == TransactionMajorState.Done,
);
const depositGroupResult = await walletClient.client.call(
@ -70,6 +82,12 @@ export async function runDepositTest(t: GlobalTestState) {
t.assertDeepEqual(depositGroupResult.transactionId, depositTxId);
await depositTrack;
await exchange.runAggregatorOnceWithTimetravel({
timetravelMicroseconds: 1000 * 1000 * 60 * 60 * 3,
});
await depositDone;
const transactions = await walletClient.client.call(

View File

@ -59,16 +59,29 @@ export async function runWalletDblessTest(t: GlobalTestState) {
const reserveKeyPair = await cryptoApi.createEddsaKeypair({});
await topupReserveWithDemobank(
http,
reserveKeyPair.pub,
bank.baseUrl,
bank.bankAccessApiBaseUrl,
exchangeInfo,
"TESTKUDOS:10",
let reserveUrl = new URL(
`reserves/${reserveKeyPair.pub}`,
exchange.baseUrl,
);
reserveUrl.searchParams.set("timeout_ms", "30000");
const longpollReq = http.fetch(reserveUrl.href, {
method: "GET",
});
await exchange.runWirewatchOnce();
await topupReserveWithDemobank({
amount: "TESTKUDOS:10",
http,
reservePub: reserveKeyPair.pub,
bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl,
bankBaseUrl: bank.baseUrl,
exchangeInfo,
});
console.log("waiting for longpoll request");
const resp = await longpollReq;
console.log(`got response, status ${resp.status}`);
console.log(exchangeInfo);
await checkReserve(http, exchange.baseUrl, reserveKeyPair.pub);

View File

@ -22,7 +22,7 @@
/**
* Imports.
*/
import { TransactionState, TransactionSubstate } from "./transactions-types.js";
import { TransactionState } from "./transactions-types.js";
import { TalerErrorDetail } from "./wallet-types.js";
export enum NotificationType {
@ -75,9 +75,7 @@ export interface TransactionStateTransitionNotification {
type: NotificationType.TransactionStateTransition;
transactionId: string;
oldTxState: TransactionState;
oldTxSubstate: TransactionSubstate;
newTxState: TransactionState;
newTxSubstate: TransactionSubstate;
}
export interface ProposalAcceptedNotification {

View File

@ -16,10 +16,9 @@
import {
TransactionType,
TransactionState,
TransactionSubstate,
PaymentStatus,
ExtendedStatus,
TransactionMajorState,
} from "./transactions-types.js";
import { RefreshReason } from "./wallet-types.js";
@ -29,8 +28,9 @@ import { RefreshReason } from "./wallet-types.js";
export const sampleWalletCoreTransactions = [
{
type: TransactionType.Payment,
txState: TransactionState.Done,
txSubstate: TransactionSubstate.None,
txState: {
major: TransactionMajorState.Done,
},
amountRaw: "KUDOS:10",
amountEffective: "KUDOS:10",
totalRefundRaw: "KUDOS:0",
@ -75,8 +75,9 @@ export const sampleWalletCoreTransactions = [
},
{
type: TransactionType.Refresh,
txState: TransactionState.Pending,
txSubstate: TransactionSubstate.None,
txState: {
major: TransactionMajorState.Pending,
},
refreshReason: RefreshReason.PayMerchant,
amountEffective: "KUDOS:0",
amountRaw: "KUDOS:0",

View File

@ -59,11 +59,6 @@ export enum ExtendedStatus {
KycRequired = "kyc-required",
}
export interface TransactionStateInfo {
txState: TransactionState;
txSubstate: TransactionSubstate;
}
export interface TransactionsRequest {
/**
* return only transactions in the given currency
@ -81,7 +76,12 @@ export interface TransactionsRequest {
includeRefreshes?: boolean;
}
export enum TransactionState {
export interface TransactionState {
major: TransactionMajorState;
minor?: TransactionMinorState;
}
export enum TransactionMajorState {
// No state, only used when reporting transitions into the initial state
None = "none",
Pending = "pending",
@ -96,15 +96,13 @@ export enum TransactionState {
Unknown = "unknown",
}
export enum TransactionSubstate {
export enum TransactionMinorState {
// Placeholder until D37 is fully implemented
Unknown = "unknown",
// No substate
None = "none",
DepositPendingInitial = "initial",
DepositKycRequired = "kyc-required",
DepositPendingTrack = "track",
DepositAbortingRefresh = "refresh",
Deposit = "deposit",
KycRequired = "kyc-required",
Track = "track",
Refresh = "refresh",
}
export interface TransactionsResponse {
@ -126,10 +124,11 @@ export interface TransactionCommon {
// main timestamp of the transaction
timestamp: TalerProtocolTimestamp;
/**
* Transaction state, as per DD37.
*/
txState: TransactionState;
txSubstate: TransactionSubstate;
/**
* @deprecated in favor of statusMajor and statusMinor
*/

View File

@ -865,8 +865,10 @@ export enum DepositGroupOperationStatus {
AbortingWithRefresh = 11 /* ACTIVE_START + 1 */,
}
// FIXME: Improve name! This enum is very specific to deposits.
export enum TransactionStatus {
/**
* Status of a single element of a deposit group.
*/
export enum DepositElementStatus {
Unknown = 10,
Accepted = 20,
KycRequired = 30,
@ -1686,7 +1688,7 @@ export interface DepositGroupRecord {
operationStatus: OperationStatus;
transactionPerCoin: TransactionStatus[];
transactionPerCoin: DepositElementStatus[];
trackingState?: {
[signature: string]: {
@ -2605,7 +2607,7 @@ export const walletDbFixups: FixupDescription[] = [
return;
}
dg.transactionPerCoin = dg.depositedPerCoin.map(
(c) => TransactionStatus.Unknown,
(c) => DepositElementStatus.Unknown,
);
await tx.depositGroups.put(dg);
});

View File

@ -109,14 +109,26 @@ export async function checkReserve(
}
}
export interface TopupReserveWithDemobankArgs {
http: HttpRequestLibrary;
reservePub: string;
bankBaseUrl: string;
bankAccessApiBaseUrl: string;
exchangeInfo: ExchangeInfo;
amount: AmountString;
}
export async function topupReserveWithDemobank(
http: HttpRequestLibrary,
reservePub: string,
bankBaseUrl: string,
bankAccessApiBaseUrl: string,
exchangeInfo: ExchangeInfo,
amount: AmountString,
args: TopupReserveWithDemobankArgs,
) {
const {
bankBaseUrl,
http,
bankAccessApiBaseUrl,
amount,
exchangeInfo,
reservePub,
} = args;
const bankHandle: BankServiceHandle = {
baseUrl: bankBaseUrl,
bankAccessApiBaseUrl: bankAccessApiBaseUrl,

View File

@ -40,6 +40,7 @@ import {
j2s,
Logger,
MerchantContractTerms,
NotificationType,
parsePaytoUri,
PayCoinSelection,
PrepareDepositRequest,
@ -49,9 +50,9 @@ import {
TalerErrorCode,
TalerProtocolTimestamp,
TrackTransaction,
TransactionMajorState,
TransactionMinorState,
TransactionState,
TransactionStateInfo,
TransactionSubstate,
TransactionType,
URL,
WireFee,
@ -60,13 +61,16 @@ import {
DenominationRecord,
DepositGroupRecord,
OperationStatus,
TransactionStatus,
DepositElementStatus,
} from "../db.js";
import { TalerError } from "@gnu-taler/taler-util";
import { getTotalRefreshCost, KycPendingInfo, KycUserType } from "../index.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { OperationAttemptResult } from "../util/retries.js";
import {
OperationAttemptResult,
OperationAttemptResultType,
} from "../util/retries.js";
import { spendCoins } from "./common.js";
import { getExchangeDetails } from "./exchanges.js";
import {
@ -89,15 +93,13 @@ const logger = new Logger("deposits.ts");
* Get the (DD37-style) transaction status based on the
* database record of a deposit group.
*/
export async function computeDepositTransactionStatus(
ws: InternalWalletState,
export function computeDepositTransactionStatus(
dg: DepositGroupRecord,
): Promise<TransactionStateInfo> {
): TransactionState {
switch (dg.operationStatus) {
case OperationStatus.Finished: {
return {
txState: TransactionState.Done,
txSubstate: TransactionSubstate.None,
major: TransactionMajorState.Done,
};
}
case OperationStatus.Pending: {
@ -110,10 +112,10 @@ export async function computeDepositTransactionStatus(
numDeposited++;
}
switch (dg.transactionPerCoin[i]) {
case TransactionStatus.KycRequired:
case DepositElementStatus.KycRequired:
numKycRequired++;
break;
case TransactionStatus.Wired:
case DepositElementStatus.Wired:
numWired++;
break;
}
@ -121,21 +123,21 @@ export async function computeDepositTransactionStatus(
if (numKycRequired > 0) {
return {
txState: TransactionState.Pending,
txSubstate: TransactionSubstate.DepositKycRequired,
major: TransactionMajorState.Pending,
minor: TransactionMinorState.KycRequired,
};
}
if (numDeposited == numTotal) {
return {
txState: TransactionState.Pending,
txSubstate: TransactionSubstate.DepositPendingTrack,
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Track,
};
}
return {
txState: TransactionState.Pending,
txSubstate: TransactionSubstate.DepositPendingInitial,
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Deposit,
};
}
default:
@ -221,6 +223,13 @@ export async function processDepositGroup(
return OperationAttemptResult.finishedEmpty();
}
const transactionId = constructTransactionIdentifier({
tag: TransactionType.Deposit,
depositGroupId,
});
const txStateOld = computeDepositTransactionStatus(depositGroup);
const contractData = extractContractData(
depositGroup.contractTermsRaw,
depositGroup.contractTermsHash,
@ -239,7 +248,7 @@ export async function processDepositGroup(
for (let i = 0; i < depositPermissions.length; i++) {
const perm = depositPermissions[i];
let updatedDeposit: boolean | undefined = undefined;
let updatedDeposit: boolean = false;
if (!depositGroup.depositedPerCoin[i]) {
const requestBody: ExchangeDepositRequest = {
@ -270,7 +279,7 @@ export async function processDepositGroup(
updatedDeposit = true;
}
let updatedTxStatus: TransactionStatus | undefined = undefined;
let updatedTxStatus: DepositElementStatus | undefined = undefined;
type ValueOf<T> = T[keyof T];
let newWiredTransaction:
@ -280,12 +289,12 @@ export async function processDepositGroup(
}
| undefined;
if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) {
const track = await trackDepositPermission(ws, depositGroup, perm);
if (depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired) {
const track = await trackDeposit(ws, depositGroup, perm);
if (track.type === "accepted") {
if (!track.kyc_ok && track.requirement_row !== undefined) {
updatedTxStatus = TransactionStatus.KycRequired;
updatedTxStatus = DepositElementStatus.KycRequired;
const { requirement_row: requirementRow } = track;
const paytoHash = encodeCrock(
hashTruncate32(stringToBytes(depositGroup.wire.payto_uri + "\0")),
@ -297,10 +306,10 @@ export async function processDepositGroup(
"individual",
);
} else {
updatedTxStatus = TransactionStatus.Accepted;
updatedTxStatus = DepositElementStatus.Accepted;
}
} else if (track.type === "wired") {
updatedTxStatus = TransactionStatus.Wired;
updatedTxStatus = DepositElementStatus.Wired;
const payto = parsePaytoUri(depositGroup.wire.payto_uri);
if (!payto) {
@ -327,11 +336,11 @@ export async function processDepositGroup(
id: track.exchange_sig,
};
} else {
updatedTxStatus = TransactionStatus.Unknown;
updatedTxStatus = DepositElementStatus.Unknown;
}
}
if (updatedTxStatus !== undefined || updatedDeposit !== undefined) {
if (updatedTxStatus !== undefined || updatedDeposit) {
await ws.db
.mktx((x) => [x.depositGroups])
.runReadWrite(async (tx) => {
@ -358,18 +367,18 @@ export async function processDepositGroup(
}
}
await ws.db
const txStatusNew = await ws.db
.mktx((x) => [x.depositGroups])
.runReadWrite(async (tx) => {
const dg = await tx.depositGroups.get(depositGroupId);
if (!dg) {
return;
return undefined;
}
let allDepositedAndWired = true;
for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) {
if (
!depositGroup.depositedPerCoin[i] ||
depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired
depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired
) {
allDepositedAndWired = false;
break;
@ -380,8 +389,36 @@ export async function processDepositGroup(
dg.operationStatus = OperationStatus.Finished;
await tx.depositGroups.put(dg);
}
return computeDepositTransactionStatus(dg);
});
return OperationAttemptResult.finishedEmpty();
if (!txStatusNew) {
// Doesn't exist anymore!
return OperationAttemptResult.finishedEmpty();
}
// Notify if state transitioned
if (
txStateOld.major !== txStatusNew.major ||
txStateOld.minor !== txStatusNew.minor
) {
ws.notify({
type: NotificationType.TransactionStateTransition,
transactionId,
oldTxState: txStateOld,
newTxState: txStatusNew,
});
}
// FIXME: consider other cases like aborting, suspend, ...
if (
txStatusNew.major === TransactionMajorState.Pending ||
txStatusNew.major === TransactionMajorState.Aborting
) {
return OperationAttemptResult.pendingEmpty();
} else {
return OperationAttemptResult.finishedEmpty();
}
}
async function getExchangeWireFee(
@ -428,7 +465,7 @@ async function getExchangeWireFee(
return fee;
}
async function trackDepositPermission(
async function trackDeposit(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
dp: CoinDepositPermission,
@ -448,6 +485,7 @@ async function trackDepositPermission(
});
url.searchParams.set("merchant_sig", sigResp.sig);
const httpResp = await ws.http.fetch(url.href, { method: "GET" });
logger.trace(`deposits response status: ${httpResp.status}`);
switch (httpResp.status) {
case HttpStatusCode.Accepted: {
const accepted = await readSuccessResponseJsonOrThrow(
@ -710,7 +748,7 @@ export async function createDepositGroup(
timestampCreated: AbsoluteTime.toTimestamp(now),
timestampFinished: undefined,
transactionPerCoin: payCoinSel.coinSel.coinPubs.map(
() => TransactionStatus.Unknown,
() => DepositElementStatus.Unknown,
),
payCoinSelection: payCoinSel.coinSel,
payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
@ -733,7 +771,7 @@ export async function createDepositGroup(
depositGroupId,
});
await ws.db
const newTxState = await ws.db
.mktx((x) => [
x.depositGroups,
x.coins,
@ -752,8 +790,18 @@ export async function createDepositGroup(
refreshReason: RefreshReason.PayDeposit,
});
await tx.depositGroups.put(depositGroup);
return computeDepositTransactionStatus(depositGroup);
});
ws.notify({
type: NotificationType.TransactionStateTransition,
transactionId,
oldTxState: {
major: TransactionMajorState.None,
},
newTxState,
});
return {
depositGroupId,
transactionId,

View File

@ -35,10 +35,9 @@ import {
Transaction,
TransactionByIdRequest,
TransactionIdStr,
TransactionMajorState,
TransactionsRequest,
TransactionsResponse,
TransactionState,
TransactionSubstate,
TransactionType,
WithdrawalType,
} from "@gnu-taler/taler-util";
@ -58,7 +57,7 @@ import {
WalletContractData,
PeerPushPaymentInitiationStatus,
PeerPullPaymentIncomingStatus,
TransactionStatus,
DepositElementStatus,
WithdrawalGroupStatus,
RefreshGroupRecord,
RefreshOperationStatus,
@ -79,7 +78,10 @@ import {
runOperationWithErrorReporting,
TombstoneTag,
} from "./common.js";
import { processDepositGroup } from "./deposits.js";
import {
computeDepositTransactionStatus,
processDepositGroup,
} from "./deposits.js";
import { getExchangeDetails } from "./exchanges.js";
import {
abortPay,
@ -425,6 +427,11 @@ export async function getTransactionById(
}
}
// FIXME: Just a marker helper for unknown states until DD37 is fully implemented.
const mkTxStateUnknown = () => ({
major: TransactionMajorState.Unknown,
});
function buildTransactionForPushPaymentDebit(
pi: PeerPushPaymentInitiationRecord,
contractTerms: PeerContractTerms,
@ -432,8 +439,7 @@ function buildTransactionForPushPaymentDebit(
): Transaction {
return {
type: TransactionType.PeerPushDebit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
amountEffective: pi.totalCost,
amountRaw: pi.amount,
exchangeBaseUrl: pi.exchangeBaseUrl,
@ -466,8 +472,7 @@ function buildTransactionForPullPaymentDebit(
): Transaction {
return {
type: TransactionType.PeerPullDebit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
amountEffective: pi.coinSel?.totalCost
? pi.coinSel?.totalCost
: Amounts.stringify(pi.contractTerms.amount),
@ -517,8 +522,7 @@ function buildTransactionForPeerPullCredit(
});
return {
type: TransactionType.PeerPullCredit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
amountRaw: Amounts.stringify(wsr.instructedAmount),
exchangeBaseUrl: wsr.exchangeBaseUrl,
@ -553,8 +557,7 @@ function buildTransactionForPeerPullCredit(
return {
type: TransactionType.PeerPullCredit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
amountEffective: Amounts.stringify(pullCredit.estimatedAmountEffective),
amountRaw: Amounts.stringify(peerContractTerms.amount),
exchangeBaseUrl: pullCredit.exchangeBaseUrl,
@ -593,8 +596,7 @@ function buildTransactionForPeerPushCredit(
return {
type: TransactionType.PeerPushCredit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
amountRaw: Amounts.stringify(wsr.instructedAmount),
exchangeBaseUrl: wsr.exchangeBaseUrl,
@ -618,8 +620,7 @@ function buildTransactionForPeerPushCredit(
return {
type: TransactionType.PeerPushCredit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
// FIXME: This is wrong, needs to consider fees!
amountEffective: Amounts.stringify(peerContractTerms.amount),
amountRaw: Amounts.stringify(peerContractTerms.amount),
@ -649,8 +650,7 @@ function buildTransactionForBankIntegratedWithdraw(
return {
type: TransactionType.Withdrawal,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
amountRaw: Amounts.stringify(wsr.instructedAmount),
withdrawalDetails: {
@ -696,8 +696,7 @@ function buildTransactionForManualWithdraw(
return {
type: TransactionType.Withdrawal,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
amountEffective: Amounts.stringify(
withdrawalGroup.denomsSel.totalCoinValue,
),
@ -748,8 +747,7 @@ function buildTransactionForRefresh(
).amount;
return {
type: TransactionType.Refresh,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
refreshReason: refreshGroupRecord.reason,
amountEffective: Amounts.stringify(
Amounts.zeroOfCurrency(refreshGroupRecord.currency),
@ -791,8 +789,7 @@ function buildTransactionForDeposit(
return {
type: TransactionType.Deposit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: computeDepositTransactionStatus(dg),
amountRaw: Amounts.stringify(dg.effectiveDepositAmount),
amountEffective: Amounts.stringify(dg.totalPayCost),
extendedStatus: dg.timestampFinished
@ -810,7 +807,7 @@ function buildTransactionForDeposit(
wireTransferProgress:
(100 *
dg.transactionPerCoin.reduce(
(prev, cur) => prev + (cur === TransactionStatus.Wired ? 1 : 0),
(prev, cur) => prev + (cur === DepositElementStatus.Wired ? 1 : 0),
0,
)) /
dg.transactionPerCoin.length,
@ -829,8 +826,7 @@ function buildTransactionForTip(
return {
type: TransactionType.Tip,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
amountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
amountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
extendedStatus: tipRecord.pickedUpTimestamp
@ -926,8 +922,7 @@ async function buildTransactionForRefund(
return {
type: TransactionType.Refund,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
info,
refundedTransactionId: makeTransactionId(
TransactionType.Payment,
@ -1030,8 +1025,7 @@ async function buildTransactionForPurchase(
return {
type: TransactionType.Payment,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
txState: mkTxStateUnknown(),
amountRaw: Amounts.stringify(contractData.amount),
amountEffective: Amounts.stringify(purchaseRecord.payInfo.totalPayCost),
totalRefundRaw: Amounts.stringify(totalRefund.raw),

View File

@ -70,6 +70,12 @@ export namespace OperationAttemptResult {
result: undefined,
};
}
export function pendingEmpty(): OperationAttemptResult<unknown, unknown> {
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
}
}
export interface OperationAttemptFinishedResult<T> {