wallet-core: CLI improvements, ToS fetching fixes
This commit is contained in:
parent
fbb7dd9e7e
commit
8d4a7d6103
@ -314,48 +314,42 @@ walletCli
|
||||
logger.info("finished handling API request");
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("", "pending", { help: "Show pending operations." })
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
const pending = await wallet.client.call(
|
||||
WalletApiOperation.GetPendingOperations,
|
||||
{},
|
||||
);
|
||||
console.log(JSON.stringify(pending, undefined, 2));
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("transactions", "transactions", { help: "Show transactions." })
|
||||
const transactionsCli = walletCli
|
||||
.subcommand("transactions", "transactions", { help: "Manage transactions." })
|
||||
.maybeOption("currency", ["--currency"], clk.STRING)
|
||||
.maybeOption("search", ["--search"], clk.STRING)
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
const pending = await wallet.client.call(
|
||||
WalletApiOperation.GetTransactions,
|
||||
{
|
||||
currency: args.transactions.currency,
|
||||
search: args.transactions.search,
|
||||
},
|
||||
);
|
||||
console.log(JSON.stringify(pending, undefined, 2));
|
||||
});
|
||||
});
|
||||
.maybeOption("search", ["--search"], clk.STRING);
|
||||
|
||||
walletCli
|
||||
.subcommand("runPendingOpt", "run-pending", {
|
||||
help: "Run pending operations.",
|
||||
// Default action
|
||||
transactionsCli.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
const pending = await wallet.client.call(
|
||||
WalletApiOperation.GetTransactions,
|
||||
{
|
||||
currency: args.transactions.currency,
|
||||
search: args.transactions.search,
|
||||
},
|
||||
);
|
||||
console.log(JSON.stringify(pending, undefined, 2));
|
||||
});
|
||||
});
|
||||
|
||||
transactionsCli
|
||||
.subcommand("deleteTransaction", "delete", {
|
||||
help: "Permanently delete a transaction from the transaction list.",
|
||||
})
|
||||
.requiredArgument("transactionId", clk.STRING, {
|
||||
help: "Identifier of the transaction to delete",
|
||||
})
|
||||
.flag("forceNow", ["-f", "--force-now"])
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
await wallet.ws.runPending(args.runPendingOpt.forceNow);
|
||||
await wallet.client.call(WalletApiOperation.DeleteTransaction, {
|
||||
transactionId: args.deleteTransaction.transactionId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("retryTransaction", "retry-transaction", {
|
||||
transactionsCli
|
||||
.subcommand("retryTransaction", "retry", {
|
||||
help: "Retry a transaction.",
|
||||
})
|
||||
.requiredArgument("transactionId", clk.STRING)
|
||||
@ -387,21 +381,6 @@ walletCli
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("deleteTransaction", "delete-transaction", {
|
||||
help: "Permanently delete a transaction from the transaction list.",
|
||||
})
|
||||
.requiredArgument("transactionId", clk.STRING, {
|
||||
help: "Identifier of the transaction to delete",
|
||||
})
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
await wallet.client.call(WalletApiOperation.DeleteTransaction, {
|
||||
transactionId: args.deleteTransaction.transactionId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("withdraw", "withdraw", {
|
||||
help: "Withdraw with a taler://withdraw/ URI",
|
||||
@ -604,17 +583,26 @@ exchangesCli
|
||||
|
||||
exchangesCli
|
||||
.subcommand("exchangesTosCmd", "tos", {
|
||||
help: "Show terms of service.",
|
||||
help: "Show/request terms of service.",
|
||||
})
|
||||
.requiredArgument("url", clk.STRING, {
|
||||
help: "Base URL of the exchange.",
|
||||
})
|
||||
.maybeOption("contentTypes", ["--content-type"], clk.STRING)
|
||||
.action(async (args) => {
|
||||
let acceptedFormat: string[] | undefined = undefined;
|
||||
if (args.exchangesTosCmd.contentTypes) {
|
||||
const split = args.exchangesTosCmd.contentTypes
|
||||
.split(",")
|
||||
.map((x) => x.trim());
|
||||
acceptedFormat = split;
|
||||
}
|
||||
await withWallet(args, async (wallet) => {
|
||||
const tosResult = await wallet.client.call(
|
||||
WalletApiOperation.GetExchangeTos,
|
||||
{
|
||||
exchangeBaseUrl: args.exchangesTosCmd.url,
|
||||
acceptedFormat,
|
||||
},
|
||||
);
|
||||
console.log(JSON.stringify(tosResult, undefined, 2));
|
||||
@ -764,6 +752,29 @@ advancedCli
|
||||
await withWallet(args, async () => {});
|
||||
});
|
||||
|
||||
advancedCli
|
||||
.subcommand("runPendingOpt", "run-pending", {
|
||||
help: "Run pending operations.",
|
||||
})
|
||||
.flag("forceNow", ["-f", "--force-now"])
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
await wallet.ws.runPending(args.runPendingOpt.forceNow);
|
||||
});
|
||||
});
|
||||
|
||||
advancedCli
|
||||
.subcommand("", "pending", { help: "Show pending operations." })
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
const pending = await wallet.client.call(
|
||||
WalletApiOperation.GetPendingOperations,
|
||||
{},
|
||||
);
|
||||
console.log(JSON.stringify(pending, undefined, 2));
|
||||
});
|
||||
});
|
||||
|
||||
advancedCli
|
||||
.subcommand("bench1", "bench1", {
|
||||
help: "Run the 'bench1' benchmark",
|
||||
|
@ -521,7 +521,7 @@ export interface ExchangeRecord {
|
||||
* Should usually not change. Only changes when the
|
||||
* exchange advertises a different master public key and/or
|
||||
* currency.
|
||||
*
|
||||
*
|
||||
* FIXME: Use a rowId here?
|
||||
*/
|
||||
detailsPointer: ExchangeDetailsPointer | undefined;
|
||||
@ -1364,7 +1364,7 @@ export interface WithdrawalGroupRecord {
|
||||
/**
|
||||
* Wire information (as payto URI) for the bank account that
|
||||
* transferred funds for this reserve.
|
||||
*
|
||||
*
|
||||
* FIXME: Doesn't this belong to the bankAccounts object store?
|
||||
*/
|
||||
senderWire?: string;
|
||||
|
@ -134,6 +134,7 @@ export async function downloadExchangeWithTermsOfService(
|
||||
timeout: Duration,
|
||||
contentType: string,
|
||||
): Promise<ExchangeTosDownloadResult> {
|
||||
logger.info(`downloading exchange tos (type ${contentType})`);
|
||||
const reqUrl = new URL("terms", exchangeBaseUrl);
|
||||
const headers = {
|
||||
Accept: contentType,
|
||||
@ -524,7 +525,9 @@ export async function downloadTosFromAcceptedFormat(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tosFound !== undefined) return tosFound;
|
||||
if (tosFound !== undefined) {
|
||||
return tosFound;
|
||||
}
|
||||
// If none of the specified format was found try text/plain
|
||||
return await downloadExchangeWithTermsOfService(
|
||||
baseUrl,
|
||||
@ -557,7 +560,7 @@ export async function updateExchangeFromUrl(
|
||||
*/
|
||||
export async function updateExchangeFromUrlHandler(
|
||||
ws: InternalWalletState,
|
||||
baseUrl: string,
|
||||
exchangeBaseUrl: string,
|
||||
options: {
|
||||
forceNow?: boolean;
|
||||
cancellationToken?: CancellationToken;
|
||||
@ -569,19 +572,21 @@ export async function updateExchangeFromUrlHandler(
|
||||
}>
|
||||
> {
|
||||
const forceNow = options.forceNow ?? false;
|
||||
logger.info(`updating exchange info for ${baseUrl}, forced: ${forceNow}`);
|
||||
logger.info(
|
||||
`updating exchange info for ${exchangeBaseUrl}, forced: ${forceNow}`,
|
||||
);
|
||||
|
||||
const now = AbsoluteTime.now();
|
||||
baseUrl = canonicalizeBaseUrl(baseUrl);
|
||||
exchangeBaseUrl = canonicalizeBaseUrl(exchangeBaseUrl);
|
||||
let isNewExchange = true;
|
||||
const { exchange, exchangeDetails } = await ws.db
|
||||
.mktx((x) => [x.exchanges, x.exchangeDetails])
|
||||
.runReadWrite(async (tx) => {
|
||||
let oldExch = await tx.exchanges.get(baseUrl);
|
||||
let oldExch = await tx.exchanges.get(exchangeBaseUrl);
|
||||
if (oldExch) {
|
||||
isNewExchange = false;
|
||||
}
|
||||
return provideExchangeRecordInTx(ws, tx, baseUrl, now);
|
||||
return provideExchangeRecordInTx(ws, tx, exchangeBaseUrl, now);
|
||||
});
|
||||
|
||||
if (
|
||||
@ -600,11 +605,15 @@ export async function updateExchangeFromUrlHandler(
|
||||
|
||||
const timeout = getExchangeRequestTimeout();
|
||||
|
||||
const keysInfo = await downloadExchangeKeysInfo(baseUrl, ws.http, timeout);
|
||||
const keysInfo = await downloadExchangeKeysInfo(
|
||||
exchangeBaseUrl,
|
||||
ws.http,
|
||||
timeout,
|
||||
);
|
||||
|
||||
logger.info("updating exchange /wire info");
|
||||
const wireInfoDownload = await downloadExchangeWireInfo(
|
||||
baseUrl,
|
||||
exchangeBaseUrl,
|
||||
ws.http,
|
||||
timeout,
|
||||
);
|
||||
@ -632,15 +641,15 @@ export async function updateExchangeFromUrlHandler(
|
||||
|
||||
logger.info("finished validating exchange /wire info");
|
||||
|
||||
// We download the text/plain version here,
|
||||
// because that one needs to exist, and we
|
||||
// will get the current etag from the response.
|
||||
const tosDownload = await downloadTosFromAcceptedFormat(
|
||||
ws,
|
||||
baseUrl,
|
||||
exchangeBaseUrl,
|
||||
timeout,
|
||||
["text/plain"],
|
||||
);
|
||||
const tosHasBeenAccepted =
|
||||
exchangeDetails?.tosAccepted &&
|
||||
exchangeDetails.tosAccepted.etag === tosDownload.tosEtag;
|
||||
|
||||
let recoupGroupId: string | undefined;
|
||||
|
||||
@ -651,6 +660,7 @@ export async function updateExchangeFromUrlHandler(
|
||||
const updated = await ws.db
|
||||
.mktx((x) => [
|
||||
x.exchanges,
|
||||
x.exchangeTos,
|
||||
x.exchangeDetails,
|
||||
x.exchangeSignkeys,
|
||||
x.denominations,
|
||||
@ -659,13 +669,13 @@ export async function updateExchangeFromUrlHandler(
|
||||
x.recoupGroups,
|
||||
])
|
||||
.runReadWrite(async (tx) => {
|
||||
const r = await tx.exchanges.get(baseUrl);
|
||||
const r = await tx.exchanges.get(exchangeBaseUrl);
|
||||
if (!r) {
|
||||
logger.warn(`exchange ${baseUrl} no longer present`);
|
||||
logger.warn(`exchange ${exchangeBaseUrl} no longer present`);
|
||||
return;
|
||||
}
|
||||
let existingDetails = await getExchangeDetails(tx, r.baseUrl);
|
||||
let acceptedTosEtag = undefined;
|
||||
let acceptedTosEtag: string | undefined = undefined;
|
||||
if (!existingDetails) {
|
||||
detailsPointerChanged = true;
|
||||
}
|
||||
@ -708,6 +718,21 @@ export async function updateExchangeFromUrlHandler(
|
||||
const drRowId = await tx.exchangeDetails.put(newDetails);
|
||||
checkDbInvariant(typeof drRowId.key === "number");
|
||||
|
||||
let tosRecord = await tx.exchangeTos.get([
|
||||
exchangeBaseUrl,
|
||||
tosDownload.tosEtag,
|
||||
]);
|
||||
|
||||
if (!tosRecord || tosRecord.etag !== existingTosAccepted?.etag) {
|
||||
tosRecord = {
|
||||
etag: tosDownload.tosEtag,
|
||||
exchangeBaseUrl,
|
||||
termsOfServiceContentType: tosDownload.tosContentType,
|
||||
termsOfServiceText: tosDownload.tosText,
|
||||
};
|
||||
await tx.exchangeTos.put(tosRecord);
|
||||
}
|
||||
|
||||
for (const sk of keysInfo.signingKeys) {
|
||||
// FIXME: validate signing keys before inserting them
|
||||
await tx.exchangeSignKeys.put({
|
||||
@ -726,7 +751,7 @@ export async function updateExchangeFromUrlHandler(
|
||||
);
|
||||
for (const currentDenom of keysInfo.currentDenominations) {
|
||||
const oldDenom = await tx.denominations.get([
|
||||
baseUrl,
|
||||
exchangeBaseUrl,
|
||||
currentDenom.denomPubHash,
|
||||
]);
|
||||
if (oldDenom) {
|
||||
@ -802,6 +827,7 @@ export async function updateExchangeFromUrlHandler(
|
||||
newlyRevokedCoinPubs,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
exchange: r,
|
||||
exchangeDetails: newDetails,
|
||||
|
@ -631,7 +631,7 @@ async function getExchangeTosStatusDetails(
|
||||
|
||||
return {
|
||||
acceptedVersion: exchangeDetails.tosAccepted?.etag,
|
||||
content: exchangeTos.termsOfServiceContentType,
|
||||
content: exchangeTos.termsOfServiceText,
|
||||
contentType: exchangeTos.termsOfServiceContentType,
|
||||
currentVersion: exchangeTos.etag,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user