helpers for auditor integration test
This commit is contained in:
parent
c4d2899562
commit
01e83df471
@ -32,6 +32,7 @@ import { classifyTalerUri, TalerUriType } from "../util/taleruri";
|
|||||||
import util = require("util");
|
import util = require("util");
|
||||||
import { Configuration } from "../util/talerconfig";
|
import { Configuration } from "../util/talerconfig";
|
||||||
import { setDangerousTimetravel } from "../util/time";
|
import { setDangerousTimetravel } from "../util/time";
|
||||||
|
import { makeCodecForList, codecForString } from "../util/codec";
|
||||||
|
|
||||||
// Backwards compatibility with nodejs<0.11, where TextEncoder and TextDecoder
|
// Backwards compatibility with nodejs<0.11, where TextEncoder and TextDecoder
|
||||||
// are not globals yet.
|
// are not globals yet.
|
||||||
@ -118,7 +119,7 @@ const walletCli = clk
|
|||||||
help: "Command line interface for the GNU Taler wallet.",
|
help: "Command line interface for the GNU Taler wallet.",
|
||||||
})
|
})
|
||||||
.maybeOption("walletDbFile", ["--wallet-db"], clk.STRING, {
|
.maybeOption("walletDbFile", ["--wallet-db"], clk.STRING, {
|
||||||
help: "location of the wallet database file"
|
help: "location of the wallet database file",
|
||||||
})
|
})
|
||||||
.maybeOption("timetravel", ["--timetravel"], clk.INT, {
|
.maybeOption("timetravel", ["--timetravel"], clk.INT, {
|
||||||
help: "modify system time by given offset in microseconds",
|
help: "modify system time by given offset in microseconds",
|
||||||
@ -172,8 +173,8 @@ walletCli
|
|||||||
.flag("json", ["--json"], {
|
.flag("json", ["--json"], {
|
||||||
help: "Show raw JSON.",
|
help: "Show raw JSON.",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const balance = await wallet.getBalances();
|
const balance = await wallet.getBalances();
|
||||||
if (args.balance.json) {
|
if (args.balance.json) {
|
||||||
console.log(JSON.stringify(balance, undefined, 2));
|
console.log(JSON.stringify(balance, undefined, 2));
|
||||||
@ -195,8 +196,8 @@ walletCli
|
|||||||
.maybeOption("to", ["--to"], clk.STRING)
|
.maybeOption("to", ["--to"], clk.STRING)
|
||||||
.maybeOption("limit", ["--limit"], clk.STRING)
|
.maybeOption("limit", ["--limit"], clk.STRING)
|
||||||
.maybeOption("contEvt", ["--continue-with"], clk.STRING)
|
.maybeOption("contEvt", ["--continue-with"], clk.STRING)
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const history = await wallet.getHistory();
|
const history = await wallet.getHistory();
|
||||||
if (args.history.json) {
|
if (args.history.json) {
|
||||||
console.log(JSON.stringify(history, undefined, 2));
|
console.log(JSON.stringify(history, undefined, 2));
|
||||||
@ -216,8 +217,8 @@ walletCli
|
|||||||
|
|
||||||
walletCli
|
walletCli
|
||||||
.subcommand("", "pending", { help: "Show pending operations." })
|
.subcommand("", "pending", { help: "Show pending operations." })
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const pending = await wallet.getPendingOperations();
|
const pending = await wallet.getPendingOperations();
|
||||||
console.log(JSON.stringify(pending, undefined, 2));
|
console.log(JSON.stringify(pending, undefined, 2));
|
||||||
});
|
});
|
||||||
@ -234,8 +235,8 @@ walletCli
|
|||||||
help: "Run pending operations.",
|
help: "Run pending operations.",
|
||||||
})
|
})
|
||||||
.flag("forceNow", ["-f", "--force-now"])
|
.flag("forceNow", ["-f", "--force-now"])
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
await wallet.runPending(args.runPendingOpt.forceNow);
|
await wallet.runPending(args.runPendingOpt.forceNow);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -246,8 +247,8 @@ walletCli
|
|||||||
})
|
})
|
||||||
.requiredArgument("uri", clk.STRING)
|
.requiredArgument("uri", clk.STRING)
|
||||||
.flag("autoYes", ["-y", "--yes"])
|
.flag("autoYes", ["-y", "--yes"])
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const uri: string = args.handleUri.uri;
|
const uri: string = args.handleUri.uri;
|
||||||
const uriType = classifyTalerUri(uri);
|
const uriType = classifyTalerUri(uri);
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
@ -294,9 +295,9 @@ exchangesCli
|
|||||||
.subcommand("exchangesListCmd", "list", {
|
.subcommand("exchangesListCmd", "list", {
|
||||||
help: "List known exchanges.",
|
help: "List known exchanges.",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
console.log("Listing exchanges ...");
|
console.log("Listing exchanges ...");
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const exchanges = await wallet.getExchanges();
|
const exchanges = await wallet.getExchanges();
|
||||||
console.log("exchanges", exchanges);
|
console.log("exchanges", exchanges);
|
||||||
});
|
});
|
||||||
@ -310,8 +311,8 @@ exchangesCli
|
|||||||
help: "Base URL of the exchange.",
|
help: "Base URL of the exchange.",
|
||||||
})
|
})
|
||||||
.flag("force", ["-f", "--force"])
|
.flag("force", ["-f", "--force"])
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const res = await wallet.updateExchangeFromUrl(
|
const res = await wallet.updateExchangeFromUrl(
|
||||||
args.exchangesUpdateCmd.url,
|
args.exchangesUpdateCmd.url,
|
||||||
args.exchangesUpdateCmd.force,
|
args.exchangesUpdateCmd.force,
|
||||||
@ -328,7 +329,7 @@ advancedCli
|
|||||||
.subcommand("decode", "decode", {
|
.subcommand("decode", "decode", {
|
||||||
help: "Decode base32-crockford.",
|
help: "Decode base32-crockford.",
|
||||||
})
|
})
|
||||||
.action(args => {
|
.action((args) => {
|
||||||
const enc = fs.readFileSync(0, "utf8");
|
const enc = fs.readFileSync(0, "utf8");
|
||||||
fs.writeFileSync(1, decodeCrock(enc.trim()));
|
fs.writeFileSync(1, decodeCrock(enc.trim()));
|
||||||
});
|
});
|
||||||
@ -338,8 +339,8 @@ advancedCli
|
|||||||
help: "Claim an order but don't pay yet.",
|
help: "Claim an order but don't pay yet.",
|
||||||
})
|
})
|
||||||
.requiredArgument("url", clk.STRING)
|
.requiredArgument("url", clk.STRING)
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const res = await wallet.preparePayForUri(args.payPrepare.url);
|
const res = await wallet.preparePayForUri(args.payPrepare.url);
|
||||||
switch (res.status) {
|
switch (res.status) {
|
||||||
case "error":
|
case "error":
|
||||||
@ -365,18 +366,75 @@ advancedCli
|
|||||||
help: "Force a refresh on a coin.",
|
help: "Force a refresh on a coin.",
|
||||||
})
|
})
|
||||||
.requiredArgument("coinPub", clk.STRING)
|
.requiredArgument("coinPub", clk.STRING)
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
await wallet.refresh(args.refresh.coinPub);
|
await wallet.refresh(args.refresh.coinPub);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
advancedCli
|
||||||
|
.subcommand("dumpCoins", "dump-coins", {
|
||||||
|
help: "Dump coins in an easy-to-process format.",
|
||||||
|
})
|
||||||
|
.action(async (args) => {
|
||||||
|
await withWallet(args, async (wallet) => {
|
||||||
|
const coinDump = await wallet.dumpCoins();
|
||||||
|
console.log(JSON.stringify(coinDump, undefined, 2));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const coinPubListCodec = makeCodecForList(codecForString);
|
||||||
|
|
||||||
|
advancedCli
|
||||||
|
.subcommand("suspendCoins", "suspend-coins", {
|
||||||
|
help: "Mark a coin as suspended, will not be used for payments.",
|
||||||
|
})
|
||||||
|
.requiredArgument("coinPubSpec", clk.STRING)
|
||||||
|
.action(async (args) => {
|
||||||
|
await withWallet(args, async (wallet) => {
|
||||||
|
let coinPubList: string[];
|
||||||
|
try {
|
||||||
|
coinPubList = coinPubListCodec.decode(
|
||||||
|
JSON.parse(args.suspendCoins.coinPubSpec),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("could not parse coin list:", e.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
for (const c of coinPubList) {
|
||||||
|
await wallet.setCoinSuspended(c, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
advancedCli
|
||||||
|
.subcommand("unsuspendCoins", "unsuspend-coins", {
|
||||||
|
help: "Mark a coin as suspended, will not be used for payments.",
|
||||||
|
})
|
||||||
|
.requiredArgument("coinPubSpec", clk.STRING)
|
||||||
|
.action(async (args) => {
|
||||||
|
await withWallet(args, async (wallet) => {
|
||||||
|
let coinPubList: string[];
|
||||||
|
try {
|
||||||
|
coinPubList = coinPubListCodec.decode(
|
||||||
|
JSON.parse(args.unsuspendCoins.coinPubSpec),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("could not parse coin list:", e.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
for (const c of coinPubList) {
|
||||||
|
await wallet.setCoinSuspended(c, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
advancedCli
|
advancedCli
|
||||||
.subcommand("coins", "list-coins", {
|
.subcommand("coins", "list-coins", {
|
||||||
help: "List coins.",
|
help: "List coins.",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const coins = await wallet.getCoins();
|
const coins = await wallet.getCoins();
|
||||||
for (const coin of coins) {
|
for (const coin of coins) {
|
||||||
console.log(`coin ${coin.coinPub}`);
|
console.log(`coin ${coin.coinPub}`);
|
||||||
@ -395,8 +453,8 @@ advancedCli
|
|||||||
help: "Update reserve status.",
|
help: "Update reserve status.",
|
||||||
})
|
})
|
||||||
.requiredArgument("reservePub", clk.STRING)
|
.requiredArgument("reservePub", clk.STRING)
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const r = await wallet.updateReserve(args.updateReserve.reservePub);
|
const r = await wallet.updateReserve(args.updateReserve.reservePub);
|
||||||
console.log("updated reserve:", JSON.stringify(r, undefined, 2));
|
console.log("updated reserve:", JSON.stringify(r, undefined, 2));
|
||||||
});
|
});
|
||||||
@ -407,8 +465,8 @@ advancedCli
|
|||||||
help: "Show the current reserve status.",
|
help: "Show the current reserve status.",
|
||||||
})
|
})
|
||||||
.requiredArgument("reservePub", clk.STRING)
|
.requiredArgument("reservePub", clk.STRING)
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
const r = await wallet.getReserve(args.updateReserve.reservePub);
|
const r = await wallet.getReserve(args.updateReserve.reservePub);
|
||||||
console.log("updated reserve:", JSON.stringify(r, undefined, 2));
|
console.log("updated reserve:", JSON.stringify(r, undefined, 2));
|
||||||
});
|
});
|
||||||
@ -421,7 +479,7 @@ const testCli = walletCli.subcommand("testingArgs", "testing", {
|
|||||||
testCli
|
testCli
|
||||||
.subcommand("integrationtestBasic", "integrationtest-basic")
|
.subcommand("integrationtestBasic", "integrationtest-basic")
|
||||||
.requiredArgument("cfgfile", clk.STRING)
|
.requiredArgument("cfgfile", clk.STRING)
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
const cfgStr = fs.readFileSync(args.integrationtestBasic.cfgfile, "utf8");
|
const cfgStr = fs.readFileSync(args.integrationtestBasic.cfgfile, "utf8");
|
||||||
const cfg = new Configuration();
|
const cfg = new Configuration();
|
||||||
cfg.loadFromString(cfgStr);
|
cfg.loadFromString(cfgStr);
|
||||||
@ -429,7 +487,7 @@ testCli
|
|||||||
await runIntegrationTestBasic(cfg);
|
await runIntegrationTestBasic(cfg);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("integration test failed");
|
console.log("integration test failed");
|
||||||
console.log(e)
|
console.log(e);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
@ -441,7 +499,7 @@ testCli
|
|||||||
.requiredOption("summary", ["-s", "--summary"], clk.STRING, {
|
.requiredOption("summary", ["-s", "--summary"], clk.STRING, {
|
||||||
default: "Test Payment",
|
default: "Test Payment",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
const cmdArgs = args.testPayCmd;
|
const cmdArgs = args.testPayCmd;
|
||||||
console.log("creating order");
|
console.log("creating order");
|
||||||
const merchantBackend = new MerchantBackendConnection(
|
const merchantBackend = new MerchantBackendConnection(
|
||||||
@ -462,7 +520,7 @@ testCli
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("taler pay URI:", talerPayUri);
|
console.log("taler pay URI:", talerPayUri);
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
await doPay(wallet, talerPayUri, { alwaysYes: true });
|
await doPay(wallet, talerPayUri, { alwaysYes: true });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -489,7 +547,7 @@ testCli
|
|||||||
.requiredOption("spendAmount", ["-s", "--spend-amount"], clk.STRING, {
|
.requiredOption("spendAmount", ["-s", "--spend-amount"], clk.STRING, {
|
||||||
default: "TESTKUDOS:4",
|
default: "TESTKUDOS:4",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
applyVerbose(args.wallet.verbose);
|
applyVerbose(args.wallet.verbose);
|
||||||
let cmdObj = args.integrationtestCmd;
|
let cmdObj = args.integrationtestCmd;
|
||||||
|
|
||||||
@ -501,7 +559,7 @@ testCli
|
|||||||
exchangeBaseUrl: cmdObj.exchange,
|
exchangeBaseUrl: cmdObj.exchange,
|
||||||
merchantApiKey: cmdObj.merchantApiKey,
|
merchantApiKey: cmdObj.merchantApiKey,
|
||||||
merchantBaseUrl: cmdObj.merchant,
|
merchantBaseUrl: cmdObj.merchant,
|
||||||
}).catch(err => {
|
}).catch((err) => {
|
||||||
console.error("Integration test failed with exception:");
|
console.error("Integration test failed with exception:");
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@ -520,7 +578,7 @@ testCli
|
|||||||
.requiredOption("amount", ["-a", "--amount"], clk.STRING, {
|
.requiredOption("amount", ["-a", "--amount"], clk.STRING, {
|
||||||
default: "TESTKUDOS:10",
|
default: "TESTKUDOS:10",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
const merchantBackend = new MerchantBackendConnection(
|
const merchantBackend = new MerchantBackendConnection(
|
||||||
"https://backend.test.taler.net/",
|
"https://backend.test.taler.net/",
|
||||||
"sandbox",
|
"sandbox",
|
||||||
@ -539,7 +597,7 @@ testCli
|
|||||||
.requiredOption("bank", ["-b", "--bank"], clk.STRING, {
|
.requiredOption("bank", ["-b", "--bank"], clk.STRING, {
|
||||||
default: "https://bank.test.taler.net/",
|
default: "https://bank.test.taler.net/",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
const b = new Bank(args.genWithdrawUri.bank);
|
const b = new Bank(args.genWithdrawUri.bank);
|
||||||
const user = await b.registerRandomUser();
|
const user = await b.registerRandomUser();
|
||||||
const url = await b.generateWithdrawUri(user, args.genWithdrawUri.amount);
|
const url = await b.generateWithdrawUri(user, args.genWithdrawUri.amount);
|
||||||
@ -559,7 +617,7 @@ testCli
|
|||||||
.requiredOption("summary", ["-s", "--summary"], clk.STRING, {
|
.requiredOption("summary", ["-s", "--summary"], clk.STRING, {
|
||||||
default: "Test Payment (for refund)",
|
default: "Test Payment (for refund)",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
const cmdArgs = args.genRefundUri;
|
const cmdArgs = args.genRefundUri;
|
||||||
const merchantBackend = new MerchantBackendConnection(
|
const merchantBackend = new MerchantBackendConnection(
|
||||||
"https://backend.test.taler.net/",
|
"https://backend.test.taler.net/",
|
||||||
@ -578,7 +636,7 @@ testCli
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
await doPay(wallet, talerPayUri, { alwaysYes: true });
|
await doPay(wallet, talerPayUri, { alwaysYes: true });
|
||||||
});
|
});
|
||||||
const refundUri = await merchantBackend.refund(
|
const refundUri = await merchantBackend.refund(
|
||||||
@ -611,7 +669,7 @@ testCli
|
|||||||
.requiredOption("merchantApiKey", ["-k", "--merchant-api-key"], clk.STRING, {
|
.requiredOption("merchantApiKey", ["-k", "--merchant-api-key"], clk.STRING, {
|
||||||
default: "sandbox",
|
default: "sandbox",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
const cmdArgs = args.genPayUri;
|
const cmdArgs = args.genPayUri;
|
||||||
console.log("creating order");
|
console.log("creating order");
|
||||||
const merchantBackend = new MerchantBackendConnection(
|
const merchantBackend = new MerchantBackendConnection(
|
||||||
@ -669,8 +727,8 @@ testCli
|
|||||||
default: "https://bank.test.taler.net/",
|
default: "https://bank.test.taler.net/",
|
||||||
help: "Bank base URL",
|
help: "Bank base URL",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async wallet => {
|
await withWallet(args, async (wallet) => {
|
||||||
await withdrawTestBalance(
|
await withdrawTestBalance(
|
||||||
wallet,
|
wallet,
|
||||||
args.withdrawArgs.amount,
|
args.withdrawArgs.amount,
|
||||||
|
@ -318,10 +318,6 @@ async function getCoinsForPayment(
|
|||||||
.iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
|
.iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
|
||||||
.toArray();
|
.toArray();
|
||||||
|
|
||||||
const denoms = await ws.db
|
|
||||||
.iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
|
|
||||||
.toArray();
|
|
||||||
|
|
||||||
if (!coins || coins.length === 0) {
|
if (!coins || coins.length === 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -412,7 +412,8 @@ async function refreshReveal(
|
|||||||
coinSource: {
|
coinSource: {
|
||||||
type: CoinSourceType.Refresh,
|
type: CoinSourceType.Refresh,
|
||||||
oldCoinPub: refreshSession.meltCoinPub,
|
oldCoinPub: refreshSession.meltCoinPub,
|
||||||
}
|
},
|
||||||
|
suspended: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
coins.push(coin);
|
coins.push(coin);
|
||||||
|
@ -240,6 +240,7 @@ async function processPlanchet(
|
|||||||
reservePub: planchet.reservePub,
|
reservePub: planchet.reservePub,
|
||||||
withdrawSessionId: withdrawalSessionId,
|
withdrawSessionId: withdrawalSessionId,
|
||||||
},
|
},
|
||||||
|
suspended: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let withdrawSessionFinished = false;
|
let withdrawSessionFinished = false;
|
||||||
|
@ -674,11 +674,9 @@ export interface CoinRecord {
|
|||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We have withdrawn the coin, but it's not accepted by the exchange anymore.
|
* The coin is currently suspended, and will not be used for payments.
|
||||||
* We have to tell an auditor and wait for compensation or for the exchange
|
|
||||||
* to fix it.
|
|
||||||
*/
|
*/
|
||||||
suspended?: boolean;
|
suspended: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blinding key used when withdrawing the coin.
|
* Blinding key used when withdrawing the coin.
|
||||||
|
@ -759,6 +759,20 @@ export class WithdrawResponse {
|
|||||||
ev_sig: string;
|
ev_sig: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CoinDumpJson {
|
||||||
|
coins: Array<{
|
||||||
|
denom_pub: string;
|
||||||
|
denom_pub_hash: string;
|
||||||
|
denom_value: string;
|
||||||
|
coin_pub: string;
|
||||||
|
exchange_base_url: string;
|
||||||
|
remaining_value: string;
|
||||||
|
refresh_parent_coin_pub: string | undefined;
|
||||||
|
withdrawal_reserve_pub: string | undefined;
|
||||||
|
coin_suspended: boolean;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
export type AmountString = string;
|
export type AmountString = string;
|
||||||
export type Base32String = string;
|
export type Base32String = string;
|
||||||
export type EddsaSignatureString = string;
|
export type EddsaSignatureString = string;
|
||||||
|
@ -19,6 +19,12 @@ export class Logger {
|
|||||||
info(message: string, ...args: any[]) {
|
info(message: string, ...args: any[]) {
|
||||||
console.log(`${new Date().toISOString()} ${this.tag} INFO ` + message, ...args);
|
console.log(`${new Date().toISOString()} ${this.tag} INFO ` + message, ...args);
|
||||||
}
|
}
|
||||||
|
warn(message: string, ...args: any[]) {
|
||||||
|
console.log(`${new Date().toISOString()} ${this.tag} WARN ` + message, ...args);
|
||||||
|
}
|
||||||
|
error(message: string, ...args: any[]) {
|
||||||
|
console.log(`${new Date().toISOString()} ${this.tag} ERROR ` + message, ...args);
|
||||||
|
}
|
||||||
trace(message: any, ...args: any[]) {
|
trace(message: any, ...args: any[]) {
|
||||||
console.log(`${new Date().toISOString()} ${this.tag} TRACE ` + message, ...args)
|
console.log(`${new Date().toISOString()} ${this.tag} TRACE ` + message, ...args)
|
||||||
}
|
}
|
||||||
|
103
src/wallet.ts
103
src/wallet.ts
@ -53,8 +53,9 @@ import {
|
|||||||
ReserveRecord,
|
ReserveRecord,
|
||||||
Stores,
|
Stores,
|
||||||
ReserveRecordStatus,
|
ReserveRecordStatus,
|
||||||
|
CoinSourceType,
|
||||||
} from "./types/dbTypes";
|
} from "./types/dbTypes";
|
||||||
import { MerchantRefundPermission } from "./types/talerTypes";
|
import { MerchantRefundPermission, CoinDumpJson } from "./types/talerTypes";
|
||||||
import {
|
import {
|
||||||
BenchmarkResult,
|
BenchmarkResult,
|
||||||
ConfirmPayResult,
|
ConfirmPayResult,
|
||||||
@ -238,7 +239,10 @@ export class Wallet {
|
|||||||
await this.processOnePendingOperation(p, forceNow);
|
await this.processOnePendingOperation(p, forceNow);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof OperationFailedAndReportedError) {
|
if (e instanceof OperationFailedAndReportedError) {
|
||||||
console.error("Operation failed:", JSON.stringify(e.operationError, undefined, 2));
|
console.error(
|
||||||
|
"Operation failed:",
|
||||||
|
JSON.stringify(e.operationError, undefined, 2),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@ -254,7 +258,7 @@ export class Wallet {
|
|||||||
public async runUntilDone(): Promise<void> {
|
public async runUntilDone(): Promise<void> {
|
||||||
const p = new Promise((resolve, reject) => {
|
const p = new Promise((resolve, reject) => {
|
||||||
// Run this asynchronously
|
// Run this asynchronously
|
||||||
this.addNotificationListener(n => {
|
this.addNotificationListener((n) => {
|
||||||
if (
|
if (
|
||||||
n.type === NotificationType.WaitingForRetry &&
|
n.type === NotificationType.WaitingForRetry &&
|
||||||
n.numGivingLiveness == 0
|
n.numGivingLiveness == 0
|
||||||
@ -263,7 +267,7 @@ export class Wallet {
|
|||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.runRetryLoop().catch(e => {
|
this.runRetryLoop().catch((e) => {
|
||||||
console.log("exception in wallet retry loop");
|
console.log("exception in wallet retry loop");
|
||||||
reject(e);
|
reject(e);
|
||||||
});
|
});
|
||||||
@ -279,7 +283,7 @@ export class Wallet {
|
|||||||
public async runUntilDoneAndStop(): Promise<void> {
|
public async runUntilDoneAndStop(): Promise<void> {
|
||||||
const p = new Promise((resolve, reject) => {
|
const p = new Promise((resolve, reject) => {
|
||||||
// Run this asynchronously
|
// Run this asynchronously
|
||||||
this.addNotificationListener(n => {
|
this.addNotificationListener((n) => {
|
||||||
if (
|
if (
|
||||||
n.type === NotificationType.WaitingForRetry &&
|
n.type === NotificationType.WaitingForRetry &&
|
||||||
n.numGivingLiveness == 0
|
n.numGivingLiveness == 0
|
||||||
@ -288,7 +292,7 @@ export class Wallet {
|
|||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.runRetryLoop().catch(e => {
|
this.runRetryLoop().catch((e) => {
|
||||||
console.log("exception in wallet retry loop");
|
console.log("exception in wallet retry loop");
|
||||||
reject(e);
|
reject(e);
|
||||||
});
|
});
|
||||||
@ -371,9 +375,9 @@ export class Wallet {
|
|||||||
async fillDefaults() {
|
async fillDefaults() {
|
||||||
await this.db.runWithWriteTransaction(
|
await this.db.runWithWriteTransaction(
|
||||||
[Stores.config, Stores.currencies],
|
[Stores.config, Stores.currencies],
|
||||||
async tx => {
|
async (tx) => {
|
||||||
let applied = false;
|
let applied = false;
|
||||||
await tx.iter(Stores.config).forEach(x => {
|
await tx.iter(Stores.config).forEach((x) => {
|
||||||
if (x.key == "currencyDefaultsApplied" && x.value == true) {
|
if (x.key == "currencyDefaultsApplied" && x.value == true) {
|
||||||
applied = true;
|
applied = true;
|
||||||
}
|
}
|
||||||
@ -506,7 +510,7 @@ export class Wallet {
|
|||||||
try {
|
try {
|
||||||
const refreshGroupId = await this.db.runWithWriteTransaction(
|
const refreshGroupId = await this.db.runWithWriteTransaction(
|
||||||
[Stores.refreshGroups],
|
[Stores.refreshGroups],
|
||||||
async tx => {
|
async (tx) => {
|
||||||
return await createRefreshGroup(
|
return await createRefreshGroup(
|
||||||
tx,
|
tx,
|
||||||
[{ coinPub: oldCoinPub }],
|
[{ coinPub: oldCoinPub }],
|
||||||
@ -573,13 +577,13 @@ export class Wallet {
|
|||||||
async getReserves(exchangeBaseUrl: string): Promise<ReserveRecord[]> {
|
async getReserves(exchangeBaseUrl: string): Promise<ReserveRecord[]> {
|
||||||
return await this.db
|
return await this.db
|
||||||
.iter(Stores.reserves)
|
.iter(Stores.reserves)
|
||||||
.filter(r => r.exchangeBaseUrl === exchangeBaseUrl);
|
.filter((r) => r.exchangeBaseUrl === exchangeBaseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCoinsForExchange(exchangeBaseUrl: string): Promise<CoinRecord[]> {
|
async getCoinsForExchange(exchangeBaseUrl: string): Promise<CoinRecord[]> {
|
||||||
return await this.db
|
return await this.db
|
||||||
.iter(Stores.coins)
|
.iter(Stores.coins)
|
||||||
.filter(c => c.exchangeBaseUrl === exchangeBaseUrl);
|
.filter((c) => c.exchangeBaseUrl === exchangeBaseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCoins(): Promise<CoinRecord[]> {
|
async getCoins(): Promise<CoinRecord[]> {
|
||||||
@ -598,22 +602,22 @@ export class Wallet {
|
|||||||
async getSenderWireInfos(): Promise<SenderWireInfos> {
|
async getSenderWireInfos(): Promise<SenderWireInfos> {
|
||||||
const m: { [url: string]: Set<string> } = {};
|
const m: { [url: string]: Set<string> } = {};
|
||||||
|
|
||||||
await this.db.iter(Stores.exchanges).forEach(x => {
|
await this.db.iter(Stores.exchanges).forEach((x) => {
|
||||||
const wi = x.wireInfo;
|
const wi = x.wireInfo;
|
||||||
if (!wi) {
|
if (!wi) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const s = (m[x.baseUrl] = m[x.baseUrl] || new Set());
|
const s = (m[x.baseUrl] = m[x.baseUrl] || new Set());
|
||||||
Object.keys(wi.feesForType).map(k => s.add(k));
|
Object.keys(wi.feesForType).map((k) => s.add(k));
|
||||||
});
|
});
|
||||||
|
|
||||||
const exchangeWireTypes: { [url: string]: string[] } = {};
|
const exchangeWireTypes: { [url: string]: string[] } = {};
|
||||||
Object.keys(m).map(e => {
|
Object.keys(m).map((e) => {
|
||||||
exchangeWireTypes[e] = Array.from(m[e]);
|
exchangeWireTypes[e] = Array.from(m[e]);
|
||||||
});
|
});
|
||||||
|
|
||||||
const senderWiresSet: Set<string> = new Set();
|
const senderWiresSet: Set<string> = new Set();
|
||||||
await this.db.iter(Stores.senderWires).forEach(x => {
|
await this.db.iter(Stores.senderWires).forEach((x) => {
|
||||||
senderWiresSet.add(x.paytoUri);
|
senderWiresSet.add(x.paytoUri);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -735,20 +739,20 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
const refundsDoneAmounts = Object.values(
|
const refundsDoneAmounts = Object.values(
|
||||||
purchase.refundState.refundsDone,
|
purchase.refundState.refundsDone,
|
||||||
).map(x => Amounts.parseOrThrow(x.perm.refund_amount));
|
).map((x) => Amounts.parseOrThrow(x.perm.refund_amount));
|
||||||
const refundsPendingAmounts = Object.values(
|
const refundsPendingAmounts = Object.values(
|
||||||
purchase.refundState.refundsPending,
|
purchase.refundState.refundsPending,
|
||||||
).map(x => Amounts.parseOrThrow(x.perm.refund_amount));
|
).map((x) => Amounts.parseOrThrow(x.perm.refund_amount));
|
||||||
const totalRefundAmount = Amounts.sum([
|
const totalRefundAmount = Amounts.sum([
|
||||||
...refundsDoneAmounts,
|
...refundsDoneAmounts,
|
||||||
...refundsPendingAmounts,
|
...refundsPendingAmounts,
|
||||||
]).amount;
|
]).amount;
|
||||||
const refundsDoneFees = Object.values(
|
const refundsDoneFees = Object.values(
|
||||||
purchase.refundState.refundsDone,
|
purchase.refundState.refundsDone,
|
||||||
).map(x => Amounts.parseOrThrow(x.perm.refund_amount));
|
).map((x) => Amounts.parseOrThrow(x.perm.refund_amount));
|
||||||
const refundsPendingFees = Object.values(
|
const refundsPendingFees = Object.values(
|
||||||
purchase.refundState.refundsPending,
|
purchase.refundState.refundsPending,
|
||||||
).map(x => Amounts.parseOrThrow(x.perm.refund_amount));
|
).map((x) => Amounts.parseOrThrow(x.perm.refund_amount));
|
||||||
const totalRefundFees = Amounts.sum([
|
const totalRefundFees = Amounts.sum([
|
||||||
...refundsDoneFees,
|
...refundsDoneFees,
|
||||||
...refundsPendingFees,
|
...refundsPendingFees,
|
||||||
@ -765,4 +769,65 @@ export class Wallet {
|
|||||||
benchmarkCrypto(repetitions: number): Promise<BenchmarkResult> {
|
benchmarkCrypto(repetitions: number): Promise<BenchmarkResult> {
|
||||||
return this.ws.cryptoApi.benchmark(repetitions);
|
return this.ws.cryptoApi.benchmark(repetitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setCoinSuspended(coinPub: string, suspended: boolean): Promise<void> {
|
||||||
|
await this.db.runWithWriteTransaction([Stores.coins], async (tx) => {
|
||||||
|
const c = await tx.get(Stores.coins, coinPub);
|
||||||
|
if (!c) {
|
||||||
|
logger.warn(`coin ${coinPub} not found, won't suspend`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
c.suspended = suspended;
|
||||||
|
await tx.put(Stores.coins, c);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump the public information of coins we have in an easy-to-process format.
|
||||||
|
*/
|
||||||
|
async dumpCoins(): Promise<CoinDumpJson> {
|
||||||
|
const coins = await this.db.iter(Stores.coins).toArray();
|
||||||
|
const coinsJson: CoinDumpJson = { coins: [] };
|
||||||
|
for (const c of coins) {
|
||||||
|
const denom = await this.db.get(Stores.denominations, [
|
||||||
|
c.exchangeBaseUrl,
|
||||||
|
c.denomPub,
|
||||||
|
]);
|
||||||
|
if (!denom) {
|
||||||
|
console.error("no denom session found for coin");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const cs = c.coinSource;
|
||||||
|
let refreshParentCoinPub: string | undefined;
|
||||||
|
if (cs.type == CoinSourceType.Refresh) {
|
||||||
|
refreshParentCoinPub = cs.oldCoinPub;
|
||||||
|
}
|
||||||
|
let withdrawalReservePub: string | undefined;
|
||||||
|
if (cs.type == CoinSourceType.Withdraw) {
|
||||||
|
const ws = await this.db.get(
|
||||||
|
Stores.withdrawalSession,
|
||||||
|
cs.withdrawSessionId,
|
||||||
|
);
|
||||||
|
if (!ws) {
|
||||||
|
console.error("no withdrawal session found for coin");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ws.source.type == "reserve") {
|
||||||
|
withdrawalReservePub = ws.source.reservePub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
coinsJson.coins.push({
|
||||||
|
coin_pub: c.coinPub,
|
||||||
|
denom_pub: c.denomPub,
|
||||||
|
denom_pub_hash: c.denomPubHash,
|
||||||
|
denom_value: Amounts.toString(denom.value),
|
||||||
|
exchange_base_url: c.exchangeBaseUrl,
|
||||||
|
refresh_parent_coin_pub: refreshParentCoinPub,
|
||||||
|
remaining_value: Amounts.toString(c.currentAmount),
|
||||||
|
withdrawal_reserve_pub: withdrawalReservePub,
|
||||||
|
coin_suspended: c.suspended,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return coinsJson;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user