gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: towards exchange linting


From: gnunet
Subject: [taler-wallet-core] branch master updated: towards exchange linting
Date: Wed, 04 Aug 2021 17:14:58 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new f88e14f6 towards exchange linting
f88e14f6 is described below

commit f88e14f66d37c339816cb9ba73a84491e7133307
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Aug 4 17:14:52 2021 +0200

    towards exchange linting
---
 packages/taler-util/src/talerTypes.ts              |  22 ++
 packages/taler-wallet-cli/src/index.ts             |   8 +-
 .../src/integrationtests/harness.ts                |  43 ----
 packages/taler-wallet-cli/src/lint.ts              | 265 ++++++++++++++++++++-
 4 files changed, 288 insertions(+), 50 deletions(-)

diff --git a/packages/taler-util/src/talerTypes.ts 
b/packages/taler-util/src/talerTypes.ts
index 0df46c6d..56110ec1 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -1429,3 +1429,25 @@ export const codecForTalerConfigResponse = (): 
Codec<TalerConfigResponse> =>
     .property("version", codecForString())
     .property("currency", codecOptional(codecForString()))
     .build("TalerConfigResponse");
+
+export interface FutureKeysResponse {
+  future_denoms: any[];
+
+  future_signkeys: any[];
+
+  master_pub: string;
+
+  denom_secmod_public_key: string;
+
+  // Public key of the signkey security module.
+  signkey_secmod_public_key: string;
+}
+
+export const codecForKeysManagementResponse = (): Codec<FutureKeysResponse> =>
+  buildCodecForObject<FutureKeysResponse>()
+    .property("master_pub", codecForString())
+    .property("future_signkeys", codecForList(codecForAny()))
+    .property("future_denoms", codecForList(codecForAny()))
+    .property("denom_secmod_public_key", codecForAny())
+    .property("signkey_secmod_public_key", codecForAny())
+    .build("FutureKeysResponse");
diff --git a/packages/taler-wallet-cli/src/index.ts 
b/packages/taler-wallet-cli/src/index.ts
index 26ee9566..f21e98f0 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -55,7 +55,7 @@ import {
   WalletCoreApiClient,
   Wallet,
 } from "@gnu-taler/taler-wallet-core";
-import { lintDeployment } from "./lint.js";
+import { lintExchangeDeployment } from "./lint.js";
 
 // This module also serves as the entry point for the crypto
 // thread worker, and thus must expose these two handlers.
@@ -870,8 +870,10 @@ const deploymentCli = 
walletCli.subcommand("deploymentArgs", "deployment", {
   help: "Subcommands for handling GNU Taler deployments.",
 });
 
