UNPKG

10.1 kBJavaScriptView Raw
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14
15'use strict';
16
17const Logger = require('@accordproject/ergo-compiler').Logger;
18const Util = require('@accordproject/ergo-compiler').Util;
19
20/**
21 * <p>
22 * Engine class. Execution of template logic against a request object, returning a response to the caller.
23 * </p>
24 * @class
25 * @public
26 * @memberof module:ergo-engine
27 */
28class Engine {
29 /**
30 * Create the Engine.
31 */
32 constructor() {
33 this.scripts = {};
34 }
35
36 /**
37 * Engine kind
38 * @return {string} which kind of engine
39 */
40 kind() {
41 return 'empty';
42 }
43
44 /**
45 /**
46 * Call to compile a script for a JavaScript machine
47 * @param {string} script - the script
48 */
49 compileVMScript(script) {
50 throw new Error('[compileVMScript] Cannot execute Engine: instantiate either VMEngine or EvalEngine');
51 }
52
53 /**
54 * Call to execute a call in a JavaScript machine
55 * @param {number} utcOffset - UTC Offset for this execution
56 * @param {object} context - global variables to set in the VM
57 * @param {object} script - the initial script to load
58 * @param {object} call - the execution call
59 */
60 runVMScriptCall(utcOffset,context,script,call) {
61 throw new Error('[runVMScriptCall] Cannot execute Engine: instantiate either VMEngine or EvalEngine');
62 }
63
64 /**
65 * Compile and cache JavaScript logic
66 * @param {ScriptManager} scriptManager - the script manager
67 * @param {string} contractId - the contract identifier
68 * @return {VMScript} the cached script
69 * @private
70 */
71 cacheJsScript(scriptManager, contractId) {
72 if (!this.scripts[contractId]) {
73 const allJsScripts = scriptManager.getCompiledJavaScript();
74 const script = this.compileVMScript(allJsScripts);
75 this.scripts[contractId] = script;
76 }
77 return this.scripts[contractId];
78 }
79
80 /**
81 * Execute a clause, passing in the request object
82 * @param {TemplateLogic} logic - the logic to execute
83 * @param {string} contractId - the contract identifier
84 * @param {object} contract - the contract data
85 * @param {object} request - the request, a JS object that can be deserialized
86 * using the Composer serializer.
87 * @param {object} state - the contract state, a JS object that can be deserialized
88 * using the Composer serializer.
89 * @param {string} currentTime - the definition of 'now'
90 * @return {Promise} a promise that resolves to a result for the clause
91 */
92 async execute(logic, contractId, contract, request, state, currentTime) {
93 // Set the current time and UTC Offset
94 const now = Util.setCurrentTime(currentTime);
95 const utcOffset = now.utcOffset();
96
97 const validContract = logic.validateInput(contract); // ensure the contract is valid
98 const validRequest = logic.validateInput(request); // ensure the request is valid
99 const validState = logic.validateInput(state); // ensure the state is valid
100
101 Logger.debug('Engine processing request ' + request.$class + ' with state ' + state.$class);
102
103 const script = this.cacheJsScript(logic.getScriptManager(), contractId);
104 const callScript = logic.getDispatchCall();
105
106 const context = {
107 data: validContract,
108 state: validState,
109 now: now,
110 request: validRequest
111 };
112
113 // execute the logic
114 const result = this.runVMScriptCall(utcOffset,context,script,callScript);
115
116 const validResponse = logic.validateOutput(result.response); // ensure the response is valid
117 const validNewState = logic.validateOutput(result.state); // ensure the new state is valid
118 const validEmit = logic.validateOutputArray(result.emit); // ensure all the emits are valid
119
120 const answer = {
121 'clause': contractId,
122 'request': validRequest,
123 'response': validResponse,
124 'state': validNewState,
125 'emit': validEmit,
126 };
127 return Promise.resolve(answer);
128 }
129
130 /**
131 * Invoke a clause, passing in the parameters for that clause
132 * @param {TemplateLogic} logic - the logic to execute
133 * @param {string} contractId - the contract identifier
134 * @param {string} clauseName - the clause name
135 * @param {object} contract - the contract data
136 * @param {object} params - the clause parameters
137 * @param {object} state - the contract state, a JS object that can be deserialized
138 * using the Composer serializer.
139 * @param {string} currentTime - the definition of 'now'
140 * @return {Promise} a promise that resolves to a result for the clause
141 */
142 async invoke(logic, contractId, clauseName, contract, params, state, currentTime) {
143 // Set the current time and UTC Offset
144 const now = Util.setCurrentTime(currentTime);
145 const utcOffset = now.utcOffset();
146
147 const validContract = logic.validateInput(contract); // ensure the contract is valid
148 const validParams = logic.validateInputRecord(params); // ensure the parameters are valid
149 const validState = logic.validateInput(state); // ensure the state is valid
150
151 Logger.debug('Engine processing clause ' + clauseName + ' with state ' + state.$class);
152
153 const script = this.cacheJsScript(logic.getScriptManager(), contractId);
154 const callScript = logic.getInvokeCall(clauseName);
155
156 const context = {
157 data: validContract,
158 state: validState,
159 now: now,
160 params: validParams
161 };
162
163 // execute the logic
164 const result = this.runVMScriptCall(utcOffset,context,script,callScript);
165
166 const validResponse = logic.validateOutput(result.response); // ensure the response is valid
167 const validNewState = logic.validateOutput(result.state); // ensure the new state is valid
168 const validEmit = logic.validateOutputArray(result.emit); // ensure all the emits are valid
169
170 const answer = {
171 'clause': contractId,
172 'params': validParams,
173 'response': validResponse,
174 'state': validNewState,
175 'emit': validEmit,
176 };
177 return Promise.resolve(answer);
178 }
179
180 /**
181 * Initialize a clause
182 * @param {TemplateLogic} logic - the logic to execute
183 * @param {string} contractId - the contract identifier
184 * @param {object} contract - the contract data
185 * @param {object} params - the clause parameters
186 * @param {string} currentTime - the definition of 'now'
187 * @return {Promise} a promise that resolves to a result for the clause initialization
188 */
189 async init(logic, contractId, contract, params, currentTime) {
190 const defaultState = {
191 '$class':'org.accordproject.cicero.contract.AccordContractState',
192 'stateId':'org.accordproject.cicero.contract.AccordContractState#1'
193 };
194 return this.invoke(logic, contractId, 'init', contract, params, defaultState, currentTime);
195 }
196
197 /**
198 * Compile then initialize a clause
199 *
200 * @param {TemplateLogic} logic - the logic to execute
201 * @param {string} contractId - the contract identifier
202 * @param {object} contract - the contract data
203 * @param {object} params - the clause parameters
204 * @param {string} currentTime - the definition of 'now'
205 * @return {Promise} a promise that resolves to a result for the clause initialization
206 */
207 compileAndInit(logic, contractId, contract, params, currentTime) {
208 return logic.compileLogic(false).then(() => {
209 return this.init(logic, contractId, contract, params, currentTime);
210 });
211 }
212
213 /**
214 * Compile then invoke a clause
215 *
216 * @param {TemplateLogic} logic - the logic to execute
217 * @param {string} contractId - the contract identifier
218 * @param {string} clauseName - the clause name
219 * @param {object} contract contract data in JSON
220 * @param {object} params - the clause parameters
221 * @param {object} state - the contract state, a JS object that can be deserialized
222 * using the Composer serializer.
223 * @param {string} currentTime - the definition of 'now'
224 * @return {Promise} a promise that resolves to a result for the clause initialization
225 */
226 compileAndInvoke(logic, contractId, clauseName, contract, params, state, currentTime) {
227 return logic.compileLogic(false).then(() => {
228 return this.invoke(logic, contractId, clauseName, contract, params, state, currentTime);
229 });
230 }
231
232 /**
233 * Compile then execute a clause, passing in the request object
234 *
235 * @param {TemplateLogic} logic - the logic to execute
236 * @param {string} contractId - the contract identifier
237 * @param {object} contract - the contract data
238 * @param {object} request - the request, a JS object that can be deserialized
239 * using the Composer serializer.
240 * @param {object} state - the contract state, a JS object that can be deserialized
241 * using the Composer serializer.
242 * @param {string} currentTime - the definition of 'now'
243 * @return {Promise} a promise that resolves to a result for the clause
244 */
245 compileAndExecute(logic, contractId, contract, request, state, currentTime) {
246 return logic.compileLogic(false).then(() => {
247 return this.execute(logic, contractId, contract, request, state, currentTime);
248 });
249 }
250
251}
252
253module.exports = Engine;