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 | ;
|
16 |
|
17 | const Logger = require('@accordproject/ergo-compiler').Logger;
|
18 | const 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 | */
|
28 | class 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 |
|
253 | module.exports = Engine;
|