-deploymentCli.subcommand("lint", "lint").action(async (args) => {
-  lintDeployment();
+deploymentCli.subcommand("lintExchange", "lint-exchange", {
+  help: "Run checks on the exchange deployment."
+}).action(async (args) => {
+  await lintExchangeDeployment();
 });
 
 deploymentCli
diff --git a/packages/taler-wallet-cli/src/integrationtests/harness.ts 
b/packages/taler-wallet-cli/src/integrationtests/harness.ts
index 18b06d1d..285e9aa1 100644
--- a/packages/taler-wallet-cli/src/integrationtests/harness.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/harness.ts
@@ -51,7 +51,6 @@ import {
   getRandomBytes,
   openPromise,
   OperationFailedError,
-  WalletApiOperation,
   WalletCoreApiClient,
 } from "@gnu-taler/taler-wallet-core";
 import {
@@ -65,49 +64,7 @@ import {
   Duration,
   parsePaytoUri,
   CoreApiResponse,
-  ApplyRefundRequest,
-  ApplyRefundResponse,
-  codecForApplyRefundResponse,
-  PreparePayRequest,
-  PreparePayResult,
-  codecForPreparePayResult,
-  CreateDepositGroupRequest,
-  CreateDepositGroupResponse,
-  AbortPayWithRefundRequest,
-  ConfirmPayRequest,
-  ConfirmPayResult,
-  codecForConfirmPayResult,
-  PrepareTipRequest,
-  PrepareTipResult,
-  codecForPrepareTipResult,
-  AcceptTipRequest,
-  CoinDumpJson,
-  codecForAny,
-  AddExchangeRequest,
-  ForceExchangeUpdateRequest,
-  ForceRefreshRequest,
-  ExchangesListRespose,
-  codecForExchangesListResponse,
-  BalancesResponse,
-  codecForBalancesResponse,
-  TransactionsResponse,
-  codecForTransactionsResponse,
-  TrackDepositGroupRequest,
-  TrackDepositGroupResponse,
-  IntegrationTestArgs,
-  TestPayArgs,
-  WithdrawTestBalanceRequest,
-  GetWithdrawalDetailsForUriRequest,
-  WithdrawUriInfoResponse,
-  codecForWithdrawUriInfoResponse,
-  BackupRecovery,
-  RecoveryLoadRequest,
 } from "@gnu-taler/taler-util";
-import {
-  AddBackupProviderRequest,
-  BackupInfo,
-} from "@gnu-taler/taler-wallet-core/src/operations/backup";
-import { PendingOperationsResponse } from 
"@gnu-taler/taler-wallet-core/src/pending-types";
 import { CoinConfig } from "./denomStructures.js";
 
 const exec = util.promisify(require("child_process").exec);
diff --git a/packages/taler-wallet-cli/src/lint.ts 
b/packages/taler-wallet-cli/src/lint.ts
index ad00143b..f7dfefe3 100644
--- a/packages/taler-wallet-cli/src/lint.ts
+++ b/packages/taler-wallet-cli/src/lint.ts
@@ -17,18 +17,94 @@
 /**
  * Imports.
  */
-import { Configuration } from "@gnu-taler/taler-util";
+import {
+  buildCodecForObject,
+  Codec,
+  codecForAny,
+  codecForExchangeKeysJson,
+  codecForKeysManagementResponse,
+  codecForList,
+  codecForString,
+  Configuration,
+} from "@gnu-taler/taler-util";
+import {
+  decodeCrock,
+  NodeHttpLib,
+  readSuccessResponseJsonOrThrow,
+} from "@gnu-taler/taler-wallet-core";
+import { URL } from "url";
+import * as fs from "fs";
+import * as path from "path";
+import { ChildProcess, spawn } from "child_process";
+
+interface BasicConf {
+  mainCurrency: string;
+}
+
+interface PubkeyConf {
+  masterPublicKey: string;
+}
+
+const httpLib = new NodeHttpLib();
+
+interface ShellResult {
+  stdout: string;
+  stderr: string;
+  status: number;
+}
 
 /**
- * Do some basic checks in the configuration of a Taler deployment.
+ * Run a shell command, return stdout.
  */
-export function lintDeployment() {
-  const cfg = Configuration.load();
+export async function sh(
+  command: string,
+  env: { [index: string]: string | undefined } = process.env,
+): Promise<ShellResult> {
+  return new Promise((resolve, reject) => {
+    const stdoutChunks: Buffer[] = [];
+    const stderrChunks: Buffer[] = [];
+    const proc = spawn(command, {
+      stdio: ["inherit", "pipe", "pipe"],
+      shell: true,
+      env: env,
+    });
+    proc.stdout.on("data", (x) => {
+      if (x instanceof Buffer) {
+        stdoutChunks.push(x);
+      } else {
+        throw Error("unexpected data chunk type");
+      }
+    });
+    proc.stderr.on("data", (x) => {
+      if (x instanceof Buffer) {
+        stderrChunks.push(x);
+      } else {
+        throw Error("unexpected data chunk type");
+      }
+    });
+    proc.on("exit", (code, signal) => {
+      console.log(`child process exited (${code} / ${signal})`);
+      const bOut = Buffer.concat(stdoutChunks).toString("utf-8");
+      const bErr = Buffer.concat(stderrChunks).toString("utf-8");
+      resolve({
+        status: code ?? -1,
+        stderr: bErr,
+        stdout: bOut,
+      });
+    });
+    proc.on("error", () => {
+      reject(Error("Child process had error"));
+    });
+  });
+}
+
+function checkBasicConf(cfg: Configuration): BasicConf {
   const currencyEntry = cfg.getString("taler", "currency");
   let mainCurrency: string | undefined;
 
   if (!currencyEntry.value) {
     console.log("error: currency not defined in section TALER option 
CURRENCY");
+    process.exit(1);
   } else {
     mainCurrency = currencyEntry.value.toUpperCase();
   }
@@ -38,4 +114,185 @@ export function lintDeployment() {
       "warning: section TALER option CURRENCY contains toy currency value 
KUDOS",
     );
   }
+
+  const roundUnit = cfg.getAmount("taler", "currency_round_unit");
+  if (!roundUnit.isDefined) {
+    console.log(
+      "error: configuration incomplete, section TALER option 
CURRENCY_ROUND_UNIT missing",
+    );
+  }
+  return { mainCurrency };
+}
+
+function checkCoinConfig(cfg: Configuration, basic: BasicConf): void {
+  const coinPrefix = "coin_";
+  let numCoins = 0;
+
+  for (const secName of cfg.getSectionNames()) {
+    if (!secName.startsWith(coinPrefix)) {
+      continue;
+    }
+    numCoins++;
+
+    // FIXME: check that section is well-formed
+  }
+
+  console.log(
+    "error: no coin denomination configured, please configure [coin_*] 
sections",
+  );
+}
+
+function checkWireConfig(cfg: Configuration): void {
+  const accountPrefix = "exchange-account-";
+  const accountCredentialsPrefix = "exchange-accountcredentials-";
+
+  let accounts = new Set<string>();
+  let credentials = new Set<string>();
+
+  for (const secName of cfg.getSectionNames()) {
+    if (secName.startsWith(accountPrefix)) {
+      accounts.add(secName.slice(accountPrefix.length));
+      // FIXME: check settings
+    }
+
+    if (secName.startsWith(accountCredentialsPrefix)) {
+      credentials.add(secName.slice(accountCredentialsPrefix.length));
+      // FIXME: check settings
+    }
+  }
+
+  for (const acc of accounts) {
+    if (!credentials.has(acc)) {
+      console.log(
+        `warning: no credentials configured for exchange-account-${acc}`,
+      );
+    }
+  }
+
+  // FIXME: now try to use taler-exchange-wire-gateway-client to connect!
+  // FIXME: run wirewatch in test mode here?
+  // FIXME: run transfer in test mode here?
+}
+
+function checkAggregatorConfig(cfg: Configuration) {
+  // FIXME: run aggregator in test mode here
+}
+
+function checkCloserConfig(cfg: Configuration) {
+  // FIXME: run closer in test mode here
+}
+
+function checkMasterPublicKeyConfig(cfg: Configuration): PubkeyConf {
+  const pub = cfg.getString("exchange", "master_public_key");
+
+  if (!pub.isDefined) {
+    console.log("error: Master public key is not set.");
+    process.exit(1);
+  }
+
+  const pubDecoded = decodeCrock(pub.required());
+
+  if (pubDecoded.length != 32) {
+    console.log("error: invalid master public key");
+    process.exit(1);
+  }
+
+  return {
+    masterPublicKey: pub.required(),
+  };
+}
+
+export async function checkExchangeHttpd(
+  cfg: Configuration,
+  pubConf: PubkeyConf,
+): Promise<void> {
+  const baseUrlEntry = cfg.getString("exchange", "base_url");
+
+  if (!baseUrlEntry.isDefined) {
+    console.log(
+      "error: configuration needs to specify section EXCHANGE option BASE_URL",
+    );
+    process.exit(1);
+  }
+
+  const baseUrl = baseUrlEntry.required();
+
+  if (!baseUrl.startsWith("http")) {
+    console.log(
+      "error: section EXCHANGE option BASE_URL needs to be an http or https 
URL",
+    );
+    process.exit(1);
+  }
+
+  if (!baseUrl.endsWith("/")) {
+    console.log(
+      "error: section EXCHANGE option BASE_URL needs to end with a slash",
+    );
+    process.exit(1);
+  }
+
+  if (!baseUrl.startsWith("https://";)) {
+    console.log(
+      "warning: section EXCHANGE option BASE_URL: it is recommended to serve 
the exchange via HTTPS",
+    );
+    process.exit(1);
+  }
+
+  {
+    const mgmtUrl = new URL("management/keys", baseUrl);
+    const resp = await httpLib.get(mgmtUrl.href);
+
+    const futureKeys = await readSuccessResponseJsonOrThrow(
+      resp,
+      codecForKeysManagementResponse(),
+    );
+
+    if (futureKeys.future_denoms.length > 0) {
+      console.log(
+        `warning: exchange has denomination keys that need to be signed by the 
offline signing procedure`,
+      );
+    }
+
+    if (futureKeys.future_signkeys.length > 0) {
+      console.log(
+        `warning: exchange has signing keys that need to be signed by the 
offline signing procedure`,
+      );
+    }
+  }
+
+  {
+    const keysUrl = new URL("keys", baseUrl);
+    const resp = await httpLib.get(keysUrl.href);
+    const keys = await readSuccessResponseJsonOrThrow(
+      resp,
+      codecForExchangeKeysJson(),
+    );
+  }
+}
+
+/**
+ * Do some basic checks in the configuration of a Taler deployment.
+ */
+export async function lintExchangeDeployment(): Promise<void> {
+  if (process.getuid() != 1) {
+    console.log(
+      "warning: the exchange deployment linter is designed to be run as root",
+    );
+  }
+
+  const cfg = Configuration.load();
+
+  const basic = checkBasicConf(cfg);
+
+  checkCoinConfig(cfg, basic);
+
+  checkWireConfig(cfg);
+
+  checkAggregatorConfig(cfg);
+
+  checkCloserConfig(cfg);
+
+  const pubConf = checkMasterPublicKeyConfig(cfg);
+
+  await checkExchangeHttpd(cfg, pubConf);
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]