gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-wallet-webex] branch master updated (3c563c7 -> 82b5


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated (3c563c7 -> 82b5754)
Date: Thu, 27 Apr 2017 03:09:33 +0200

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

dold pushed a change to branch master
in repository wallet-webex.

    from 3c563c7  consider auditors when selecting exchange for payment
     new 68e44e0  fix style initialization
     new 82b5754  download, store and check signatures for wire fees

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/checkable.ts              | 119 ++++++++++++++++++++++++++++++----------
 src/content_scripts/notify.ts |  11 +++-
 src/cryptoApi.ts              |   6 ++-
 src/cryptoWorker.ts           |  21 +++++++-
 src/emscriptif.ts             |  39 ++++++++++++++
 src/types.ts                  |  17 ++++++
 src/wallet.ts                 | 122 +++++++++++++++++++++++++++++++++++++++---
 7 files changed, 298 insertions(+), 37 deletions(-)

diff --git a/src/checkable.ts b/src/checkable.ts
index c8cc27b..8af70f5 100644
--- a/src/checkable.ts
+++ b/src/checkable.ts
@@ -40,9 +40,17 @@ export namespace Checkable {
   interface Prop {
     propertyKey: any;
     checker: any;
-    type: any;
+    type?: any;
     elementChecker?: any;
     elementProp?: any;
+    keyProp?: any;
+    valueProp?: any;
+    optional?: boolean;
+    extraAllowed?: boolean;
+  }
+
+  interface CheckableInfo {
+    props: Prop[];
   }
 
   export let SchemaError = (function SchemaError(message: string) {
@@ -54,7 +62,24 @@ export namespace Checkable {
 
   SchemaError.prototype = new Error;
 
-  let chkSym = Symbol("checkable");
+  /**
+   * Classes that are checkable are annotated with this
+   * checkable info symbol, which contains the information necessary
+   * to check if they're valid.
+   */
+  let checkableInfoSym = Symbol("checkableInfo");
+
+  /**
+   * Get the current property list for a checkable type.
+   */
+  function getCheckableInfo(target: any): CheckableInfo {
+    let chk = target[checkableInfoSym] as CheckableInfo|undefined;
+    if (!chk) {
+      chk = { props: [] };
+      target[checkableInfoSym] = chk;
+    }
+    return chk;
+  }
 
 
   function checkNumber(target: any, prop: Prop, path: Path): any {
@@ -104,6 +129,17 @@ export namespace Checkable {
     return target;
   }
 
+  function checkMap(target: any, prop: Prop, path: Path): any {
+    if (typeof target !== "object") {
+      throw new SchemaError(`expected  object for ${path}, got ${typeof 
target} instead`);
+    }
+    for (let key in target) {
+      prop.keyProp.checker(key, prop.keyProp, path.concat([key]));
+      let value = target[key];
+      prop.valueProp.checker(value, prop.valueProp, path.concat([key]));
+    }
+  }
+
 
   function checkOptional(target: any, prop: Prop, path: Path): any {
     console.assert(prop.propertyKey);
@@ -124,7 +160,7 @@ export namespace Checkable {
       throw new SchemaError(
         `expected object for ${path.join(".")}, got ${typeof v} instead`);
     }
-    let props = type.prototype[chkSym].props;
+    let props = type.prototype[checkableInfoSym].props;
     let remainingPropNames = new Set(Object.getOwnPropertyNames(v));
     let obj = new type();
     for (let prop of props) {
@@ -132,7 +168,7 @@ export namespace Checkable {
         if (prop.optional) {
           continue;
         }
-        throw new SchemaError("Property missing: " + prop.propertyKey);
+        throw new SchemaError(`Property ${prop.propertyKey} missing on 
${path}`);
       }
       if (!remainingPropNames.delete(prop.propertyKey)) {
         throw new SchemaError("assertion failed");
@@ -143,7 +179,7 @@ export namespace Checkable {
         path.concat([prop.propertyKey]));
     }
 
-    if (remainingPropNames.size != 0) {
+    if (!prop.extraAllowed && remainingPropNames.size != 0) {
       throw new SchemaError("superfluous properties " + 
JSON.stringify(Array.from(
         remainingPropNames.values())));
     }
@@ -162,6 +198,18 @@ export namespace Checkable {
     return target;
   }
 
+  export function ClassWithExtra(target: any) {
+    target.checked = (v: any) => {
+      return checkValue(v, {
+        propertyKey: "(root)",
+        type: target,
+        extraAllowed: true,
+        checker: checkValue
+      }, ["(root)"]);
+    };
+    return target;
+  }
+
 
   export function ClassWithValidator(target: any) {
     target.checked = (v: any) => {
@@ -187,7 +235,7 @@ export namespace Checkable {
       throw Error("Type does not exist yet (wrong order of definitions?)");
     }
     function deco(target: Object, propertyKey: string | symbol): void {
-      let chk = mkChk(target);
+      let chk = getCheckableInfo(target);
       chk.props.push({
         propertyKey: propertyKey,
         checker: checkValue,
@@ -202,13 +250,13 @@ export namespace Checkable {
   export function List(type: any) {
     let stub = {};
     type(stub, "(list-element)");
-    let elementProp = mkChk(stub).props[0];
+    let elementProp = getCheckableInfo(stub).props[0];
     let elementChecker = elementProp.checker;
     if (!elementChecker) {
       throw Error("assertion failed");
     }
     function deco(target: Object, propertyKey: string | symbol): void {
-      let chk = mkChk(target);
+      let chk = getCheckableInfo(target);
       chk.props.push({
         elementChecker,
         elementProp,
@@ -221,16 +269,43 @@ export namespace Checkable {
   }
 
 
+  export function Map(keyType: any, valueType: any) {
+    let keyStub = {};
+    keyType(keyStub, "(map-key)");
+    let keyProp = getCheckableInfo(keyStub).props[0];
+    if (!keyProp) {
+      throw Error("assertion failed");
+    }
+    let valueStub = {};
+    valueType(valueStub, "(map-value)");
+    let valueProp = getCheckableInfo(valueStub).props[0];
+    if (!valueProp) {
+      throw Error("assertion failed");
+    }
+    function deco(target: Object, propertyKey: string | symbol): void {
+      let chk = getCheckableInfo(target);
+      chk.props.push({
+        keyProp,
+        valueProp,
+        propertyKey: propertyKey,
+        checker: checkMap,
+      });
+    }
+
+    return deco;
+  }
+
+
   export function Optional(type: any) {
     let stub = {};
     type(stub, "(optional-element)");
-    let elementProp = mkChk(stub).props[0];
+    let elementProp = getCheckableInfo(stub).props[0];
     let elementChecker = elementProp.checker;
     if (!elementChecker) {
       throw Error("assertion failed");
     }
     function deco(target: Object, propertyKey: string | symbol): void {
-      let chk = mkChk(target);
+      let chk = getCheckableInfo(target);
       chk.props.push({
         elementChecker,
         elementProp,
@@ -245,14 +320,13 @@ export namespace Checkable {
 
 
   export function Number(target: Object, propertyKey: string | symbol): void {
-    let chk = mkChk(target);
+    let chk = getCheckableInfo(target);
     chk.props.push({ propertyKey: propertyKey, checker: checkNumber });
   }
 
 
-  export function AnyObject(target: Object,
-    propertyKey: string | symbol): void {
-    let chk = mkChk(target);
+  export function AnyObject(target: Object, propertyKey: string | symbol): 
void {
+    let chk = getCheckableInfo(target);
     chk.props.push({
       propertyKey: propertyKey,
       checker: checkAnyObject
@@ -260,9 +334,8 @@ export namespace Checkable {
   }
 
 
-  export function Any(target: Object,
-    propertyKey: string | symbol): void {
-    let chk = mkChk(target);
+  export function Any(target: Object, propertyKey: string | symbol): void {
+    let chk = getCheckableInfo(target);
     chk.props.push({
       propertyKey: propertyKey,
       checker: checkAny,
@@ -272,22 +345,14 @@ export namespace Checkable {
 
 
   export function String(target: Object, propertyKey: string | symbol): void {
-    let chk = mkChk(target);
+    let chk = getCheckableInfo(target);
     chk.props.push({ propertyKey: propertyKey, checker: checkString });
   }
 
   export function Boolean(target: Object, propertyKey: string | symbol): void {
-    let chk = mkChk(target);
+    let chk = getCheckableInfo(target);
     chk.props.push({ propertyKey: propertyKey, checker: checkBoolean });
   }
 
 
-  function mkChk(target: any) {
-    let chk = target[chkSym];
-    if (!chk) {
-      chk = { props: [] };
-      target[chkSym] = chk;
-    }
-    return chk;
-  }
 }
diff --git a/src/content_scripts/notify.ts b/src/content_scripts/notify.ts
index 726b5ae..166b1c6 100644
--- a/src/content_scripts/notify.ts
+++ b/src/content_scripts/notify.ts
@@ -257,8 +257,15 @@ function init() {
       return;
     }
     if (document.documentElement.getAttribute("data-taler-nojs")) {
-      initStyle();
-      setStyles(true);
+      const onload = () => {
+        initStyle();
+        setStyles(true);
+      };
+      if (document.readyState == "complete") {
+        onload();
+      } else {
+        document.addEventListener("DOMContentLoaded", onload);
+      }
     }
     registerHandlers();
     // Hack to know when the extension is unloaded
diff --git a/src/cryptoApi.ts b/src/cryptoApi.ts
index 98fc2c6..5657d74 100644
--- a/src/cryptoApi.ts
+++ b/src/cryptoApi.ts
@@ -28,7 +28,7 @@ import {
 import {OfferRecord} from "./wallet";
 import {CoinWithDenom} from "./wallet";
 import {PayCoinInfo} from "./types";
-import {RefreshSessionRecord} from "./types";
+import {RefreshSessionRecord, WireFee} from "./types";
 
 
 interface WorkerState {
@@ -235,6 +235,10 @@ export class CryptoApi {
     return this.doRpc<boolean>("isValidDenom", 2, denom, masterPub);
   }
 
+  isValidWireFee(type: string, wf: WireFee, masterPub: string): 
Promise<boolean> {
+    return this.doRpc<boolean>("isValidWireFee", 2, type, wf, masterPub);
+  }
+
   isValidPaymentSignature(sig: string, contractHash: string, merchantPub: 
string) {
     return this.doRpc<PayCoinInfo>("isValidPaymentSignature", 1, sig, 
contractHash, merchantPub);
   }
diff --git a/src/cryptoWorker.ts b/src/cryptoWorker.ts
index cb7bee4..4275d65 100644
--- a/src/cryptoWorker.ts
+++ b/src/cryptoWorker.ts
@@ -30,7 +30,7 @@ import create = chrome.alarms.create;
 import {OfferRecord} from "./wallet";
 import {CoinWithDenom} from "./wallet";
 import {CoinPaySig, CoinRecord} from "./types";
-import {DenominationRecord, Amounts} from "./types";
+import {DenominationRecord, Amounts, WireFee} from "./types";
 import {Amount} from "./emscriptif";
 import {HashContext} from "./emscriptif";
 import {RefreshMeltCoinAffirmationPS} from "./emscriptif";
@@ -110,6 +110,25 @@ namespace RpcFunctions {
                               nativePub);
   }
 
+  export function isValidWireFee(type: string, wf: WireFee, masterPub: 
string): boolean {
+    let p = new native.MasterWireFeePS({
+      h_wire_method: native.ByteArray.fromStringWithNull(type).hash(),
+      start_date: native.AbsoluteTimeNbo.fromStamp(wf.startStamp),
+      end_date: native.AbsoluteTimeNbo.fromStamp(wf.endStamp),
+      wire_fee: (new native.Amount(wf.wireFee)).toNbo(),
+      closing_fee: (new native.Amount(wf.closingFee)).toNbo(),
+    });
+
+    let nativeSig = new native.EddsaSignature();
+    nativeSig.loadCrock(wf.sig);
+    let nativePub = native.EddsaPublicKey.fromCrock(masterPub);
+
+    return native.eddsaVerify(native.SignaturePurpose.MASTER_WIRE_FEES,
+                              p.toPurpose(),
+                              nativeSig,
+                              nativePub);
+  }
+
 
   export function isValidDenom(denom: DenominationRecord,
                                masterPub: string): boolean {
diff --git a/src/emscriptif.ts b/src/emscriptif.ts
index 3a34f64..3f23476 100644
--- a/src/emscriptif.ts
+++ b/src/emscriptif.ts
@@ -207,6 +207,7 @@ export enum SignaturePurpose {
   WALLET_COIN_MELT = 1202,
   TEST = 4242,
   MERCHANT_PAYMENT_OK = 1104,
+  MASTER_WIRE_FEES = 1028,
 }
 
 
@@ -993,6 +994,35 @@ export class RefreshMeltCoinAffirmationPS extends 
SignatureStruct {
 }
 
 
+interface MasterWireFeePS_Args {
+  h_wire_method: HashCode;
+  start_date: AbsoluteTimeNbo;
+  end_date: AbsoluteTimeNbo;
+  wire_fee: AmountNbo;
+  closing_fee: AmountNbo;
+}
+
+export class MasterWireFeePS extends SignatureStruct {
+  constructor(w: MasterWireFeePS_Args) {
+    super(w);
+  }
+
+  purpose() {
+    return SignaturePurpose.MASTER_WIRE_FEES;
+  }
+
+  fieldTypes() {
+    return [
+      ["h_wire_method", HashCode],
+      ["start_date", AbsoluteTimeNbo],
+      ["end_date", AbsoluteTimeNbo],
+      ["wire_fee", AmountNbo],
+      ["closing_fee", AmountNbo],
+    ];
+  }
+}
+
+
 export class AbsoluteTimeNbo extends PackedArenaObject {
   static fromTalerString(s: string): AbsoluteTimeNbo {
     let x = new AbsoluteTimeNbo();
@@ -1008,6 +1038,15 @@ export class AbsoluteTimeNbo extends PackedArenaObject {
     return x;
   }
 
+  static fromStamp(stamp: number): AbsoluteTimeNbo {
+    let x = new AbsoluteTimeNbo();
+    x.alloc();
+    // XXX: This only works up to 54 bit numbers.
+    set64(x.nativePtr, stamp);
+    return x;
+  }
+
+
   size() {
     return 8;
   }
diff --git a/src/types.ts b/src/types.ts
index e0baa16..c6111bd 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -474,6 +474,9 @@ export class Contract {
   @Checkable.String
   H_wire: string;
 
+  @Checkable.String
+  wire_method: string;
+
   @Checkable.Optional(Checkable.String)
   summary?: string;
 
@@ -535,6 +538,20 @@ export class Contract {
 }
 
 
+export interface WireFee {
+  wireFee: AmountJson;
+  closingFee: AmountJson;
+  startStamp: number;
+  endStamp: number;
+  sig: string;
+}
+
+export interface ExchangeWireFeesRecord {
+  exchangeBaseUrl: string;
+  feesForType: { [type: string]: WireFee[] };
+}
+
+
 export type PayCoinInfo = Array<{ updatedCoin: CoinRecord, sig: CoinPaySig }>;
 
 
diff --git a/src/wallet.ts b/src/wallet.ts
index e8b655f..8b220ec 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -42,6 +42,8 @@ import {
   AuditorRecord,
   WalletBalance,
   WalletBalanceEntry,
+  WireFee,
+  ExchangeWireFeesRecord,
   WireInfo, DenominationRecord, DenominationStatus, denominationRecordFromKeys,
   CoinStatus,
 } from "./types";
@@ -113,6 +115,41 @@ export class KeysJson {
 }
 
 
+
+
address@hidden
+class WireFeesJson {
+  @Checkable.Value(AmountJson)
+  wire_fee: AmountJson;
+
+  @Checkable.Value(AmountJson)
+  closing_fee: AmountJson;
+
+  @Checkable.String
+  sig: string;
+
+  @Checkable.String
+  start_date: string;
+
+  @Checkable.String
+  end_date: string;
+
+  static checked: (obj: any) => WireFeesJson;
+}
+
+
address@hidden
+class WireDetailJson {
+  @Checkable.String
+  type: string;
+
+  @Checkable.List(Checkable.Value(WireFeesJson))
+  fees: WireFeesJson[];
+
+  static checked: (obj: any) => WireDetailJson;
+}
+
+
 @Checkable.Class
 export class CreateReserveRequest {
   /**
@@ -223,6 +260,7 @@ export interface ConfigRecord {
 }
 
 
+
 const builtinCurrencies: CurrencyRecord[] = [
   {
     name: "KUDOS",
@@ -417,7 +455,13 @@ export namespace Stores {
     }
   }
 
+  class ExchangeWireFeesStore extends Store<ExchangeWireFeesRecord> {
+    constructor() {
+      super("exchangeWireFees", {keyPath: "exchangeBaseUrl"});
+    }
+  }
   export const exchanges: ExchangeStore = new ExchangeStore();
+  export const exchangeWireFees: ExchangeWireFeesStore = new 
ExchangeWireFeesStore();
   export const nonces: NonceStore = new NonceStore();
   export const transactions: TransactionsStore = new TransactionsStore();
   export const reserves: Store<ReserveRecord> = new 
Store<ReserveRecord>("reserves", {keyPath: "reserve_pub"});
@@ -1254,13 +1298,27 @@ export class Wallet {
    */
   async updateExchangeFromUrl(baseUrl: string): Promise<ExchangeRecord> {
     baseUrl = canonicalizeBaseUrl(baseUrl);
-    let reqUrl = new URI("keys").absoluteTo(baseUrl);
-    let resp = await this.http.get(reqUrl.href());
-    if (resp.status != 200) {
+    let keysUrl = new URI("keys").absoluteTo(baseUrl);
+    let wireUrl = new URI("wire").absoluteTo(baseUrl);
+    let keysResp = await this.http.get(keysUrl.href());
+    if (keysResp.status != 200) {
       throw Error("/keys request failed");
     }
-    let exchangeKeysJson = KeysJson.checked(JSON.parse(resp.responseText));
-    return this.updateExchangeFromJson(baseUrl, exchangeKeysJson);
+    let wireResp = await this.http.get(wireUrl.href());
+    if (wireResp.status != 200) {
+      throw Error("/wire request failed");
+    }
+    let exchangeKeysJson = KeysJson.checked(JSON.parse(keysResp.responseText));
+    let wireRespJson = JSON.parse(wireResp.responseText);
+    if (typeof wireRespJson !== "object") {
+      throw Error("/wire response is not an object");
+    }
+    console.log("exchange wire", wireRespJson);
+    let wireMethodDetails: WireDetailJson[] = [];
+    for (let methodName in wireRespJson) {
+      wireMethodDetails.push(WireDetailJson.checked(wireRespJson[methodName]));
+    }
+    return this.updateExchangeFromJson(baseUrl, exchangeKeysJson, 
wireMethodDetails);
   }
 
 
@@ -1289,7 +1347,10 @@ export class Wallet {
 
 
   private async updateExchangeFromJson(baseUrl: string,
-                                       exchangeKeysJson: KeysJson): 
Promise<ExchangeRecord> {
+                                       exchangeKeysJson: KeysJson,
+                                       wireMethodDetails: WireDetailJson[]): 
Promise<ExchangeRecord> {
+
+    // FIXME: all this should probably be commited atomically
     const updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
     if (updateTimeSec === null) {
       throw Error("invalid update time");
@@ -1325,6 +1386,55 @@ export class Wallet {
               .put(Stores.exchanges, updatedExchangeInfo)
               .finish();
 
+    let oldWireFees = await this.q().get(Stores.exchangeWireFees, baseUrl);
+    if (!oldWireFees) {
+      oldWireFees = {
+        exchangeBaseUrl: baseUrl,
+        feesForType: {},
+      };
+    }
+
+    for (let detail of wireMethodDetails) {
+      let latestFeeStamp = 0;
+      let fees = oldWireFees.feesForType[detail.type] || [];
+      oldWireFees.feesForType[detail.type] = fees;
+      for (let oldFee of fees) {
+        if (oldFee.endStamp > latestFeeStamp) {
+          latestFeeStamp = oldFee.endStamp;
+        }
+      }
+      for (let fee of detail.fees) {
+        let start = getTalerStampSec(fee.start_date);
+        if (start == null) {
+          console.error("invalid start stamp in fee", fee);
+          continue;
+        }
+        if (start < latestFeeStamp) {
+          continue;
+        }
+        let end = getTalerStampSec(fee.end_date);
+        if (end == null) {
+          console.error("invalid end stamp in fee", fee);
+          continue;
+        }
+        let wf: WireFee = {
+          wireFee: fee.wire_fee,
+          closingFee: fee.closing_fee,
+          sig: fee.sig,
+          startStamp: start,
+          endStamp: end,
+        }
+        let valid: boolean = await this.cryptoApi.isValidWireFee(detail.type, 
wf, exchangeInfo.masterPublicKey);
+        if (!valid) {
+          console.error("fee signature invalid", fee);
+          continue;
+        }
+        fees.push(wf);
+      }
+    }
+
+    await this.q().put(Stores.exchangeWireFees, oldWireFees);
+
     return updatedExchangeInfo;
   }
 

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

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