UNPKG

ilp-plugin-virtual

Version:

ILP virtual ledger plugin for directly transacting connectors

157 lines (128 loc) 4.14 kB
'use strict' const BigNumber = require('bignumber.js') const InvalidFieldsError = require('./errors').InvalidFieldsError const util = require('util') // Regex matching a string containing 32 base64url-encoded bytes const REGEX_32_BYTES_AS_BASE64URL = /^[A-Za-z0-9_-]{43}$/ const xor = (a, b) => ((a || b) && (!a || !b)) module.exports = class Validator { constructor (opts) { this._account = opts.account this._peer = opts.peer this._prefix = opts.prefix } validateIncomingTransfer (t) { this.validateTransfer(t) if (t.account) return this.assertIncoming(t) } validateOutgoingTransfer (t) { this.validateTransfer(t) if (t.account) return this.assertOutgoing(t) } validateTransfer (t) { assert(t.id, 'must have an id') assert(t.ledger, 'must have a ledger') assert(t.amount, 'must have an amount') assertString(t.id, 'id') assertNumber(t.amount, 'amount') assertObject(t.data, 'data') assertObject(t.noteToSelf, 'noteToSelf') assertObject(t.custom, 'custom') assertConditionOrPreimage(t.executionCondition, 'executionCondition') assertString(t.expiresAt, 'expiresAt') if (xor(t.executionCondition, t.expiresAt)) { throw new Error('executionCondition (' + t.executionCondition + ') and expiresAt (' + t.expiresAt + ') must both be set if either is set') } if (t.account) { util.deprecate(() => {}, 'switch from the "account" field to the "to" and "from" fields!')() assertString(t.account, 'account') return } assert(t.to, 'must have a destination (.to)') assert(t.from, 'must have a source (.from)') assertPrefix(t.ledger, this._prefix, 'ledger') } validateIncomingMessage (m) { this.validateMessage(m) if (m.account) return this.assertIncoming(m) } validateOutgoingMessage (m) { this.validateMessage(m) if (m.account) return this.assertOutgoing(m) } validateMessage (m) { assert(m.ledger, 'must have a ledger') if (m.ilp) { assertString(m.ilp, 'message ilp must be a string') } if (m.account) { util.deprecate(() => {}, 'switch from the "account" field to the "to" and "from" fields!')() assertString(m.account, 'account') return } assert(m.to, 'must have a destination (.to)') assert(m.from, 'must have a source (.from)') assertPrefix(m.ledger, this._prefix, 'ledger') } validateFulfillment (f) { assert(f, 'fulfillment must not be "' + f + '"') assertConditionOrPreimage(f, 'fulfillment') } assertIncoming (o) { assertAccount(o.to, this._account, 'to') assertAccount(o.from, this._peer, 'from') } assertOutgoing (o) { assertAccount(o.to, this._peer, 'to') assertAccount(o.from, this._account, 'from') } } function assert (cond, msg) { if (!cond) throw new InvalidFieldsError(msg) } function assertType (value, name, type) { assert(!value || typeof (value) === type, 'if defined, ' + name + ' (' + value + ') must be a ' + type) } function assertString (value, name) { assertType(value, name, 'string') } function assertObject (value, name) { assertType(value, name, 'object') } function assertPrefix (value, prefix, name) { assertString(value, name) assert(value === prefix, name + ' (' + value + ') must match ILP prefix: ' + prefix) } function assertAccount (value, account, name) { assertString(value, name) assert(value === account, name + ' (' + value + ') must match account: ' + account) } function assertConditionOrPreimage (value, name) { if (!value) return assertString(value, name) if (!REGEX_32_BYTES_AS_BASE64URL.test(value)) { throw new InvalidFieldsError(name + ' (' + value + '): Not a valid 32-byte base64url encoded string') } } function isNumber (number) { try { return !!(new BigNumber(number)) } catch (e) { return false } } function assertNumber (value, name) { assert(isNumber(value), name + ' (' + value + ') must be a number') assert((new BigNumber(value)).gt(new BigNumber('0')), name + ' (' + value + ') must be positive') }