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,461 @@
1
+
2
+ require 'yaml'
3
+ require 'tsort'
4
+
5
+ module Sbuilder
6
+ module Ethereum
7
+
8
+
9
+ # Assume
10
+ # - symbols defined && resolved
11
+ # -- Scope#enclosing_scope set
12
+ # -- DeclRef#enclosingScope set
13
+ # -- DeclRef#declarationRef set
14
+ #
15
+ # Actions
16
+ # - traverse AST and collect dataflow edges
17
+ # -- Identifier: exp - exp#declarationRef
18
+ # -- Assignment: exp.variable - exp.expression
19
+ # -- Return: exp.functionReturnList.parameters[] - exp.expressions[]
20
+ # - identify domains
21
+ # -- use tsort#strongly_connected_components to identify
22
+ #
23
+ # Implementation uses +id+ -property assigned by solc compiler to
24
+ # distinguish nodes in SexpAst -tree.
25
+
26
+ class SexpProcessorDataflow < SexpProcessorEthereum
27
+
28
+ # @attr [Hash] dataflowEdges map SexpAst#id --> [SexpAst#id:Array]
29
+ attr_accessor :dataflowEdges
30
+
31
+ # @attr [Hash] dataflowNodes map SexpAst#id -> [SexpAst]
32
+ attr_accessor :dataflowNodes
33
+
34
+ # # @attr [Domains] domains
35
+ attr_accessor :domains
36
+
37
+ # Map sexp_type to (priority, domain) to use SexpAst -node to give name
38
+ SEXP_TYPE_2_PRIORITY = {
39
+ :ContractDefinition => ->(sexp) {
40
+ return 80, nil
41
+ },
42
+ :VariableDeclaration => ->(sexp) {
43
+ if sexp.typeSexp.sexp_type == :Domain
44
+ # has already been determined. e.g. adding common msg
45
+ # properties
46
+ return 100, sexp.typeSexp
47
+ elsif sexp.typeSexp.sexp_type == :UserDefinedTypeName && sexp.typeSexp.symbolTypeReference.sexp_type == :ContractDefinition
48
+ # pointing contract
49
+ return 100, Domain.s( Constants::DOMAIN_ADDRESS )
50
+ else
51
+ case sexp.scopeDefining.sexp_type
52
+ when :ContractDefinition
53
+ return 80, nil
54
+ # return 100, Domain.s( Constants::DOMAIN_ADDRESS )
55
+ when :ParameterList
56
+ return 35, nil
57
+ when :FunctionDefinition
58
+ return 40, nil
59
+ when :ParameterList
60
+ return 35, nil
61
+ else
62
+ raise SexpProcessorDataflowException.new "Unknown scope type '#{sexp.scopeDefining.sexp_type}' for #{sexp}"
63
+ end
64
+ end
65
+ },
66
+ :Literal => ->(sexp) {
67
+ if sexp.type == Constants::SOL_BOOLEAN_TYPE
68
+ # Map boolean to fixed domain name
69
+ return 100, Domain.s( Constants::DOMAIN_BOOLEAN )
70
+ elsif sexp.type =~ /int_const /
71
+ # Integer const does identify domain - nor results to error
72
+ return nil, nil
73
+ else
74
+ raise SexpProcessorDataflowException.new "Unknown literal type '#{sexp.type}' for #{sexp}"
75
+ end
76
+ },
77
+ :MemberAccess => ->(sexp) { return 30, nil },
78
+ :Identifier => ->(sexp) { return 30, nil },
79
+ :IndexAccess => ->(sexp) {
80
+ # variable declartion of identifier must be 'Mapping', resolve
81
+ # to domain of 'mapping-function'
82
+ domainOfMapping = sexp.identifier.declationRef.typeSexp.mapTo.domainReference
83
+ return 100, sexp.identifier.declationRef.typeSexp.mapTo.domainReference unless domainOfMapping.nil?
84
+ return nil, nil
85
+ },
86
+ :ElementaryTypeName => ->(sexp) {
87
+ if sexp.typeName == Constants::SOL_ADDRESS
88
+ return 100, Domain.s( Constants::DOMAIN_ADDRESS )
89
+ elsif sexp.typeName == Constants::SOL_UINT
90
+ # uint value domain overridden by better candiadate
91
+ # (e.g. msg.value)
92
+ return 10, Domain.s( Constants::DOMAIN_DEFAULT )
93
+ else
94
+ return nil, nil
95
+ # raise SexpProcessorDataflowException.new "Unknown elementary type name for #{sexp}"
96
+ end
97
+ },
98
+ :FunctionCallReadReturn => ->(sexp) { return nil, nil },
99
+ :FunctionDefinition => ->(sexp) {
100
+ if sexp.returnList.parameters.length == 1 && sexp.returnList.parameters[0].type == Constants::DOMAIN_BOOLEAN
101
+ return 100, Domain.s( Constants::DOMAIN_BOOLEAN )
102
+ elsif sexp.returnList.parameters.length == 1 && sexp.returnList.parameters[0].type == Constants::SOL_BOOLEAN_TYPE
103
+ return 100, Domain.s( Constants::DOMAIN_BOOLEAN )
104
+ elsif sexp.returnList.parameters.length == 0
105
+ return 100, Domain.s( Constants::DOMAIN_BOOLEAN )
106
+ else
107
+ raise SexpProcessorDataflowException.new "Unknown function return type '#{sexp.returnList}' for #{sexp}"
108
+ end
109
+ },
110
+ :BinaryOperation => ->(sexp) { return nil, nil },
111
+ }
112
+
113
+
114
+
115
+ # ------------------------------------------------------------------
116
+ # @!group Construct & configure
117
+
118
+ def initialize( options={} )
119
+
120
+ super( options )
121
+
122
+ # see addEdge to populate
123
+ self.dataflowEdges = THash.new
124
+ self.dataflowNodes = {}
125
+ end
126
+
127
+ # @!endgroup
128
+
129
+ # ------------------------------------------------------------------
130
+ # @!group scope support
131
+
132
+ # @attr [VariableDeclaration] currentDecl
133
+ attr_accessor :currentDecl
134
+
135
+ def varDecled( expDecl )
136
+ prevDecl = currentDecl
137
+ self.currentDecl = expDecl
138
+ begin
139
+ yield
140
+ ensure
141
+ self.currentDecl = prevDecl
142
+ end
143
+
144
+ end
145
+
146
+ # @!endgroup
147
+
148
+
149
+ # ------------------------------------------------------------------
150
+ # @!group Entry point & return the identified domains
151
+
152
+ # private def processGlobals( exp )
153
+ # return if @processGlobals
154
+
155
+ # # identify data flows in function global
156
+ # exp.globalScope.symbolDefinitions.
157
+ # select { |globalExp| globalExp.sexp_type == :FunctionDefinition }.
158
+ # each { |expFunction| process_rest( expFunction ) }
159
+
160
+ # @processGlobals = true
161
+
162
+ # end
163
+ #
164
+ #
165
+ # @return [Domains] domains Sexp, with domain -property
166
+ def process_SourceUnit( exp )
167
+
168
+ logger.debug "#{__method__}: starting with '#{exp}" if logger.debug?
169
+
170
+ # traverse tree collect 'dataflowEdges', 'dataflowNodes'
171
+ process_rest( exp )
172
+
173
+ analyzeDomains
174
+
175
+ end
176
+
177
+ def process_GlobalScope( exp )
178
+ logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
179
+ # identify data flows in function global
180
+ # exp.globalScope.symbolDefinitions.
181
+ # select { |globalExp| globalExp.sexp_type == :FunctionDefinition }.
182
+ # each { |expFunction| process_rest( expFunction ) }
183
+
184
+ # traverse tree collect 'dataflowEdges', 'dataflowNodes'
185
+ process_rest( exp )
186
+
187
+ analyzeDomains
188
+
189
+ end
190
+
191
+ ##
192
+ # Cm
193
+ private def analyzeDomains
194
+ # Identify domains after tree traversal
195
+
196
+ # identify strongly connected components in 'dataflowEdges'
197
+ strong_components = find_strong_components()
198
+ logger.debug "#{__method__}: strong_components: #{strong_components}" if logger.debug?
199
+
200
+ # for each 'strong_component' find representative sexp to generate
201
+ # name for the domain
202
+ domainsIdentified=[]
203
+ strong_components.each do |strong_component|
204
+
205
+ # find SexpAst -node to give name for the domain
206
+ strong_component_name_sexp = find_representative_name_sexp( strong_component )
207
+
208
+ # Create Domain with +domainName+
209
+ domain = Domain.new( domainName(strong_component_name_sexp) )
210
+ logger.info "#{__method__}: identified domain #{domain}"
211
+ domainsIdentified << domain
212
+
213
+ # update pointer to domain on sexp node
214
+ update_domain_on_sexps( domain, strong_component )
215
+
216
+ end
217
+
218
+ # Sexp with collection of domains
219
+ self.domains = Domains.new( domainsIdentified )
220
+
221
+ logger.info "#{__method__}: analyzed domains='#{domains}"
222
+ domains
223
+
224
+
225
+ end
226
+
227
+
228
+
229
+ # @!endgroup
230
+
231
+ # ------------------------------------------------------------------
232
+ # @!group Collect dataflow edges
233
+
234
+
235
+ def process_ContractDefinition( exp )
236
+ logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
237
+
238
+ # # Add globals also domain analysis
239
+ # processGlobals( exp )
240
+
241
+ # add a domain to configure number of contracts
242
+ addEdge( exp.sexp_type, exp, nil )
243
+ process_rest( exp )
244
+
245
+ end
246
+
247
+ def process_VariableDeclaration( exp )
248
+ logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
249
+
250
+ # declaration is part of its own strong component
251
+ addEdge( exp.sexp_type, exp, exp ) unless exp.isMapping
252
+ varDecled( exp ) do
253
+ process_rest( exp )
254
+ end
255
+ s()
256
+ end
257
+
258
+ def process_ElementaryTypeName( exp )
259
+ logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
260
+
261
+ # non elemenary declaration
262
+ if currentDecl && currentDecl.isMapping
263
+ logger.info "#{__method__} compposite #{currentDecl.name} for #{exp}"
264
+ addEdge( exp.sexp_type, exp, exp )
265
+ end
266
+ s()
267
+ end
268
+
269
+ def process_MemberAccess( exp )
270
+ logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
271
+ if exp.isFunctionOnFunction
272
+ # AST in 'function on function' case, e.g. demo1.set.value(20)(
273
+ # 6 ) like s(:MemberAccess, "value", s(:MemberAccess, "set",
274
+ # s(:Identifier, "contract Demo1", "demo1")))
275
+ #
276
+ # we are processing s(:MemberAccess, "value", just recurse
277
+ process_rest( exp )
278
+ else
279
+ raise SexpProcessorDataflowException.new "Missing declationRef in #{exp}" unless exp.isDeclarationRef && !exp.declationRef.nil?
280
+ addEdge( exp.sexp_type, exp, exp.declationRef )
281
+ end
282
+ s()
283
+ end
284
+
285
+ def process_Identifier( exp )
286
+ logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
287
+ raise SexpProcessorDataflowException.new "Missing declationRef in #{exp}" unless exp.isDeclarationRef && !exp.declationRef.nil?
288
+ addEdge( exp.sexp_type, exp, exp.declationRef )
289
+ process_rest( exp )
290
+ end
291
+
292
+ def process_Assignment( exp )
293
+ logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
294
+
295
+ addEdge( exp.sexp_type, exp.lval, exp.rval )
296
+ process_rest( exp )
297
+ end
298
+
299
+ def process_Return( exp )
300
+ logger.debug "#{__method__}: exp=#{exp}" if logger.debug?
301
+ logger.debug "#{__method__}, exp.functionReturnList=#{exp.functionReturnList}" if logger.debug?
302
+
303
+ # iterate each return expression/output parameter mapping
304
+ exp.functionReturnList.parameters.each_with_index do |parameter,i|
305
+ logger.debug "#{__method__}, parameter[#{i}]=#{parameter}" if logger.debug?
306
+ addEdge( exp.sexp_type, parameter, exp.expressions[i] )
307
+ end
308
+
309
+ process_rest( exp )
310
+ end
311
+
312
+
313
+
314
+ # @!endgroup
315
+ # ------------------------------------------------------------------
316
+ # @!group Methods to build edges
317
+
318
+ # Add bidirectional edge '+sexp1+ --- +sexp2+' to +dataflowEdges+
319
+ # when processing +sexp_type+ node. If +sexp2+ is nil add just node.
320
+ #
321
+ # @param [SexpAst|nil] sexp1 data flown between +sexp1+ - +sexp2+,
322
+ #
323
+ # @param [SexpAst|nil] sexp2 may be nil, in which case add just node +sexp1+
324
+ #
325
+ private def addEdge( sexp_type, sexp1, sexp2 )
326
+ logger.debug "#{__method__}: '#{sexp1.id}:#{sexp1} --- #{sexp2.nil? ? 'nil' : sexp2.id}:#{sexp2}' for process_#{sexp_type}" if logger.debug?
327
+
328
+ # make sura that the nodes are there
329
+ addNode( sexp1 )
330
+ addNode( sexp2 ) unless sexp2.nil?
331
+
332
+ # ensure key known
333
+ self.dataflowEdges[sexp1.id] = [] unless dataflowEdges.key?(sexp1.id)
334
+ self.dataflowEdges[sexp2.id] = [] unless sexp2.nil? || dataflowEdges.key?(sexp2.id)
335
+
336
+ # add to directional edges - unless either node nil, or it is
337
+ # already there
338
+ if sexp1 && sexp2 then
339
+ self.dataflowEdges[sexp1.id] << sexp2.id unless dataflowEdges[sexp1.id].include?( sexp2.id )
340
+ self.dataflowEdges[sexp2.id] << sexp1.id unless dataflowEdges[sexp2.id].include?( sexp1.id )
341
+ end
342
+
343
+ end
344
+
345
+
346
+ private def addNode( sexp )
347
+ self.dataflowNodes[sexp.id] = sexp unless dataflowNodes.key?(sexp.id)
348
+ end
349
+
350
+ # @!endgroup
351
+
352
+ # ------------------------------------------------------------------
353
+ # @!group Propage domain information
354
+
355
+
356
+
357
+ private def update_domain_on_sexps( domain, strong_component )
358
+
359
+ strong_component.each do |sexpAstId|
360
+
361
+ sexp_node = dataflowNodes[sexpAstId]
362
+
363
+ # should mix DomainRef
364
+ sexp_node.domainReference = domain
365
+ logger.info "#{__method__}: --> set domain: domain=#{domain} -> #{sexp_node[0]},#{sexp_node[1]}"
366
+ logger.debug "#{__method__}: set domain: domain=#{domain} in full #{sexp_node}" if logger.debug?
367
+ end
368
+
369
+
370
+ end
371
+
372
+ # @!endgroup
373
+
374
+ # ------------------------------------------------------------------
375
+ # @!group Helper class to implement tsort#strongly_connected_components
376
+
377
+
378
+ class THash < Hash
379
+
380
+ include TSort
381
+
382
+ # methods used by tsort
383
+ alias tsort_each_node each_key
384
+ def tsort_each_child(node, &block)
385
+ fetch(node).each(&block)
386
+ end
387
+ end
388
+
389
+ # @return [Array] strong_components
390
+
391
+ private def find_strong_components
392
+ dataflowEdges.strongly_connected_components
393
+ end
394
+
395
+ # @!endgroup
396
+
397
+ # ------------------------------------------------------------------
398
+ # @!group Use strong components to define domain
399
+
400
+ # Identify SexpAst -node among +strong_component+ array, which gives
401
+ # name for the domain.
402
+ #
403
+ # @param [Id:Array] strong_component array of SexpAst#id values mapping to 'dataflowNodes'
404
+ #
405
+ # @return [SexpAst] name_sexp used to generate name for the domain entry
406
+ #
407
+ private def find_representative_name_sexp( strong_component )
408
+ #
409
+ name_sexp_priority=0
410
+ name_sexp = nil
411
+ if logger.debug?
412
+ logger.debug "#{__method__}: strong_component=#{strong_component.map { |sexpAstId | dataflowNodes[sexpAstId].inspect }.join(';')}" if logger.debug?
413
+ end
414
+ strong_component.each do |sexpAstId|
415
+ sexp_type = dataflowNodes[sexpAstId].sexp_type
416
+ sexp_node = dataflowNodes[sexpAstId]
417
+ logger.debug "Lookup in SEXP_TYPE_2_PRIORITY #{dataflowNodes[sexpAstId]}" if logger.debug?
418
+ raise "Missing implementation in SEXP_TYPE_2_PRIORITY for #{sexp_type}(is symbol or string?= #{sexp_type.class}) for #{sexp_node}" unless SEXP_TYPE_2_PRIORITY.key?(sexp_type)
419
+ # Use lambda to callucate priority
420
+ priority, domain_sexp = SEXP_TYPE_2_PRIORITY[sexp_type].call( sexp_node )
421
+ logger.debug "#{__method__}: id=#{sexpAstId}->#{sexp_type}->#{priority}, node=#{sexp_node}" if logger.debug?
422
+ # better name candiate found
423
+ if domain_sexp
424
+ # lambda new it best
425
+ name_sexp, name_sexp_priority = domain_sexp, priority if !priority.nil? && (priority > name_sexp_priority)
426
+ else
427
+ name_sexp, name_sexp_priority = dataflowNodes[sexpAstId], priority if !priority.nil? && (priority > name_sexp_priority)
428
+ end
429
+ end
430
+
431
+ raise SexpProcessorDataflowException.new "Could not find domain for components #{strong_component.nil? ? 'nil' : strong_component.map{ |c| dataflowNodes[c].to_s }.join(',')}" if name_sexp.nil?
432
+
433
+ logger.info "#{__method__}: name_sexp=#{name_sexp} for strong_component=#{strong_component.map { |sexpAstId | dataflowNodes[sexpAstId].inspect }.join(';')}"
434
+
435
+ name_sexp
436
+ end
437
+
438
+ # @param [SexpAst] name_sexp used to generate name for the domain entry
439
+ #
440
+ # @return [String] domainName to use for domain
441
+ private def domainName( name_sexp )
442
+ case name_sexp.sexp_type
443
+ when :VariableDeclaration
444
+ "#{ name_sexp.scopeDefining.name}_#{name_sexp.name}"
445
+ when :ContractDefinition
446
+ "#{name_sexp.name}"
447
+ when :Domain
448
+ "#{name_sexp.name}"
449
+ else
450
+ raise SexpProcessorDataflowException.new "Do not know how to create name for '#{name_sexp.sexp_type}' for #{name_sexp}"
451
+ end
452
+ end
453
+
454
+ # @!endgroup
455
+ end # class
456
+
457
+
458
+
459
+ end
460
+ end
461
+