sbuilder-ethereum 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/VERSION +1 -0
  3. data/lib/mixer/decl_ref.rb +19 -0
  4. data/lib/mixer/domain_ref.rb +18 -0
  5. data/lib/mixer/scope.rb +92 -0
  6. data/lib/mixer/scoped.rb +17 -0
  7. data/lib/mixer/symbol_ref.rb +16 -0
  8. data/lib/mixer/type_symbol.rb +75 -0
  9. data/lib/plugin/plugin.rb +332 -0
  10. data/lib/resources/correctness/accouns_type.tla +1 -0
  11. data/lib/resources/correctness/accounts_unique.tla +2 -0
  12. data/lib/resources/correctness/accounts_valid.tla +2 -0
  13. data/lib/resources/correctness/storage_root_unique.tla +2 -0
  14. data/lib/resources/correctness/total_value.tla +1 -0
  15. data/lib/resources/eth/accounts_state.tla +2 -0
  16. data/lib/resources/eth/accounts_temp.tla +2 -0
  17. data/lib/resources/eth/address_free.tla +2 -0
  18. data/lib/resources/eth/mined_state.tla +1 -0
  19. data/lib/resources/eth/storageRoot.tla +1 -0
  20. data/lib/resources/eth/storageRoot_temp.tla +1 -0
  21. data/lib/resources/mine/mine_entry.tla +4 -0
  22. data/lib/resources/mine/mine_service.tla +22 -0
  23. data/lib/resources/operators/elementExists.tla +4 -0
  24. data/lib/resources/operators/gasPrice.tla +2 -0
  25. data/lib/resources/operators/gasValue.tla +2 -0
  26. data/lib/resources/operators/getElement.tla +5 -0
  27. data/lib/resources/operators/intrinsicGas.tla +4 -0
  28. data/lib/resources/operators/transactionGas.tla +4 -0
  29. data/lib/resources/operators/upFrontCost.tla +6 -0
  30. data/lib/resources/personal_newAccount/personal_newAccount_done.tla +14 -0
  31. data/lib/resources/personal_newAccount/personal_newAccount_entry.tla +10 -0
  32. data/lib/resources/personal_newAccount/personal_newAccount_service.tla +29 -0
  33. data/lib/resources/removed/sendTransaction_entry.tla +5 -0
  34. data/lib/resources/removed/sendTransaction_service.tla +36 -0
  35. data/lib/resources/removed/tst.tla +1 -0
  36. data/lib/resources/transaction/ethereum_service_done.tla +24 -0
  37. data/lib/resources/transaction/ethereum_service_pop.tla +24 -0
  38. data/lib/resources/transaction/ethereum_service_push.tla +14 -0
  39. data/lib/resources/transaction/ethereum_service_start.tla +13 -0
  40. data/lib/resources/transaction/status_fail.tla +1 -0
  41. data/lib/resources/transaction/status_ok.tla +1 -0
  42. data/lib/sbuilder-ethereum.rb +52 -0
  43. data/lib/sbuilder/compile.rb +163 -0
  44. data/lib/sbuilder/constants.rb +93 -0
  45. data/lib/sbuilder/exception.rb +22 -0
  46. data/lib/sbuilder/generate/sexp_processor_tla.rb +2674 -0
  47. data/lib/sbuilder/generate/tla_element_generator.rb +1206 -0
  48. data/lib/sbuilder/generate/tla_element_text.rb +703 -0
  49. data/lib/sbuilder/load.rb +119 -0
  50. data/lib/sbuilder/mustache/renderer.rb +152 -0
  51. data/lib/sbuilder/render.rb +141 -0
  52. data/lib/sbuilder/s.rb +21 -0
  53. data/lib/sbuilder/sexp_ast.rb +1378 -0
  54. data/lib/sbuilder/sexp_processor_api.rb +184 -0
  55. data/lib/sbuilder/sexp_processor_canonize.rb +326 -0
  56. data/lib/sbuilder/sexp_processor_dataflow.rb +461 -0
  57. data/lib/sbuilder/sexp_processor_ethereum.rb +127 -0
  58. data/lib/sbuilder/sexp_processor_need_to_canonize.rb +572 -0
  59. data/lib/sbuilder/sexp_processor_snippet.rb +154 -0
  60. data/lib/sbuilder/sexp_processor_symboltable1.rb +296 -0
  61. data/lib/sbuilder/sexp_processor_symboltable2.rb +175 -0
  62. data/lib/sbuilder/sexp_utils.rb +417 -0
  63. data/lib/utils/logger.rb +82 -0
  64. data/lib/utils/string_inject.rb +11 -0
  65. data/sbuilder-ethereum.gemspec +39 -0
  66. metadata +190 -0
