UNPKG

five-bells-condition

Version:
228 lines (199 loc) 5.96 kB
'use strict' /** * @module types */ const TypeRegistry = require('./type-registry') const Condition = require('./condition') const base64url = require('../util/base64url') const Asn1Fulfillment = require('../schemas/fulfillment').Fulfillment // Regex for validating fulfillments // // This is a generic, future-proof version of the fulfillment regular // expression. const FULFILLMENT_REGEX = /^cf:([1-9a-f][0-9a-f]{0,3}|0):[a-zA-Z0-9_-]*$/ /** * Base class for fulfillment types. */ class Fulfillment { /** * Create a Fulfillment object from a URI. * * This method will parse a fulfillment URI and construct a corresponding * Fulfillment object. * * @param {String} serializedFulfillment URI representing the fulfillment * @return {Fulfillment} Resulting object */ static fromUri (serializedFulfillment) { if (serializedFulfillment instanceof Fulfillment) { return serializedFulfillment } else if (typeof serializedFulfillment !== 'string') { throw new TypeError('Serialized fulfillment must be a string') } const fulfillment = Fulfillment.fromBinary(Buffer.from(serializedFulfillment, 'base64')) return fulfillment } /** * Create a Fulfillment object from a binary blob. * * This method will parse a stream of binary data and construct a * corresponding Fulfillment object. * * @param {Buffer} data Binary buffer * @return {Fulfillment} Resulting object */ static fromBinary (data) { const fulfillmentJson = Asn1Fulfillment.decode(data) return Fulfillment.fromAsn1Json(fulfillmentJson) } static fromAsn1Json (json) { const FulfillmentClass = TypeRegistry.findByAsn1FulfillmentType(json.type).Class const condition = new FulfillmentClass() condition.parseAsn1JsonPayload(json.value) return condition } static fromJson (json) { const ConditionClass = TypeRegistry.findByName(json.type).Class const condition = new ConditionClass() condition.parseJson(json) return condition } /** * Return the type ID of this fulfillment. * * @return {Number} Type ID as an integer. */ getTypeId () { return this.constructor.TYPE_ID } getTypeName () { return this.constructor.TYPE_NAME } /** * Return the bitmask of this fulfillment. * * For simple fulfillment types this is simply the empty set. * * For compound fulfillments, this returns the set of names of all * subfulfillment types, recursively. * * @return {Set<String>} Set of subtype names. */ getSubtypes () { return new Set() } /** * Generate condition corresponding to this fulfillment. * * An important property of crypto-conditions is that the condition can always * be derived from the fulfillment. This makes it very easy to post * fulfillments to a system without having to specify which condition the * relate to. The system can keep an index of conditions and look up any * matching events related to that condition. * * @return {Condition} Condition corresponding to this fulfillment. */ getCondition () { const condition = new Condition() condition.setHash(this.generateHash()) condition.setTypeId(this.getTypeId()) condition.setCost(this.calculateCost()) condition.setSubtypes(this.getSubtypes()) return condition } /** * Shorthand for getting condition URI. * * Stands for getCondition().serializeUri(). * * @return {String} Condition URI. */ getConditionUri () { return this.getCondition().serializeUri() } /** * Shorthand for getting condition encoded as binary. * * Stands for getCondition().serializeBinary(). * * @return {Buffer} Binary encoded condition. */ getConditionBinary () { return this.getCondition().serializeBinary() } /** * Generate the hash of the fulfillment. * * This method is a stub and will be overridden by subclasses. * * @return {Buffer} Fingerprint of the condition. * * @private */ generateHash () { throw new Error('This method should be implemented by a subclass') } /** * Calculate the cost of the fulfillment payload. * * Each condition type has a standard deterministic formula for estimating the * cost of validating the fulfillment. This is an abstract function which will * be overridden by each of the types with the actual formula. * * @return {Number} Cost * * @private */ calculateCost () { throw new Error('Condition types must implement calculateCost') } parseAsn1JsonPayload (json) { this.parseJson(json) } /** * Generate the URI form encoding of this fulfillment. * * Turns the fulfillment into a URI containing only URL-safe characters. This * format is convenient for passing around fulfillments in URLs, JSON and * other text-based formats. * * @return {String} Fulfillment as a URI */ serializeUri () { return base64url.encode(this.serializeBinary()) } getAsn1Json () { return { type: this.constructor.TYPE_ASN1_FULFILLMENT, value: this.getAsn1JsonPayload() } } /** * Serialize fulfillment to a buffer. * * Encodes the fulfillment as a string of bytes. This is used internally for * encoding subfulfillments, but can also be used to passing around * fulfillments in a binary protocol for instance. * * @return {Buffer} Serialized fulfillment */ serializeBinary () { const asn1Json = this.getAsn1Json() return Asn1Fulfillment.encode(asn1Json) } serializeBase64Url () { return base64url.encode(this.serializeBinary()) } /** * Validate this fulfillment. * * This implementation is a stub and will be overridden by the subclasses. * * @return {Boolean} Validation result */ validate () { throw new Error('Not implemented') } } Fulfillment.REGEX = FULFILLMENT_REGEX module.exports = Fulfillment