download, store and check signatures for wire fees
This commit is contained in:
parent
68e44e0e80
commit
82b5754e15
119
src/checkable.ts
119
src/checkable.ts
@ -40,9 +40,17 @@ export namespace Checkable {
|
|||||||
interface Prop {
|
interface Prop {
|
||||||
propertyKey: any;
|
propertyKey: any;
|
||||||
checker: any;
|
checker: any;
|
||||||
type: any;
|
type?: any;
|
||||||
elementChecker?: any;
|
elementChecker?: any;
|
||||||
elementProp?: any;
|
elementProp?: any;
|
||||||
|
keyProp?: any;
|
||||||
|
valueProp?: any;
|
||||||
|
optional?: boolean;
|
||||||
|
extraAllowed?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CheckableInfo {
|
||||||
|
props: Prop[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export let SchemaError = (function SchemaError(message: string) {
|
export let SchemaError = (function SchemaError(message: string) {
|
||||||
@ -54,7 +62,24 @@ export namespace Checkable {
|
|||||||
|
|
||||||
SchemaError.prototype = new Error;
|
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 {
|
function checkNumber(target: any, prop: Prop, path: Path): any {
|
||||||
@ -104,6 +129,17 @@ export namespace Checkable {
|
|||||||
return target;
|
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 {
|
function checkOptional(target: any, prop: Prop, path: Path): any {
|
||||||
console.assert(prop.propertyKey);
|
console.assert(prop.propertyKey);
|
||||||
@ -124,7 +160,7 @@ export namespace Checkable {
|
|||||||
throw new SchemaError(
|
throw new SchemaError(
|
||||||
`expected object for ${path.join(".")}, got ${typeof v} instead`);
|
`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 remainingPropNames = new Set(Object.getOwnPropertyNames(v));
|
||||||
let obj = new type();
|
let obj = new type();
|
||||||
for (let prop of props) {
|
for (let prop of props) {
|
||||||
@ -132,7 +168,7 @@ export namespace Checkable {
|
|||||||
if (prop.optional) {
|
if (prop.optional) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw new SchemaError("Property missing: " + prop.propertyKey);
|
throw new SchemaError(`Property ${prop.propertyKey} missing on ${path}`);
|
||||||
}
|
}
|
||||||
if (!remainingPropNames.delete(prop.propertyKey)) {
|
if (!remainingPropNames.delete(prop.propertyKey)) {
|
||||||
throw new SchemaError("assertion failed");
|
throw new SchemaError("assertion failed");
|
||||||
@ -143,7 +179,7 @@ export namespace Checkable {
|
|||||||
path.concat([prop.propertyKey]));
|
path.concat([prop.propertyKey]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remainingPropNames.size != 0) {
|
if (!prop.extraAllowed && remainingPropNames.size != 0) {
|
||||||
throw new SchemaError("superfluous properties " + JSON.stringify(Array.from(
|
throw new SchemaError("superfluous properties " + JSON.stringify(Array.from(
|
||||||
remainingPropNames.values())));
|
remainingPropNames.values())));
|
||||||
}
|
}
|
||||||
@ -162,6 +198,18 @@ export namespace Checkable {
|
|||||||
return target;
|
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) {
|
export function ClassWithValidator(target: any) {
|
||||||
target.checked = (v: any) => {
|
target.checked = (v: any) => {
|
||||||
@ -187,7 +235,7 @@ export namespace Checkable {
|
|||||||
throw Error("Type does not exist yet (wrong order of definitions?)");
|
throw Error("Type does not exist yet (wrong order of definitions?)");
|
||||||
}
|
}
|
||||||
function deco(target: Object, propertyKey: string | symbol): void {
|
function deco(target: Object, propertyKey: string | symbol): void {
|
||||||
let chk = mkChk(target);
|
let chk = getCheckableInfo(target);
|
||||||
chk.props.push({
|
chk.props.push({
|
||||||
propertyKey: propertyKey,
|
propertyKey: propertyKey,
|
||||||
checker: checkValue,
|
checker: checkValue,
|
||||||
@ -202,13 +250,13 @@ export namespace Checkable {
|
|||||||
export function List(type: any) {
|
export function List(type: any) {
|
||||||
let stub = {};
|
let stub = {};
|
||||||
type(stub, "(list-element)");
|
type(stub, "(list-element)");
|
||||||
let elementProp = mkChk(stub).props[0];
|
let elementProp = getCheckableInfo(stub).props[0];
|
||||||
let elementChecker = elementProp.checker;
|
let elementChecker = elementProp.checker;
|
||||||
if (!elementChecker) {
|
if (!elementChecker) {
|
||||||
throw Error("assertion failed");
|
throw Error("assertion failed");
|
||||||
}
|
}
|
||||||
function deco(target: Object, propertyKey: string | symbol): void {
|
function deco(target: Object, propertyKey: string | symbol): void {
|
||||||
let chk = mkChk(target);
|
let chk = getCheckableInfo(target);
|
||||||
chk.props.push({
|
chk.props.push({
|
||||||
elementChecker,
|
elementChecker,
|
||||||
elementProp,
|
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) {
|
export function Optional(type: any) {
|
||||||
let stub = {};
|
let stub = {};
|
||||||
type(stub, "(optional-element)");
|
type(stub, "(optional-element)");
|
||||||
let elementProp = mkChk(stub).props[0];
|
let elementProp = getCheckableInfo(stub).props[0];
|
||||||
let elementChecker = elementProp.checker;
|
let elementChecker = elementProp.checker;
|
||||||
if (!elementChecker) {
|
if (!elementChecker) {
|
||||||
throw Error("assertion failed");
|
throw Error("assertion failed");
|
||||||
}
|
}
|
||||||
function deco(target: Object, propertyKey: string | symbol): void {
|
function deco(target: Object, propertyKey: string | symbol): void {
|
||||||
let chk = mkChk(target);
|
let chk = getCheckableInfo(target);
|
||||||
chk.props.push({
|
chk.props.push({
|
||||||
elementChecker,
|
elementChecker,
|
||||||
elementProp,
|
elementProp,
|
||||||
@ -245,14 +320,13 @@ export namespace Checkable {
|
|||||||
|
|
||||||
|
|
||||||
export function Number(target: Object, propertyKey: string | symbol): void {
|
export function Number(target: Object, propertyKey: string | symbol): void {
|
||||||
let chk = mkChk(target);
|
let chk = getCheckableInfo(target);
|
||||||
chk.props.push({ propertyKey: propertyKey, checker: checkNumber });
|
chk.props.push({ propertyKey: propertyKey, checker: checkNumber });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function AnyObject(target: Object,
|
export function AnyObject(target: Object, propertyKey: string | symbol): void {
|
||||||
propertyKey: string | symbol): void {
|
let chk = getCheckableInfo(target);
|
||||||
let chk = mkChk(target);
|
|
||||||
chk.props.push({
|
chk.props.push({
|
||||||
propertyKey: propertyKey,
|
propertyKey: propertyKey,
|
||||||
checker: checkAnyObject
|
checker: checkAnyObject
|
||||||
@ -260,9 +334,8 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function Any(target: Object,
|
export function Any(target: Object, propertyKey: string | symbol): void {
|
||||||
propertyKey: string | symbol): void {
|
let chk = getCheckableInfo(target);
|
||||||
let chk = mkChk(target);
|
|
||||||
chk.props.push({
|
chk.props.push({
|
||||||
propertyKey: propertyKey,
|
propertyKey: propertyKey,
|
||||||
checker: checkAny,
|
checker: checkAny,
|
||||||
@ -272,22 +345,14 @@ export namespace Checkable {
|
|||||||
|
|
||||||
|
|
||||||
export function String(target: Object, propertyKey: string | symbol): void {
|
export function String(target: Object, propertyKey: string | symbol): void {
|
||||||
let chk = mkChk(target);
|
let chk = getCheckableInfo(target);
|
||||||
chk.props.push({ propertyKey: propertyKey, checker: checkString });
|
chk.props.push({ propertyKey: propertyKey, checker: checkString });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Boolean(target: Object, propertyKey: string | symbol): void {
|
export function Boolean(target: Object, propertyKey: string | symbol): void {
|
||||||
let chk = mkChk(target);
|
let chk = getCheckableInfo(target);
|
||||||
chk.props.push({ propertyKey: propertyKey, checker: checkBoolean });
|
chk.props.push({ propertyKey: propertyKey, checker: checkBoolean });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mkChk(target: any) {
|
|
||||||
let chk = target[chkSym];
|
|
||||||
if (!chk) {
|
|
||||||
chk = { props: [] };
|
|
||||||
target[chkSym] = chk;
|
|
||||||
}
|
|
||||||
return chk;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ import {
|
|||||||
import {OfferRecord} from "./wallet";
|
import {OfferRecord} from "./wallet";
|
||||||
import {CoinWithDenom} from "./wallet";
|
import {CoinWithDenom} from "./wallet";
|
||||||
import {PayCoinInfo} from "./types";
|
import {PayCoinInfo} from "./types";
|
||||||
import {RefreshSessionRecord} from "./types";
|
import {RefreshSessionRecord, WireFee} from "./types";
|
||||||
|
|
||||||
|
|
||||||
interface WorkerState {
|
interface WorkerState {
|
||||||
@ -235,6 +235,10 @@ export class CryptoApi {
|
|||||||
return this.doRpc<boolean>("isValidDenom", 2, denom, masterPub);
|
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) {
|
isValidPaymentSignature(sig: string, contractHash: string, merchantPub: string) {
|
||||||
return this.doRpc<PayCoinInfo>("isValidPaymentSignature", 1, sig, contractHash, merchantPub);
|
return this.doRpc<PayCoinInfo>("isValidPaymentSignature", 1, sig, contractHash, merchantPub);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import create = chrome.alarms.create;
|
|||||||
import {OfferRecord} from "./wallet";
|
import {OfferRecord} from "./wallet";
|
||||||
import {CoinWithDenom} from "./wallet";
|
import {CoinWithDenom} from "./wallet";
|
||||||
import {CoinPaySig, CoinRecord} from "./types";
|
import {CoinPaySig, CoinRecord} from "./types";
|
||||||
import {DenominationRecord, Amounts} from "./types";
|
import {DenominationRecord, Amounts, WireFee} from "./types";
|
||||||
import {Amount} from "./emscriptif";
|
import {Amount} from "./emscriptif";
|
||||||
import {HashContext} from "./emscriptif";
|
import {HashContext} from "./emscriptif";
|
||||||
import {RefreshMeltCoinAffirmationPS} from "./emscriptif";
|
import {RefreshMeltCoinAffirmationPS} from "./emscriptif";
|
||||||
@ -110,6 +110,25 @@ namespace RpcFunctions {
|
|||||||
nativePub);
|
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,
|
export function isValidDenom(denom: DenominationRecord,
|
||||||
masterPub: string): boolean {
|
masterPub: string): boolean {
|
||||||
|
@ -207,6 +207,7 @@ export enum SignaturePurpose {
|
|||||||
WALLET_COIN_MELT = 1202,
|
WALLET_COIN_MELT = 1202,
|
||||||
TEST = 4242,
|
TEST = 4242,
|
||||||
MERCHANT_PAYMENT_OK = 1104,
|
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 {
|
export class AbsoluteTimeNbo extends PackedArenaObject {
|
||||||
static fromTalerString(s: string): AbsoluteTimeNbo {
|
static fromTalerString(s: string): AbsoluteTimeNbo {
|
||||||
let x = new AbsoluteTimeNbo();
|
let x = new AbsoluteTimeNbo();
|
||||||
@ -1008,6 +1038,15 @@ export class AbsoluteTimeNbo extends PackedArenaObject {
|
|||||||
return x;
|
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() {
|
size() {
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
17
src/types.ts
17
src/types.ts
@ -474,6 +474,9 @@ export class Contract {
|
|||||||
@Checkable.String
|
@Checkable.String
|
||||||
H_wire: string;
|
H_wire: string;
|
||||||
|
|
||||||
|
@Checkable.String
|
||||||
|
wire_method: string;
|
||||||
|
|
||||||
@Checkable.Optional(Checkable.String)
|
@Checkable.Optional(Checkable.String)
|
||||||
summary?: 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 }>;
|
export type PayCoinInfo = Array<{ updatedCoin: CoinRecord, sig: CoinPaySig }>;
|
||||||
|
|
||||||
|
|
||||||
|
122
src/wallet.ts
122
src/wallet.ts
@ -42,6 +42,8 @@ import {
|
|||||||
AuditorRecord,
|
AuditorRecord,
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
WalletBalanceEntry,
|
WalletBalanceEntry,
|
||||||
|
WireFee,
|
||||||
|
ExchangeWireFeesRecord,
|
||||||
WireInfo, DenominationRecord, DenominationStatus, denominationRecordFromKeys,
|
WireInfo, DenominationRecord, DenominationStatus, denominationRecordFromKeys,
|
||||||
CoinStatus,
|
CoinStatus,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
@ -113,6 +115,41 @@ export class KeysJson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Checkable.Class
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Checkable.ClassWithExtra
|
||||||
|
class WireDetailJson {
|
||||||
|
@Checkable.String
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
@Checkable.List(Checkable.Value(WireFeesJson))
|
||||||
|
fees: WireFeesJson[];
|
||||||
|
|
||||||
|
static checked: (obj: any) => WireDetailJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Checkable.Class
|
@Checkable.Class
|
||||||
export class CreateReserveRequest {
|
export class CreateReserveRequest {
|
||||||
/**
|
/**
|
||||||
@ -223,6 +260,7 @@ export interface ConfigRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const builtinCurrencies: CurrencyRecord[] = [
|
const builtinCurrencies: CurrencyRecord[] = [
|
||||||
{
|
{
|
||||||
name: "KUDOS",
|
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 exchanges: ExchangeStore = new ExchangeStore();
|
||||||
|
export const exchangeWireFees: ExchangeWireFeesStore = new ExchangeWireFeesStore();
|
||||||
export const nonces: NonceStore = new NonceStore();
|
export const nonces: NonceStore = new NonceStore();
|
||||||
export const transactions: TransactionsStore = new TransactionsStore();
|
export const transactions: TransactionsStore = new TransactionsStore();
|
||||||
export const reserves: Store<ReserveRecord> = new Store<ReserveRecord>("reserves", {keyPath: "reserve_pub"});
|
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> {
|
async updateExchangeFromUrl(baseUrl: string): Promise<ExchangeRecord> {
|
||||||
baseUrl = canonicalizeBaseUrl(baseUrl);
|
baseUrl = canonicalizeBaseUrl(baseUrl);
|
||||||
let reqUrl = new URI("keys").absoluteTo(baseUrl);
|
let keysUrl = new URI("keys").absoluteTo(baseUrl);
|
||||||
let resp = await this.http.get(reqUrl.href());
|
let wireUrl = new URI("wire").absoluteTo(baseUrl);
|
||||||
if (resp.status != 200) {
|
let keysResp = await this.http.get(keysUrl.href());
|
||||||
|
if (keysResp.status != 200) {
|
||||||
throw Error("/keys request failed");
|
throw Error("/keys request failed");
|
||||||
}
|
}
|
||||||
let exchangeKeysJson = KeysJson.checked(JSON.parse(resp.responseText));
|
let wireResp = await this.http.get(wireUrl.href());
|
||||||
return this.updateExchangeFromJson(baseUrl, exchangeKeysJson);
|
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,
|
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);
|
const updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
|
||||||
if (updateTimeSec === null) {
|
if (updateTimeSec === null) {
|
||||||
throw Error("invalid update time");
|
throw Error("invalid update time");
|
||||||
@ -1325,6 +1386,55 @@ export class Wallet {
|
|||||||
.put(Stores.exchanges, updatedExchangeInfo)
|
.put(Stores.exchanges, updatedExchangeInfo)
|
||||||
.finish();
|
.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;
|
return updatedExchangeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user