@@ -0,0 +1,93 @@
1
+ module Sbuilder
2
+ module Ethereum
3
+
4
+ class Constants
5
+
6
+ # ------------------------------------------------------------------
7
+ # Domains
8
+ DOMAIN_ADDRESS = "eth_address"
9
+ DOMAIN_VALUE = "eth_value"
10
+ DOMAIN_GAS = "eth_gas"
11
+ DOMAIN_CODE_HASH = "eth_code_hash"
12
+ DOMAIN_BOOLEAN = "BOOLEAN"
13
+ DOMAIN_DEFAULT = "default" # when no better candidate not found
14
+
15
+
16
+ # ------------------------------------------------------------------
17
+ # Symbol table helpers
18
+ SYMBOL_RETURN_PREFIX="return-"
19
+
20
+
21
+ # ------------------------------------------------------------------
22
+ # Solidity constans
23
+
24
+ SOL_MSG="msg"
25
+ SOL_NOW="now"
26
+ SOL_ADDRESS="address"
27
+ SOL_UINT="uint"
28
+ SOL_SEND="send"
29
+ SOL_BOOLEAN_TYPE="bool"
30
+
31
+ # ------------------------------------------------------------------
32
+ # Configuration defaults
33
+
34
+ # configuration validation
35
+ VALIDATION = {
36
+ :class_configure => {
37
+ :allowed => %w( solc_command ),
38
+ :required => %w( ),
39
+ },
40
+ :configure => {
41
+ :allowed => %w( preferences ),
42
+ :required => %w( ),
43
+ }
44
+ }
45
+
46
+
47
+ # preferences used - unless otherwise stated
48
+ DEFAULT_PREFERENCES = {
49
+ "tla-debug" => false, # debug messages
50
+ "tla-trace" => false, # framework trace messages
51
+ }
52
+
53
+
54
+ # defauls for configuration
55
+
56
+ CONFIGURATION_DEFAULTS = {
57
+ 'service_completion' => nil # TLA macro called to complete interface service
58
+ }
59
+
60
+
61
+ # ------------------------------------------------------------------
62
+ # Field names
63
+ # Contract ID
64
+ FIELD_NAME_CID = "cid"
65
+ # Original transactor
66
+ FIELD_NAME_ORIGINATOR = "originator"
67
+ # Original transactor
68
+ FIELD_NAME_SENDER = "sender"
69
+ # Account ID
70
+ FIELD_NAME_ADDRESS = "address"
71
+ # Account ID
72
+ FIELD_NAME_RECIPIENT = "recipient"
73
+ # Beneficiary account (in mining)
74
+ FIELD_NAME_BENEFICIARY = "beneficiary"
75
+ # Eth VALUE
76
+ FIELD_NAME_BALANCE = "balance"
77
+ # Eth VALUE
78
+ FIELD_NAME_VALUE = "value"
79
+ # Result
80
+ FIELD_NAME_RESULT = "result"
81
+ # Result
82
+ FIELD_NAME_GAS = "gas"
83
+ # EVM code (name of class)
84
+ FIELD_NAME_CODE_HASH = "codeHash"
85
+ # # Balace on address, inherited to contract
86
+ # FIELD_NAME_BALANCE = "balance"
87
+ FIELD_NAME_STATUS = "status"
88
+
89
+
90
+
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,22 @@
1
+ module Sbuilder
2
+ module Ethereum
3
+ class SbuilderEtherumException < Exception
4
+ end
5
+
6
+ class SbuilderEtherumScopeException < SbuilderEtherumException
7
+ end
8
+
9
+ class SexpException < SbuilderEtherumException
10
+ end
11
+
12
+ class SexpProcessorDataflowException < SbuilderEtherumException
13
+ end
14
+
15
+ class SbuilderEtherumMustacheException < SbuilderEtherumException
16
+ end
17
+ class SbuilderEtherumPluginException < SbuilderEtherumException
18
+ end
19
+ class SbuilderEtherumCompileException < SbuilderEtherumException
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,2674 @@
1
+ # coding: utf-8
2
+ module Sbuilder
3
+ module Ethereum
4
+
5
+ ##
6
+ # Extract snippets and interfaces from SexpAst.
7
+ #
8
+ # Assume
9
+ # - symbols defined && resolved
10
+ # - dataflows identified
11
+ #
12
+ # Actions
13
+ # - Create Snippets:
14
+ # -- VariableDeclaration --> TLA_State, :name
15
+ # -- FunctionDefinition --> TLA_procedure, :name, :body
16
+ # -- :body:
17
+ # Return --> :TLA_Return
18
+ # Assignment --> :TLA_Assignment
19
+ # Identifier --> :TLA_Identifier
20
+
21
+ ##
22
+ # This abstract class manages 'elementGenerator, which knows how
23
+ # to create TLA language building block (gen_ methods). This class
24
+ # also manages 'partials' datastructure, which actually know, how
25
+ # to render s-expressio to TLA building block string. Element
26
+ # generator is created in entry klass 'SexpProcessorTla', which
27
+ # passes reference to this one element generator to specialized
28
+ # klasses e.g. 'FunctionBodyProcessor'.
29
+ class SexpProcessorTlaRoot < SexpProcessorEthereum
30
+
31
+ # ------------------------------------------------------------------
32
+ # element generator methods
33
+
34
+ # @attr [TlaElementGenerator] elementGenerator helper to create
35
+ # SEXP data elements, and mustache partials to render the SEXP
36
+ # data elements to TLA language.
37
+ attr_reader :elementGenerator
38
+
39
+ extend Forwardable # for easy delegation
40
+
41
+ def_delegators :elementGenerator, # generator methods
42
+ :requiresLabelAfter, # return true is next statement should have a label
43
+ :gen_empty_set,
44
+ :gen_set,
45
+ :gen_list,
46
+ :gen_nilElement,
47
+ :gen_op_set_minus,
48
+ :gen_operator,
49
+ :gen_macro_call,
50
+ :gen_operator_call,
51
+ :gen_expression2,
52
+ :gen_identifier,
53
+ :gen_service_name,
54
+ :gen_state_variable,
55
+ :gen_record_definition,
56
+ :gen_name_op_operand,
57
+ # names
58
+ :gen_specname,
59
+ :gen_interface_input_variable_name,
60
+ :gen_interface_abort_label,
61
+ :gen_interface_fail_label,
62
+ :gen_interface_end_label,
63
+ :gen_plainname,
64
+ #
65
+ :gen_constant,
66
+ :gen_str,
67
+ :gen_parenthesis,
68
+ # :gen_service_entry,
69
+ :gen_service_procedure,
70
+ :gen_free_text,
71
+ :gen_debug_msg,
72
+ :gen_assert,
73
+ :gen_commented,
74
+ :gen_traced_stmt,
75
+ :gen_labeled_stmt, # add label to stmt
76
+ :gen_label, # skip which is followed by label
77
+ :gen_label_it, # wrapts stmt to require label after
78
+ :gen_goto,
79
+ :gen_either,
80
+ :gen_skip,
81
+ # :gen_check_resume,
82
+ :gen_assign,
83
+ :gen_record_field_definition,
84
+ :gen_record_named_field,
85
+ :gen_record_field,
86
+ :gen_stmts,
87
+ :gen_operator_dummy,
88
+ :gen_construct_contract,
89
+ :gen_new_record,
90
+ :gen_procedure_call,
91
+ :gen_if,
92
+ :gen_not,
93
+ :gen_IF,
94
+ :gen_TRUE,
95
+ :gen_FALSE,
96
+ :gen_EXCEPT,
97
+ :gen_and,
98
+ :gen_bin_op,
99
+ :gen_unary_op,
100
+ :gen_set_iterate,
101
+ :gen_set_map,
102
+ :gen_macro
103
+
104
+
105
+
106
+ # ------------------------------------------------------------------
107
+ # Attributes set in configuration
108
+
109
+ # @attr [Hash] options set in initialize
110
+ attr_accessor :options
111
+
112
+
113
+ def initialize( options )
114
+ super( options )
115
+ @options = options
116
+
117
+ # visitors just recurses - do not shift sexp (as sexp_processor default does)
118
+ self.default_method = :process_skip
119
+
120
+ end
121
+
122
+ # ------------------------------------------------------------------
123
+ # Utitities callable from base klasses
124
+
125
+ ##
126
+ # Address of storage root - support transaction
127
+ def g_storageRoot
128
+ gen_specname(:eth, TlaElementText::FW_STATE_STORAGE_ROOT)
129
+ end
130
+
131
+ def g_storageRoot_temp
132
+ gen_specname(:eth, TlaElementText::FW_STATE_STORAGE_ROOT_TEMP)
133
+ end
134
+
135
+ #
136
+ # storage root to use in execution
137
+ def g_storageRoot_exec
138
+ gen_operator_call( 'Head', g_storageRoot_temp )
139
+ end
140
+
141
+ def g_dispatcherMeta
142
+ :dispatcher
143
+ end
144
+ def g_dispatcherReturnMeta
145
+ :dispatcher_ret
146
+ end
147
+
148
+ # @return [sTla] for 'dispatcherName' macro
149
+ def g_dispatcher( dispatcherName )
150
+ gen_specname( g_dispatcherMeta, dispatcherName )
151
+ end
152
+
153
+ def g_dispatcherReturn( dispatcherName )
154
+ gen_specname( g_dispatcherReturnMeta, dispatcherName )
155
+ end
156
+
157
+ # def g_ethGasUsed
158
+ # gen_specname(:eth, TlaElementText::FW_STATE_GAS_USED )
159
+ # end
160
+
161
+ # def g_ethGasUsed_temp
162
+ # gen_specname(:eth, TlaElementText::FW_STATE_GAS_USED_TEMP )
163
+ # end
164
+
165
+ # def g_ethGasUsed_exec
166
+ # gen_operator_call( 'Head', g_ethGasUsed_temp )
167
+ # end
168
+
169
+ # def g_ethGasUsed_temp
170
+ # gen_specname(:eth, TlaElementText::FW_STATE_GAS_USED_TEMP )
171
+ # end
172
+
173
+ def g_ethAccounts
174
+ gen_specname(:eth, TlaElementText::FW_STATE_ACCOUNTS )
175
+ end
176
+
177
+ def g_ethAccounts_temp
178
+ gen_specname(:eth, TlaElementText::FW_STATE_ACCOUNTS_TEMP )
179
+ end
180
+
181
+ def g_ethAccounts_exec
182
+ gen_operator_call( 'Head', g_ethAccounts_temp )
183
+ end
184
+
185
+
186
+ # stack = <<value> \o Tail( stack )
187
+ def gen_assignOnTop( stackElement, value )
188
+ gen_assign(
189
+ stackElement,
190
+ gen_operator_call( TlaElementText::OP_INFRA_UPDATE_TOP, s(stackElement, value ))
191
+ )
192
+
193
+ end
194
+
195
+
196
+ ##
197
+ # Utilitiy operator stackVariable := valtoAssing \o Tail(
198
+ # g_blockStack ). Direct replacement to gen_assign
199
+ def g_assignOnTop( stackVariable, valtoAssing )
200
+ gen_assign(
201
+ stackVariable,
202
+ gen_operator_call( 'Push', s(gen_operator_call("Tail", stackVariable ), valtoAssing)))
203
+ end
204
+
205
+ ##
206
+ # Make it permanent
207
+ def g_assignOnStack( stackVariable, valtoAssing )
208
+ gen_assign(
209
+ stackVariable,
210
+ gen_operator_call(
211
+ 'Push',
212
+ s(
213
+ gen_operator_call( 'PropageOnStack', s(gen_operator_call("Tail", stackVariable ), valtoAssing)),
214
+ valtoAssing
215
+ )))
216
+ end
217
+
218
+
219
+ # def g_useIntrinsicGas( interfaceName, service_formal_parameter_name )
220
+ # sender_account = gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_SENDER)
221
+ # gasValue = g_gasToValue(gen_operator_call( TlaElementText::ETH_OPEATOR_INTRINSIC_GAS) )
222
+ # accountsUpdated = gen_set_map(
223
+ # gen_IF( gen_bin_op(
224
+ # "=",
225
+ # gen_record_field( gen_plainname('a'), Constants::FIELD_NAME_ADDRESS),
226
+ # sender_account
227
+ # ),
228
+ # gen_EXCEPT( "a",
229
+ # gen_bin_op("=",
230
+ # gen_record_field( "!", Constants::FIELD_NAME_BALANCE),
231
+ # gen_bin_op(
232
+ # "-",
233
+ # gen_record_field( 'a', Constants::FIELD_NAME_BALANCE),
234
+ # gasValue
235
+ # ))),
236
+ # gen_plainname('a')),
237
+ # gen_set_iterate( "a", g_ethAccounts_exec ))
238
+
239
+ # s(
240
+ # # eth_gasUsed += intrinsicGas
241
+ # gen_traced_stmt(
242
+ # g_assignOnStack( g_ethGasUsed_temp,
243
+ # gen_bin_op( '+', g_ethGasUsed_exec, gasValue )),
244
+ # s( "SVC-exec@#{interfaceName}: credit #{TlaElementText::FW_STATE_GAS_USED} with value for #{TlaElementText::ETH_OPEATOR_INTRINSIC_GAS}", gasValue )
245
+ # ),
246
+
247
+
248
+ # # eth_accounts := { IF a.account = i.sender THEN [ a EXCEPT !.balance = a.balance - instrinc ] ELSE a : a \in eth_accounts }
249
+ # gen_traced_stmt(
250
+ # g_assignOnStack( g_ethAccounts_temp, accountsUpdated ),
251
+ # s( "SVC-exec@#{interfaceName}: debit sender account", sender_account, "with gasValue", gasValue )
252
+ # ),
253
+
254
+ # ##
255
+ # # make changes perament so that they persist rollback
256
+
257
+ # gen_traced_stmt(
258
+ # gen_assign( g_ethGasUsed, g_ethGasUsed_exec ),
259
+ # s( "SVC-exec@#{interfaceName}: persist credit #{TlaElementText::FW_STATE_GAS_USED}", gasValue )
260
+ # ),
261
+
262
+ # gen_traced_stmt(
263
+ # gen_assign( g_ethAccounts, g_ethAccounts_exec ),
264
+ # s( "SVC-exec@#{interfaceName}: persist debit sender account", sender_account )
265
+ # ),
266
+
267
+ # )
268
+ # end
269
+
270
+ def g_persist()
271
+
272
+ s(
273
+ # gen_traced_stmt(
274
+ # gen_assign( g_ethGasUsed, g_ethGasUsed_exec ),
275
+ # s( "SVC-exec@#{interfaceName}: persist #{TlaElementText::FW_STATE_GAS_USED}" )
276
+ # ),
277
+
278
+ gen_traced_stmt(
279
+ gen_assign( g_ethAccounts, g_ethAccounts_exec ),
280
+ s( "SVC-exec@#{interfaceName}: persist #{TlaElementText::FW_STATE_ACCOUNTS}" )
281
+ ),
282
+
283
+ )
284
+ end
285
+
286
+
287
+
288
+ # Call servicve procedure
289
+ def g_serviceExec( interfaceName, service_formal_parameter_name )
290
+ s(
291
+
292
+ # call procecedure implementing procedule
293
+ gen_traced_stmt(
294
+ gen_procedure_call(gen_specname(:solidity_contract_function, interfaceName ), s(gen_identifier(service_formal_parameter_name))),
295
+ s( "TX-start@#{interfaceName}: call call service implementation", gen_str(gen_specname(:solidity_contract_function, interfaceName )) )
296
+ ),
297
+ )
298
+ end
299
+
300
+ ##
301
+ # Allways true i.e. TRUE
302
+ def g_TRUE
303
+ gen_TRUE
304
+ end
305
+
306
+ ##
307
+ # Operator call to check that recipient account non nil. NOTICE
308
+ # called from entry macro, and must use 'g_ethAccounts'
309
+ def g_RecipientAccountExists( service_formal_parameter_name )
310
+ gen_bin_op(
311
+ "#",
312
+ gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_RECIPIENT),
313
+ gen_nilElement
314
+ )
315
+
316
+ end
317
+
318
+ ##
319
+ # Operator call to check that recipient contract exists
320
+ def g_RecipientContractExists( bindAddress )
321
+ bindAddress = gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_RECIPIENT) if bindAddress.nil?
322
+ gen_operator_call(
323
+ TlaElementText::FW_OP_ELEMENT_EXISTS,
324
+ s( g_storageRoot_exec,
325
+ gen_str( Constants::FIELD_NAME_ADDRESS ),
326
+ bindAddress,
327
+ ))
328
+ end
329
+
330
+
331
+ # Possible to start transaction?
332
+ #
333
+ # - if non constror we require that g_RecipientAccountExists
334
+ # - in any case
335
+ # -- sender must account must exist
336
+ # -- senaer account holds enough balance
337
+
338
+ def g_serviceExecValidity( service_formal_parameter_name, isConstructor )
339
+
340
+ condition =
341
+ gen_and(
342
+ # Check account for sender exists
343
+ gen_operator_call(
344
+ TlaElementText::FW_OP_ELEMENT_EXISTS,
345
+ s( g_ethAccounts,
346
+ gen_str( Constants::FIELD_NAME_ADDRESS ),
347
+ gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_SENDER),
348
+ )),
349
+ # Sender has enough balance in eth_account
350
+ gen_bin_op(
351
+ ">=",
352
+ gen_record_field(
353
+ gen_operator_call(
354
+ TlaElementText::FW_OP_GET_ELEMENT,
355
+ s(
356
+ g_ethAccounts,
357
+ gen_str( Constants::FIELD_NAME_ADDRESS ),
358
+ gen_record_field( gen_identifier(service_formal_parameter_name), Constants::FIELD_NAME_SENDER)
359
+ )),
360
+ Constants::FIELD_NAME_BALANCE
361
+ ),
362
+ # to cover upfront cost
363
+ gen_operator_call( TlaElementText::ETH_OPEATOR_UP_FRONT_COST, gen_identifier(service_formal_parameter_name))
364
+ )
365
+ )
366
+
367
+
368
+
369
+ # # additional check to ensure that recipent not nil - if non constructor
370
+ if !isConstructor then
371
+ condition = gen_and( g_RecipientAccountExists(service_formal_parameter_name), condition )
372
+ end
373
+
374
+ condition
375
+ end
376
+
377
+
378
+ # If 'codeHash' of account entry in 'g_ethAccounts_exec'
379
+ # identified by 'parameterName.recipient' is among
380
+ # 'contracts.typeName'.
381
+ #
382
+ # @param [ContractDefinition:Array] contracts
383
+ # @param [String] paramterName
384
+ def g_dispatcherCondition( contracts, parameterName )
385
+
386
+ # if_condition:
387
+ list_of_contracts = contracts.inject( s() ) do |s,contractDefinition|
388
+ s << gen_str(contractDefinition.typeName)
389
+ s
390
+ end
391
+
392
+ if_confidition = gen_set_iterate(
393
+ gen_record_field(
394
+ gen_operator_call(
395
+ TlaElementText::FW_OP_GET_ELEMENT,
396
+ s( g_ethAccounts_exec,
397
+ gen_str( Constants::FIELD_NAME_ADDRESS ),
398
+ gen_record_field( parameterName, Constants::FIELD_NAME_RECIPIENT)
399
+ )),
400
+ Constants::FIELD_NAME_CODE_HASH
401
+ ),
402
+ gen_set( gen_list( list_of_contracts ))
403
+ )
404
+
405
+ if_confidition
406
+
407
+ end
408
+
409
+
410
+
411
+ ##
412
+ # Populate address fields for external call
413
+ def g_procCallAddresses( g_current_formal_input )
414
+ s(
415
+ gen_record_field_definition(
416
+ Constants::FIELD_NAME_SENDER,
417
+ gen_record_field( g_current_formal_input, Constants::FIELD_NAME_SENDER)),
418
+ gen_record_field_definition(
419
+ Constants::FIELD_NAME_ORIGINATOR,
420
+ gen_record_field( g_current_formal_input, Constants::FIELD_NAME_ORIGINATOR)),
421
+ )
422
+ end
423
+
424
+ def g_procCallValue( constantValue=0 )
425
+ s(gen_record_field_definition(
426
+ Constants::FIELD_NAME_VALUE,
427
+ gen_constant( constantValue )))
428
+ end
429
+ ##
430
+ # create new entry to 'ethStorageRoot'
431
+ def g_newEntryToStorageRoot( interfaceName, contractFields )
432
+ s(
433
+ gen_traced_stmt(
434
+ gen_assignOnTop(
435
+ g_storageRoot_temp,
436
+ gen_bin_op( '\union', gen_set(gen_record_definition(contractFields)), g_storageRoot_exec )
437
+ ),
438
+ s("SVC-exec@#{interfaceName}: create contract entry to #{TlaElementText::FW_STATE_STORAGE_ROOT_TEMP}")
439
+ )
440
+ )
441
+ end
442
+
443
+ # @return [s(record_field_definition)] record field defintion
444
+ # bind for common message parameters (recipient, sender,
445
+ # originator)
446
+ def g_commonMsgParameters( specname_input_variable, recipientTla )
447
+ s(
448
+ gen_record_field_definition( Constants::FIELD_NAME_RECIPIENT,
449
+ recipientTla ),
450
+ gen_record_field_definition( Constants::FIELD_NAME_SENDER,
451
+ g_localCurrentAccountId ),
452
+ gen_record_field_definition( Constants::FIELD_NAME_ORIGINATOR,
453
+ gen_record_field( specname_input_variable, Constants::FIELD_NAME_ORIGINATOR )),
454
+ )
455
+ end
456
+
457
+ ##
458
+ # @return InfrastructureServiceGetResponse( "personal_newAccount()" )
459
+ def g_readNewAccountId
460
+ gen_operator_call(
461
+ TlaElementText::OP_INFRA_GET_RESPONSE,
462
+ gen_str( gen_service_name(TlaElementText::SERVICE_ACCOUNT_NEW))
463
+ )
464
+ end
465
+
466
+ ##
467
+ # @param [stla] newAddress Nil|address of recipient to create
468
+ #
469
+ # @return call 'eth_personal_newAccount( [ codeHash |-> "Demo2", address |-> Nil|address ])'
470
+ def g_createNewAccount( interfaceName, contractName, newAddress )
471
+ # s(
472
+ # gen_procedure_call(
473
+ # gen_specname( setMetatype(:eth), TlaElementText::INTERFACE_ACCOUNT_NEW),
474
+ # gen_record_definition(
475
+ # gen_record_field_definition(
476
+ # TlaElementText::TLA_RECORD_FIELD_FOR_SINGLE_VALUE,
477
+ # # use quotes around contractName
478
+ # gen_str(contractName) ))
479
+ # )
480
+ # )
481
+ s(
482
+ gen_traced_stmt(
483
+ gen_procedure_call(
484
+ gen_specname( setMetatype(:eth), TlaElementText::INTERFACE_ACCOUNT_NEW),
485
+ gen_record_definition(
486
+ s(
487
+ gen_record_field_definition( Constants::FIELD_NAME_CODE_HASH, contractName ),
488
+ gen_record_field_definition( Constants::FIELD_NAME_ADDRESS, newAddress )
489
+ ))),
490
+ s("SVC-exec@#{interfaceName}: call '#{TlaElementText::INTERFACE_ACCOUNT_NEW}' to create new account entry")
491
+ )
492
+ )
493
+ end
494
+
495
+ ##
496
+ # Put sender address to a well known place stored in stack
497
+ # locals := 'g_IdRval'
498
+ def g_copyCurrentAccountIdToLocals( g_IdRval )
499
+ s(
500
+ gen_assign( TlaElementText::TLA_LOCALS_VARIABLE, g_IdRval ),
501
+ )
502
+ end
503
+
504
+ #
505
+ # @param [String] appLocalName in in application domain
506
+ # @return [sTla] tlaName for 'appLocalName'
507
+ def g_locaVariableName( appLocalName )
508
+ gen_identifier( appLocalName )
509
+ end
510
+
511
+ ##
512
+ # It is in 'locals' variable
513
+ #
514
+ # @see g_copyCurrentAccountIdToLocals
515
+ #
516
+ def g_localCurrentAccountId
517
+ # gen_record_field(gen_interface_input_variable_name( interfaceName ), Constants::FIELD_NAME_RECIPIENT )
518
+ # gen_identifier( TlaElementText::TLA_LOCALS_VARIABLE )
519
+ g_locaVariableName( TlaElementText::TLA_LOCALS_VARIABLE )
520
+ end
521
+
522
+ # Constant to set to 'responses.status'
523
+ def g_statusOk
524
+ gen_operator_call(TlaElementText::TLA_SERVICE_STATUS_OK)
525
+ end
526
+
527
+ # Initial value to set to 'responses.status', something wrong,
528
+ # if abort seen on return
529
+ def g_statusAbort
530
+ gen_operator_call(TlaElementText::TLA_SERVICE_STATUS_ABORT)
531
+ end
532
+
533
+ # Constant to set to 'responses.status'
534
+ def g_statusFail
535
+ gen_operator_call(TlaElementText::TLA_SERVICE_STATUS_FAIL)
536
+ end
537
+
538
+ ##
539
+ # SvcReturn( "interfaceName()", status, getRespons("interfaceName()"))
540
+ def g_updateReturnStatus( interfaceName, status )
541
+ gen_operator_call(
542
+ TlaElementText::OP_INFRA_RETURN,
543
+ s(gen_str(interfaceName ),
544
+ status,
545
+ gen_operator_call( TlaElementText::OP_INFRA_GET_RESPONSE, gen_str(interfaceName ))))
546
+ end
547
+
548
+ ##
549
+ # last step in service procesure, just sets status, withoun modifying response value.
550
+ #
551
+ # infraReturn( "Demo()", OK, infraRespons( "Demo"))
552
+ def g_ServiceReturn( interfaceName, status=g_statusOk )
553
+ s(
554
+ gen_traced_stmt(
555
+ g_updateReturnStatus( interfaceName, status ),
556
+ s( "SVC-exec@#{interfaceName}: set return status", status )
557
+ )
558
+ )
559
+ end
560
+
561
+ ##
562
+ # Service procedure starting - push stack
563
+ def g_svcStackEnter( interfaceName, serviceName )
564
+ s(
565
+ gen_label,
566
+ gen_traced_stmt(
567
+ # gen_labeled_stmt(
568
+ gen_macro_call( TlaElementText::TLA_SERVICE_PUSH ),
569
+ # serviceName, "_push"
570
+ # ),
571
+ s( "SVC-exec@#{interfaceName}: init return status", g_statusAbort )))
572
+ end
573
+
574
+ # # E.g. infraReturn( "Demo()", Nil, Nil )
575
+ # def g_ServiceReturnInit( interfaceName )
576
+ # s( gen_traced_stmt(
577
+ # gen_operator_call(
578
+ # TlaElementText::OP_INFRA_RETURN,
579
+ # s( gen_str(interfaceName ), g_statusAbort, gen_nilElement )),
580
+ # s( "SVC-exec@#{interfaceName}: init return status", g_statusAbort )))
581
+ # end
582
+
583
+ ##
584
+ # last step - returs account id created && set status OK
585
+ def g_constructorReturn( interfaceName )
586
+ s(
587
+ # ServiceReturn( "Demo()", 0, new-account-id )
588
+ gen_traced_stmt(
589
+ gen_operator_call(
590
+ TlaElementText::OP_INFRA_RETURN,
591
+ s(gen_str(interfaceName ), g_statusOk, g_localCurrentAccountId )),
592
+ s( "SVC-exec@#{interfaceName}: set return status", g_statusOk, "return account id created", g_localCurrentAccountId )
593
+ )
594
+
595
+ )
596
+ end
597
+
598
+ # def g_procedureCall( g_procedure, g_params )
599
+ # gen_procedure_call(
600
+ # g_procedure,
601
+ # gen_record_definition( g_params )
602
+ # )
603
+ # end
604
+
605
+ # Goto abort label in 'interfaceName' procedure
606
+ def g_abort( interfaceName, serviceName )
607
+ gen_goto( gen_interface_abort_label( interfaceName, serviceName ))
608
+ end
609
+
610
+ ##
611
+ # Goto fail label (afte) user code should check for errors
612
+ def g_fail( interfaceName, serviceName )
613
+ gen_goto( gen_interface_fail_label( interfaceName ) )
614
+ end
615
+
616
+ def g_end( interfaceName, serviceName, status )
617
+ s(
618
+ gen_labeled_stmt(
619
+ g_updateReturnStatus( interfaceName, status ),
620
+ gen_specname(:solidity_contract_function, interfaceName ),
621
+ "call_construct"
622
+ ),
623
+ gen_goto( gen_interface_end_label( interfaceName )),
624
+ )
625
+ end
626
+
627
+ # Geneate code to dispatch a call to 'functionCalled' with
628
+ # 'parameters'. After the call, a jump to abort label in
629
+ # 'interfaceName'/'serviceName' is called function resulted to
630
+ # abort.
631
+ #
632
+ # Use direct call to 'functionCalled.interfaceName', if
633
+ # 'findMethodsToDispatch' returns a singleton method, else call
634
+ # dispatcher macro, also returned by 'findMethodsToDispatch'.
635
+ def g_dispatch_call( functionCalled, parameters, interfaceName, serviceName )
636
+
637
+ dispatcherName, methodsToDispatch = findMethodsToDispatch( functionCalled )
638
+
639
+ if methodsToDispatch.length == 1 then
640
+ # only one function to consider
641
+ called = gen_specname( :solidity_contract_function, functionCalled.interfaceName )
642
+ s(
643
+ gen_procedure_call( called, parameters ),
644
+ g_abortCheckIncall( interfaceName, serviceName, functionCalled.interfaceName)
645
+ )
646
+ else
647
+
648
+ # multiple functions - check status of all of the function
649
+ # (assumes that dispatcher macro resets statuses )
650
+ if_condition = gen_FALSE
651
+
652
+ methodsToDispatch.map do |methodToDispatch|
653
+ methodToDispatch[:function].interfaceName
654
+ end.uniq.each do |interfaceName|
655
+ if_condition =
656
+ gen_bin_op( '\\/',
657
+ if_condition,
658
+ g_abortedInCall( interfaceName )
659
+ )
660
+ end
661
+ if_block = s( g_abort( interfaceName, serviceName ))
662
+
663
+ s(
664
+ gen_label_it(gen_macro_call( g_dispatcher(dispatcherName), parameters )),
665
+ gen_if( if_condition, if_block )
666
+
667
+ )
668
+ end
669
+ end
670
+
671
+ # Geneate code to read response from 'functionCalled'.
672
+ #
673
+ # If 'functionCalled' is implemented by just one contract, the
674
+ # is done using a simple 'InfrastructureServiceGetResponse' call
675
+ # for functionCalled.interfaceName. If 'functionCalled' is
676
+ # implemented in serveral contracts, we take the result from
677
+ # where 'InfrastructureServiceGetStatus' is not Nil.
678
+
679
+
680
+ def g_dispatch_read_return( functionCalled )
681
+ logger.debug "#{__method__}: functionCall:#{functionCalled}" if logger.debug?
682
+
683
+ dispatcherName, methodsToDispatch = findMethodsToDispatch( functionCalled )
684
+ if methodsToDispatch.length == 1 then
685
+
686
+ readReturn = gen_operator_call( TlaElementText::OP_INFRA_GET_RESPONSE, gen_str( functionCalled.interfaceName ))
687
+ else
688
+
689
+ actions = methodsToDispatch.map do |methodToDispatch|
690
+ methodToDispatch[:function].interfaceName
691
+ end.uniq.map do |interfaceName|
692
+ {
693
+ :condition => gen_bin_op( '#', gen_operator_call( TlaElementText::OP_INFRA_GET_STATUS, gen_str(interfaceName)), gen_nilElement ),
694
+ :action => gen_operator_call( TlaElementText::OP_INFRA_GET_RESPONSE, gen_str(interfaceName )),
695
+ }
696
+ end
697
+
698
+ # reverse array - last
699
+ readReturn = actions.reverse.inject( actions.last[:action] ) do |m,a|
700
+ r = gen_IF( a[:condition], a[:action], m )
701
+ r
702
+ end
703
+
704
+ # raise "Not yet implemeteded"
705
+ end
706
+ readReturn
707
+
708
+ end
709
+
710
+ def g_abortedInCall( called_interfaceName )
711
+ gen_bin_op("=",g_statusAbort, gen_operator_call( TlaElementText::OP_INFRA_GET_STATUS, gen_str(called_interfaceName)))
712
+ end
713
+
714
+ ##
715
+ # Jump to 'g_abort('interfaceName', 'serviceName' )' -label if
716
+ # 'g_abortedInCall( called_interfaceName )'
717
+ def g_abortCheckIncall( interfaceName, serviceName, called_interfaceName )
718
+ gen_if( g_abortedInCall( called_interfaceName ), g_abort(interfaceName, serviceName ))
719
+ end
720
+
721
+ ##
722
+ # @param [stla] newAddress Nil|address of recipient to create
723
+ #
724
+ # consrructo - user in constructor and for message call to
725
+ # undefined recipient
726
+ def g_construct( interfaceName, contractName, contractExp, newAddress )
727
+ # call personal_newAccounts( [ dummy |-> 'Demo' ] )
728
+ g_createNewAccount(interfaceName, contractName, newAddress) +
729
+
730
+ # locals := ReadResponse('Demo')
731
+ g_copyCurrentAccountIdToLocals(g_readNewAccountId) +
732
+
733
+ # eth_storageRoot := storage \union [ f1 |-> val1, ...]
734
+ g_newEntryToStorageRoot( interfaceName, u_initContractFields(contractExp))
735
+
736
+ end
737
+
738
+ ##
739
+ # Common code for constructor and normal method call
740
+ #
741
+ def g_serviceProcedurePreamble( interfaceName, serviceName, specname_input_variable, isContract )
742
+
743
+ # specname_input_variable = gen_interface_input_variable_name( interfaceName )
744
+
745
+ # ether balance of sender - on block stack
746
+ sender_balance = gen_record_field(
747
+ gen_operator_call(
748
+ TlaElementText::FW_OP_GET_ELEMENT,
749
+ s( g_ethAccounts_exec,
750
+ gen_str( Constants::FIELD_NAME_ADDRESS ),
751
+ gen_record_field( specname_input_variable, Constants::FIELD_NAME_SENDER )
752
+ )),
753
+ Constants::FIELD_NAME_BALANCE)
754
+
755
+ # needed value == tx-gas*gas-price + msg.value
756
+ value_needed_expr =
757
+ gen_parenthesis(gen_bin_op(
758
+ '+',
759
+ g_gasToValue( gen_operator_call( TlaElementText::ETH_OPEATOR_TRANSACTION_GAS)),
760
+ gen_record_field( specname_input_variable, Constants::FIELD_NAME_VALUE)))
761
+
762
+ request_value_expr = gen_record_field( specname_input_variable, Constants::FIELD_NAME_VALUE)
763
+
764
+ account_id_sending = gen_record_field( specname_input_variable, Constants::FIELD_NAME_SENDER )
765
+
766
+
767
+ # Return( "Demo()", Nil, Nil )
768
+ # TODO remove - unecassary
769
+ # g_ServiceReturnInit( interfaceName ) +
770
+
771
+ # ( isContract ? g_useIntrinsicGas( interfaceName, specname_input_variable ) : s()) +
772
+ ( isContract ? g_persist() : s() ) +
773
+
774
+ g_svcStackEnter( interfaceName, serviceName ) +
775
+
776
+ # model non-deterministic choice of out of gas
777
+ s(
778
+ gen_traced_stmt(
779
+ gen_either( gen_skip, g_abort( interfaceName, serviceName )),
780
+ s( "SVC-exec@#{interfaceName}: non-deterministic out-of-gas-check" )
781
+ )
782
+ ) +
783
+
784
+ ( isContract ?
785
+ s(
786
+ # if eth_account[send].balance < transactionGas goto abort
787
+ gen_traced_stmt(
788
+ gen_if(
789
+ gen_bin_op( "<", sender_balance, value_needed_expr ),
790
+ gen_traced_stmt(
791
+ g_abort( interfaceName, serviceName ),
792
+ s( "SVC-EXEC#{interfaceName}: abort" ) # then
793
+ )),
794
+ s(
795
+ "SVC-exec@#{interfaceName}: check sender balance >= #{TlaElementText::ETH_OPEATOR_GAS_VALUE}(#{TlaElementText::ETH_OPEATOR_TRANSACTION_GAS}) + #{Constants::FIELD_NAME_VALUE}",
796
+ gen_record_field( specname_input_variable, Constants::FIELD_NAME_SENDER ),
797
+ "balance", sender_balance,
798
+ "value needed", value_needed_expr,
799
+ ) # trace message
800
+ ),
801
+
802
+ # goto requires a label here
803
+ # gen_labeled_stmt( gen_skip, specname_functionName, "_value_checked" ),
804
+ gen_skip,
805
+
806
+ ) # s
807
+ : s()
808
+ ) # isContract?
809
+ end
810
+
811
+
812
+ ##
813
+ # Move value to 'account_receiving_value' (new account in
814
+ # constructor, recipient in normal call) from
815
+ # 'account_id_sending' (=sender).
816
+ #
817
+ # Implementation notice: no need to do anything if
818
+ # 'account_receiving' == 'account_sending'. As a matter of fact,
819
+ # the code used to do the actual move would result to
820
+ # duplicating the balance.
821
+ #
822
+ # @param [SexpTla] account_id_receiving_value defines account recveiving value
823
+ #
824
+ # @return [Sexp:Array] statements to start for the procedure
825
+ def g_serviceProcedureMoveValue( interfaceName, specname_input_variable, account_id_receiving_value )
826
+
827
+ request_value_expr = gen_record_field( specname_input_variable, Constants::FIELD_NAME_VALUE)
828
+ account_id_sending = gen_record_field( specname_input_variable, Constants::FIELD_NAME_SENDER )
829
+
830
+ s(
831
+ # eth_accounts[receiver].balance += request.value
832
+ # eth_accounts[sender].balance -= request.value
833
+ # move 'value' from account 'input.send' to new account ()
834
+ gen_if(
835
+ gen_bin_op( "#", account_id_sending, account_id_receiving_value),
836
+ # gen_traced_stmt(
837
+ gen_assignOnTop(
838
+ g_ethAccounts_temp,
839
+ gen_set_map(
840
+ gen_IF(
841
+ gen_bin_op(
842
+ "=",
843
+ gen_record_field( 'a', Constants::FIELD_NAME_ADDRESS),
844
+ account_id_receiving_value
845
+ ),
846
+ gen_EXCEPT( "a",
847
+ gen_bin_op("=",
848
+ gen_record_field( "!", Constants::FIELD_NAME_BALANCE),
849
+ gen_bin_op(
850
+ "+",
851
+ gen_record_field( "a", Constants::FIELD_NAME_BALANCE),
852
+ request_value_expr
853
+ ))),
854
+ gen_IF(
855
+ gen_bin_op( "=",
856
+ gen_record_field( 'a', Constants::FIELD_NAME_ADDRESS),
857
+ account_id_sending) ,
858
+ gen_EXCEPT( "a",
859
+ gen_bin_op(
860
+ "=",
861
+ gen_record_field( "!", Constants::FIELD_NAME_BALANCE),
862
+ gen_bin_op(
863
+ '-',
864
+ gen_record_field( "a", Constants::FIELD_NAME_BALANCE),
865
+ request_value_expr
866
+ )
867
+ )),
868
+ 'a')),
869
+ gen_set_iterate( "a", g_ethAccounts_exec ))
870
+ ),
871
+ # s( "SVC-exec@#{interfaceName}: move value", request_value_expr, "from sender", account_id_sending, "to receiving", account_id_receiving_value ),
872
+ # ),
873
+ ),
874
+ gen_traced_stmt(
875
+ gen_skip, s("SVC-exec@#{interfaceName}: moved value" )
876
+ )
877
+ )
878
+ end
879
+
880
+ ##
881
+ #
882
+ def g_gasToValue( gas )
883
+ gen_operator_call(TlaElementText::ETH_OPEATOR_GAS_VALUE, gas )
884
+ end
885
+
886
+
887
+ ##
888
+ # @return [Sexp:Array] statements to end the procedure
889
+ def g_serviceProcedureEpilogue( interfaceName, serviceName, specname_input_variable )
890
+ s(
891
+ # Skip with label so that state changes generated here do
892
+ # not interfere with rest of the code
893
+ gen_label,
894
+ )
895
+
896
+ # expr_transaction_gasValue = g_gasToValue(gen_operator_call( TlaElementText::ETH_OPEATOR_TRANSACTION_GAS))
897
+
898
+ # # specname_accounts = gen_specname(:eth, TlaElementText::FW_STATE_ACCOUNTS)
899
+
900
+ # s(
901
+ # # Skip with label so that state changes generated here do
902
+ # # not interfere with rest of the code
903
+ # gen_label,
904
+
905
+ # # increment 'eth_gasUsed' for running the transaction
906
+ # gen_traced_stmt(
907
+ # gen_assignOnTop(g_ethGasUsed_temp, gen_bin_op( '+', g_ethGasUsed_exec, expr_transaction_gasValue )),
908
+ # s( "SVC-exec@#{interfaceName}: credit '#{TlaElementText::ETH_OPEATOR_TRANSACTION_GAS}' for transaction gas value", expr_transaction_gasValue )
909
+ # ),
910
+
911
+ # # decreas gasUsed from sender account
912
+ # gen_traced_stmt(
913
+ # gen_assignOnTop(
914
+ # g_ethAccounts_temp,
915
+ # gen_set_map(
916
+ # gen_IF(
917
+ # gen_bin_op( "=", gen_record_field(specname_input_variable, Constants::FIELD_NAME_SENDER), gen_record_field( "a", Constants::FIELD_NAME_ADDRESS ) ),
918
+ # gen_EXCEPT(
919
+ # 'a',
920
+ # gen_bin_op( "=",
921
+ # gen_record_field("!", Constants::FIELD_NAME_BALANCE),
922
+ # gen_bin_op( "-", gen_record_field( "a", Constants::FIELD_NAME_BALANCE ), expr_transaction_gasValue ) )),
923
+ # 'a'),
924
+ # gen_set_iterate("a", g_ethAccounts_exec ))
925
+ # ),
926
+ # s( "SVC-exec@#{interfaceName}: debit sender", gen_record_field(specname_input_variable, Constants::FIELD_NAME_SENDER), "for transaction gas value", expr_transaction_gasValue )
927
+ # ),
928
+ # )
929
+ end
930
+
931
+ # ------------------------------------------------------------------
932
+ # @!group Utilities
933
+
934
+ def g_zero
935
+ gen_plainname('0')
936
+ end
937
+
938
+ #
939
+ # @return [Hash:Array] params
940
+ # @option params [:Symbol] :name
941
+ def u_functionParameters( functionDecl )
942
+ functionDecl.parameterList.parameters.map { |param| { :name => param.name } }
943
+ end
944
+
945
+ def u_initValue( typeName )
946
+ TlaElementText.solIsNumericType(typeName) ? g_zero : gen_nilElement
947
+ end
948
+
949
+ def u_initContractFields( contractExp )
950
+ contractExp.contractVariables.map do |v|
951
+ logger.debug "#{__method__}: contract variable=#{v}" if logger.debug?
952
+
953
+ # process VariableDeclaration
954
+ if v.name == Constants::FIELD_NAME_ADDRESS
955
+ # access return from 'newAccount'
956
+ { :name => v.name, :value=> g_readNewAccountId }
957
+ else
958
+ if v.isMapping then
959
+
960
+ # mapping variable input domain name e.g. d_eth_address
961
+ inputDomainName = v.typeSexp.mapFrom.domainReference.domainName
962
+ # numeric solidity types init with 0, default to use Nil
963
+ # initialValue = TlaElementText.solIsNumericType(v.typeSexp.mapTo.typeName) ? gen_plainname('0') : gen_nilElement
964
+ initialValue = u_initValue( v.typeSexp.mapTo.typeName )
965
+
966
+
967
+ # e.g. [ i \in d_eth_address |-> Nil ] or [ i \in d_eth_address |-> 0 ]
968
+ initMapping = gen_record_definition(
969
+ gen_record_field_definition(
970
+ gen_set_iterate( 'a',
971
+ gen_specname( setMetatype(Sbuilder::Facade::META_MODEL_DOMAINS), inputDomainName )),
972
+ initialValue )
973
+ )
974
+ {
975
+ :name => v.name,
976
+ :value => initMapping,
977
+ }
978
+ else
979
+ { :name => v.name, :value => u_initValue( v.typeSexp.typeName ) }
980
+ end
981
+ end
982
+ end.inject( s() ) do |memo,nv|
983
+ memo << gen_name_op_operand( nv[:name], "|->", nv[:value] )
984
+ memo
985
+ end
986
+ end
987
+
988
+
989
+
990
+
991
+
992
+
993
+ # @!endgroup
994
+
995
+ end
996
+
997
+ # This created in plugin
998
+ class SexpProcessorTla < SexpProcessorTlaRoot
999
+
1000
+
1001
+ # Constant to initialize partials hash. Particularly, add
1002
+ # mapping for :Snippet TLA generate element sexp_type, because
1003
+ # snippets are not processed by #elementGenerator, which is
1004
+ # responsible for adding mapping entries.
1005
+ PARTIALS = {
1006
+
1007
+ :Snippet => <<-EOS.unindent,
1008
+ (* {|metatype|}:{|appName|}{|# comment |} - {|{ comment }|}{|/ comment |} *)
1009
+ {|#snippet|}{|#EVAL|}.{|/EVAL|}{|/snippet|}
1010
+ EOS
1011
+ }
1012
+
1013
+ # @attr [Hash] baseMetatypes implement in 'sbuilder' framework
1014
+ # can be referenced in TLA translation
1015
+ attr_reader :baseMetatypes
1016
+
1017
+ # ------------------------------------------------------------------
1018
+ # @!group Recusion parameters
1019
+
1020
+ # @attr [STla] specname_input_variable
1021
+ attr_accessor :specname_input_variable
1022
+
1023
+ # @attr [String] interfaceName
1024
+ attr_accessor :interfaceName
1025
+
1026
+ # @attr [String] serviceName
1027
+ attr_accessor :serviceName
1028
+
1029
+ # @!endgroup
1030
+
1031
+ # ------------------------------------------------------------------
1032
+ # @!group Construct & configure
1033
+
1034
+ def initialize( metatypes, options={} )
1035
+ super( options )
1036
+
1037
+ # Init partial for snippets , which are not found in SexpAst
1038
+ @partials = PARTIALS
1039
+ @collectedSnippetNames = []
1040
+ @collectedSnippets = []
1041
+ @collectedInterfaces = []
1042
+ @collectedDefintions = []
1043
+
1044
+ logger.info "#{__method__} init-metatypes #{metatypes}"
1045
+ # allow access to frame work metatypesa
1046
+ setBaseMetatypes( metatypes )
1047
+
1048
+ # gen_x method + adds partials
1049
+ initElementGenerator
1050
+
1051
+ # load only once
1052
+ loadFixedSnippets
1053
+ loadEthereumSnippets
1054
+
1055
+ end
1056
+
1057
+ # configure +baseMetatypes+ in sbuilder immediatelly after constructuor
1058
+ #
1059
+ # @param [Hash] baseMetatypes
1060
+ # @option baseMetatypes [:Symbol] :name
1061
+ # @option baseMetatypes [:Symbol] :desc
1062
+ private def setBaseMetatypes( metatypes )
1063
+ @baseMetatypes = metatypes
1064
+ logger.debug( "#{__method__}: baseMetatypes=#{baseMetatypes}" ) if logger.debug?
1065
+ end
1066
+
1067
+ private def initElementGenerator
1068
+
1069
+ # gen_* methods delegated here
1070
+ @elementGenerator = TlaElementGenerator.new( options )
1071
+ @elementGenerator.setPartials( partials )
1072
+ @elementGenerator.setValidMetatypes( validMetatypes )
1073
+
1074
+ end
1075
+
1076
+
1077
+ # @!endgroup
1078
+
1079
+ # ------------------------------------------------------------------
1080
+ # @!group Scoped
1081
+
1082
+ # @attr [Contract] currentContract which currently processing
1083
+ attr_accessor :currentContract
1084
+
1085
+ def contracted( expContract )
1086
+
1087
+ self.currentContract = expContract
1088
+ begin
1089
+ yield
1090
+ ensure
1091
+ self.currentContract = nil
1092
+ end
1093
+ end
1094
+
1095
+ # @!endgroup
1096
+
1097
+
1098
+ # ------------------------------------------------------------------
1099
+ # @!group Routines to build module result
1100
+
1101
+ # @attr [Hash] partials mapping tla generate sexp to mustache
1102
+ # template, created in this module
1103
+ attr_reader :partials
1104
+
1105
+ ##
1106
+ # Return partials created in #process -method.
1107
+ #
1108
+ # @return [Hash] partials mapping TLA generate element sexp:s to
1109
+ # mustache partial strings for rendering TLA generate element.
1110
+ def getPartials
1111
+ logger.info "#{__method__}: partials.keys=#{partials.keys.join(',')}"
1112
+ partials
1113
+ end
1114
+
1115
+ # Add 'snippet' to '@collectedSnippets' unless it is already defined in '@collectedSnippetNames'
1116
+ def addSnippet( snippet )
1117
+ logger.debug "#{__method__}: snippet=#{snippet}" if logger.debug?
1118
+ if ! @collectedSnippetNames.include?( snippet.snippetId )
1119
+ logger.info "#{__method__}: added metatype='#{snippet.metatype}' appName='#{snippet.name}', specName '#{snippet.specName}': #{snippet.comment}"
1120
+ @collectedSnippets << snippet
1121
+ @collectedSnippetNames << snippet.snippetId
1122
+ else
1123
+ logger.info "#{__method__}: duplitac snippet ='#{snippet.snippetId} - not added"
1124
+ end
1125
+ end
1126
+
1127
+
1128
+ # @return [Snippets] snippets to pass to snippet loader plugin
1129
+ def snippets
1130
+ Snippets.new( @collectedSnippets )
1131
+ end
1132
+
1133
+ def addInterface( interface )
1134
+ logger.info "#{__method__}: interfaceDefinition=#{interface.interfaceDefinition}"
1135
+ @collectedInterfaces << interface
1136
+ end
1137
+
1138
+ # @return [Interface:Array] interfaces to pass to api loader plugin
1139
+ def interfaces
1140
+ Interfaces.new( @collectedInterfaces )
1141
+ end
1142
+
1143
+
1144
+ def addDefinition( definition )
1145
+ logger.info "#{__method__}: definition=#{definition}"
1146
+ @collectedDefintions << definition
1147
+ end
1148
+
1149
+ # @return [Definition:Array] definitiosn to pass to api loader plugin
1150
+ def definitions
1151
+ Definitions.new( @collectedDefintions )
1152
+ end
1153
+
1154
+
1155
+ ##
1156
+ # Include +domainName+ to TLA specicification code
1157
+ def addDomainName( domainName )
1158
+ logger.info "#{__method__}: domainName=#{domainName}"
1159
+ @domainNames ||= []
1160
+ @domainNames << Domain.s(domainName)
1161
+ end
1162
+
1163
+ ##
1164
+ # Add 'domainValue' to 'domainName'
1165
+ def addDomainValue( domainName, domainValue )
1166
+ logger.info "#{__method__}: #{domainName} value= #{domainValue}"
1167
+ domain = domainNames.find { |d| d.domainName == domainName }
1168
+ raise "Unknow domain #{domainName} - known domains #{domainNames.join(',')}" if domain.nil?
1169
+ # Just add it to list
1170
+ domain << domainValue
1171
+ end
1172
+
1173
+ ##
1174
+ # @return [String:Array] domainNames to define in TLA specification code
1175
+ def domainNames
1176
+ @domainNames ||= []
1177
+ end
1178
+
1179
+ # @!endgroup
1180
+
1181
+ # ------------------------------------------------------------------
1182
+ # @!group Access specialized sexp processor
1183
+
1184
+ def getFunctionBodyProcessor
1185
+ proc = FunctionBodyProcessor.new( self, elementGenerator, options )
1186
+ # Move context to processeos
1187
+ proc.interfaceName = interfaceName
1188
+ proc.serviceName = serviceName
1189
+ proc.currentContract = currentContract
1190
+ proc.specname_input_variable = specname_input_variable
1191
+ proc
1192
+ end
1193
+
1194
+ def getFunctionLocalsProcessor
1195
+ proc = FunctionLocalsProcessor.new( elementGenerator, options )
1196
+ # Move context to processeos
1197
+ # proc.interfaceName = interfaceName
1198
+ # proc.serviceName = serviceName
1199
+ # proc.specname_input_variable = specname_input_variable
1200
+ proc
1201
+ end
1202
+
1203
+
1204
+ # @!endgroup
1205
+
1206
+ # ------------------------------------------------------------------
1207
+ # @!group Metatype management and utilities
1208
+
1209
+ ##
1210
+ # @return [Hash] metatypes for ethreum loader
1211
+ def metatypes
1212
+ TlaElementText::METATYPES
1213
+ end
1214
+
1215
+ ##
1216
+ # Return cached valid metatypes, whica are +baseMetatypes+ and
1217
+ # +metatypes+ for ethereum loader plugin
1218
+ #
1219
+ # @return [Hash] validMetatypes
1220
+ # @option [Symbol] :name metatype name
1221
+ # @option [Symbol] :desc metatype description
1222
+ def validMetatypes
1223
+ # baseMetatypes :name/:desc array mapped to a hash
1224
+ raise "Should have called setBaseMetatypes earlier" if baseMetatypes.nil?
1225
+ @validMetatypes ||= baseMetatypes.inject( metatypes.clone ) {|memo,mt| memo[mt[:name]] = mt[:desc]; memo }
1226
+ end
1227
+
1228
+
1229
+ ##
1230
+ # This method needs to sync with the name created when api
1231
+ # loader plugin loads interface for the +function+.
1232
+ #
1233
+ # @param [String] function appName (in application domain)
1234
+ #
1235
+ # @return [String] intrefaceName +function+ postfixed with '()'
1236
+ #
1237
+ ##
1238
+ # Pass +metatype+ trough, and ensure that it exists in
1239
+ # +validMetatypes+ i.e. in +metatypes+ defined in this module, and
1240
+ # in +baseMetatypes+ defined by sbuilder framework
1241
+
1242
+ def setMetatype( metatype )
1243
+ raise SbuilderEtherumException, <<-EOS unless validMetatypes.key?( metatype )
1244
+ Unknown metatype '#{metatype}' type '#{metatype.class}'
1245
+
1246
+ Known metatypes: #{validMetatypes.keys.join(',')}
1247
+
1248
+ EOS
1249
+
1250
+ metatype
1251
+ end
1252
+
1253
+
1254
+ # @!endgroup
1255
+
1256
+ # ------------------------------------------------------------------
1257
+ # @!group Load once
1258
+
1259
+ # Create constant (technical) base snippets, which do not have
1260
+ # any connection to TLA translation
1261
+ def loadFixedSnippets
1262
+ logger.debug "#{__method__}: starts" if logger.debug?
1263
+ TlaElementText.fixedSnippetSpecs.each do |snippetDef|
1264
+
1265
+ # :metatype, :appName, :specName, :body|:file
1266
+
1267
+ metatype = snippetDef[:metatype]
1268
+ appName = snippetDef[:appName]
1269
+ # nil --> allow sbuilder to generate name
1270
+ specName = snippetDef[:specName]
1271
+ logger.debug "#{__method__}: metatype=#{metatype}, appName=#{appName}" if logger.debug?
1272
+ raise "Must give :body, :file, :specName #{snippetDef}" unless snippetDef[:body] || snippetDef[:file] || snippetDef[:specName]
1273
+ text = snippetDef[:body] ? snippetDef[:body] : snippetDef[:file] ? File.read( File.join( File.dirname(__FILE__), "../../resources/", snippetDef[:file] )) : nil
1274
+ bodySpec = gen_free_text( text ) if text
1275
+ logger.debug "#{__method__}: text=#{text}" if logger.debug?
1276
+ addSnippet(
1277
+ Snippet.new( metatype, appName, bodySpec, specName, snippetDef[:comment] )
1278
+ )
1279
+
1280
+ end
1281
+ end
1282
+
1283
+ ##
1284
+ # Load Ethreum environment domains,snippet, and interfaces into
1285
+ # sbuilder context.
1286
+ def loadEthereumSnippets
1287
+ logger.debug "#{__method__}: starts" if logger.debug?
1288
+
1289
+ TlaElementText.ethereumSnippetSpecs.each do |spec|
1290
+ # run lamdba ':snipetType' and pass parameters ':definition'
1291
+ # instance_exec( spec[:definition], &SNIPPETLOADERS[spec[:snipetType]] )
1292
+ instance_exec( spec, &TlaElementText.getLoaderLamdba( spec ))
1293
+ end
1294
+ end
1295
+
1296
+ # ------------------------------------------------------------------
1297
+ # @!group Entry point & return the generated snippets
1298
+
1299
+
1300
+ # @return [Domains] domains Sexp, with domain -property
1301
+ def process_SourceUnit( exp )
1302
+ logger.info "#{__method__}: starting with '#{exp}"
1303
+
1304
+ # # gen_* methods delegated here
1305
+ # @elementGenerator = TlaElementGenerator.new( options )
1306
+ # @elementGenerator.setPartials( partials )
1307
+ # @elementGenerator.setValidMetatypes( validMetatypes )
1308
+
1309
+ # traverse tree and create Sexps for snippets & interfaces
1310
+ results = process_rest( exp )
1311
+ logger.debug "#{__method__}, results=#{results}" if logger.debug?
1312
+
1313
+ # add snippets to framework
1314
+ # loadFixedSnippets
1315
+ # loadEthereumSnippets
1316
+
1317
+ results
1318
+ end
1319
+
1320
+ def process_GlobalScope( exp )
1321
+ logger.debug "#{__method__}: starting with '#{exp}" if logger.debug?
1322
+
1323
+ # # Create snippets and interfaces for all ':FunctionDefinition' in top-scope
1324
+ # exp.globalScope.symbolDefinitions.
1325
+ # select { |globalExp| globalExp.sexp_type == :FunctionDefinition }.
1326
+ # each { |expFunction| loadGlobalFunction( expFunction ) }
1327
+ process_rest( exp )
1328
+
1329
+ s()
1330
+ end
1331
+
1332
+
1333
+ def process_ContractDefinition( exp )
1334
+ logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
1335
+
1336
+ # Need to know contract names in 'eth_code_hash'. Used for
1337
+ # example to define type invariant for valid contract types
1338
+ addDomainValue( Constants::DOMAIN_CODE_HASH, exp.name )
1339
+
1340
+ # add a (function) type definition for mapping variables
1341
+ exp.contractVariables.each do |variable|
1342
+ if variable.isMapping
1343
+ name = TlaElementText.compositeDefinitionName( exp, variable )
1344
+ mappings = variable.typeSexp.maps.inject( s() ) do |memo, elementaryType|
1345
+ memo << s( :parameter, elementaryType.typeName, elementaryType.domainReference.domainName )
1346
+ memo
1347
+ end
1348
+ addDefinition( Definition.define( name, mappings, :function ) )
1349
+ end
1350
+ end
1351
+
1352
+ # contract type definition with record fields corresponding contract variables
1353
+ contractVariableFields =
1354
+ exp.contractVariables.inject( s() ) do |memo,variable|
1355
+ if variable.isMapping
1356
+ # mapping variables refer function type definition created separately
1357
+ compositeName = TlaElementText.compositeDefinitionName( exp, variable )
1358
+ memo << s( :reference, variable.name, compositeName )
1359
+ else
1360
+ memo << s( :parameter, variable.name, variable.domainReference.domainName )
1361
+ end
1362
+ memo
1363
+ end
1364
+ addDefinition( Definition.define( exp.name, contractVariableFields) )
1365
+
1366
+ # wrap & recurse
1367
+ contracted( exp ) do
1368
+ process_rest( exp )
1369
+ end
1370
+
1371
+ end
1372
+
1373
+
1374
+ # Dummy operation - needed because default `process_skip` expect
1375
+ # that each 'process_' -function return either valid data or
1376
+ # empty array.
1377
+ def process_Mapping( exp )
1378
+ s()
1379
+ end
1380
+
1381
+
1382
+ def process_FunctionDefinition( exp )
1383
+ logger.debug "#{__method__}: start=#{exp}" if logger.debug?
1384
+
1385
+ # should set 'constructor'/'method-call'
1386
+ messageType = "unknow-function-definition"
1387
+
1388
+ # extract context
1389
+ contractExp = exp.enclosing_scope
1390
+ contractName = contractExp.name
1391
+ functionName = exp.name
1392
+ logger.info "#{__method__}: contractExp=#{contractExp}, contractName=#{contractName}, functionName=#{functionName}"
1393
+
1394
+ # context helpers
1395
+ isContract = contractExp.sexp_type == :ContractDefinition
1396
+ isConstructor = functionName == contractName ? true : false
1397
+
1398
+
1399
+ # processor context
1400
+ self.interfaceName = exp.interfaceName
1401
+ self.serviceName = gen_specname(:solidity_contract_function, interfaceName )
1402
+ # genenerator defined constants
1403
+ specname_serviceName = gen_service_name( contractName )
1404
+ self.specname_input_variable = gen_interface_input_variable_name( interfaceName )
1405
+
1406
+ # --------------------
1407
+ # Body block statements
1408
+
1409
+ # Contructor & normal function differ
1410
+ if isConstructor
1411
+ # constructor: default constructor + any user specific code
1412
+ messageType = true # 'constructor'
1413
+
1414
+ account_id_receiving_value = g_readNewAccountId
1415
+
1416
+ stmts =
1417
+ g_serviceProcedurePreamble( interfaceName, serviceName, specname_input_variable, isContract) +
1418
+
1419
+ g_construct( interfaceName, gen_str(contractName), contractExp, gen_nilElement ) +
1420
+
1421
+ g_serviceProcedureMoveValue( interfaceName, specname_input_variable, account_id_receiving_value ) +
1422
+ s(
1423
+ gen_traced_stmt(
1424
+ gen_labeled_stmt( gen_skip, gen_specname(:solidity_contract_function, interfaceName ), "_body"),
1425
+ s("SVC-exec@#{interfaceName}: process constructor body" )
1426
+ )
1427
+ ) +
1428
+ getFunctionBodyProcessor.process( exp.block ) +
1429
+ g_constructorReturn( interfaceName ) +
1430
+ g_serviceProcedureEpilogue( interfaceName, serviceName, specname_input_variable )
1431
+ content = "Constructor for contract #{interfaceName}'"
1432
+ else
1433
+ # 'normal' public method-call
1434
+ messageType = false # 'method-call'
1435
+ account_id_receiving_value = gen_record_field( specname_input_variable, Constants::FIELD_NAME_RECIPIENT )
1436
+ stmts =
1437
+ g_serviceProcedurePreamble( interfaceName, serviceName, specname_input_variable, isContract ) +
1438
+ # locals := x3_demo_input.recipient
1439
+ g_copyCurrentAccountIdToLocals(gen_record_field(specname_input_variable, Constants::FIELD_NAME_RECIPIENT )) +
1440
+ (
1441
+ isContract ?
1442
+ s(
1443
+ # conctract function called for non existing contract
1444
+ gen_if(
1445
+ gen_not( gen_operator_call(
1446
+ TlaElementText::FW_OP_ELEMENT_EXISTS,
1447
+ s(
1448
+ g_ethAccounts,
1449
+ gen_str( Constants::FIELD_NAME_ADDRESS),
1450
+ gen_record_field( specname_input_variable, Constants::FIELD_NAME_RECIPIENT ))
1451
+ )),
1452
+ g_construct( interfaceName, gen_nilElement, contractExp, account_id_receiving_value ) +
1453
+ g_serviceProcedureMoveValue(interfaceName, specname_input_variable, account_id_receiving_value )+
1454
+ g_end( interfaceName, serviceName, g_statusOk )
1455
+ )
1456
+ ) :
1457
+ # non contract ie. GlobalScope function called - no need to create contract
1458
+ s()
1459
+ ) +
1460
+ ( isContract ?
1461
+ s(
1462
+ # correct type
1463
+ gen_if(
1464
+ gen_bin_op(
1465
+ '\/',
1466
+ gen_not(g_RecipientContractExists(g_localCurrentAccountId)),
1467
+ gen_bin_op(
1468
+ "#",
1469
+ gen_str(contractName),
1470
+ gen_record_field(
1471
+ gen_operator_call(
1472
+ TlaElementText::FW_OP_GET_ELEMENT,
1473
+ s(
1474
+ g_ethAccounts_exec,
1475
+ gen_str( Constants::FIELD_NAME_ADDRESS ),
1476
+ g_localCurrentAccountId
1477
+ )
1478
+ ),
1479
+ Constants::FIELD_NAME_CODE_HASH),
1480
+ )),
1481
+ gen_traced_stmt( g_abort( interfaceName, serviceName ), "SVC-exec@#{interfaceName}: wrong type")
1482
+ )
1483
+ ) :
1484
+ s(gen_label)
1485
+ ) +
1486
+ g_serviceProcedureMoveValue( interfaceName, specname_input_variable, account_id_receiving_value ) +
1487
+ getFunctionBodyProcessor.process( exp.block ) +
1488
+ # s(process_rest( exp.statements )) +
1489
+ ( isContract ? g_serviceProcedureEpilogue( interfaceName, serviceName, specname_input_variable ) : s( gen_label )) +
1490
+ g_ServiceReturn( interfaceName )
1491
+
1492
+
1493
+ content = "Service procedure for #{interfaceName}"
1494
+ end
1495
+
1496
+ logger.debug "#{__method__} stmts=#{stmts}" if logger.debug?
1497
+
1498
+ # --------------------
1499
+ # Local variables
1500
+
1501
+ localVariablesExps = getFunctionLocalsProcessor.process( exp.block )
1502
+ localVariables =
1503
+ s(g_localCurrentAccountId) +
1504
+ localVariablesExps.map do |varDecl|
1505
+ g_locaVariableName( varDecl.name )
1506
+ end
1507
+ logger.info "#{__method__}: localVariables=#{localVariables} in in function #{exp.name}, localVariablesExps=#{localVariablesExps} "
1508
+
1509
+
1510
+ # --------------------
1511
+ # Create snippet for function procedure
1512
+
1513
+ addSnippet(
1514
+ Snippet.new(
1515
+ setMetatype(:solidity_contract_function), interfaceName,
1516
+ gen_service_procedure(
1517
+ gen_specname( :solidity_contract_function, interfaceName),
1518
+ interfaceName,
1519
+ localVariables,
1520
+ gen_stmts(serviceProcedureLabelStatements(
1521
+ stmts,
1522
+ gen_specname(:solidity_contract_function, interfaceName )
1523
+ )) # gen_stmts
1524
+ ), nil, content )
1525
+ )
1526
+
1527
+ # --------------------
1528
+ # Service entry macro for contract function (only if contract function)
1529
+
1530
+ if isContract
1531
+ # Create service entry macro for Solidity contract function (not for global scope)
1532
+ #
1533
+ # macro call 'contract_function' procedure if 'serviceExecValidity'
1534
+ service_formal_parameter_name = 'par'
1535
+ addSnippet(
1536
+ Snippet.new(
1537
+ setMetatype(Sbuilder::Facade::META_MODEL_SERVICE_IMPLEMENTATION), interfaceName,
1538
+ gen_macro(
1539
+ gen_specname( Sbuilder::Facade::META_MODEL_SERVICE_IMPLEMENTATION, interfaceName ),
1540
+ s(gen_plainname( service_formal_parameter_name )),
1541
+ gen_stmts(
1542
+ s(
1543
+ gen_traced_stmt(
1544
+
1545
+ # X_temp := <<X>>;
1546
+ gen_macro_call(
1547
+ TlaElementText::TLA_SERVICE_START,
1548
+ ),
1549
+ s( "TX-start@#{interfaceName}: push block state to stack" )
1550
+ ),
1551
+
1552
+ gen_traced_stmt(
1553
+ gen_if(
1554
+ g_serviceExecValidity(service_formal_parameter_name, isConstructor),
1555
+ g_serviceExec(interfaceName, service_formal_parameter_name),
1556
+ ),
1557
+ s( "TX-start@#{interfaceName}: check pre-conditions",
1558
+ "1) sender account exists",
1559
+ "2) sender balance>= #{TlaElementText::ETH_OPEATOR_UP_FRONT_COST}",
1560
+ !isConstructor ? "3) receive account non nil" : ""
1561
+ )
1562
+ )
1563
+ ) # s
1564
+ )
1565
+ ),
1566
+ nil, # specname
1567
+ "Service entry for #{interfaceName}"
1568
+ ))
1569
+
1570
+ # Map 'service-completion' on interface to fixed specName
1571
+ # 'TLA_SERVICE_COMPLETION', which implements transaction
1572
+ # semantics in TLA code.
1573
+ addSnippet(
1574
+ Snippet.new(
1575
+ setMetatype(Sbuilder::Facade::META_MODEL_SERVICE_COMPLETION), interfaceName, nil,
1576
+ TlaElementText::TLA_SERVICE_COMPLETION, # specname
1577
+ "Macro called when #{interfaceName} completes"
1578
+ ))
1579
+
1580
+ end # if contract
1581
+
1582
+ # --------------------
1583
+ # Function dispatcher
1584
+
1585
+ # check inheritance hierarchy && create dispatcher macro
1586
+ dispatcherName, methodsToDispatch = findMethodsToDispatch( exp )
1587
+ addDispatcherSnippet( dispatcherName, methodsToDispatch )
1588
+
1589
+ # --------------------
1590
+ # Interface
1591
+
1592
+ # Define an interface for a function +functionName+ in
1593
+ # contract +contractName+. Contructor name f(+contractName+),
1594
+ # non constructor name f(+contractName+, +functionName+)
1595
+ #
1596
+ #
1597
+ addInterface(
1598
+ Interface.define(
1599
+ contractName,
1600
+ isConstructor ? "" : functionName,
1601
+ # request parameters + common parameters
1602
+ exp.parameterList.parameters.map do |param|
1603
+ raise "Domain reference not resolved on #{param}" unless param.domainReference
1604
+ s( :parameter, param.name, param.domainReference.name )
1605
+ end, # + TlaElementText.commonMsgProperties( 'request', messageType ),
1606
+ # response paremeters + common parameters
1607
+ exp.returnList.parameters.map do |param|
1608
+ raise "Domain reference not resolved on #{param}" unless param.domainReference
1609
+ s( :parameter, param.name, param.domainReference.name )
1610
+ end + TlaElementText.commonMsgProperties( 'response', messageType ),
1611
+ )) # addInterface
1612
+
1613
+ # clear processor context
1614
+ self.specname_input_variable = nil
1615
+ self.interfaceName = nil
1616
+ self.serviceName = nil
1617
+ s()
1618
+ end
1619
+
1620
+ # @!endgroup
1621
+ # ------------------------------------------------------------------
1622
+ # @!group Helpers to create service procesure
1623
+
1624
+
1625
+ # Create snippet with 'dispatcherName'. Uses addSnippet method
1626
+ # in top level tla processor
1627
+ #
1628
+ #
1629
+ # @param [Hash:Array] methodsToDispatch
1630
+ # @option methodsToDispatch [:Symbol] :contracts array of ContractDefinition
1631
+ # @option methodsToDispatch [:Symbol] :function FunctionDefinition
1632
+
1633
+ private def createDispatcherSnippet( dispatcherName, methodsToDispatch )
1634
+
1635
+ end
1636
+
1637
+ # Find data structure to manage dispatching of 'funtionSexp'.
1638
+ #
1639
+ # @param [FunctionDefinition] funtionSexp
1640
+ #
1641
+ # @return [String], [Hash:Array] dispatcherName, methodNameImplementations
1642
+ #
1643
+ # @option methodNameImplementations [:Symbol] :contracts array
1644
+ # of ContractDefinition serviced by :function
1645
+ #
1646
+ # @option methodNameImplementations [:Symbol] :function
1647
+ # FunctionDefinition
1648
+ def findMethodsToDispatch( funtionSexp )
1649
+ logger.debug "#{__method__}: funtionSexp=#{funtionSexp}" if logger.debug?
1650
+ raise "Invalid type #{funtionSexp.sexp_type} in #{funtionSexp}" unless funtionSexp.sexp_type == :FunctionDefinition
1651
+
1652
+ methodName = funtionSexp.name
1653
+ currentContract = funtionSexp.contractDefinition
1654
+
1655
+ logger.info "#{__method__}: create dispatcher data structure for #{currentContract.name}.#{methodName}"
1656
+
1657
+ # inhertitanceHierarchy is a hash mapping typeName ->
1658
+ # [ContractDefinition:Array] for each contract in inherintance
1659
+ # hierarhcy of 'currentContract'
1660
+ inhertitanceHierarchy = currentContract.rootSymbol.allDerivedTypes( InheritanceSort.new )
1661
+ logger.debug "#{__method__}: inhertitanceHierarchy=#{inhertitanceHierarchy.inject({}) { |m,(k,v)| m[k] = v.map { |a| a.typeName}; m }}" if logger.debug?
1662
+
1663
+ # topologically sorted functionNames in inhertitance hierarchy
1664
+ sortedInheritanceHierarchy = inhertitanceHierarchy.tsort
1665
+ logger.debug "#{__method__}: sortedInheritanceHierarchy=#{sortedInheritanceHierarchy.join(',')}" if logger.debug?
1666
+
1667
+ dispatcherName = "#{currentContract.rootSymbol.name}_#{methodName}"
1668
+
1669
+ seenContracts = {}
1670
+ # iterate 'contractNames' in inhiritance order
1671
+ methodNameImplementations = sortedInheritanceHierarchy.map do |contractName|
1672
+ # contract for 'contractName' from 'inhertitanceHierarchy'
1673
+ contract = inhertitanceHierarchy[contractName].select { |inheritsContractDefintition| inheritsContractDefintition.typeName == contractName }.first
1674
+ {
1675
+ :contract => contract,
1676
+ :contracts => inhertitanceHierarchy[contractName],
1677
+ :function => contract.resolve( methodName ),
1678
+ }
1679
+ end.select do |methodNameImplementation|
1680
+ # accept only contracts, which implementing 'methodName'
1681
+ methodNameImplementation[:contract].getSymbol( methodName )
1682
+ end.map do |methodNameImplementation|
1683
+ # use first entry for a contract
1684
+ methodNameImplementation[:contracts] = methodNameImplementation[:contracts].reject { |c| seenContracts.key?( c.name ) }
1685
+ seenContracts[methodNameImplementation[:contract].name] = true
1686
+ methodNameImplementation
1687
+ end
1688
+
1689
+ raise "Could not find any implementation for '#{methodName}' in #{sortedInheritanceHierarchy.join(',')}" unless methodNameImplementations.any?
1690
+
1691
+ # logger.debug "#{__method__}: methodNameImplementations=#{methodNameImplementations.join(',')}" if logger.debug?
1692
+
1693
+ logger.info "#{__method__}: implementing dispatcherName=#{dispatcherName}, methodName '#{methodName}' =#{methodNameImplementations.map { |i| i[:function].interfaceName + '(' + i[:contracts].map{|c| c.name }.join(',')+ ')' }.join('; ')}" if logger.debug?
1694
+ return dispatcherName, methodNameImplementations
1695
+ end
1696
+
1697
+ # Create macro 'dispatcherName' to dispatch 'methodsToDispatch',
1698
+ # and pass it to 'addSnippets'. Optimization: if array
1699
+ # 'methodsToDispatch' length is 0, no need dispatcher macro is
1700
+ # made (method dispatching is replaced with direct method call,
1701
+ # and accessing response status/data can are simple calls.
1702
+ #
1703
+ # @param [Hash:Array] methodsToDispatch
1704
+ # @option methodsToDispatch [:Symbol] :contracts array ContractDefinition
1705
+ # @option methodsToDispatch [:Symbol] :function FunctionDefinition
1706
+
1707
+ def addDispatcherSnippet( dispatcherName, methodsToDispatch )
1708
+
1709
+ # optimization
1710
+ return if methodsToDispatch.length == 1
1711
+
1712
+ parameterName = 'rec'
1713
+
1714
+
1715
+ bodyStmts =
1716
+ s(
1717
+ # init all responses
1718
+ gen_assign( TlaElementText::OP_INFRA_RESPONSES, TlaElementText::OP_INFRA_STATUS_INIT ),
1719
+ generateMethodDispatch( methodsToDispatch, parameterName ),
1720
+ )
1721
+
1722
+ body =
1723
+ gen_macro(
1724
+ g_dispatcher(dispatcherName), parameterName,
1725
+ gen_stmts( bodyStmts )
1726
+ )
1727
+
1728
+ # define snippet, and add it main tla processor
1729
+ snippet = Snippet.new( g_dispatcherMeta, dispatcherName, body )
1730
+ addSnippet( snippet )
1731
+ end
1732
+
1733
+ #
1734
+ # @param [Hash:Array] methodsToDispatch
1735
+ # @option methodsToDispatch [:Symbol] :contracts array ContractDefinition
1736
+ # @option methodsToDispatch [:Symbol] :function FunctionDefinition
1737
+ #
1738
+ # @param [sTla] parameterName name of input parameter to the snippet
1739
+ #
1740
+ # @return [sTla:Array] dispatch statement if the
1741
+ #
1742
+ private def generateMethodDispatch( methodsToDispatch, parameterName )
1743
+
1744
+ # process first here, tail recursion on rest
1745
+ contracts = methodsToDispatch.first[:contracts]
1746
+ function = methodsToDispatch.first[:function]
1747
+
1748
+ if_condition = g_dispatcherCondition( contracts, parameterName )
1749
+
1750
+ if_block = s(
1751
+ gen_procedure_call( gen_specname( :solidity_contract_function, function.interfaceName ), parameterName )
1752
+ )
1753
+
1754
+ # tail recursion on 'methodsToDispatch'
1755
+ if methodsToDispatch.length == 1
1756
+ gen_if( if_condition, if_block, gen_assert( "FALSE", "Unkwon interface" ))
1757
+ else
1758
+ gen_if( if_condition, if_block, generateMethodDispatch( methodsToDispatch[1..-1], parameterName))
1759
+ end
1760
+ end
1761
+
1762
+
1763
+ ##
1764
+ # Add label #{labelPrefix}_#{index} to statements in 'stmts' array
1765
+ # recursively in 'labelBlockStatements'
1766
+ #
1767
+ # Add label when:
1768
+ # - for the first statement in the list (start a procedure)
1769
+ # - previous statement (sexp_type) requires a label
1770
+ # - recurse down to :labelBlockStatements statements
1771
+ #
1772
+ private def serviceProcedureLabelStatements( stmts, labelPrefix )
1773
+ stmts, index = serviceProcedureLabelStatementsWorker( stmts, labelPrefix, 0, 0, true )
1774
+ stmts
1775
+ end
1776
+
1777
+ ##
1778
+ # @return [Array, index] stmts as passed as input, index incremented
1779
+ private def serviceProcedureLabelStatementsWorker( stmts, labelPrefix, index, level, labelFirst )
1780
+ stmts.each_with_index do |stmt,i|
1781
+
1782
+ # prev stamement in current block
1783
+ prevStmt = stmts[i-1] if i > 0
1784
+ # puts "#{' ' * level } - #{level}: #{labelPrefix[1][:specname][:appName]}-#{index} #{stmts[i][0]}"
1785
+ if labelFirst || !prevStmt.nil? && requiresLabelAfter( prevStmt )
1786
+ stmts[i] = gen_labeled_stmt( stmt, labelPrefix, index )
1787
+ # puts "#{' ' * level } - #{level}: --> label #{labelPrefix[1][:specname][:appName]}-#{index} #{stmt[0]}"
1788
+ index += 1
1789
+ end
1790
+ labelFirst = false
1791
+
1792
+ # then & while>?
1793
+ if stmt.respond_to?( :labelBlockStatements )
1794
+ # recursion
1795
+ s, index = serviceProcedureLabelStatementsWorker( stmt.labelBlockStatements, labelPrefix, index, level+1, false )
1796
+ end
1797
+
1798
+ # else block
1799
+ if stmt.respond_to?( :labelBlockStatementsElse )
1800
+ # recursion
1801
+ s, index = serviceProcedureLabelStatementsWorker( stmt.labelBlockStatementsElse, labelPrefix, index, level+1, false )
1802
+ end
1803
+
1804
+
1805
+ end
1806
+ return stmts, index
1807
+ end
1808
+
1809
+ # @!endgroup
1810
+
1811
+
1812
+ # ------------------------------------------------------------------
1813
+ # @!group Specialized processeros
1814
+
1815
+ #
1816
+ #
1817
+ class FunctionBodyProcessor < SexpProcessorTlaRoot
1818
+
1819
+ def initialize( tlaProcessor, elementGenerator, options )
1820
+ super( options )
1821
+ @tlaProcessor = tlaProcessor
1822
+ @elementGenerator = elementGenerator
1823
+
1824
+ # initially no variables assigned within this body
1825
+ self.variablesAssigned = {}
1826
+ end
1827
+
1828
+ # @attr [SexpProcessorTla] tlaProcessor collecting snippets
1829
+ attr_accessor :tlaProcessor
1830
+
1831
+ # @attr [Contract] currentContract which currently processing
1832
+ attr_accessor :currentContract
1833
+
1834
+ # @attr [STla] specname_input_variable
1835
+ attr_accessor :specname_input_variable
1836
+
1837
+ # @attr [String] interfaceName e.g. Demo(send)
1838
+ attr_accessor :interfaceName
1839
+
1840
+ # @attr [String] serviceName
1841
+ attr_accessor :serviceName
1842
+
1843
+
1844
+ # ------------------------------------------------------------------
1845
+ # @!group Access specialized sexp processor
1846
+
1847
+ def getFunctionBodyProcessor
1848
+ proc = FunctionBodyProcessor.new( tlaProcessor, elementGenerator, options )
1849
+ # Move context to processor
1850
+ proc.interfaceName = interfaceName
1851
+ proc.serviceName = serviceName
1852
+ proc.currentContract = currentContract
1853
+ proc.specname_input_variable = specname_input_variable
1854
+ proc
1855
+ end
1856
+
1857
+ def getRvalProcessor
1858
+ proc = RvalExprProcessor.new( tlaProcessor, elementGenerator,options)
1859
+ proc.interfaceName = interfaceName
1860
+ proc.specname_input_variable = specname_input_variable
1861
+ proc
1862
+ end
1863
+
1864
+ def getFunctionCallProcessor( actualParameters )
1865
+ proc = FunctionCallProcessor.new( tlaProcessor, elementGenerator, options )
1866
+ # Move context to processeos
1867
+ proc.interfaceName = interfaceName
1868
+ proc.serviceName = serviceName
1869
+ proc.currentContract = currentContract
1870
+ proc.actualParameters = actualParameters
1871
+ proc.specname_input_variable = specname_input_variable
1872
+ proc
1873
+ end
1874
+
1875
+ def getLvalProcessor( rval )
1876
+ proc = LvalExprProcessor.new( tlaProcessor, elementGenerator, options )
1877
+ proc.rval = rval
1878
+ proc.bodyProcessor = self
1879
+ proc.interfaceName = interfaceName
1880
+ proc.specname_input_variable = specname_input_variable
1881
+ proc
1882
+ end
1883
+
1884
+ # @!endgroup
1885
+
1886
+ # ------------------------------------------------------------------
1887
+ # @!group Lval interface
1888
+
1889
+ # @attr [Hash] variablesAssigned
1890
+ attr_accessor :variablesAssigned
1891
+
1892
+ # Maintain list of variables assigned in lval processes. If
1893
+ # assigining twice to same variable instruct to create a
1894
+ # label, and reset variables being assigned
1895
+ #
1896
+ # @return [Boolean] true if lval processor should label
1897
+ # assigment statement
1898
+ def shouldUseLabelToAssignTo( variableName )
1899
+ if variablesAssigned.key?( variableName )
1900
+ self.variablesAssigned = {
1901
+ variableName => true
1902
+ }
1903
+ ret = true
1904
+ else
1905
+ self.variablesAssigned[variableName] = true
1906
+ ret = false
1907
+ end
1908
+ logger.debug "#{__method__}: variableName: #{variableName} should have label=#{ret}" if logger.debug?
1909
+ ret
1910
+ end
1911
+
1912
+ # @!endgroup
1913
+ # ------------------------------------------------------------------
1914
+ # @!group Process sexp nodes
1915
+
1916
+ ##
1917
+ # Concatenate statements together
1918
+ def process_Block( exp )
1919
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
1920
+
1921
+ # s( s(for statement) s())
1922
+ stmsArra = process_rest( exp )
1923
+ stmsArra.flatten(1)
1924
+
1925
+ end
1926
+
1927
+ def process_IfStatement( exp )
1928
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
1929
+
1930
+ stmts = s()
1931
+
1932
+ # condition
1933
+ condition = getRvalProcessor.process( exp.condition )
1934
+ logger.debug "#{__method__}: condition=#{condition}" if logger.debug?
1935
+ thenBlock = getFunctionBodyProcessor.process( exp.then_branch )
1936
+ logger.debug "#{__method__}: thenBlock=#{thenBlock}" if logger.debug?
1937
+ elseBlock = getFunctionBodyProcessor.process( exp.else_branch ) if exp.else_branch
1938
+
1939
+ stmts << gen_if( condition, thenBlock, elseBlock )
1940
+ logger.debug "#{__method__}: stmts=#{stmts}" if logger.debug?
1941
+ stmts
1942
+ end
1943
+
1944
+
1945
+ def process_Throw( exp )
1946
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
1947
+ stmts = s()
1948
+
1949
+ logger.info "#{__method__}: abort-interfaceName=#{interfaceName}"
1950
+ # stmts << gen_traced_stmt(
1951
+ # g_updateReturnStatus( interfaceName, g_statusFail ),
1952
+ # s( "SVC-exec@#{interfaceName}: return status", g_statusFail )
1953
+ # )
1954
+
1955
+ stmts << g_fail( interfaceName, serviceName )
1956
+ # stmts << gen_traced_stmt(
1957
+ # g_fail( interfaceName, serviceName ),
1958
+ # s( "SVC-exec@#{interfaceName}: throw goes to fail label which set FALSE return" )
1959
+ # )
1960
+ stmts
1961
+ end
1962
+
1963
+ # Within Block inteprete 'VariableDefinitionStatement' like 'Assignment'
1964
+ def process_VariableDefinitionStatement( exp )
1965
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
1966
+ rval = getRvalProcessor.process(exp.rval)
1967
+ logger.debug "#{__method__}: assigning-rval=#{rval}" if logger.debug?
1968
+
1969
+ stmts = getLvalProcessor(rval).process( exp.lval )
1970
+
1971
+ logger.debug "#{__method__}: stmts=#{stmts}" if logger.debug?
1972
+ stmts
1973
+
1974
+ end
1975
+
1976
+ # @return [Sexp] s() wrapping statements for assignments
1977
+ def process_Assignment( exp )
1978
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
1979
+ raise "Operator '#{exp.operator}' not supported in #{exp}" unless exp.operator == '='
1980
+
1981
+ # find rval
1982
+ rval = getRvalProcessor.process(exp.rval)
1983
+ logger.debug "#{__method__}: assigning-rval=#{rval}" if logger.debug?
1984
+
1985
+
1986
+ stmts = getLvalProcessor(rval).process( exp.lval )
1987
+ # stmts << LvalExprProcessor.new(elementGenerator, options).process( exp.rval )
1988
+
1989
+ logger.debug "#{__method__}: stmts=#{stmts}" if logger.debug?
1990
+ stmts
1991
+ end
1992
+
1993
+ ##
1994
+ # In :Functioncall 'functionExpression' is either
1995
+ # ':NewExpression' or ':MemberAccess'. tla-statemts for these
1996
+ # two cases created using FunctionCallProcessor.
1997
+ def process_FunctionCall( exp )
1998
+ logger.debug "#{__method__}: start #{exp}" if logger.debug?
1999
+
2000
+ # find acual parameters
2001
+
2002
+ logger.debug "#{__method__}: exp.actualParameters=#{exp.actualParameters}" if logger.debug?
2003
+ actualParameters = exp.actualParameters.map.with_index do |expression,i|
2004
+ # returnVariableDecl = exp.functionReturnParameter( i )
2005
+ # retExpr = getRvalProcessor.process( expression )
2006
+ # gen_bin_op( "|->", returnVariableDecl.name, retExpr )
2007
+ getRvalProcessor.process( expression )
2008
+ end
2009
+ logger.debug "#{__method__}: rval-actualParameters=#{actualParameters}" if logger.debug?
2010
+
2011
+
2012
+ # create call
2013
+
2014
+ logger.debug "#{__method__}: exp.functionExpression=#{exp.functionExpression}" if logger.debug?
2015
+ # callStmts = self.process( exp.functionExpression )
2016
+ callStmts = getFunctionCallProcessor(actualParameters).process( exp.functionExpression )
2017
+ logger.info "#{__method__}, callStmts=#{callStmts}"
2018
+ callStmts
2019
+ end
2020
+
2021
+ def process_FunctionCallReadReturn( exp )
2022
+ logger.debug "#{__method__}: start #{exp}" if logger.debug?
2023
+ # no operation because return wraps in return statment &&
2024
+ # expression original function call, which is split up in
2025
+ # canonization phase
2026
+ s()
2027
+ end
2028
+
2029
+ # Return 'status' and 'return.expressions' from procedure modeling 'inferfaceName'
2030
+ def process_Return( exp )
2031
+ stmts = s()
2032
+ retRvals = exp.expressions.map.with_index do |expression,i|
2033
+ # returnVariableDecl = exp.functionReturnParameter( i )
2034
+ # retExpr = getRvalProcessor.process( expression )
2035
+ # gen_bin_op( "|->", returnVariableDecl.name, retExpr )
2036
+ getRvalProcessor.process( expression )
2037
+ end
2038
+ # using 'g_statusFail' here because success is flagged only
2039
+ # after all statements processed successfully
2040
+ stmts << gen_operator_call(
2041
+ TlaElementText::OP_INFRA_RETURN,
2042
+ s( gen_str(interfaceName), g_statusFail, retRvals.first ) )
2043
+ # assign to function return list
2044
+ # stmts << gen_debug_msg( "JUST from getter" )
2045
+ stmts
2046
+ end
2047
+
2048
+ # @!endgroup
2049
+
2050
+
2051
+ end # FunctionBody
2052
+
2053
+ # Resolve local variable in function
2054
+ #
2055
+ class FunctionLocalsProcessor < SexpProcessorTlaRoot
2056
+
2057
+ # @attr [VariableDeclaration:Array] localVariables
2058
+ attr_accessor :localVariables
2059
+
2060
+ # ------------------------------------------------------------------
2061
+ # @!group Construct & configure
2062
+
2063
+ def initialize( elementGenerator, options )
2064
+ super( options )
2065
+ @elementGenerator = elementGenerator
2066
+
2067
+ # ignore unknown nodes
2068
+ self.default_method = :process_none
2069
+
2070
+ self.localVariables = s()
2071
+ end
2072
+
2073
+ # @!endgroup
2074
+
2075
+
2076
+ # ------------------------------------------------------------------
2077
+ # @!group Wrap scopes
2078
+
2079
+ # @attr [VariableDefinitionStatement] currentDeffi
2080
+ attr_accessor :currentDeffi
2081
+
2082
+ def deffed( expDeffi )
2083
+ prevDeffi = currentDeffi
2084
+ self.currentDeffi = expDeffi
2085
+ begin
2086
+ yield
2087
+ ensure
2088
+ self.currentDeffi = prevDeffi
2089
+ end
2090
+
2091
+ end
2092
+
2093
+ # @!endgroup
2094
+
2095
+ # ------------------------------------------------------------------
2096
+ # @!group Process nodes
2097
+
2098
+ def process_Block( exp )
2099
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2100
+ process_rest( exp )
2101
+ localVariables
2102
+ end
2103
+
2104
+ def process_VariableDefinitionStatement( exp )
2105
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2106
+ deffed( exp ) do
2107
+ process_rest( exp )
2108
+ end
2109
+ s()
2110
+ end
2111
+
2112
+ def process_VariableDeclaration( exp )
2113
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2114
+
2115
+ # localVariable declaration if within VariableDefinitionStatement
2116
+ localVariables << exp if currentDeffi
2117
+
2118
+ s()
2119
+ end
2120
+
2121
+ # @!endgroup
2122
+
2123
+ end # FunctionLocalsProcessor
2124
+
2125
+ class FunctionCallProcessor < SexpProcessorTlaRoot
2126
+
2127
+ # ----------
2128
+ # set during initialize -phase
2129
+
2130
+ # @attr [SexpProcessorTla] tlaProcessor collecting snippets
2131
+ attr_accessor :tlaProcessor
2132
+
2133
+ # @attr [Contract] currentContract which currently processing
2134
+ attr_accessor :currentContract
2135
+
2136
+ # @attr [STla] specname_input_variable
2137
+ attr_accessor :specname_input_variable
2138
+
2139
+ # @attr [String] interfaceName
2140
+ attr_accessor :interfaceName
2141
+
2142
+ # @attr [String] serviceName
2143
+ attr_accessor :serviceName
2144
+
2145
+ # @attr [s(rval, rval)] actualParameters
2146
+ attr_accessor :actualParameters
2147
+
2148
+ # ----------
2149
+ # properties set process -method in this class
2150
+
2151
+ # @attr [String:Array] functionOnFunctionMembers
2152
+ attr_accessor :functionOnFunctionMembers
2153
+
2154
+ # @attr [String:Array] functionOnFunctionMembers
2155
+ attr_accessor :functionOnFunctionValues
2156
+
2157
+ def initialize( tlaProcessor, elementGenerator, options )
2158
+ super( options )
2159
+ @tlaProcessor = tlaProcessor
2160
+ @elementGenerator = elementGenerator
2161
+ self.functionOnFunctionMembers = []
2162
+ self.functionOnFunctionValues = []
2163
+ end
2164
+
2165
+ def_delegators :tlaProcessor, # top level tla -processes
2166
+ :setMetatype,
2167
+ :findMethodsToDispatch
2168
+
2169
+ # ------------------------------------------------------------------
2170
+ # @!group Access specialized sexp processor
2171
+
2172
+ def getRvalProcessor
2173
+ proc = RvalExprProcessor.new(tlaProcessor, elementGenerator,options)
2174
+ proc.interfaceName = interfaceName
2175
+ proc.specname_input_variable = specname_input_variable
2176
+ proc
2177
+ end
2178
+
2179
+ # @!endgroup
2180
+
2181
+ # ------------------------------------------------------------------
2182
+ # @!group Process sexp nodes
2183
+
2184
+ # functionOnFunction case here
2185
+ def process_FunctionCall( exp )
2186
+ logger.debug "#{__method__}: #{exp} in functionOnFunction" if logger.debug?
2187
+
2188
+ actualParameters = exp.actualParameters.map.with_index do |expression,i|
2189
+ # returnVariableDecl = exp.functionReturnParameter( i )
2190
+ # retExpr = getRvalProcessor.process( expression )
2191
+ # gen_bin_op( "|->", returnVariableDecl.name, retExpr )
2192
+ getRvalProcessor.process( expression )
2193
+ end
2194
+ raise "Expect function on function actual parameters to equal 1, was #{actualParameters.length} in #{actualParameters.join(',')}" unless actualParameters.length == 1
2195
+ self.functionOnFunctionValues << actualParameters.first
2196
+ logger.debug "#{__method__}: func-on-func-rval-actualParameters=#{actualParameters}" if logger.debug?
2197
+
2198
+ process( exp.functionExpression )
2199
+
2200
+ end
2201
+
2202
+ ##
2203
+ # We ended here to process the first 'MemberAccess' sexp in
2204
+ # s(:FunctionCall, "bool", s(:MemberAccess, "send",
2205
+ # s(:MemberAccess, "sender", s(:Identifier, "msg", "msg"))),
2206
+ # s(:Literal, "int_const 1", "1"))))
2207
+ #
2208
+ # @see process_NewExpression
2209
+ def process_MemberAccess( exp )
2210
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2211
+
2212
+ ret = nil
2213
+ if exp.isFunctionOnFunction
2214
+ # AST in 'function on function' case, e.g. demo1.set.value(20)(
2215
+ # 6 ) like s(:MemberAccess, "value", s(:MemberAccess, "set",
2216
+ # s(:Identifier, "contract Demo1", "demo1")))
2217
+ #
2218
+ # we are processing s(:MemberAccess, "value", collect
2219
+ # names so that we can consume actualParameters before
2220
+ # binding formal parameters
2221
+ self.functionOnFunctionMembers << exp.member_name
2222
+ logger.info "#{__method__}: collected functionOnFunctionMembers=#{functionOnFunctionMembers.join(',')}"
2223
+ ret = process( exp.memberStruct )
2224
+ else
2225
+ # implement function calll
2226
+ ret = memberAccessAsFunctionCall( exp )
2227
+ end
2228
+ ret
2229
+ end
2230
+
2231
+ # called by process_MemberAccess to create statements to make
2232
+ # a function call
2233
+ #
2234
+ # @param [MemberAccess] exp in process_MemberAccess
2235
+ private def memberAccessAsFunctionCall( exp )
2236
+
2237
+ # context
2238
+ functionCalled = exp.declationRef
2239
+ called_interfaceName = functionCalled.interfaceName
2240
+ # send gets message 'value' as actual parameter
2241
+ valueAsActualParameter = [Constants::SOL_SEND].include?( functionCalled.name )
2242
+
2243
+ recipientSexp = exp.memberStruct
2244
+ logger.debug "#{__method__}: dispatch a call to called=#{called_interfaceName}->#{functionCalled}/ recipient #{recipientSexp}}" if logger.debug?
2245
+ logger.debug "#{__method__}: functionOnFunctionMembers=#{functionOnFunctionMembers}" if logger.debug?
2246
+
2247
+ # bind formal parameters with actual parameters in
2248
+ # '@actualParameters'. 1) find [VariableDeclation] of formal
2249
+ # parameters 2) bind actual parmeters in '@actualParameters'
2250
+ formalParameters = functionFormalParameters( functionCalled, false, valueAsActualParameter )
2251
+
2252
+ # bind formal parameters with '@actualParameters'
2253
+ formalParameterBinds = formalParameters.any? && formalParameters.map.with_index do |parameter,i|
2254
+ gen_record_field_definition( parameter.name, actualParameters[i] )
2255
+ end
2256
+ formalParameterBinds = s() unless formalParameterBinds
2257
+ logger.info "#{__method__}: functionCalled: #{functionCalled.name} formal parameters=#{ formalParameters.map.with_index{ |p,i| p.name + '->' + actualParameters[i].inspect } }"
2258
+
2259
+
2260
+ # generate code to access recipient
2261
+ recipientTla = getRvalProcessor.process( recipientSexp )
2262
+ logger.debug "#{__method__}: recipientTla=#{recipientTla}" if logger.debug?
2263
+
2264
+ # find 'callValue' i.e. what is assigned to 'value' field 1)
2265
+ # send function gets initial value as actual parameter 2) in
2266
+ # functionOnFunction e.g. f.gas(2).value(20)() search
2267
+ # 'functionOnFunctionMembers' and 'functionOnFunctionValues'
2268
+ # to find value 3) use g_zero
2269
+
2270
+ if valueAsActualParameter
2271
+ # send function
2272
+ callValue = s()
2273
+ else
2274
+ # 3) default g_zero
2275
+ callValueInit = g_zero
2276
+ functionOnFunctionMembers.each_with_index do |functionOnFunctionMember,i|
2277
+ # 2) functionOnFunction case
2278
+ callValueInit = functionOnFunctionValues[i] if functionOnFunctionMember == Constants::FIELD_NAME_VALUE
2279
+ end
2280
+ callValue = s( gen_record_field_definition( Constants::FIELD_NAME_VALUE, callValueInit ))
2281
+ end
2282
+
2283
+ # actual call
2284
+ logger.debug "#{__method__}: callValue=#{callValue.join(',')}, formalParameterBinds=#{formalParameterBinds.join(',')}" if logger.debug?
2285
+
2286
+ # call procedure with formal paremeters bound + common msg parameters set
2287
+ parameters = gen_record_definition(
2288
+ formalParameterBinds +
2289
+ g_commonMsgParameters( specname_input_variable, recipientTla ) +
2290
+ callValue
2291
+ )
2292
+
2293
+
2294
+
2295
+ stmts = s()
2296
+ # goto abort unless called recipientTla address exist
2297
+ stmts << gen_if(
2298
+ gen_not( g_RecipientContractExists( recipientTla )),
2299
+ gen_traced_stmt(
2300
+ g_abort(interfaceName, serviceName),
2301
+ "SVC-exec@#{interfaceName}: abort because called address of contract does not exist" )
2302
+
2303
+ )
2304
+
2305
+ g_dispatch_call( exp.declationRef, parameters, interfaceName, serviceName ).each { |s| stmts << s }
2306
+ logger.debug "#{__method__}: stmts=#{stmts}" if logger.debug?
2307
+
2308
+ stmts
2309
+
2310
+ end
2311
+
2312
+ ##
2313
+ # We ended here to process 'NewExpression' in s(:FunctionCall,
2314
+ # "contract Demo1", s(:NewExpression, "function () returns
2315
+ # (contract Demo1)", s(:UserDefinedTypeName, "Demo1")))'
2316
+ #
2317
+ # @see process_MemberAccess
2318
+ def process_NewExpression( exp )
2319
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2320
+ contructorFunctionDeclaration = exp.declationRef
2321
+ logger.debug "#{__method__}: contructorFunctionDeclaration=#{contructorFunctionDeclaration} for #{exp.type}" if logger.debug?
2322
+
2323
+ called_interfaceName = contructorFunctionDeclaration.interfaceName
2324
+ called = gen_specname( :solidity_contract_function, called_interfaceName )
2325
+ logger.debug "#{__method__}: called= #{called}" if logger.debug?
2326
+ s(
2327
+ gen_procedure_call(
2328
+ called,
2329
+ gen_record_definition(
2330
+ g_procCallAddresses( specname_input_variable ) +
2331
+ g_procCallValue)
2332
+ ),
2333
+ # abort checked without solidity dispatcher
2334
+ g_abortCheckIncall( interfaceName, serviceName, called_interfaceName )
2335
+ )
2336
+ end
2337
+
2338
+ # @!endgroup
2339
+
2340
+ # ------------------------------------------------------------------
2341
+ # @!group FunctionCallProcessor utilities
2342
+
2343
+
2344
+
2345
+ # Abort 'interfaceName'/'serviceName' for if any of
2346
+ # 'methodsToDispatch' aborts. Actually only one dispatched.
2347
+ # TODO remove
2348
+ # def addDispatcherStatusCheck( methodsToDispatch, interfaceName, serviceName )
2349
+ # if_condition = gen_FALSE
2350
+
2351
+ # methodsToDispatch.map do |methodToDispatch|
2352
+ # methodToDispatch[:function].interfaceName
2353
+ # end.uniq.each do |interfaceName|
2354
+ # if_condition =
2355
+ # gen_bin_op( '\\/',
2356
+ # if_condition,
2357
+ # g_abortedInCall( interfaceName )
2358
+ # )
2359
+ # end
2360
+ # if_block = s( g_abort( interfaceName, serviceName ))
2361
+ # gen_if( if_condition, if_block )
2362
+ # end
2363
+
2364
+ ##
2365
+ # Formal parameters are all request parameters expect 'commonMsgproperties'
2366
+ # An expection 'send' operation formal parameter include 'value'.
2367
+ #
2368
+ # @param [Boolean] isConstructor
2369
+ #
2370
+ # @return [s(:VariableDeclaration)] formal parameters in 'functionSexp'
2371
+ private def functionFormalParameters( functionSexp, isConstructor, valueAsActualParameter=false )
2372
+ logger.debug "#{__method__}: #{functionSexp}" if logger.debug?
2373
+
2374
+ commonMsgParameters =
2375
+ TlaElementText.commonMsgProperties( 'request', isConstructor ).map { |sexp| sexp[1] }
2376
+ logger.debug "#{__method__}: commonMsgParameters=#{commonMsgParameters}" if logger.debug?
2377
+
2378
+ formalParameters =
2379
+ functionSexp.parameterList.parameters.
2380
+ select do |parameter|
2381
+ # accect request parameter if not in common
2382
+ !commonMsgParameters.include?( parameter.name ) ||
2383
+ # OR 'send( value )'
2384
+ ( valueAsActualParameter && parameter.name == Constants::FIELD_NAME_VALUE )
2385
+ end
2386
+
2387
+ logger.debug "#{__method__}: formalParameters=#{formalParameters}" if logger.debug?
2388
+ formalParameters
2389
+ end
2390
+
2391
+ # @!endgroup
2392
+
2393
+
2394
+ end
2395
+
2396
+ class RvalExprProcessor < SexpProcessorTlaRoot
2397
+
2398
+ def initialize( tlaProcessor, elementGenerator, options )
2399
+ super( options )
2400
+ @tlaProcessor = tlaProcessor
2401
+ @elementGenerator = elementGenerator
2402
+ # alert if new unknown nodes added
2403
+ self.default_method = :process_abort
2404
+
2405
+ end
2406
+
2407
+ # @attr [String] interfaceName ie. function of the body:
2408
+ # constructor 'Demo()' or function 'Demo(set)'
2409
+ attr_accessor :interfaceName
2410
+
2411
+ # @attr [STla] specname_input_variable
2412
+ attr_accessor :specname_input_variable
2413
+
2414
+
2415
+ # @attr [SexpProcessorTla] tlaProcessor collecting snippets
2416
+ attr_accessor :tlaProcessor
2417
+
2418
+ def_delegators :tlaProcessor, # top level tla -processes
2419
+ :findMethodsToDispatch
2420
+
2421
+ # ------------------------------------------------------------------
2422
+ # @!group Process sexp nodes
2423
+
2424
+
2425
+ ##
2426
+ # Rval which wraps function call - modeled as
2427
+ # 'InfrastructureServiceGetResponse( "function" ) '
2428
+ def process_FunctionCallReadReturn( exp )
2429
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2430
+ functionCall = exp.functionCalled
2431
+ g_dispatch_read_return( functionCall.functionExpression.declationRef )
2432
+ # interfaceName = functionCall.functionExpression.declationRef.interfaceName
2433
+ # logger.info "#{__method__}: functionCall:#{functionCall} -> interfaceName=#{interfaceName}"
2434
+ # gen_operator_call(
2435
+ # TlaElementText::OP_INFRA_GET_RESPONSE,
2436
+ # gen_str( interfaceName )
2437
+ # )
2438
+ end
2439
+
2440
+ def process_UnaryOperation( exp )
2441
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2442
+ tlaExpr = process( exp.expr )
2443
+ opExpr = TlaElementText.solcOperator2Tla(exp.operator)
2444
+ gen_unary_op( opExpr, tlaExpr )
2445
+ end
2446
+
2447
+ def process_BinaryOperation( exp )
2448
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2449
+ opExpr = TlaElementText.solcOperator2Tla(exp.operator)
2450
+ lhsExpr = process( exp.lhs )
2451
+ rhsExpr = process( exp.rhs )
2452
+ gen_bin_op( opExpr, lhsExpr, rhsExpr )
2453
+ end
2454
+
2455
+ def process_Literal( exp )
2456
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2457
+ # TODO: map 'Literl.value' to domain.value in 'exp.domainReference'
2458
+ gen_plainname( exp.value )
2459
+ end
2460
+
2461
+ def process_MemberAccess( exp )
2462
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2463
+ variableDeclaration = exp.declationRef
2464
+ resolvedDeclarationScope = variableDeclaration.scopeDefining
2465
+ accessVariable( resolvedDeclarationScope, variableDeclaration.name )
2466
+ end
2467
+
2468
+
2469
+ def process_Identifier( exp )
2470
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2471
+ variableDeclaration = exp.declationRef
2472
+ resolvedDeclarationScope = variableDeclaration.scopeDefining
2473
+ accessVariable( resolvedDeclarationScope, variableDeclaration.name )
2474
+ end
2475
+
2476
+ def process_IndexAccess( exp )
2477
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2478
+
2479
+ # variable being indexed
2480
+ variableDeclaration = exp.identifier.declationRef
2481
+ resolvedDeclarationScope = variableDeclaration.scopeDefining
2482
+
2483
+ # access 'normal' variable
2484
+ variableToAccess = accessVariable( resolvedDeclarationScope, variableDeclaration.name )
2485
+ logger.debug "#{__method__}: variableToAccess= #{variableToAccess}" if logger.debug?
2486
+
2487
+ # rval indexing the variable
2488
+ indexerRval = process( exp.indexer )
2489
+ logger.debug "#{__method__}: exp.indexer #{exp.indexer} index-rval=#{indexerRval}" if logger.debug?
2490
+
2491
+ # variableToAccess[indexerRval]
2492
+ gen_record_named_field( variableToAccess, indexerRval )
2493
+
2494
+ end
2495
+
2496
+ # @!endgroup
2497
+
2498
+ # ------------------------------------------------------------------
2499
+ # @!group Utilities
2500
+
2501
+ private def accessVariable( resolvedDeclarationScope, variableAccess, accountId=g_localCurrentAccountId )
2502
+ logger.debug "#{__method__}: variableAccess=#{variableAccess}, resolvedDeclarationScope=#{resolvedDeclarationScope[0]}:#{resolvedDeclarationScope[1]}" if logger.debug?
2503
+ case resolvedDeclarationScope.sexp_type
2504
+ when :ContractDefinition
2505
+ # access 'variableAccess' on record in
2506
+ # 'eth_StorageRoot' with using account id in 'locals',
2507
+ # where account id is kept for constructor & normal message call
2508
+ gen_record_field(
2509
+ gen_operator_call(
2510
+ TlaElementText::FW_OP_GET_ELEMENT,
2511
+ s(
2512
+ g_storageRoot_exec,
2513
+ gen_str(Constants::FIELD_NAME_ADDRESS),
2514
+ accountId,
2515
+ # gen_record_field(gen_interface_input_variable_name( interfaceName ), Constants::FIELD_NAME_RECIPIENT )
2516
+ )),
2517
+ variableAccess )
2518
+ when :FunctionDefinition
2519
+ # access 'variableAccess' in function local varibales
2520
+ g_locaVariableName( variableAccess )
2521
+ when :ParameterList
2522
+ # # access 'variableAccess' on input variable for 'interfaceName' procedure
2523
+ gen_record_field( gen_interface_input_variable_name( interfaceName ), variableAccess )
2524
+ else
2525
+ raise SbuilderEtherumException, "Missing implemtation for sexp_type #{resolvedDeclarationScope.sexp_type} for rValExpression"
2526
+ end
2527
+
2528
+ end
2529
+ # @!endgroup
2530
+
2531
+ end # RvalExprProcessor
2532
+
2533
+ class LvalExprProcessor < SexpProcessorTlaRoot
2534
+
2535
+ def initialize( tlaProcessor, elementGenerator, options )
2536
+ super( options )
2537
+ @tlaProcessor = tlaProcessor
2538
+ @elementGenerator = elementGenerator
2539
+ end
2540
+
2541
+ # @attr [SexpProcessorTla] tlaProcessor collecting snippets
2542
+ attr_accessor :tlaProcessor
2543
+
2544
+ # @attr [FunctionBodyProcessor] bodyProcessor keeping tract of variables assigned
2545
+ attr_accessor :bodyProcessor
2546
+
2547
+ # @attr [String] rval generate expression
2548
+ attr_accessor :rval
2549
+
2550
+ # @attr [String] interfaceName ie. function of the body:
2551
+ # constructor 'Demo()' or function 'Demo(set)'
2552
+ attr_accessor :interfaceName
2553
+
2554
+ # @attr [STla] specname_input_variable
2555
+ attr_accessor :specname_input_variable
2556
+
2557
+
2558
+ def getRvalProcessor
2559
+ proc = RvalExprProcessor.new( tlaProcessor, elementGenerator,options)
2560
+ proc.interfaceName = interfaceName
2561
+ proc.specname_input_variable = specname_input_variable
2562
+ proc
2563
+ end
2564
+
2565
+
2566
+ # ------------------------------------------------------------------
2567
+ # @!group Process nodes
2568
+
2569
+ #
2570
+ # Assignin to a local variable
2571
+ def process_VariableDeclaration( exp )
2572
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2573
+ variableDeclaration = exp
2574
+ stmts = s()
2575
+ stmts << gen_assign( g_locaVariableName( exp.name ), rval )
2576
+ stmts
2577
+ end
2578
+
2579
+
2580
+ # Shadows identifier!
2581
+ def process_IndexAccess( exp )
2582
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2583
+ variableDeclaration = exp.identifier.declationRef
2584
+
2585
+
2586
+ # using 'local' variable to hold contract address
2587
+
2588
+ indexerRval = getRvalProcessor.process( exp.indexer )
2589
+ logger.debug "#{__method__}: exp.lval-indexer #{exp.indexer} index-rval=#{indexerRval}" if logger.debug?
2590
+
2591
+ lvalAccess = gen_record_named_field( variableDeclaration.name, indexerRval )
2592
+ setExcept( lvalAccess, rval )
2593
+ end
2594
+
2595
+ ##
2596
+ # Identifier: find identifier declaration and its scope,
2597
+ # generate code. Generate code to set 'rval' for element
2598
+ # identified by 'key'.
2599
+ #
2600
+ # Currently supports:
2601
+ # - contract member variable: update eth_storageRoot with
2602
+ #
2603
+ def process_Identifier( exp )
2604
+ logger.debug "#{__method__}: #{exp}" if logger.debug?
2605
+ variableDeclaration = exp.declationRef
2606
+ lvalAccess = gen_identifier(variableDeclaration.name)
2607
+ setExcept( lvalAccess, rval )
2608
+ end
2609
+
2610
+ # @!endgroup
2611
+
2612
+
2613
+ # ------------------------------------------------------------------
2614
+ # @!group Utils
2615
+
2616
+ ##
2617
+ # Generate code:
2618
+ #
2619
+ # var := { IF a.id = 'idValue' THEN [ a EXCEPT = !.a = 'value'] ELSE a: a \in var }
2620
+ #
2621
+ # @param [String] expectName name of variable to set
2622
+ # @param [String] exceptValue to set
2623
+ private def setExcept( expectName, exceptValue, idKey=g_localCurrentAccountId )
2624
+ logger.debug "#{__method__}: expectName=#{expectName}, exceptValue=#{exceptValue}" if logger.debug?
2625
+ # case resolvedDeclarationScope.sexp_type
2626
+ # when :ContractDefinition
2627
+ stmts = s()
2628
+ variableToAssignTo = TlaElementText::FW_STATE_STORAGE_ROOT_TEMP
2629
+ if bodyProcessor.shouldUseLabelToAssignTo( variableToAssignTo )
2630
+ stmts << gen_label
2631
+ end
2632
+ # Updating contract in process for 'interfaceName'. Access contract address using address in local 'variable '
2633
+
2634
+ stmts << gen_assignOnTop(
2635
+ g_storageRoot_temp,
2636
+ gen_set_map(
2637
+ gen_IF( gen_bin_op( "=", gen_record_field( 'a', Constants::FIELD_NAME_ADDRESS), idKey ),
2638
+ gen_EXCEPT(
2639
+ 'a',
2640
+ gen_bin_op( '=',
2641
+ gen_record_field("!", expectName),
2642
+ exceptValue )), 'a' ),
2643
+ gen_set_iterate( 'a', g_storageRoot_exec),
2644
+ ))
2645
+ stmts
2646
+ end
2647
+
2648
+ # @!endgroup
2649
+
2650
+ end
2651
+
2652
+ # @!endgroup
2653
+
2654
+ end # LvalExprProcessor
2655
+
2656
+
2657
+ # ------------------------------------------------------------------
2658
+ # @!group Helper class to implement tsort#strongly_connected_components
2659
+
2660
+ class InheritanceSort < Hash
2661
+
2662
+ include TSort
2663
+
2664
+ # methods used by tsort
2665
+ alias tsort_each_node each_key
2666
+ def tsort_each_child(node, &block)
2667
+ fetch(node).map { |n| n.typeName }.each(&block)
2668
+ end
2669
+
2670
+ end
2671
+ # @!endgroup
2672
+
2673
+ end
2674
+ end