wallet-core: p2p support for transactions list
This commit is contained in:
parent
bf516a77e8
commit
a11ac57535
@ -102,7 +102,11 @@ export type Transaction =
|
|||||||
| TransactionRefund
|
| TransactionRefund
|
||||||
| TransactionTip
|
| TransactionTip
|
||||||
| TransactionRefresh
|
| TransactionRefresh
|
||||||
| TransactionDeposit;
|
| TransactionDeposit
|
||||||
|
| TransactionPeerPullCredit
|
||||||
|
| TransactionPeerPullDebit
|
||||||
|
| TransactionPeerPushCredit
|
||||||
|
| TransactionPeerPushDebit;
|
||||||
|
|
||||||
export enum TransactionType {
|
export enum TransactionType {
|
||||||
Withdrawal = "withdrawal",
|
Withdrawal = "withdrawal",
|
||||||
@ -111,6 +115,10 @@ export enum TransactionType {
|
|||||||
Refresh = "refresh",
|
Refresh = "refresh",
|
||||||
Tip = "tip",
|
Tip = "tip",
|
||||||
Deposit = "deposit",
|
Deposit = "deposit",
|
||||||
|
PeerPushDebit = "peer-push-debit",
|
||||||
|
PeerPushCredit = "peer-push-credit",
|
||||||
|
PeerPullDebit = "peer-pull-debit",
|
||||||
|
PeerPullCredit = "peer-pull-credit",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum WithdrawalType {
|
export enum WithdrawalType {
|
||||||
@ -179,6 +187,76 @@ export interface TransactionWithdrawal extends TransactionCommon {
|
|||||||
withdrawalDetails: WithdrawalDetails;
|
withdrawalDetails: WithdrawalDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TransactionPeerPullCredit extends TransactionCommon {
|
||||||
|
type: TransactionType.PeerPullCredit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exchange used.
|
||||||
|
*/
|
||||||
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount that got subtracted from the reserve balance.
|
||||||
|
*/
|
||||||
|
amountRaw: AmountString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount that actually was (or will be) added to the wallet's balance.
|
||||||
|
*/
|
||||||
|
amountEffective: AmountString;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionPeerPullDebit extends TransactionCommon {
|
||||||
|
type: TransactionType.PeerPullDebit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exchange used.
|
||||||
|
*/
|
||||||
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
|
amountRaw: AmountString;
|
||||||
|
|
||||||
|
amountEffective: AmountString;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionPeerPushDebit extends TransactionCommon {
|
||||||
|
type: TransactionType.PeerPushDebit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exchange used.
|
||||||
|
*/
|
||||||
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount that got subtracted from the reserve balance.
|
||||||
|
*/
|
||||||
|
amountRaw: AmountString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount that actually was (or will be) added to the wallet's balance.
|
||||||
|
*/
|
||||||
|
amountEffective: AmountString;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionPeerPushCredit extends TransactionCommon {
|
||||||
|
type: TransactionType.PeerPushCredit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exchange used.
|
||||||
|
*/
|
||||||
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount that got subtracted from the reserve balance.
|
||||||
|
*/
|
||||||
|
amountRaw: AmountString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount that actually was (or will be) added to the wallet's balance.
|
||||||
|
*/
|
||||||
|
amountEffective: AmountString;
|
||||||
|
}
|
||||||
|
|
||||||
export enum PaymentStatus {
|
export enum PaymentStatus {
|
||||||
/**
|
/**
|
||||||
* Explicitly aborted after timeout / failure
|
* Explicitly aborted after timeout / failure
|
||||||
@ -311,10 +389,10 @@ export interface OrderShortInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RefundInfoShort {
|
export interface RefundInfoShort {
|
||||||
transactionId: string,
|
transactionId: string;
|
||||||
timestamp: TalerProtocolTimestamp,
|
timestamp: TalerProtocolTimestamp;
|
||||||
amountEffective: AmountString,
|
amountEffective: AmountString;
|
||||||
amountRaw: AmountString,
|
amountRaw: AmountString;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransactionRefund extends TransactionCommon {
|
export interface TransactionRefund extends TransactionCommon {
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
import { j2s } from "@gnu-taler/taler-util";
|
import { j2s } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { GlobalTestState } from "../harness/harness.js";
|
import { GlobalTestState, WalletCli } from "../harness/harness.js";
|
||||||
import {
|
import {
|
||||||
createSimpleTestkudosEnvironment,
|
createSimpleTestkudosEnvironment,
|
||||||
withdrawViaBank,
|
withdrawViaBank,
|
||||||
@ -31,16 +31,23 @@ import {
|
|||||||
export async function runPeerToPeerPullTest(t: GlobalTestState) {
|
export async function runPeerToPeerPullTest(t: GlobalTestState) {
|
||||||
// Set up test environment
|
// Set up test environment
|
||||||
|
|
||||||
const { wallet, bank, exchange, merchant } =
|
const { bank, exchange, merchant } = await createSimpleTestkudosEnvironment(
|
||||||
await createSimpleTestkudosEnvironment(t);
|
t,
|
||||||
|
);
|
||||||
|
|
||||||
// Withdraw digital cash into the wallet.
|
// Withdraw digital cash into the wallet.
|
||||||
|
const wallet1 = new WalletCli(t, "w1");
|
||||||
|
const wallet2 = new WalletCli(t, "w2");
|
||||||
|
await withdrawViaBank(t, {
|
||||||
|
wallet: wallet2,
|
||||||
|
bank,
|
||||||
|
exchange,
|
||||||
|
amount: "TESTKUDOS:20",
|
||||||
|
});
|
||||||
|
|
||||||
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
|
await wallet1.runUntilDone();
|
||||||
|
|
||||||
await wallet.runUntilDone();
|
const resp = await wallet1.client.call(
|
||||||
|
|
||||||
const resp = await wallet.client.call(
|
|
||||||
WalletApiOperation.InitiatePeerPullPayment,
|
WalletApiOperation.InitiatePeerPullPayment,
|
||||||
{
|
{
|
||||||
exchangeBaseUrl: exchange.baseUrl,
|
exchangeBaseUrl: exchange.baseUrl,
|
||||||
@ -51,7 +58,7 @@ export async function runPeerToPeerPullTest(t: GlobalTestState) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const checkResp = await wallet.client.call(
|
const checkResp = await wallet2.client.call(
|
||||||
WalletApiOperation.CheckPeerPullPayment,
|
WalletApiOperation.CheckPeerPullPayment,
|
||||||
{
|
{
|
||||||
talerUri: resp.talerUri,
|
talerUri: resp.talerUri,
|
||||||
@ -60,18 +67,27 @@ export async function runPeerToPeerPullTest(t: GlobalTestState) {
|
|||||||
|
|
||||||
console.log(`checkResp: ${j2s(checkResp)}`);
|
console.log(`checkResp: ${j2s(checkResp)}`);
|
||||||
|
|
||||||
const acceptResp = await wallet.client.call(
|
const acceptResp = await wallet2.client.call(
|
||||||
WalletApiOperation.AcceptPeerPullPayment,
|
WalletApiOperation.AcceptPeerPullPayment,
|
||||||
{
|
{
|
||||||
peerPullPaymentIncomingId: checkResp.peerPullPaymentIncomingId,
|
peerPullPaymentIncomingId: checkResp.peerPullPaymentIncomingId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {});
|
await wallet1.runUntilDone();
|
||||||
|
await wallet2.runUntilDone();
|
||||||
|
|
||||||
console.log(`transactions: ${j2s(txs)}`);
|
const txn1 = await wallet1.client.call(
|
||||||
|
WalletApiOperation.GetTransactions,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
const txn2 = await wallet2.client.call(
|
||||||
|
WalletApiOperation.GetTransactions,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
await wallet.runUntilDone();
|
console.log(`txn1: ${j2s(txn1)}`);
|
||||||
|
console.log(`txn2: ${j2s(txn2)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
runPeerToPeerPullTest.suites = ["wallet"];
|
runPeerToPeerPullTest.suites = ["wallet"];
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
|
import { j2s } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { GlobalTestState, WalletCli } from "../harness/harness.js";
|
import { GlobalTestState, WalletCli } from "../harness/harness.js";
|
||||||
import {
|
import {
|
||||||
@ -78,6 +79,18 @@ export async function runPeerToPeerPushTest(t: GlobalTestState) {
|
|||||||
|
|
||||||
await wallet1.runUntilDone();
|
await wallet1.runUntilDone();
|
||||||
await wallet2.runUntilDone();
|
await wallet2.runUntilDone();
|
||||||
|
|
||||||
|
const txn1 = await wallet1.client.call(
|
||||||
|
WalletApiOperation.GetTransactions,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
const txn2 = await wallet2.client.call(
|
||||||
|
WalletApiOperation.GetTransactions,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`txn1: ${j2s(txn1)}`);
|
||||||
|
console.log(`txn2: ${j2s(txn2)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
runPeerToPeerPushTest.suites = ["wallet"];
|
runPeerToPeerPushTest.suites = ["wallet"];
|
||||||
|
@ -1219,6 +1219,13 @@ export interface DenomSelectionState {
|
|||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum WithdrawalRecordType {
|
||||||
|
BankManual = "bank-manual",
|
||||||
|
BankIntegrated = "bank-integrated",
|
||||||
|
PeerPullCredit = "peer-pull-credit",
|
||||||
|
PeerPushCredit = "peer-push-credit",
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Group of withdrawal operations that need to be executed.
|
* Group of withdrawal operations that need to be executed.
|
||||||
* (Either for a normal withdrawal or from a tip.)
|
* (Either for a normal withdrawal or from a tip.)
|
||||||
@ -1232,6 +1239,8 @@ export interface WithdrawalGroupRecord {
|
|||||||
*/
|
*/
|
||||||
withdrawalGroupId: string;
|
withdrawalGroupId: string;
|
||||||
|
|
||||||
|
withdrawalType: WithdrawalRecordType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Secret seed used to derive planchets.
|
* Secret seed used to derive planchets.
|
||||||
* Stored since planchets are created lazily.
|
* Stored since planchets are created lazily.
|
||||||
@ -1607,8 +1616,6 @@ export interface PeerPushPaymentInitiationRecord {
|
|||||||
|
|
||||||
contractPriv: string;
|
contractPriv: string;
|
||||||
|
|
||||||
contractPub: string;
|
|
||||||
|
|
||||||
purseExpiration: TalerProtocolTimestamp;
|
purseExpiration: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1681,7 +1688,11 @@ export interface PeerPullPaymentIncomingRecord {
|
|||||||
|
|
||||||
contractTerms: PeerContractTerms;
|
contractTerms: PeerContractTerms;
|
||||||
|
|
||||||
timestamp: TalerProtocolTimestamp;
|
timestampCreated: TalerProtocolTimestamp;
|
||||||
|
|
||||||
|
paid: boolean;
|
||||||
|
|
||||||
|
accepted: boolean;
|
||||||
|
|
||||||
contractPriv: string;
|
contractPriv: string;
|
||||||
}
|
}
|
||||||
@ -1878,9 +1889,18 @@ export const WalletStoresV1 = {
|
|||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
peerPullPaymentInitiation: describeStore(
|
peerPullPaymentInitiations: describeStore(
|
||||||
describeContents<PeerPullPaymentInitiationRecord>(
|
describeContents<PeerPullPaymentInitiationRecord>(
|
||||||
"peerPushPaymentInitiation",
|
"peerPullPaymentInitiations",
|
||||||
|
{
|
||||||
|
keyPath: "pursePub",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
peerPushPaymentInitiations: describeStore(
|
||||||
|
describeContents<PeerPushPaymentInitiationRecord>(
|
||||||
|
"peerPushPaymentInitiations",
|
||||||
{
|
{
|
||||||
keyPath: "pursePub",
|
keyPath: "pursePub",
|
||||||
},
|
},
|
||||||
|
@ -65,6 +65,7 @@ import {
|
|||||||
MergeReserveInfo,
|
MergeReserveInfo,
|
||||||
ReserveRecordStatus,
|
ReserveRecordStatus,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
|
WithdrawalRecordType,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
|
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
@ -208,39 +209,6 @@ export async function initiatePeerToPeerPush(
|
|||||||
): Promise<InitiatePeerPushPaymentResponse> {
|
): Promise<InitiatePeerPushPaymentResponse> {
|
||||||
// FIXME: actually create a record for retries here!
|
// FIXME: actually create a record for retries here!
|
||||||
const instructedAmount = Amounts.parseOrThrow(req.amount);
|
const instructedAmount = Amounts.parseOrThrow(req.amount);
|
||||||
const coinSelRes: PeerCoinSelection | undefined = await ws.db
|
|
||||||
.mktx((x) => ({
|
|
||||||
exchanges: x.exchanges,
|
|
||||||
coins: x.coins,
|
|
||||||
denominations: x.denominations,
|
|
||||||
refreshGroups: x.refreshGroups,
|
|
||||||
}))
|
|
||||||
.runReadWrite(async (tx) => {
|
|
||||||
const sel = await selectPeerCoins(ws, tx, instructedAmount);
|
|
||||||
if (!sel) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pubs: CoinPublicKey[] = [];
|
|
||||||
for (const c of sel.coins) {
|
|
||||||
const coin = await tx.coins.get(c.coinPub);
|
|
||||||
checkDbInvariant(!!coin);
|
|
||||||
coin.currentAmount = Amounts.sub(
|
|
||||||
coin.currentAmount,
|
|
||||||
Amounts.parseOrThrow(c.contribution),
|
|
||||||
).amount;
|
|
||||||
await tx.coins.put(coin);
|
|
||||||
}
|
|
||||||
|
|
||||||
await createRefreshGroup(ws, tx, pubs, RefreshReason.Pay);
|
|
||||||
|
|
||||||
return sel;
|
|
||||||
});
|
|
||||||
logger.info(`selected p2p coins: ${j2s(coinSelRes)}`);
|
|
||||||
|
|
||||||
if (!coinSelRes) {
|
|
||||||
throw Error("insufficient balance");
|
|
||||||
}
|
|
||||||
|
|
||||||
const pursePair = await ws.cryptoApi.createEddsaKeypair({});
|
const pursePair = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
const mergePair = await ws.cryptoApi.createEddsaKeypair({});
|
const mergePair = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
@ -260,6 +228,62 @@ export async function initiatePeerToPeerPush(
|
|||||||
|
|
||||||
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
|
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
|
||||||
|
|
||||||
|
const econtractResp = await ws.cryptoApi.encryptContractForMerge({
|
||||||
|
contractTerms,
|
||||||
|
mergePriv: mergePair.priv,
|
||||||
|
pursePriv: pursePair.priv,
|
||||||
|
pursePub: pursePair.pub,
|
||||||
|
});
|
||||||
|
|
||||||
|
const coinSelRes: PeerCoinSelection | undefined = await ws.db
|
||||||
|
.mktx((x) => ({
|
||||||
|
exchanges: x.exchanges,
|
||||||
|
coins: x.coins,
|
||||||
|
denominations: x.denominations,
|
||||||
|
refreshGroups: x.refreshGroups,
|
||||||
|
peerPushPaymentInitiations: x.peerPushPaymentInitiations,
|
||||||
|
}))
|
||||||
|
.runReadWrite(async (tx) => {
|
||||||
|
const sel = await selectPeerCoins(ws, tx, instructedAmount);
|
||||||
|
if (!sel) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pubs: CoinPublicKey[] = [];
|
||||||
|
for (const c of sel.coins) {
|
||||||
|
const coin = await tx.coins.get(c.coinPub);
|
||||||
|
checkDbInvariant(!!coin);
|
||||||
|
coin.currentAmount = Amounts.sub(
|
||||||
|
coin.currentAmount,
|
||||||
|
Amounts.parseOrThrow(c.contribution),
|
||||||
|
).amount;
|
||||||
|
await tx.coins.put(coin);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tx.peerPushPaymentInitiations.add({
|
||||||
|
amount: Amounts.stringify(instructedAmount),
|
||||||
|
contractPriv: econtractResp.contractPriv,
|
||||||
|
exchangeBaseUrl: sel.exchangeBaseUrl,
|
||||||
|
mergePriv: mergePair.priv,
|
||||||
|
mergePub: mergePair.pub,
|
||||||
|
// FIXME: only set this later!
|
||||||
|
purseCreated: true,
|
||||||
|
purseExpiration: purseExpiration,
|
||||||
|
pursePriv: pursePair.priv,
|
||||||
|
pursePub: pursePair.pub,
|
||||||
|
timestampCreated: TalerProtocolTimestamp.now(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await createRefreshGroup(ws, tx, pubs, RefreshReason.Pay);
|
||||||
|
|
||||||
|
return sel;
|
||||||
|
});
|
||||||
|
logger.info(`selected p2p coins: ${j2s(coinSelRes)}`);
|
||||||
|
|
||||||
|
if (!coinSelRes) {
|
||||||
|
throw Error("insufficient balance");
|
||||||
|
}
|
||||||
|
|
||||||
const purseSigResp = await ws.cryptoApi.signPurseCreation({
|
const purseSigResp = await ws.cryptoApi.signPurseCreation({
|
||||||
hContractTerms,
|
hContractTerms,
|
||||||
mergePub: mergePair.pub,
|
mergePub: mergePair.pub,
|
||||||
@ -280,13 +304,6 @@ export async function initiatePeerToPeerPush(
|
|||||||
coinSelRes.exchangeBaseUrl,
|
coinSelRes.exchangeBaseUrl,
|
||||||
);
|
);
|
||||||
|
|
||||||
const econtractResp = await ws.cryptoApi.encryptContractForMerge({
|
|
||||||
contractTerms,
|
|
||||||
mergePriv: mergePair.priv,
|
|
||||||
pursePriv: pursePair.priv,
|
|
||||||
pursePub: pursePair.pub,
|
|
||||||
});
|
|
||||||
|
|
||||||
const httpResp = await ws.http.postJson(createPurseUrl.href, {
|
const httpResp = await ws.http.postJson(createPurseUrl.href, {
|
||||||
amount: Amounts.stringify(instructedAmount),
|
amount: Amounts.stringify(instructedAmount),
|
||||||
merge_pub: mergePair.pub,
|
merge_pub: mergePair.pub,
|
||||||
@ -517,6 +534,7 @@ export async function acceptPeerPushPayment(
|
|||||||
|
|
||||||
await internalCreateWithdrawalGroup(ws, {
|
await internalCreateWithdrawalGroup(ws, {
|
||||||
amount,
|
amount,
|
||||||
|
withdrawalType: WithdrawalRecordType.PeerPushCredit,
|
||||||
exchangeBaseUrl: peerInc.exchangeBaseUrl,
|
exchangeBaseUrl: peerInc.exchangeBaseUrl,
|
||||||
reserveStatus: ReserveRecordStatus.QueryingStatus,
|
reserveStatus: ReserveRecordStatus.QueryingStatus,
|
||||||
reserveKeyPair: {
|
reserveKeyPair: {
|
||||||
@ -554,6 +572,7 @@ export async function acceptPeerPullPayment(
|
|||||||
coins: x.coins,
|
coins: x.coins,
|
||||||
denominations: x.denominations,
|
denominations: x.denominations,
|
||||||
refreshGroups: x.refreshGroups,
|
refreshGroups: x.refreshGroups,
|
||||||
|
peerPullPaymentIncoming: x.peerPullPaymentIncoming,
|
||||||
}))
|
}))
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const sel = await selectPeerCoins(ws, tx, instructedAmount);
|
const sel = await selectPeerCoins(ws, tx, instructedAmount);
|
||||||
@ -574,6 +593,15 @@ export async function acceptPeerPullPayment(
|
|||||||
|
|
||||||
await createRefreshGroup(ws, tx, pubs, RefreshReason.Pay);
|
await createRefreshGroup(ws, tx, pubs, RefreshReason.Pay);
|
||||||
|
|
||||||
|
const pi = await tx.peerPullPaymentIncoming.get(
|
||||||
|
req.peerPullPaymentIncomingId,
|
||||||
|
);
|
||||||
|
if (!pi) {
|
||||||
|
throw Error();
|
||||||
|
}
|
||||||
|
pi.accepted = true;
|
||||||
|
await tx.peerPullPaymentIncoming.put(pi);
|
||||||
|
|
||||||
return sel;
|
return sel;
|
||||||
});
|
});
|
||||||
logger.info(`selected p2p coins: ${j2s(coinSelRes)}`);
|
logger.info(`selected p2p coins: ${j2s(coinSelRes)}`);
|
||||||
@ -656,8 +684,10 @@ export async function checkPeerPullPayment(
|
|||||||
contractPriv: contractPriv,
|
contractPriv: contractPriv,
|
||||||
exchangeBaseUrl: exchangeBaseUrl,
|
exchangeBaseUrl: exchangeBaseUrl,
|
||||||
pursePub: pursePub,
|
pursePub: pursePub,
|
||||||
timestamp: TalerProtocolTimestamp.now(),
|
timestampCreated: TalerProtocolTimestamp.now(),
|
||||||
contractTerms: dec.contractTerms,
|
contractTerms: dec.contractTerms,
|
||||||
|
paid: false,
|
||||||
|
accepted: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -672,6 +702,8 @@ export async function initiatePeerRequestForPay(
|
|||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
req: InitiatePeerPullPaymentRequest,
|
req: InitiatePeerPullPaymentRequest,
|
||||||
): Promise<InitiatePeerPullPaymentResponse> {
|
): Promise<InitiatePeerPullPaymentResponse> {
|
||||||
|
await updateExchangeFromUrl(ws, req.exchangeBaseUrl);
|
||||||
|
|
||||||
const mergeReserveInfo = await getMergeReserveInfo(ws, {
|
const mergeReserveInfo = await getMergeReserveInfo(ws, {
|
||||||
exchangeBaseUrl: req.exchangeBaseUrl,
|
exchangeBaseUrl: req.exchangeBaseUrl,
|
||||||
});
|
});
|
||||||
@ -727,7 +759,7 @@ export async function initiatePeerRequestForPay(
|
|||||||
|
|
||||||
await ws.db
|
await ws.db
|
||||||
.mktx((x) => ({
|
.mktx((x) => ({
|
||||||
peerPullPaymentInitiation: x.peerPullPaymentInitiation,
|
peerPullPaymentInitiation: x.peerPullPaymentInitiations,
|
||||||
}))
|
}))
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
await tx.peerPullPaymentInitiation.put({
|
await tx.peerPullPaymentInitiation.put({
|
||||||
@ -772,6 +804,7 @@ export async function initiatePeerRequestForPay(
|
|||||||
|
|
||||||
await internalCreateWithdrawalGroup(ws, {
|
await internalCreateWithdrawalGroup(ws, {
|
||||||
amount: Amounts.parseOrThrow(req.amount),
|
amount: Amounts.parseOrThrow(req.amount),
|
||||||
|
withdrawalType: WithdrawalRecordType.PeerPullCredit,
|
||||||
exchangeBaseUrl: req.exchangeBaseUrl,
|
exchangeBaseUrl: req.exchangeBaseUrl,
|
||||||
reserveStatus: ReserveRecordStatus.QueryingStatus,
|
reserveStatus: ReserveRecordStatus.QueryingStatus,
|
||||||
reserveKeyPair: {
|
reserveKeyPair: {
|
||||||
|
@ -38,6 +38,7 @@ import {
|
|||||||
RefundState,
|
RefundState,
|
||||||
ReserveRecordStatus,
|
ReserveRecordStatus,
|
||||||
WalletRefundItem,
|
WalletRefundItem,
|
||||||
|
WithdrawalRecordType,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { processDepositGroup } from "./deposits.js";
|
import { processDepositGroup } from "./deposits.js";
|
||||||
import { getExchangeDetails } from "./exchanges.js";
|
import { getExchangeDetails } from "./exchanges.js";
|
||||||
@ -101,10 +102,14 @@ const txOrder: { [t in TransactionType]: number } = {
|
|||||||
[TransactionType.Withdrawal]: 1,
|
[TransactionType.Withdrawal]: 1,
|
||||||
[TransactionType.Tip]: 2,
|
[TransactionType.Tip]: 2,
|
||||||
[TransactionType.Payment]: 3,
|
[TransactionType.Payment]: 3,
|
||||||
[TransactionType.Refund]: 4,
|
[TransactionType.PeerPullCredit]: 4,
|
||||||
[TransactionType.Deposit]: 5,
|
[TransactionType.PeerPullDebit]: 5,
|
||||||
[TransactionType.Refresh]: 6,
|
[TransactionType.PeerPushCredit]: 6,
|
||||||
[TransactionType.Tip]: 7,
|
[TransactionType.PeerPushDebit]: 7,
|
||||||
|
[TransactionType.Refund]: 8,
|
||||||
|
[TransactionType.Deposit]: 9,
|
||||||
|
[TransactionType.Refresh]: 10,
|
||||||
|
[TransactionType.Tip]: 11,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,10 +136,59 @@ export async function getTransactions(
|
|||||||
recoupGroups: x.recoupGroups,
|
recoupGroups: x.recoupGroups,
|
||||||
depositGroups: x.depositGroups,
|
depositGroups: x.depositGroups,
|
||||||
tombstones: x.tombstones,
|
tombstones: x.tombstones,
|
||||||
|
peerPushPaymentInitiations: x.peerPushPaymentInitiations,
|
||||||
|
peerPullPaymentIncoming: x.peerPullPaymentIncoming,
|
||||||
}))
|
}))
|
||||||
.runReadOnly(
|
.runReadOnly(async (tx) => {
|
||||||
// Report withdrawals that are currently in progress.
|
tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => {
|
||||||
async (tx) => {
|
const amount = Amounts.parseOrThrow(pi.amount);
|
||||||
|
if (shouldSkipCurrency(transactionsRequest, amount.currency)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (shouldSkipSearch(transactionsRequest, [])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
transactions.push({
|
||||||
|
type: TransactionType.PeerPushDebit,
|
||||||
|
amountEffective: pi.amount,
|
||||||
|
amountRaw: pi.amount,
|
||||||
|
exchangeBaseUrl: pi.exchangeBaseUrl,
|
||||||
|
frozen: false,
|
||||||
|
pending: !pi.purseCreated,
|
||||||
|
timestamp: pi.timestampCreated,
|
||||||
|
transactionId: makeEventId(
|
||||||
|
TransactionType.PeerPushDebit,
|
||||||
|
pi.pursePub,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tx.peerPullPaymentIncoming.iter().forEachAsync(async (pi) => {
|
||||||
|
const amount = Amounts.parseOrThrow(pi.contractTerms.amount);
|
||||||
|
if (shouldSkipCurrency(transactionsRequest, amount.currency)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (shouldSkipSearch(transactionsRequest, [])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!pi.accepted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
transactions.push({
|
||||||
|
type: TransactionType.PeerPullDebit,
|
||||||
|
amountEffective: Amounts.stringify(amount),
|
||||||
|
amountRaw: Amounts.stringify(amount),
|
||||||
|
exchangeBaseUrl: pi.exchangeBaseUrl,
|
||||||
|
frozen: false,
|
||||||
|
pending: false,
|
||||||
|
timestamp: pi.timestampCreated,
|
||||||
|
transactionId: makeEventId(
|
||||||
|
TransactionType.PeerPullDebit,
|
||||||
|
pi.pursePub,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
tx.withdrawalGroups.iter().forEachAsync(async (wsr) => {
|
tx.withdrawalGroups.iter().forEachAsync(async (wsr) => {
|
||||||
if (
|
if (
|
||||||
shouldSkipCurrency(
|
shouldSkipCurrency(
|
||||||
@ -149,7 +203,39 @@ export async function getTransactions(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let withdrawalDetails: WithdrawalDetails;
|
let withdrawalDetails: WithdrawalDetails;
|
||||||
if (wsr.bankInfo) {
|
if (wsr.withdrawalType === WithdrawalRecordType.PeerPullCredit) {
|
||||||
|
transactions.push({
|
||||||
|
type: TransactionType.PeerPullCredit,
|
||||||
|
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
|
||||||
|
amountRaw: Amounts.stringify(wsr.rawWithdrawalAmount),
|
||||||
|
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
||||||
|
pending: !wsr.timestampFinish,
|
||||||
|
timestamp: wsr.timestampStart,
|
||||||
|
transactionId: makeEventId(
|
||||||
|
TransactionType.PeerPullCredit,
|
||||||
|
wsr.withdrawalGroupId,
|
||||||
|
),
|
||||||
|
frozen: false,
|
||||||
|
...(wsr.lastError ? { error: wsr.lastError } : {}),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else if (wsr.withdrawalType === WithdrawalRecordType.PeerPushCredit) {
|
||||||
|
transactions.push({
|
||||||
|
type: TransactionType.PeerPushCredit,
|
||||||
|
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
|
||||||
|
amountRaw: Amounts.stringify(wsr.rawWithdrawalAmount),
|
||||||
|
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
||||||
|
pending: !wsr.timestampFinish,
|
||||||
|
timestamp: wsr.timestampStart,
|
||||||
|
transactionId: makeEventId(
|
||||||
|
TransactionType.PeerPushCredit,
|
||||||
|
wsr.withdrawalGroupId,
|
||||||
|
),
|
||||||
|
frozen: false,
|
||||||
|
...(wsr.lastError ? { error: wsr.lastError } : {}),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else if (wsr.bankInfo) {
|
||||||
withdrawalDetails = {
|
withdrawalDetails = {
|
||||||
type: WithdrawalType.TalerBankIntegrationApi,
|
type: WithdrawalType.TalerBankIntegrationApi,
|
||||||
confirmed: wsr.bankInfo.timestampBankConfirmed ? true : false,
|
confirmed: wsr.bankInfo.timestampBankConfirmed ? true : false,
|
||||||
@ -169,8 +255,9 @@ export async function getTransactions(
|
|||||||
type: WithdrawalType.ManualTransfer,
|
type: WithdrawalType.ManualTransfer,
|
||||||
reservePub: wsr.reservePub,
|
reservePub: wsr.reservePub,
|
||||||
exchangePaytoUris:
|
exchangePaytoUris:
|
||||||
exchangeDetails.wireInfo?.accounts.map((x) => `${x.payto_uri}?subject=${wsr.reservePub}`) ??
|
exchangeDetails.wireInfo?.accounts.map(
|
||||||
[],
|
(x) => `${x.payto_uri}?subject=${wsr.reservePub}`,
|
||||||
|
) ?? [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,8 +477,7 @@ export async function getTransactions(
|
|||||||
error: tipRecord.lastError,
|
error: tipRecord.lastError,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const txPending = transactions.filter((x) => x.pending);
|
const txPending = transactions.filter((x) => x.pending);
|
||||||
const txNotPending = transactions.filter((x) => !x.pending);
|
const txNotPending = transactions.filter((x) => !x.pending);
|
||||||
|
@ -74,6 +74,7 @@ import {
|
|||||||
ReserveRecordStatus,
|
ReserveRecordStatus,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
WithdrawalGroupRecord,
|
WithdrawalGroupRecord,
|
||||||
|
WithdrawalRecordType,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import {
|
import {
|
||||||
getErrorDetailFromException,
|
getErrorDetailFromException,
|
||||||
@ -1700,6 +1701,7 @@ export async function internalCreateWithdrawalGroup(
|
|||||||
forcedDenomSel?: ForcedDenomSel;
|
forcedDenomSel?: ForcedDenomSel;
|
||||||
reserveKeyPair?: EddsaKeypair;
|
reserveKeyPair?: EddsaKeypair;
|
||||||
restrictAge?: number;
|
restrictAge?: number;
|
||||||
|
withdrawalType: WithdrawalRecordType;
|
||||||
},
|
},
|
||||||
): Promise<WithdrawalGroupRecord> {
|
): Promise<WithdrawalGroupRecord> {
|
||||||
const reserveKeyPair =
|
const reserveKeyPair =
|
||||||
@ -1745,6 +1747,7 @@ export async function internalCreateWithdrawalGroup(
|
|||||||
restrictAge: args.restrictAge,
|
restrictAge: args.restrictAge,
|
||||||
senderWire: undefined,
|
senderWire: undefined,
|
||||||
timestampFinish: undefined,
|
timestampFinish: undefined,
|
||||||
|
withdrawalType: args.withdrawalType,
|
||||||
};
|
};
|
||||||
|
|
||||||
const exchangeInfo = await updateExchangeFromUrl(ws, canonExchange);
|
const exchangeInfo = await updateExchangeFromUrl(ws, canonExchange);
|
||||||
@ -1819,6 +1822,7 @@ export async function acceptWithdrawalFromUri(
|
|||||||
const withdrawalGroup = await internalCreateWithdrawalGroup(ws, {
|
const withdrawalGroup = await internalCreateWithdrawalGroup(ws, {
|
||||||
amount: withdrawInfo.amount,
|
amount: withdrawInfo.amount,
|
||||||
exchangeBaseUrl: req.selectedExchange,
|
exchangeBaseUrl: req.selectedExchange,
|
||||||
|
withdrawalType: WithdrawalRecordType.BankIntegrated,
|
||||||
forcedDenomSel: req.forcedDenomSel,
|
forcedDenomSel: req.forcedDenomSel,
|
||||||
reserveStatus: ReserveRecordStatus.RegisteringBank,
|
reserveStatus: ReserveRecordStatus.RegisteringBank,
|
||||||
bankInfo: {
|
bankInfo: {
|
||||||
@ -1877,6 +1881,7 @@ export async function createManualWithdrawal(
|
|||||||
): Promise<AcceptManualWithdrawalResult> {
|
): Promise<AcceptManualWithdrawalResult> {
|
||||||
const withdrawalGroup = await internalCreateWithdrawalGroup(ws, {
|
const withdrawalGroup = await internalCreateWithdrawalGroup(ws, {
|
||||||
amount: Amounts.jsonifyAmount(req.amount),
|
amount: Amounts.jsonifyAmount(req.amount),
|
||||||
|
withdrawalType: WithdrawalRecordType.BankManual,
|
||||||
exchangeBaseUrl: req.exchangeBaseUrl,
|
exchangeBaseUrl: req.exchangeBaseUrl,
|
||||||
bankInfo: undefined,
|
bankInfo: undefined,
|
||||||
forcedDenomSel: req.forcedDenomSel,
|
forcedDenomSel: req.forcedDenomSel,
|
||||||
|
Loading…
Reference in New Issue
Block a user