sbuilder-eth 0.0.4

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.
@@ -0,0 +1,354 @@
1
+ require "sbuilder-al"
2
+
3
+ module Sbuilder
4
+ module Eth
5
+ module Constants
6
+
7
+ # @return [String] specName mustache template to generate
8
+ # 'specName' = name in tla specification code representing
9
+ # 'appName' in namespace 'meta'
10
+ def self.templateSpecname( appName, meta=Constants::META_ETH )
11
+ "{{#SPEC_NAME}}#{strSpecname(appName, meta)}{{/SPEC_NAME}}"
12
+ end
13
+
14
+ # gateway to 'snippetFacadeParamsetDomain'
15
+ def self.snippetFacadeParamsetDomain( definition, field )
16
+ Sbuilder::FacadeConstants.snippetFacadeParamsetDomain( definition, field )
17
+ end
18
+
19
+ # gateway to 'snippetFacadeParamsetDomain'
20
+ def self.snippetFacadeParamsetDomainInit( definition, field )
21
+ Sbuilder::FacadeConstants.snippetFacadeParamsetDomainInit( definition, field )
22
+ end
23
+
24
+ def self.snippetFacadeResponseDomainInit( definition, field )
25
+ Sbuilder::FacadeConstants.snippetFacadeResponseDomainInit( definition, field )
26
+ end
27
+
28
+
29
+ def self.strSpecname( appName, meta=Constants::META_ETH )
30
+ "#{meta}.#{appName}"
31
+ end
32
+
33
+ # Links to sbuilder facade
34
+ # META_SERVICE_ENTRY = "service_implementation"
35
+ META_SERVICE_ENTRY = Sbuilder::FacadeConstants::META_MODEL_SERVICE_IMPLEMENTATION
36
+ META_SERVICE_EXIT = Sbuilder::FacadeConstants::META_MODEL_SERVICE_COMPLETION
37
+ META_DOMAIN = Sbuilder::FacadeConstants::META_MODEL_DOMAINS
38
+ META_TLA = Sbuilder::FacadeConstants::META_MODEL_FRAMEWORK_SVC
39
+ META_DEFINITIONS = Sbuilder::FacadeConstants::META_MODEL_DEFINITIONS
40
+
41
+ DOMAIN_BOOLEAN = Sbuilder::FacadeConstants::DOMAIN_BOOLEAN
42
+
43
+
44
+ SBUILDER_GET_RESPONSE = "InfrastructureServiceGetResponse"
45
+ SBUILDER_GET_STATUS = "InfrastructureServiceGetStatus"
46
+ SBUILDER_RETURN = "InfrastructureServiceReturn"
47
+
48
+ # Ethreum runtime services
49
+ CONTRACT_INFRA = "geth"
50
+ SERVICE_NEW_ACCOUNT="newAccount"
51
+ SERVICE_NEW_STORAGE_ROOT="newStorageRoot"
52
+ SERVICE_MINE = "mine"
53
+
54
+
55
+ # Solidity constants
56
+ SOL_MSG="msg"
57
+ SOL_NOW="now"
58
+ SOL_ADDRESS="address"
59
+ SOL_UINT="uint"
60
+ SOL_SEND="send"
61
+ SOL_BOOLEAN_TYPE="bool"
62
+ SOL_SELF_DESTRUCT="selfdestruct"
63
+ SOL_REVERT="revert"
64
+ SOL_REQUIRE="require"
65
+ SOL_THIS="this"
66
+ SOL_VISIBILITY_PUBLIC="public"
67
+
68
+
69
+ # sbuilder-eth constants
70
+ SYMBOL_RESPONSE_MSG = "rply-"
71
+ GETTER_RESPONSE_PREFIX="ret_"
72
+
73
+ # Solidity & ethereum domains
74
+ DOMAIN_ADDRESS = "eth_address"
75
+ DOMAIN_VALUE = "eth_value"
76
+ # Balance cumulates msg values, if it is not passed
77
+ # in message balacnce default value could be Int
78
+ DOMAIN_BALANCE = "eth_balance"
79
+ DOMAIN_GAS = "eth_gas"
80
+ DOMAIN_CODE_HASH = "eth_code_hash"
81
+ # DOMAIN_DEFAULT = "default" # when no better candidate not found
82
+ SIMPLE_DOMAINS = [DOMAIN_ADDRESS, DOMAIN_VALUE, DOMAIN_GAS, DOMAIN_BALANCE ]
83
+ DOMAINS = SIMPLE_DOMAINS + [DOMAIN_CODE_HASH]
84
+
85
+ FIELD_NAME_ORIGINATOR = "originator" # First sender
86
+ FIELD_NAME_SENDER = "sender" # Transaction caller (sender)
87
+ FIELD_NAME_VALUE = "value" # Eth passed on message
88
+ FIELD_NAME_RECIPIENT = "recipient" # called
89
+ FIELD_NAME_ADDRESS = "address" # Account ID
90
+ FIELD_NAME_BENEFICIARY = "beneficiary" # Beneficiary account (in mining)
91
+ FIELD_NAME_BALANCE = "balance" # Eth VALUE
92
+ FIELD_NAME_RESULT = "result" # Result
93
+ FIELD_NAME_GAS = "gas" #
94
+ FIELD_NAME_CODE_HASH = "codeHash" # EVM code (name of class)
95
+ FIELD_NAME_STATUS = "status" #
96
+
97
+
98
+ # Metatypes
99
+ META_ETH = "eth"
100
+
101
+ # prefix: -> sbuilder generates prefix, here use fixed value :eth
102
+ METADEFS = {
103
+ META_ETH => { "description" => "Ethereum runtime", "prefix" => "eth" }
104
+ }
105
+
106
+ # State variables Variables
107
+ VARIABLE_ACCOUNTS = "accounts"
108
+ VARIABLE_ACCOUNTS_STACK = "accounts_stack"
109
+ VARIABLE_STORAGE_ROOT = "storageRoot"
110
+ VARIABLE_STORAGE_ROOT_STACK = "storageRoot_stack"
111
+ VARIABLE_ADDRESS_FREE = "addressPool"
112
+ VARIABLE_MINED = "mined"
113
+ VARIABLE_MINED_STACK = "mined_stack"
114
+
115
+ # Types
116
+ TYPE_VARIABLE_ACCOUNTS = VARIABLE_ACCOUNTS
117
+
118
+
119
+ VARIABLE_LOCAL_CURRENT_CONTRACT="thisCid"
120
+
121
+ VARIABLES = [
122
+ VARIABLE_ACCOUNTS,
123
+ VARIABLE_STORAGE_ROOT,
124
+ # VARIABLE_ADDRESS,
125
+ # VARIABLE_MINED,
126
+ # VARIABLE_ACCOUNTS_TEMP,
127
+ # VARIABLE_STORAGE_ROOT_TEMP,
128
+ VARIABLE_ADDRESS_FREE,
129
+ ]
130
+
131
+ # Common parameters in types for entries variable
132
+ # 'VARIABLE_STORAGE_ROOT'
133
+ COMMON_STORAGE_ROOT_PARAMETERS = [
134
+ # [ FIELD_NAME_CODE_HASH, DOMAIN_CODE_HASH ],
135
+ [ FIELD_NAME_ADDRESS, DOMAIN_ADDRESS],
136
+ ]
137
+
138
+ # Type definition for entries in variable 'VARIABLE_ACCOUNTS'
139
+ COMMON_ACCOUNTS_PARAMETERS = [
140
+ [ FIELD_NAME_CODE_HASH, DOMAIN_CODE_HASH ],
141
+ [ FIELD_NAME_BALANCE, Constants::DOMAIN_BALANCE],
142
+ [ FIELD_NAME_ADDRESS, DOMAIN_ADDRESS],
143
+ ]
144
+
145
+
146
+ # Fields (and domains) in normal request messages
147
+ COMMON_REQ_MSG = [
148
+ [Constants::FIELD_NAME_SENDER, Constants::DOMAIN_ADDRESS],
149
+ [Constants::FIELD_NAME_ORIGINATOR, Constants::DOMAIN_ADDRESS],
150
+ [Constants::FIELD_NAME_RECIPIENT, Constants::DOMAIN_ADDRESS],
151
+ [Constants::FIELD_NAME_VALUE, Constants::DOMAIN_VALUE],
152
+ ]
153
+
154
+ # common fields in constructor request
155
+ COMMON_REQ_CONSTRUCTOR = [
156
+ [Constants::FIELD_NAME_SENDER, Constants::DOMAIN_ADDRESS],
157
+ [Constants::FIELD_NAME_ORIGINATOR, Constants::DOMAIN_ADDRESS],
158
+ # [Constants::FIELD_NAME_RECIPIENT, Constants::DOMAIN_ADDRESS],
159
+ [Constants::FIELD_NAME_VALUE, Constants::DOMAIN_VALUE],
160
+ ]
161
+
162
+ # Fields (and domains) in all response messages
163
+ COMMON_RPLY = [
164
+ # [Constants::FIELD_NAME_ADDRESS, Constants::DOMAIN_ADDRESS]
165
+ ]
166
+
167
+ # Runtime snippets
168
+ RUNTIME_NEXTID = "NextId"
169
+ RUNTIME_APPARENT_VALUE = "isApparentValue"
170
+ RUNTIME_APPARENT_VALUE_FIELD = "isApparentValue"
171
+ RUNTIME_GET_ELEMENT = "getElement"
172
+ RUNTIME_GET_ELEMENT_SAFE = "getElementSafe"
173
+ RUNTIME_ELEMENT_EXISTS="elementExists"
174
+ RUNTIME_PUSH_CONTRACT_STATE="PushState"
175
+ RUNTIME_POP_CONTRACT_STATE="PopState"
176
+ RUNTIME_SERVICE_DONE="service_done"
177
+ RUNTIME_SERVICE_UP_FRONT_COST="upFrontCost"
178
+
179
+
180
+ RUNTIME_CORRECT_TOTAL_VALUE="total_value"
181
+ RUNTIME_CORRECT_ACCOUNTS_TYPE="Accounts_type"
182
+
183
+ RUNTIME_SNIPPETS_CORRECTNESS = [
184
+ {
185
+ :name => RUNTIME_CORRECT_TOTAL_VALUE,
186
+ :meta => META_ETH,
187
+ :txt => <<~HERE
188
+ (*
189
+ * Sum of account balances is equal to value mined.
190
+ * when not executing a transaction
191
+ *)
192
+ #{templateSpecname(RUNTIME_CORRECT_TOTAL_VALUE)} ==
193
+ tx_running = FALSE => ( #{templateSpecname(VARIABLE_MINED)} = #{templateSpecname('SumRecordField')}( #{templateSpecname(VARIABLE_ACCOUNTS)}, "balance" ) )
194
+ HERE
195
+ },
196
+ {
197
+ :name => RUNTIME_CORRECT_ACCOUNTS_TYPE,
198
+ :meta => META_ETH,
199
+ :txt => <<~HERE
200
+ (*
201
+ * Sum of account balances is equal to value mined.
202
+ * when not executing a transaction
203
+ *)
204
+ #{templateSpecname(RUNTIME_CORRECT_ACCOUNTS_TYPE)} ==
205
+ tx_running = FALSE => \\A e \\in #{templateSpecname(VARIABLE_ACCOUNTS)}: e \\in #{templateSpecname(META_ETH + '_' + VARIABLE_ACCOUNTS, META_DEFINITIONS)}
206
+ HERE
207
+ },
208
+ ]
209
+
210
+ RUNTIME_SNIPPETS = [
211
+ {
212
+ :name=>"elementExists",
213
+ :meta=>META_ETH,
214
+ :txt=> <<~HERE
215
+ (*
216
+ * Unique element exists
217
+ *)
218
+ #{templateSpecname('elementExists')}( set, key, id ) == Cardinality( { e \\in set : e[key] = id } ) = 1
219
+ HERE
220
+ },
221
+ {
222
+ :name => "gasPrice",
223
+ :meta => META_ETH,
224
+ :txt => <<~HERE
225
+ (* Use integer math, and gasPrice is just 0 *)
226
+ #{templateSpecname('gasPrice')} == 0
227
+ HERE
228
+ },
229
+ {
230
+ :name => "gasValue",
231
+ :meta => META_ETH,
232
+ :txt => <<~HERE
233
+ (* @return [Integer] value of 'gas' *)
234
+ #{templateSpecname('gasValue')}( gas ) == gas * #{templateSpecname('gasPrice')}
235
+ HERE
236
+ },
237
+ {
238
+ :name => "intrinsicGas",
239
+ :meta => META_ETH,
240
+ :txt => <<~HERE
241
+ (*
242
+ *Always consume at least ''intrinsicGas' units of gas, i.e.
243
+ * account must have balance >= intrinsicGas*gasPrice before execution
244
+ *)
245
+ #{templateSpecname('intrinsicGas')} == 1
246
+ HERE
247
+ },
248
+ {
249
+ :name => RUNTIME_NEXTID,
250
+ :meta => META_ETH,
251
+ :txt => <<~HERE
252
+ (*
253
+ * Take one 'address'' from set 'ids'. If 'address' parameter 'Nil' address
254
+ * is unspecified is bound to an element in 'ids' when NextId called for the
255
+ * first time in a transition.
256
+ *)
257
+ #{templateSpecname(RUNTIME_NEXTID)}( ids, address ) == CHOOSE x \\in ids: (address = x /\\ address # Nil) \\/ address = Nil
258
+ HERE
259
+ },
260
+ {
261
+ :name => RUNTIME_GET_ELEMENT,
262
+ :meta => META_ETH,
263
+ :txt => <<~HERE
264
+ (*
265
+ * Return element from 'set' such that element['key'] == 'id'
266
+ * Assume 'id' unique, and element exists
267
+ *)
268
+ #{templateSpecname(RUNTIME_GET_ELEMENT)}( set, key, id ) == ( CHOOSE x \\in { e \\in set : e[key] = id } : TRUE )
269
+ HERE
270
+ },
271
+ {
272
+ :name => RUNTIME_ELEMENT_EXISTS,
273
+ :meta => META_ETH,
274
+ :txt => <<~HERE
275
+ (*
276
+ * Unique element exists
277
+ *)
278
+ #{templateSpecname(RUNTIME_ELEMENT_EXISTS)}( set, key, id ) == Cardinality( { e \\in set : e[key] = id } ) = 1
279
+ HERE
280
+ },
281
+ {
282
+ :name => RUNTIME_GET_ELEMENT_SAFE,
283
+ :meta => META_ETH,
284
+ :txt => <<~HERE
285
+ (*
286
+ * Get property 'prop' from element 'e' in 'set'
287
+ * where element['key'] == 'id' if such an element exists
288
+ *)
289
+ #{templateSpecname(RUNTIME_GET_ELEMENT_SAFE)}( set, key, id, prop ) ==
290
+ IF #{templateSpecname(RUNTIME_ELEMENT_EXISTS)}( set, key, id )
291
+ THEN (#{templateSpecname(RUNTIME_GET_ELEMENT)}( set, key, id ))[prop]
292
+ ELSE "_undefined_"
293
+ HERE
294
+ },
295
+
296
+ {
297
+ :name => RUNTIME_SERVICE_UP_FRONT_COST,
298
+ :meta => META_ETH,
299
+ :txt => <<~HERE
300
+ (*
301
+ ** Value account must hold before transaction possible to start
302
+ *
303
+ * @return [Integer] request.value + gasPrice * intrinsicGas
304
+ *)
305
+ #{templateSpecname(RUNTIME_SERVICE_UP_FRONT_COST)}( request ) == request.value + #{templateSpecname('gasPrice')} * #{templateSpecname('intrinsicGas')}
306
+ HERE
307
+ },
308
+ {
309
+ :name => "Push",
310
+ :meta => META_ETH,
311
+ :txt => <<~HERE
312
+ (*
313
+ ** Add element to sequence head
314
+ *)
315
+ #{templateSpecname('Push')}( s, e ) == <<e>> \\o s
316
+ HERE
317
+ },
318
+ {
319
+ :name => RUNTIME_APPARENT_VALUE,
320
+ :meta => META_ETH,
321
+ :txt => <<~HERE
322
+ (*
323
+ ** Check whether 'rec' has NOT field 'delegatecall'.
324
+ ** Precence means that no value transfer should take
325
+ ** placee. See EVM machine code 0xf4 DELEGATECALL
326
+ ** for meaning.
327
+ *)
328
+ #{templateSpecname( RUNTIME_APPARENT_VALUE )}( rec ) == ~ "#{RUNTIME_APPARENT_VALUE_FIELD}" \\in DOMAIN rec
329
+ HERE
330
+ },
331
+
332
+ {
333
+ :name => 'SumRecordField',
334
+ :meta => META_ETH,
335
+ :txt => <<~HERE
336
+ RECURSIVE #{templateSpecname('SumRecordField')}(_,_)
337
+ (*
338
+ * Sum of field 'f' of record elements in set 'S'
339
+ *
340
+ * @param [Set] S set of records
341
+ * @param [String] f name of field
342
+ *)
343
+ #{templateSpecname('SumRecordField')}(S,f) ==
344
+ IF S = {} THEN 0
345
+ ELSE LET x == CHOOSE x \\in S : TRUE
346
+ IN x[f] + #{templateSpecname('SumRecordField')}(S \\ {x}, f)
347
+ HERE
348
+ },
349
+ ]
350
+
351
+
352
+ end
353
+ end
354
+ end
@@ -0,0 +1,2054 @@
1
+ # coding: utf-8
2
+ module Sbuilder
3
+
4
+ module Eth
5
+ class Ethereum
6
+
7
+ # ------------------------------------------------------------------
8
+ # Mixins
9
+
10
+ extend Forwardable # for easy delegation
11
+ include Sbuilder::Al::Util::MyLogger # mix logger
12
+ include Sbuilder::Eth::EthereumExpression # methods for building expresion
13
+ include Sbuilder::Al::Model::Api # Mix Sbuilder API Language (AL).
14
+
15
+ # ------------------------------------------------------------------
16
+ # @!group Attributes
17
+
18
+ # @!attribute instance the singleton
19
+ @@instance = nil
20
+
21
+ # @!attribute [Sbuilder::Al::Model::Api] alApi
22
+ attr_accessor :alApi
23
+
24
+ # @!endgroup
25
+
26
+ # ------------------------------------------------------------------
27
+ # @!group Constructor and singleton pattern
28
+
29
+ private def initialize( options = {} )
30
+ self.alApi = AlApi.start( Constants::METADEFS )
31
+ @logger = getLogger( nil, options )
32
+ @logger.info( "#{__method__}: singleton instance created")
33
+ end
34
+
35
+ def self.instance( options={} )
36
+ @@instance = @@instance || Ethereum.new( options )
37
+ end
38
+
39
+ # @!endgroup
40
+
41
+ # ------------------------------------------------------------------
42
+ # @!group Initalization
43
+
44
+ # Create runtime AL-obejcts, which should be entered only once
45
+ #
46
+ # @return [Array<AlObject>] Runtime environemnt
47
+ def ethRuntime
48
+
49
+ # tla-variables & domains
50
+ ethRuntimeContractState +
51
+ ethRuntimeContractTransaction +
52
+ ethRuntimeAddressPool +
53
+ Constants::SIMPLE_DOMAINS.map { |d| ethDomain(d) } +
54
+
55
+ # Types
56
+ [ ethRuntimeAccountsType ] +
57
+
58
+ # tla-code into sbuilder (for runtime support)
59
+ ethRuntimeTlaSnippets.map do |snippet|
60
+ ethSnippet( snippet[:name], snippet[:txt], snippet[:meta] )
61
+ end +
62
+
63
+ ethRuntimeNewAccount + # geth(newAccount)
64
+ ethRuntimeMine + # geth(mine)
65
+ [
66
+ ethServiceExitCommon, # macro eth_service_done
67
+ ethPushContractState, # macro push state in svc entry
68
+ # ethMacroPopState, # macro pop state in svc cmpltion
69
+ ]
70
+
71
+ end
72
+
73
+ # @return [Array<Hash>] array of hashes (with properties :name,
74
+ # :meta, :txt) as Ethereum runtime snippets. This allows us load
75
+ # tla-code directly into sbuilder.
76
+ def ethRuntimeTlaSnippets
77
+ Constants::RUNTIME_SNIPPETS +
78
+ ethRuntimeTlaSnippetsCorrectness
79
+ #
80
+ end
81
+
82
+
83
+ # @return [Array<Hash>] array of hashes (with properties :name,
84
+ # :meta, :txt) as Ethereum runtime snippets for checking
85
+ # correctness criteria.
86
+ def ethRuntimeTlaSnippetsCorrectness
87
+ Constants::RUNTIME_SNIPPETS_CORRECTNESS
88
+ end
89
+
90
+
91
+ # {#ethRuntimeContractState}: Variables recording Solidity
92
+ # contract state.
93
+ #
94
+ # - eth.accounts : account state includes balance and 'codeHash'
95
+ # for contract type, intialized to empty set (=no accounts)
96
+ #
97
+ # - eth_storageRoot: contract state variables, initialized to
98
+ # empty set (=no contracts)
99
+ #
100
+ # - eth_mined: cumulate 'value' mined, initialize to 0.
101
+ #
102
+ def ethRuntimeContractState
103
+ [
104
+ ethVariable(Constants::VARIABLE_ACCOUNTS ),
105
+ ethVariable(nStorageRoot),
106
+ ethVariable(
107
+ Constants::VARIABLE_MINED,
108
+ alApi.constantExpression do
109
+ value 0
110
+ end
111
+ )
112
+ ]
113
+ end
114
+
115
+ # {#ethRuntimeContractTransaction}: Stack storing contract state
116
+ # during transaction processing.
117
+ #
118
+ # * eth.accounts_stack: pushed acccount state, initized to
119
+ # empty sequence (=empty stack)
120
+ #
121
+ # * eth.storageRoot_stack: pushed contract state, init empty
122
+ # sequence (=empty stack)
123
+ #
124
+ # * eth.ethMined_stack: pushed contract state, init empty
125
+ # sequence (=empty stack)
126
+ #
127
+ def ethRuntimeContractTransaction
128
+ [
129
+ ethVariable(Constants::VARIABLE_ACCOUNTS_STACK, alApi.tlaSequence ),
130
+ ethVariable(Constants::VARIABLE_STORAGE_ROOT_STACK, alApi.tlaSequence ),
131
+ ethVariable(Constants::VARIABLE_MINED_STACK, alApi.tlaSequence ),
132
+ ]
133
+ end
134
+
135
+
136
+ def ethRuntimeMine
137
+ [
138
+ ethTransaction( Constants::CONTRACT_INFRA, Constants::SERVICE_MINE ),
139
+ ethServiceEntry( Constants::CONTRACT_INFRA, Constants::SERVICE_MINE )
140
+ ]
141
+ end
142
+
143
+ # Create
144
+ # - macro to create new entries modeling contract
145
+ # - service procedure for geth(newAccount)
146
+ # - service entry function for geth(newAccount)
147
+ def ethRuntimeNewAccount
148
+ [
149
+ ethTransactionNewAccount, # implement service CONTRACT_INFRA.SERVICE_NEW_ACCOUNT
150
+ ethServiceEntryInfra( Constants::CONTRACT_INFRA, Constants::SERVICE_NEW_ACCOUNT),
151
+ ]
152
+ end
153
+
154
+ # @return [Array<sTla>] addressPool := d_address \ { Nil }
155
+ def ethRuntimeAddressPool #
156
+ [
157
+ ethVariable(
158
+ Constants::VARIABLE_ADDRESS_FREE,
159
+ alApi.binaryExpression do
160
+ lval ethDomainReference(Constants::DOMAIN_ADDRESS)
161
+ op :set_minus
162
+ rval alApi.tlaSet( alApi.tlaNil)
163
+ end
164
+ ),
165
+ ]
166
+ end
167
+
168
+ # @!endgroup
169
+
170
+
171
+ # ------------------------------------------------------------------
172
+ # @!group Build services
173
+
174
+ # @return [Hash] metatype definition used by AlObject created in
175
+ # this module
176
+ def ethMetaDefs
177
+ Constants::METADEFS
178
+ end
179
+
180
+ # @param initRval [AlObject:RVal] Expression to initalize variable with, default nil=empty set
181
+ def ethVariable( variableName, initRval=nil )
182
+ # default init empty set
183
+ initRval = alApi.tlaSet if initRval.nil?
184
+
185
+ # construct variable snippet
186
+ alApi.variable( Constants::META_ETH ) do
187
+ name variableName
188
+ init initRval
189
+ end
190
+ end
191
+
192
+ # Create domain with name 'domainName'
193
+ #
194
+ # @param domainName [String] name of domain to create
195
+ #
196
+ # @param names [Array<String|Int|true|false>] values to assign
197
+ # to domain
198
+ #
199
+ def ethDomain( domainName, names=[] )
200
+ @logger.info "#{__method__}: domainName=#{domainName}, names=#{names.join(',')}"
201
+ alApi.domain do
202
+
203
+ name domainName
204
+
205
+ # Add names as literal values to 'domainConstructor'
206
+ names.each do |v|
207
+ literal( v )
208
+ end
209
+ end
210
+ end
211
+
212
+ ##
213
+ # Elements in domain 'eth_code_hash' are names of translated
214
+ # contract classes passed in parameter 'contracClasses'. This
215
+ # domain give type for field 'codeHash' in records in variable
216
+ # 'eth_accounts'.
217
+ #
218
+ # @param contracClasses [Array<String>] names of translated
219
+ # Solidity classes
220
+ #
221
+ # @return [Al:Domain] 'codeHash' domain definition with values
222
+ # for contract class names 'contracClasses'
223
+ #
224
+ def ethDomainCodeHash( contracClasses )
225
+ ethDomain( Constants::DOMAIN_CODE_HASH, contracClasses)
226
+ end
227
+
228
+
229
+
230
+ # @param refVariable [Al:Refrence] reference to variable
231
+ # @param expression [Al::Expression] Expression to add to set in variable
232
+ #
233
+ # variable \union expression
234
+ def ethVariableUnion( refVariable, expression )
235
+ alApi.binaryExpression do
236
+ lval alApi.expression( refVariable )
237
+ op :union
238
+ rval expression
239
+ end
240
+ end
241
+
242
+ # @return [Al::Expression] refVariable \ expression
243
+ def ethVariableMinus( refVariable, expression )
244
+ alApi.binaryExpression do
245
+ lval alApi.expression( refVariable )
246
+ op :set_minus
247
+ rval expression
248
+ end
249
+ end
250
+
251
+
252
+ # [ address |-> NextId( '#SPEC_NAME'eth.address_free'/SPEC_NAME', input.address ), balance |-> value, codeHash |-> "Demo1" ]
253
+ #
254
+ # @param contractAst [SexpAst] AST for contract to construct, nil
255
+ # for human account (geth(newAccount))
256
+ #
257
+ # @param value [Integer] initial value to assing to account
258
+ #
259
+ # @return [Al:TlaExpression] tla-expression to initialize account record
260
+ #
261
+ def ethNewAccountRecord( contractAst, value=0 )
262
+ # Array of field names and init -expressions for account record
263
+ fldInitDefs = [
264
+ [Constants::FIELD_NAME_ADDRESS, ethNextId ],
265
+ [Constants::FIELD_NAME_BALANCE, alApi.expression(value)],
266
+ # [Constants::FIELD_NAME_CODE_HASH, contractAst.nil? ? alApi.tlaNil : alApi.constantExpression(contractAst["name"]) ],
267
+ [Constants::FIELD_NAME_CODE_HASH, ethContract2CodeHashExpr(contractAst) ],
268
+ ]
269
+ fldInitDefs.map do |fldInitDef|
270
+ alApi.tlaRecordFieldDef( fldInitDef[0], fldInitDef[1])
271
+ end
272
+ end
273
+
274
+ # @return [Al::Expression] codeHash AL-expression from contract
275
+ # AST 'name' property, tlaNil if contractAst.nil? (=non
276
+ # contract account)
277
+ def ethContract2CodeHashExpr( contractAst )
278
+ contractAst.nil? ? alApi.tlaNil : alApi.constantExpression(contractAst['name'])
279
+ end
280
+
281
+ # TLA-record modeling initial state of contract 'contractAst'
282
+ #
283
+ # [ address |-> NextId, storageRoot |-> .., variable1 |-> .., variable2 |-> ... ]
284
+ #
285
+ # @param contractAst [SexpAst] AST for contract to construt, nil
286
+ # when contracting non-contract account
287
+ #
288
+ # @return [Al::TlaExpression] tla-expression initi contract storageRooot record
289
+ #
290
+ def ethNewStorageRootRecord( contractAst )
291
+
292
+ # Create initialization for fixed fields (address, ...)
293
+ constantInits = [
294
+ [Constants::FIELD_NAME_ADDRESS, ethNextId ],
295
+ # [Constants::FIELD_NAME_CODE_HASH, ethContract2CodeHashExpr(contractAst) ],
296
+ ]
297
+
298
+ # Create member inits using {#typeInitDispatcher} - if possible
299
+ memberVariableInits= (
300
+ contractAst.nil? ?
301
+ [] :
302
+ contractAst.memberVariables
303
+ ).map do |v|
304
+ [ v['name'],
305
+ Ethereum.dispatcher(
306
+ typeInitDispatcher,
307
+ v.variableType.sexp_type,
308
+ { :contractAst => contractAst, :variableAst => v }
309
+ )
310
+ ]
311
+ end
312
+
313
+ @logger.info "#{__method__}: memberVariableInits=#{memberVariableInits.join(',')}"
314
+
315
+ # Create tla expression [ fld |-> value ]
316
+ (constantInits + memberVariableInits).map do |fldInitDef|
317
+ alApi.tlaRecordFieldDef( fldInitDef[0], fldInitDef[1])
318
+ end
319
+ end
320
+
321
+
322
+ # Create 'Al::Definition' for 'contractName' to give type for
323
+ # entries in 'storageRoot' variable. Definition includes fixed
324
+ # parameters {#ethStorageRootFixedParameters} (e.g. address),
325
+ # and yields the 'Al::Definition' for caller allowing contract
326
+ # member variables to be added to the definition.
327
+ #
328
+ # @param contractName [String] name of contract for which
329
+ # definition is created
330
+ def ethContractType( contractName )
331
+
332
+ ethDefinition(contractName) do |defi|
333
+ # Add fixed parameters e.g. codeHash && address
334
+ ethStorageRootFixedParameters.each do |parameterDef|
335
+ # name and domain, contract defs are not arrays
336
+ isArray = false
337
+ defi.parameter parameterDef[0], isArray, parameterDef[1]
338
+ end
339
+
340
+ # Add contract member variables
341
+ yield defi
342
+ end
343
+ end
344
+
345
+ # Construct 'Defition' corresponding record entries in
346
+ # 'eth_accounts' variable. Definition includes fixed parameters
347
+ # from {#Constants::COMMON_ACCOUNTS_PARAMETERS} such as
348
+ # 'address', 'balance', 'codeHash'.
349
+ def ethRuntimeAccountsType
350
+ alApi.definition do
351
+ name Constants.strSpecname(Constants::TYPE_VARIABLE_ACCOUNTS)
352
+
353
+ # Add fixed parameters e.g. codeHash && address
354
+ Constants::COMMON_ACCOUNTS_PARAMETERS.each do |parameterDef|
355
+ # name and domain, not array type
356
+ isArray = false
357
+ parameter parameterDef[0], isArray, parameterDef[1]
358
+ end
359
+ end
360
+ end
361
+
362
+ # Create 'Al::Defintion' with name 'defName'. Yield definition
363
+ # to caller allowing parameters/references to be added to the
364
+ # definition constructed.
365
+ def ethDefinition( defName )
366
+ alApi.definition do
367
+ name defName
368
+ yield self
369
+ end
370
+ end
371
+
372
+
373
+ # Create 'Al::FunctionDefinition' for contract 'contractAst'
374
+ # member variable 'variableAst' of sexp_type
375
+ #
376
+ # Example Solidity code: mapping(address => uint256 ) balances;
377
+ #
378
+ # @param contractAst [SexpASst] contract AST node
379
+ #
380
+ # @param variableAst [SexpAst] contract member variable AST node
381
+ def ethContractMappingFunction( contractAst, variableAst )
382
+ mappingAst = variableAst.variableType
383
+ @logger.info "#{__method__}: mappingAst=#{mappingAst}"
384
+ alApi.functionDefinition do
385
+ isArray = false
386
+ name nContractMappingMemberDefinition( contractAst['name'], variableAst['name'] )
387
+ inputParameter isArray, ethWellKnownDomain( mappingAst.children[0] ) # map from
388
+ outputParameter isArray, ethWellKnownDomain( mappingAst.children[1] ) # map to
389
+ end
390
+ end
391
+
392
+ # @return [String, nil] name of well knwon domain, nil if not
393
+ # known. currently map "address" -> eth_address, "value" ->
394
+ # "eth_value"
395
+ def ethWellKnownDomain( typeAst )
396
+ return nil unless typeAst.sexp_type == :ElementaryTypeName
397
+ case typeAst['name']
398
+ when Constants::FIELD_NAME_ADDRESS
399
+ Constants::DOMAIN_ADDRESS
400
+ when Constants::FIELD_NAME_VALUE
401
+ Constants::DOMAIN_VALUE
402
+ when Constants::FIELD_NAME_BALANCE
403
+ Constants::DOMAIN_BALANCE
404
+ else
405
+ return nil
406
+ end
407
+ end
408
+
409
+ # @return [Array<Arrays>] Array of parameter definitions ([name,
410
+ # domain] -pairs) common for all stroageRoot contract
411
+ # entries.
412
+ def ethStorageRootFixedParameters
413
+ Constants::COMMON_STORAGE_ROOT_PARAMETERS
414
+ end
415
+
416
+ # Enter transaction 'contractName(functionName)', which models
417
+ # calling the function 'functionName' in contract
418
+ # 'contractName'.
419
+ #
420
+ # Assume:
421
+ # - sbuilder scheduler has initialized 'responses' 'status' to FALSE
422
+ # 'response' -data to Nil
423
+ #
424
+ # Actions:
425
+ # - push contract state to stack using {#ethPushContractState}
426
+ #
427
+ # - check if incalling of the contract 'contractName' function
428
+ # 'functionName' if possible:
429
+ #
430
+ # -- contract creation (constructior function):
431
+ #
432
+ # -- message call (non constructor):
433
+ #
434
+ # - call service procedure
435
+ #
436
+ # @param contractName [String] contract for the function
437
+ # 'functionName'
438
+ #
439
+ # @param functionName [String] name of the function of the
440
+ # transaction to invoke
441
+ #
442
+ # @return [Al::Macro] macro invoking transaction
443
+ # 'contractName.functionName'
444
+ #
445
+ def ethServiceEntry( contractName, functionName )
446
+
447
+ localParameterName = "input"
448
+
449
+ alApi.macro( Constants::META_SERVICE_ENTRY ) do
450
+ name nInterfaceOperation( contractName, functionName )
451
+ parameter localParameterName
452
+ block {
453
+
454
+ # push contract state to stack
455
+ callMacro( Constants::META_ETH, Constants::RUNTIME_PUSH_CONTRACT_STATE )
456
+
457
+ operationName = nContractFunctionOperation(contractName, functionName)
458
+
459
+ if isConstructor( contractName, functionName ) then
460
+ # contract creation in a constructor
461
+
462
+ # call to constructor allowed ?
463
+ ifThen( ethServiceConstructorCanStart( localParameterName ) ) do
464
+ callService(
465
+ Constants::META_ETH,
466
+ contractName,
467
+ operationName ) { |call| call.parameter( alApi.reference( localParameterName )) }
468
+ end
469
+
470
+ else
471
+ # message call to a non constructor function
472
+
473
+ # if call service procedure allowed?
474
+ ifThen(
475
+ ethServiceMsgCallCanStart( localParameterName )
476
+ ) do
477
+ callService(
478
+ Constants::META_ETH, contractName, operationName
479
+ ) { |call| call.parameter( alApi.reference( localParameterName ) ) }
480
+ end
481
+ end
482
+
483
+ }
484
+ end
485
+ end
486
+
487
+
488
+ # Service Entry macro for infra services, particularly
489
+ # geth(newAccount).
490
+ #
491
+ # NB: Infra service are used to bootstrap block chain state
492
+ #
493
+ # - geth(newAccount): create account entry && mine value to the
494
+ # - account
495
+ #
496
+ # Actions:
497
+ #
498
+ # - push contract state to stack
499
+ # - call service procedure contarac(functionName)
500
+ #
501
+ # @return [Al::Macro] Macro to start infrastructure service
502
+ # transaction
503
+
504
+ def ethServiceEntryInfra( contractName, functionName )
505
+
506
+ localParameterName = "input"
507
+
508
+ alApi.macro( Constants::META_SERVICE_ENTRY ) do
509
+ name nInterfaceOperation( contractName, functionName )
510
+ parameter localParameterName
511
+ block {
512
+
513
+ # PushState: push contract state to stack
514
+ callMacro( Constants::META_ETH, Constants::RUNTIME_PUSH_CONTRACT_STATE )
515
+
516
+ # Call service for interface operation 'contractName(functionName)'
517
+ operationName = nContractFunctionOperation(contractName, functionName)
518
+ callService( Constants::META_ETH, contractName, operationName ) { |call|
519
+ call.parameter( alApi.reference( localParameterName ) )
520
+ }
521
+ }
522
+ end
523
+ end
524
+
525
+
526
+ # Map 'contractName, functionName' service completion operation
527
+ # to a common macro {#ethServiceExitCommon}
528
+ def ethServiceExit( contractName, functionName )
529
+
530
+ alApi.aliased( Constants::META_SERVICE_EXIT ) do
531
+ name nInterfaceOperation( contractName, functionName )
532
+ specName Constants.strSpecname(Constants::RUNTIME_SERVICE_DONE)
533
+ end
534
+ end
535
+
536
+ # Push constract state variables into stack.
537
+ #
538
+ # Actions:
539
+ #
540
+ # - eth_accounts_stack := eth_Push( eth_accounts_stack,eth_accounts )
541
+ #
542
+ # - eth_storageRoot_stack := eth_Push( eth_storageRoot_stack,
543
+ # eth_storageRoot )
544
+ #
545
+ # - eth_mined_stack := eth_Push( eth_mined_stack, eth_mined)
546
+ #
547
+ def ethPushContractState
548
+ alApi.macro( Constants::META_ETH ) do
549
+ name Constants::RUNTIME_PUSH_CONTRACT_STATE
550
+ block {
551
+ # eth-accounts
552
+ assignTo do
553
+ variable ethVariableReference( Constants::VARIABLE_ACCOUNTS_STACK )
554
+ rval alApi.tlaOperatorCall(
555
+ Constants.templateSpecname("Push"),
556
+ [ethVariableReferenceExpression( Constants::VARIABLE_ACCOUNTS_STACK ),
557
+ ethVariableReferenceExpression( Constants::VARIABLE_ACCOUNTS )])
558
+ end
559
+
560
+ # eth-storage-root
561
+ assignTo do
562
+ variable ethVariableReference( Constants::VARIABLE_STORAGE_ROOT_STACK )
563
+ rval alApi.tlaOperatorCall(
564
+ Constants.templateSpecname("Push"),
565
+ [ ethVariableReferenceExpression( Constants::VARIABLE_STORAGE_ROOT_STACK ),
566
+ ethVariableReferenceExpression( Constants::VARIABLE_STORAGE_ROOT )])
567
+ end
568
+
569
+ # eth-mined
570
+ assignTo do
571
+ variable ethVariableReference( Constants::VARIABLE_MINED_STACK )
572
+ rval alApi.tlaOperatorCall(
573
+ Constants.templateSpecname("Push"),
574
+ [ ethVariableReferenceExpression( Constants::VARIABLE_MINED_STACK ),
575
+ ethVariableReferenceExpression( Constants::VARIABLE_MINED ) ])
576
+ end
577
+ } # block
578
+ end
579
+ end
580
+
581
+
582
+
583
+ # {#ethServiceMsgCallCanStart}: Expression, which should
584
+ # evaluate to TRUE allowing service procedure for a message call
585
+ # to a contract function to execute.
586
+ #
587
+ # Evalutate TRUE when the following conditions are TRUE:
588
+ #
589
+ # * elemement 'accounts[msg.sender]' exists
590
+ #
591
+ # * accounts[msg.sender].balance >= upFrontConst(msg.value)
592
+ #
593
+ # @param parameterName [String] name of parameterholding 'msg'
594
+ #
595
+ # @return [Al::Expression] expression to evaluate allowing
596
+ # service of a msg call to be executed
597
+ def ethServiceMsgCallCanStart( parameterName )
598
+
599
+ # Expressions used below
600
+ exprSender = alApi.expression(alApi.reference( parameterName, Constants::FIELD_NAME_SENDER ))
601
+ exprParameter = alApi.expression( alApi.reference( parameterName ))
602
+
603
+ ethBinaryExpression(
604
+
605
+ # 'accounts[msg.sender]' exists?
606
+ alApi.tlaOperatorCall(
607
+ Constants.templateSpecname(Constants::RUNTIME_ELEMENT_EXISTS),
608
+ [ # set, key, id
609
+ alApi.expression(refAccount),
610
+ alApi.expression(Constants::FIELD_NAME_ADDRESS), # constant string expression
611
+ exprSender,
612
+ ] ),
613
+ binaryOperator( "&&" ),
614
+
615
+ # accounts[msg.sender].balance >= upFrontConst(msg.value)
616
+ ethBinaryExpression(
617
+ alApi.tlaRecordField(
618
+ alApi.tlaOperatorCall(
619
+ Constants.templateSpecname(Constants::RUNTIME_GET_ELEMENT),
620
+ [
621
+ alApi.expression(refAccount),
622
+ alApi.expression(Constants::FIELD_NAME_ADDRESS),
623
+ exprSender,
624
+ ]
625
+ ), Constants::FIELD_NAME_BALANCE
626
+ ),
627
+ binaryOperator( ">=" ),
628
+ alApi.tlaOperatorCall(
629
+ Constants.templateSpecname(Constants::RUNTIME_SERVICE_UP_FRONT_COST),
630
+ [exprParameter] )
631
+ )
632
+ ) # AND
633
+ end
634
+ # {#ethServiceExitCommon} Macro to finish the transaction after
635
+ # executing the service procedure for 'interfaceOperation', the
636
+ # name of which is passed to the macro in a formal parameter.
637
+ #
638
+ # Actions:
639
+ #
640
+ # - restore contract state from top from transaction stack if
641
+ # 'interfaceOperation' has failed
642
+ #
643
+ # - remove contract state from transaction stack
644
+ #
645
+ # @return [Al::Macro] macro implmenting service exit actions
646
+ def ethServiceExitCommon #
647
+
648
+ interfaceOperation = "input"
649
+
650
+ alApi.macro( Constants::META_ETH ) do
651
+ name Constants::RUNTIME_SERVICE_DONE # service_done
652
+ parameter interfaceOperation
653
+ block do
654
+
655
+ ifThen( ethServiceFail(ethLocalReference(interfaceOperation)) ) do
656
+
657
+ # Restore state from stack
658
+ # - eth_accounts := Head( eth_accounts_stack );
659
+ # - eth_storageRoot := Head( eth_storageRoot_stack );
660
+ # - eth_mined := Head( eth_mined_stack );
661
+ assignTo do
662
+ variable ethVariableReference( nAccount )
663
+ rval ethStackTop( Constants::VARIABLE_ACCOUNTS_STACK )
664
+ end
665
+
666
+
667
+ assignTo do
668
+ variable ethVariableReference( nStorageRoot )
669
+ rval ethStackTop( Constants::VARIABLE_STORAGE_ROOT_STACK )
670
+ end
671
+
672
+ assignTo do
673
+ variable ethVariableReference( nMined )
674
+ rval ethStackTop( Constants::VARIABLE_MINED_STACK )
675
+ end
676
+
677
+ end # if then block
678
+
679
+ # Remove contract state from stack
680
+ assignTo do
681
+ variable ethVariableReference( Constants::VARIABLE_ACCOUNTS_STACK )
682
+ rval ethStackRest(Constants::VARIABLE_ACCOUNTS_STACK )
683
+ end
684
+ assignTo do
685
+ variable ethVariableReference( Constants::VARIABLE_STORAGE_ROOT_STACK )
686
+ rval ethStackRest(Constants::VARIABLE_STORAGE_ROOT_STACK )
687
+ end
688
+ assignTo do
689
+ variable ethVariableReference( Constants::VARIABLE_MINED_STACK )
690
+ rval ethStackRest(Constants::VARIABLE_MINED_STACK )
691
+ end
692
+
693
+
694
+ end # block
695
+ end # macro
696
+ end # ethServiceExitCommon
697
+
698
+
699
+ # Crerate AL object for free text tla-snippets.
700
+ #
701
+ # @return [Al:Snippet] Snippet object with name 'snippetName' and txt 'snippetTxt'
702
+ def ethSnippet( snippetName, snippetTxt, snippetMeta=Constants::META_ETH)
703
+ alApi.snippet( snippetMeta ) {
704
+ name snippetName
705
+ txt snippetTxt
706
+ }
707
+ end
708
+
709
+ # @param contractName [String] name of Solidity contract of 'functionName'
710
+ # @param functionName [String] name of function modeled as a transaction
711
+ # @param requestParameters [lambda] lambda function returning function input parameters
712
+ #
713
+ # @param responseParameters [lambda] lambda function returning
714
+ # function return parameters
715
+ #
716
+ # @param sourceLink [Hash] link to solidity implementation with
717
+ # :sourceModule, and :sourceLine
718
+ #
719
+ # @return [Al::Transaction] transaction object for operation
720
+ # 'functionName' in contract 'contractName'
721
+ #
722
+ # @yield [Al::Block] yield block which should be filled with AlObject for statemens
723
+ def ethTransaction( contractName, functionName, requestParameters: nil, responseParameters: nil, sourceLink: nil )
724
+ @logger.info( "#{__method__}: contractName=#{contractName}, functionName=#{functionName}, sourceLink=#{sourceLink}")
725
+ @logger.debug( "#{__method__}: contractName=#{contractName}/#{functionName}, requestParameters=#{requestParameters}, responseParameters=#{responseParameters}") if @logger.debug?
726
+
727
+ # Common lambda to add parameters for request/response using a
728
+ # hash with :type, :name and optional :domain -properties
729
+ lMessageParameters = ->(paramSetBuilder, parameterHash) do
730
+ case parameterHash[:type]
731
+ when :ElementaryTypeName, :VariableDeclarationWithDomain
732
+ paramSetBuilder.parameter parameterHash[:name], parameterHash[:isArray], parameterHash[:domain]
733
+ else
734
+ raise MissingImplementation, "Unkwon type #{parameterHash[:type]} in parameterHash #{parameterHash}"
735
+ end
736
+ end
737
+
738
+ alApi.transaction( Constants::META_ETH ) do
739
+
740
+ name contractName
741
+ # Empty operation name for constructor
742
+ operation nContractFunctionOperation( contractName, functionName)
743
+ # Keep address of currently running contract in local variable
744
+ local nRunningContractAddress
745
+
746
+ returnOption true # infrastructureService --> generate response slot
747
+
748
+ interfaceOption true # interfaceService --> generate interface process && t_req, r_response
749
+
750
+ # Add link to source code - if possible
751
+ if ( sourceLink ) then
752
+ sourceModule sourceLink[:sourceModule]
753
+ sourceLine sourceLink[:sourceLine]
754
+ end
755
+
756
+ # Define request message
757
+ request alApi.request {
758
+
759
+ requestParameters.each{ |parameterHash| lMessageParameters[self, parameterHash] } if requestParameters
760
+ }
761
+
762
+ # Define response message
763
+ response alApi.response {
764
+
765
+ Constants::COMMON_RPLY.each do |fldDomPair|
766
+ parameter(fldDomPair[0], fldDomPair[1] )
767
+ end
768
+ if (contractName == functionName ) then
769
+ # Solidity Contructor returns address created
770
+ isArray = false
771
+ parameter Constants::FIELD_NAME_ADDRESS, isArray, Constants::DOMAIN_ADDRESS
772
+ end
773
+
774
+ responseParameters.each{ |parameterHash| lMessageParameters[self, parameterHash] } if responseParameters
775
+ }
776
+
777
+ # yield txBlock (to be populated with AlObjects for stmts)
778
+ block {
779
+ yield self
780
+
781
+
782
+ } if block_given? # block
783
+
784
+ end # alApi.transaction( Constants::META_ETH ) do
785
+ end # def ethTransaction
786
+
787
+ # # @return [Array<Array>] array common request parameters,
788
+ # # where each element defines field-name, and domain-name
789
+ # #
790
+ # # @param contractName [String] f
791
+ # #
792
+ # # @param functionName [String] for constructor return differnt
793
+ # # request message
794
+ # #
795
+ # def ethTransactionCommonRequestParameter( contractName, functionName )
796
+ # isConstructor( contractName, functionName ) ?
797
+ # Constants::COMMON_REQ_CONSTRUCTOR : Constants::COMMON_REQ_MSG
798
+ # end
799
+
800
+ # {#ethTransactionNewAccount}: Service procedure for
801
+ # infrastructure service 'geth(newAccount).
802
+ #
803
+ #
804
+ # We need to have a way to create initial account because
805
+ # 'normal' solidity transactions require that sender account
806
+ # exists.
807
+ #
808
+ # Actions:
809
+ #
810
+ # - reserve NextId address from 'addressPool'
811
+ # - create entry into 'accounts' variable'
812
+ # - create entry into 'storageRoot' variable
813
+ # - cumulate value in 'mined'
814
+ #
815
+ def ethTransactionNewAccount
816
+
817
+ valueMined = ethFunctionParameterReference( Constants::CONTRACT_INFRA, Constants::SERVICE_NEW_ACCOUNT, Constants::FIELD_NAME_VALUE )
818
+
819
+ # No contracts for an account
820
+ contractAst = nil
821
+
822
+ # only COMMON_REQ_MSG -parameters
823
+ requestParameters = ->() do
824
+ Constants::COMMON_REQ_MSG.map do |name_domain|
825
+ ethVariableDeclaration( VariableDeclarationWithDomain.create(name_domain) )
826
+ end
827
+ end
828
+
829
+ ethTransaction(Constants::CONTRACT_INFRA, Constants::SERVICE_NEW_ACCOUNT, requestParameters: requestParameters.call ) do |txBlock|
830
+
831
+ # accouts := accounts \union [ address |-> NextId, balance |-> input.value ]
832
+ ethActionNewAddress(txBlock, contractAst, valueMined )
833
+
834
+ # storage root
835
+ ethActionNewStorageRoot( txBlock, contractAst )
836
+
837
+ # mined := mined + value
838
+ ethActionMine( txBlock, valueMined )
839
+
840
+ # eth_AddressPool := eth_AddressPool \ { NextId() }
841
+ ethActionConsumeAddress( txBlock )
842
+
843
+ end
844
+
845
+ end
846
+
847
+ # Delete from 'address' and 'storageRoot' entry pointed by
848
+ # 'refRunningContractAddress' (=addess of currently running
849
+ # contract). Move balance on delete account 'astRecipient'
850
+ #
851
+ # @param astRecipient [SexpAst] recient to receive balance from
852
+ # the deleted contract
853
+ #
854
+ #
855
+ def ethDeleteAccount( txBlock, astRecipient )
856
+ @logger.debug( "#{__method__}: astRecipient=#{astRecipient}") if @logger.debug?
857
+
858
+ # Delete account of currently running contract
859
+ exprAccountToDelete = refRunningContractAddress
860
+
861
+ # Account entries to process
862
+ setIteratorVariable = "accountEntry"
863
+ noAction = setIteratorVariable
864
+
865
+ # Identifiy account to receive balance of deleted account
866
+ exprRecipient = exprRecipient = dispatchExpression( astRecipient )
867
+ shouldAddToRecipient =
868
+ alApi.equal(tlaRecordField( setIteratorVariable, Constants::FIELD_NAME_ADDRESS), exprRecipient )
869
+
870
+ # expression to move balace
871
+ exprValueToMove = alApi.tlaRecordField(
872
+ alApi.tlaOperatorCall( Constants.templateSpecname(Constants::RUNTIME_GET_ELEMENT),
873
+ [
874
+ alApi.expression(refAccount),
875
+ alApi.expression(Constants::FIELD_NAME_ADDRESS),
876
+ exprAccountToDelete,
877
+ ]),
878
+ Constants::FIELD_NAME_BALANCE
879
+ )
880
+ exprMoveToRecipient = alApi.tlaEXCEPT(
881
+ setIteratorVariable,
882
+ [ alApi.equal(tlaRecordField("!", Constants::FIELD_NAME_BALANCE ),
883
+ alApi.plus( tlaRecordField( setIteratorVariable, Constants::FIELD_NAME_BALANCE ), exprValueToMove ))])
884
+
885
+ # Expression for move balanace
886
+ moveExpr =
887
+ alApi.tlaSetGenerate(
888
+ alApi.tlaIF( shouldAddToRecipient,
889
+ exprMoveToRecipient,
890
+ noAction
891
+ ),
892
+ setIteratorVariable,
893
+ alApi.expression(refAccount)
894
+ )
895
+
896
+ entryWithBalanceMoved = 'b'
897
+ # Access all expect except entry from currently running contract
898
+ exprAccepEntries = ethBinaryExpression(alApi.tlaRecordField(
899
+ entryWithBalanceMoved, Constants::FIELD_NAME_ADDRESS),
900
+ binaryOperator( "!="),
901
+ exprAccountToDelete )
902
+ exprAccountsAfterDelete = alApi.tlaSetSelect( entryWithBalanceMoved, moveExpr, exprAccepEntries )
903
+
904
+
905
+ # eth_account := 'exprAccountsAfterDelete'
906
+ txBlock.assignTo do
907
+ variable refAccount
908
+ rval exprAccountsAfterDelete
909
+ end
910
+
911
+ # eth_storageRoot := storageRoot :setMinus {exprAccountToDelete}
912
+ # ethActionDeleteEntry( txBlock, refStorageRoot, exprAccountToDelete )
913
+ txBlock.assignTo do
914
+ variable refStorageRoot
915
+ rval ethVariableMinus(
916
+ refStorageRoot,
917
+ alApi.tlaSetSelect(
918
+ "var", alApi.expression(refStorageRoot),
919
+ alApi.equal( alApi.tlaRecordField("var", "address"), exprAccountToDelete )
920
+ )
921
+ )
922
+ end
923
+
924
+ end
925
+
926
+ # @!endgroup
927
+
928
+ # ------------------------------------------------------------------
929
+ # @!group Actions
930
+
931
+ # Action in 'txBlock' to create new entry into 'accounts' variable.
932
+ #
933
+ # @param txBlock [Al::BlockBuilder] block to add action into
934
+ #
935
+ # @param contractAst [SexpAst] AST for contract to construct, nil
936
+ # for human account (geth(newAccount))
937
+ #
938
+ # @param value [Integer] initial value to assign to account, default 0
939
+ #
940
+ # eth_accounts := eth_accounts,
941
+ # { [ address |-> NextId( eth_address_free, input.address ), balance |-> 'value', codeHash |-> input.codeHash ]}
942
+
943
+ def ethActionNewAddress(txBlock, contractAst=nil, value=0)
944
+ # accounts := accounts \union [ address |-> xx, balance |-> 0, codeHash |-> Nil ]
945
+ # codeHash = contractAst.nil? ? nil : contractAst['name']
946
+ @logger.info "#{__method__}: contractAst=#{contractAst.nil? ? 'contractAst was nil' : contractAst['name']}"
947
+ txBlock.assignTo do
948
+ variable refAccount
949
+ rval ethVariableUnion(
950
+ refAccount,
951
+ alApi.tlaSet(alApi.tlaRecord( ethNewAccountRecord( contractAst, value) )) )
952
+ end
953
+ end
954
+
955
+
956
+
957
+ # Action in 'txBlock' to cumulate 'value' to total value mined.
958
+ #
959
+ # @param txBlock [Al::BlockBuilder] block to add action into
960
+ #
961
+ # @param value [Al::Expression] value to cumulate to 'mined'
962
+ def ethActionMine( txBlock, value )
963
+
964
+ txBlock.assignTo do
965
+ variable ethVariableReference(Constants::VARIABLE_MINED)
966
+ rval ethBinaryExpression(
967
+ alApi.expression(ethVariableReference(Constants::VARIABLE_MINED)),
968
+ binaryOperator( '+'),
969
+ value
970
+ )
971
+ end
972
+
973
+ end
974
+
975
+
976
+ # Action 'txBlock' to create new entry into 'storageRoot' variable
977
+ #
978
+ # @param txBlock [Al::BlockBuilder] block to add action into
979
+ #
980
+ # @param contractAst [SexpAst] AST for contract to construt
981
+ def ethActionNewStorageRoot(txBlock, contractAst=nil)
982
+ txBlock.assignTo do
983
+ variable refStorageRoot
984
+ rval ethVariableUnion(
985
+ refStorageRoot,
986
+ alApi.tlaSet(alApi.tlaRecord( ethNewStorageRootRecord(contractAst) )) )
987
+ end
988
+ end
989
+
990
+ # Add actions to check that currently contract for currently
991
+ # executing transaction exists. For a constructor that account
992
+ # has been created before reaching this point, but for a message
993
+ # call a situation of non-existing contract code may take place.
994
+ #
995
+ # TODO: add implementation to create empty account && move value
996
+ #
997
+ def ethActionCheckContractExists( txBlock, contractAst, functionAst )
998
+
999
+ getCurrentAccountExists =
1000
+ alApi.not(
1001
+ alApi.tlaOperatorCall(
1002
+ Constants.templateSpecname(Constants::RUNTIME_ELEMENT_EXISTS),
1003
+ [
1004
+ alApi.expression(refAccount),
1005
+ alApi.expression(Constants::FIELD_NAME_ADDRESS),
1006
+ refRunningContractAddress,
1007
+ ]
1008
+ )
1009
+ )
1010
+
1011
+ warn "Implementation missing for action ethActionCheckContractExists"
1012
+
1013
+ txBlock.ifThen( getCurrentAccountExists ) {
1014
+ # if block
1015
+ ifError = self
1016
+ # jump to failure
1017
+ ifError.done( nEthnInterfaceOperation(contractAst['name'], functionAst['name'] ))
1018
+ } # ifThen
1019
+
1020
+ end
1021
+
1022
+ # Add action to call procedure contractCalled(functionCalled)
1023
+ # with parameters.
1024
+ #
1025
+ # @param functionCalled [String] name of called function
1026
+ #
1027
+ # @param txBlock [Al::BlockBuilder] block to add action into
1028
+ #
1029
+
1030
+ def ethCallStatement( txBlock, contractCalled, functionCalled, parameters )
1031
+ @logger.info "#{__method__}: call to #{contractCalled}(#{functionCalled})"
1032
+ txBlock.callService( Constants::META_ETH, contractCalled, nContractFunctionOperation(contractCalled, functionCalled) ) do |call|
1033
+ call.parameter( parameters )
1034
+ end
1035
+
1036
+ end
1037
+
1038
+ # Call 'contractCalled(functionCalled)' within transaction with
1039
+ # 'paramters'. After the call use
1040
+ # {Constants::RUNTIME_SERVICE_DONE} to check tx status pop
1041
+ # tx-stack.
1042
+ #
1043
+ # Implementation make a macro call to
1044
+ # 'nContractFunctionOperation' within namespace
1045
+ # META_SERVICE_ENTRY
1046
+
1047
+ def ethCallTransaction( txBlock, contractCalled, functionCalled, parameters )
1048
+ @logger.info "#{__method__}: call to #{nInterfaceOperation(contractCalled, functionCalled)} )"
1049
+ txBlock.callMacro( Constants::META_SERVICE_ENTRY, nInterfaceOperation(contractCalled, functionCalled) ) do |call|
1050
+ call.parameter( parameters )
1051
+ end
1052
+
1053
+ # macro makes a function call --> need to label
1054
+ txBlock.label
1055
+
1056
+ # restore state if error - pop stack
1057
+ txBlock.callMacro( Constants::META_ETH, Constants::RUNTIME_SERVICE_DONE) do |call|
1058
+ call.parameter nInterfaceOperation(contractCalled, functionCalled)
1059
+ end
1060
+
1061
+ # macro makes uses if --> need to label
1062
+ txBlock.label
1063
+
1064
+ end
1065
+
1066
+ # @param callType [:normal, :constructor] Normal call and
1067
+ # constructor call have different paramatere
1068
+ #
1069
+ #
1070
+ # @param parameters [Array<[String, Al::Expression]>]
1071
+ # parameters to pass to function
1072
+ #
1073
+ # @return [TlaRecord] record with fiel mappings
1074
+ def ethCallParameters( callType, callerContract, callerFunction, commonRequest, parameters )
1075
+ alApi.tlaRecord(
1076
+ commonRequest +
1077
+ parameters.map do |name_expression|
1078
+ raise "Name was nil in #{name_expression} " unless name_expression[0]
1079
+ alApi.tlaRecordFieldDef( ethLocalReference(name_expression[0]), name_expression[1] )
1080
+ end
1081
+ )
1082
+ end
1083
+
1084
+ # Create array of parameter mappings for common request
1085
+ # parameters (value, sender, recipient, originator) based on
1086
+ # callType.
1087
+ #
1088
+ # @param callType [:call, :create, :callcode, :delegatecall]
1089
+ # Controls parameter mapping
1090
+ #
1091
+ # @param callerContract [String] name of contract making the call
1092
+ #
1093
+ # @param callerFunction [String] name of tx-function where call
1094
+ # is invoked
1095
+ #
1096
+ # @param functionCallStmt [:FunctionCall] sexp AST for function call, to
1097
+ # evaluate for recipient
1098
+ #
1099
+ # @param valueModifierExpression [tlaExpresssion] value to
1100
+ # transfer, default to 0 if no value modifier in call
1101
+ #
1102
+ #
1103
+ # @return [Array<tlaRecorField>] field mappings created by
1104
+ # tlaRecordFieldDef (name |-> tlaExpression ) for
1105
+ # common parameters
1106
+ def ethCallCommonRequestParameters( callType, callerContract, callerFunction, functionCallStmt, valueModifierExpression )
1107
+
1108
+ # Iterate ':map_fields', map fields to [name, expression] pair
1109
+ # using ':mapper' (with default parameter expression), convert
1110
+ # to 'tlaRecordFieldDef'.
1111
+
1112
+ mapper = getParameterMapper(callType)
1113
+
1114
+ # map_fields mapped + add_fieds added
1115
+ (
1116
+ mapper[:map_fields].map do |name_domain|
1117
+ # pair [ name, expression ]
1118
+ [
1119
+ name_domain[0],
1120
+ (
1121
+ mapper[:mapper][name_domain[0]] ||
1122
+ ->(api, callerContract, callerFunction, functionCallStmt, name, valueExpression ) { ethFunctionParameterExpression(callerContract, callerFunction, name ) }
1123
+ ).call(self, callerContract, callerFunction, functionCallStmt, name_domain[0], valueModifierExpression )
1124
+ ]
1125
+ end +
1126
+
1127
+ mapper[:add_fields].map do |addfield|
1128
+ # pair [field, lambda.call]
1129
+ [
1130
+ addfield[0],
1131
+ addfield[1].call( self, callerContract, callerFunction, functionCallStmt, addfield[0], valueModifierExpression )
1132
+ ]
1133
+ end
1134
+
1135
+ ).
1136
+
1137
+
1138
+ # Create tlaRecordFieldDef
1139
+
1140
+ map do |name_init|
1141
+ # name |-> contract_function_input.name
1142
+ alApi.tlaRecordFieldDef( name_init[0], name_init[1] )
1143
+ end
1144
+ end
1145
+
1146
+
1147
+ # Maps 'callType' to hash defining how to set common paramerters
1148
+ # sender, originator, value, recipient for the 'callType'.
1149
+ # Hash defines
1150
+ #
1151
+ # :map_fields : array of field_names (actually [name,domain] pairs)
1152
+ #
1153
+ # :mapper : to act upon :map_fields to create [name,tla-expr] pairs
1154
+ #
1155
+ # :add_fields: array of [name, tla-expr] -pairs to add to common
1156
+ # request
1157
+
1158
+ def getParameterMapper( callType )
1159
+
1160
+ parameter_mapper = {
1161
+
1162
+ # local call
1163
+ :delegatecall => {
1164
+ # - sender=Is (no change),
1165
+ # - origin:Io(no change),
1166
+ # - recipient=no change(=current contact=thisCid),
1167
+ # - value : no change
1168
+ # - add field APPARENT_VALUE_FIELD to flag that no value transferred
1169
+ :map_fields => Constants::COMMON_REQ_MSG,
1170
+ :mapper => {
1171
+ # Constants::FIELD_NAME_VALUE => ->(api, callerContract, callerFunction, functionCallStmt, name, valueExpression) { valueExpression },
1172
+ Constants::FIELD_NAME_SENDER => ->(api, callerContract, callerFunction, functionCallStmt, name, valueExpression) { api.exprRunningContractAddress },
1173
+ },
1174
+ :add_fields => [
1175
+ [
1176
+ Constants::RUNTIME_APPARENT_VALUE_FIELD,
1177
+ ->(api, callerContract, callerFunction, functionCallStmt, name, valueExpression ) { api.expression(true) },
1178
+ ]],
1179
+ },
1180
+
1181
+ # remote call:
1182
+ :call => {
1183
+ # - sender: Ia = account exectuting
1184
+ # - origin:Io (no change),
1185
+ # - recipient: eval call
1186
+ # - value=value expression
1187
+ :map_fields => Constants::COMMON_REQ_MSG,
1188
+ :mapper => {
1189
+ Constants::FIELD_NAME_VALUE => ->(api, callerContract, callerFunction, functionCallStmt, name, valueExpression) { valueExpression },
1190
+ Constants::FIELD_NAME_SENDER => ->(api, callerContract, callerFunction, functionCallStmt, name, valueExpression) { api.exprRunningContractAddress },
1191
+ Constants::FIELD_NAME_RECIPIENT =>
1192
+ ->(api, callerContract, callerFunction, functionCallStmt, name, valueExpression ) do
1193
+
1194
+ # remote calls either var.func() or this.func(), where
1195
+ # 'var' and 'this' in :MemberAccess AST node eval
1196
+ # struct of MemberAccess expression msg.recipient
1197
+
1198
+ api.dispatchExpression(functionCallStmt.callExpression)
1199
+
1200
+ end,
1201
+ },
1202
+ :add_fields => [],
1203
+ },
1204
+
1205
+ # constructor
1206
+ :create => {
1207
+ # - sender: Ia = account exectuting
1208
+ # - origin:Io (no change),
1209
+ # - value=value expression
1210
+ :map_fields => Constants::COMMON_REQ_CONSTRUCTOR,
1211
+ :mapper => {
1212
+ Constants::FIELD_NAME_SENDER => ->(api, callerContract, callerFunction, functionCallStmt, name, valueExpression) { api.exprRunningContractAddress },
1213
+ Constants::FIELD_NAME_VALUE => ->(api, callerContract, callerFunction, functionCallStmt, name, valueExpression) { valueExpression },
1214
+ },
1215
+ :add_fields => [],
1216
+ }
1217
+ }
1218
+
1219
+ # TODO: add :callcode, libary?
1220
+ raise "Unknown callType #{callType}, expect one of #{parameter_mapper.keys.join(",")}" if parameter_mapper[callType].nil?
1221
+ parameter_mapper[callType]
1222
+
1223
+ end
1224
+
1225
+ # Add actions moving value from sender to recipient. Expect that
1226
+ # both sender and recipient exist.
1227
+ #
1228
+ # Must check that sender # recipent and make no balance move
1229
+ #
1230
+ # Creates something like this:
1231
+ # { IF addR THEN bal = bal + msg.value ELSE IF minusS THEN bal =
1232
+ # bal -msg.value ELSE a : a \in eth_account }
1233
+ #
1234
+ # @param txBlock [Al::BlockBuilder] block to add action into
1235
+ #
1236
+ # @param contractAst [AstSexp] contract to construct
1237
+ #
1238
+ # @param functionAst [AstSexp] ast of contract constructor
1239
+ def ethActionMovevalue( txBlock, contractAst, functionAst)
1240
+
1241
+ setIteratorVariable = "a"
1242
+
1243
+ # Value to move
1244
+ msgValue = ethFunctionParameterReference( contractAst['name'], functionAst['name'], Constants::FIELD_NAME_VALUE )
1245
+
1246
+ # Recipient and sender of the value move
1247
+ recipient = refRunningContractAddress
1248
+ sender = ethFunctionParameterReference( contractAst['name'], functionAst['name'], Constants::FIELD_NAME_SENDER )
1249
+
1250
+ isNotApparentValue = alApi.tlaOperatorCall( Constants.templateSpecname(Constants::RUNTIME_APPARENT_VALUE),
1251
+ [ethFunctionParameterExpression( contractAst['name'], functionAst['name'] )])
1252
+
1253
+ senderNotEqualRecipient = ethBinaryExpression( sender, binaryOperator( "!="), recipient )
1254
+
1255
+ # Conditions for the move
1256
+ shouldAddToRecipient = alApi.and(
1257
+ isNotApparentValue,
1258
+ alApi.and(
1259
+ senderNotEqualRecipient,
1260
+ alApi.equal(tlaRecordField( setIteratorVariable, Constants::FIELD_NAME_ADDRESS), recipient )
1261
+ ))
1262
+
1263
+ shouldRemoveFromSender = alApi.and(
1264
+ isNotApparentValue,
1265
+ alApi.and(
1266
+ senderNotEqualRecipient,
1267
+ alApi.equal(tlaRecordField( setIteratorVariable, Constants::FIELD_NAME_ADDRESS), sender )
1268
+ ))
1269
+
1270
+ # Expression for updating balance
1271
+ exprAddToRecipient = alApi.tlaEXCEPT(
1272
+ setIteratorVariable,[
1273
+ alApi.equal( tlaRecordField("!", Constants::FIELD_NAME_BALANCE ),
1274
+ alApi.plus( tlaRecordField( setIteratorVariable, Constants::FIELD_NAME_BALANCE ), msgValue ))]
1275
+ )
1276
+
1277
+ exprRemoveFromSender = alApi.tlaEXCEPT(
1278
+ setIteratorVariable,[
1279
+ alApi.equal( tlaRecordField("!", Constants::FIELD_NAME_BALANCE ),
1280
+ alApi.minus(tlaRecordField( setIteratorVariable, Constants::FIELD_NAME_BALANCE ), msgValue)) ,
1281
+ ])
1282
+
1283
+ noAction = setIteratorVariable
1284
+
1285
+
1286
+ # Put it all together
1287
+ moveExpr = alApi.tlaSetGenerate(
1288
+ alApi.tlaIF( shouldAddToRecipient,
1289
+ exprAddToRecipient,
1290
+ alApi.tlaIF(
1291
+ shouldRemoveFromSender,
1292
+ exprRemoveFromSender,
1293
+ noAction
1294
+ )
1295
+ ),
1296
+ setIteratorVariable,
1297
+ alApi.expression(refAccount)
1298
+ )
1299
+
1300
+ # Change variable 'eth_accounts'
1301
+ txBlock.assignTo do
1302
+ variable refAccount
1303
+ rval moveExpr
1304
+ end
1305
+
1306
+ end
1307
+
1308
+ # Add action to check that codeHash of the currenly executing
1309
+ # transaction matches code being executed (=check added in this
1310
+ # method). In error failure.
1311
+ #
1312
+ # @param txBlock [Al::BlockBuilder] block to add action into
1313
+ #
1314
+ # @param contractAst [AstSexp] contract to construct
1315
+ #
1316
+ # @param functionAst [AstSexp] ast of contract constructor
1317
+ def ethActionCheckContractType( txBlock, contractAst, functionAst )
1318
+
1319
+ getCurrentAccountCodeHash =
1320
+ alApi.tlaRecordField(
1321
+ alApi.tlaOperatorCall(
1322
+ Constants.templateSpecname(Constants::RUNTIME_GET_ELEMENT),
1323
+ [ alApi.expression(refAccount),
1324
+ alApi.expression(Constants::FIELD_NAME_ADDRESS),
1325
+ refRunningContractAddress, ]
1326
+ ), Constants::FIELD_NAME_CODE_HASH
1327
+ )
1328
+
1329
+ txBlock.ifThen(
1330
+ ethBinaryExpression(
1331
+ getCurrentAccountCodeHash,
1332
+ binaryOperator( "!=" ),
1333
+ contractAst['name'] )
1334
+ ) {
1335
+ # block represent error
1336
+ ifError = self
1337
+ # jump to failure
1338
+ ifError.done( nEthnInterfaceOperation(contractAst['name'], functionAst['name'] ))
1339
+ } # ifThen
1340
+
1341
+ end
1342
+
1343
+ # eth_AddressPool := eth_AddressPool \ { NextId() }
1344
+ def ethActionConsumeAddress(txBlock)
1345
+ txBlock.assignTo do
1346
+ variable refFreeAddressPool
1347
+ rval ethVariableMinus( refFreeAddressPool, alApi.tlaSet( ethNextId ) )
1348
+ end
1349
+ end
1350
+
1351
+ # Action to initialize pointer to currently running contract
1352
+ # 'this' in local variable of contract service
1353
+ #
1354
+ # @param txBlock [Al::BlockBuilder] block to add action into
1355
+ #
1356
+ # @param thisExpression [Al::Expression] expression to set this
1357
+ # to. NB: nextId or msg.sender
1358
+ #
1359
+ def ethActionInitThis( txBlock, thisExpression )
1360
+ txBlock.assignTo do
1361
+ variable refRunningContractAddress
1362
+ rval thisExpression
1363
+ end
1364
+ end
1365
+
1366
+ # Create an action to initiaze SBUILDER_RETURN status and
1367
+ # reponse values.
1368
+ #
1369
+ # Init value for response is a record, which assings domain init
1370
+ # value for each 'parameter' in 'responseParameters' of
1371
+ # 'functionAst'. Reponse value Nil no 'responseParameters'.
1372
+ #
1373
+
1374
+ def ethActioInitReturnValue( txBlock, contractAst, functionAst )
1375
+ @logger.debug "#{__method__}: functionAst=#{functionAst}" if @logger.debug?
1376
+
1377
+ # Type name of function response
1378
+ functionResponseTypeName =
1379
+
1380
+
1381
+ responseInit =
1382
+
1383
+ if functionAst.responseParameters.parameters.length == 0
1384
+
1385
+ # No return parametes - Nil
1386
+ alApi.tlaNil
1387
+
1388
+ else
1389
+
1390
+ # Create init record, which initialized 'parameter' with domain initial value
1391
+ # in 'functionResponseType'
1392
+ alApi.tlaRecord(
1393
+ functionAst.responseParameters.parameters.map do |parameter|
1394
+ alApi.tlaRecordFieldDef(
1395
+ parameter["name"],
1396
+ Constants.snippetFacadeResponseDomainInit(
1397
+ nInterfaceOperation(contractAst["name"], functionAst["name"]), parameter["name"] )
1398
+ )
1399
+ end
1400
+ )
1401
+ end
1402
+
1403
+ @logger.debug "#{__method__}: responseInit=#{responseInit.strOutput}" if @logger.debug?
1404
+
1405
+ txBlock.callMacro( Constants::SBUILDER_RETURN ) do |call|
1406
+ call.parameter nInterfaceOperation( contractAst["name"], functionAst["name"] )
1407
+ call.parameter true
1408
+ call.parameter responseInit
1409
+ end
1410
+
1411
+ end
1412
+
1413
+
1414
+ # Action sets set status 'true', data value nil
1415
+ #
1416
+ # @param txBlock [Al::BlockBuilder] block to add action into
1417
+ #
1418
+ # @param contractName [String] contract for the function
1419
+ # 'functionName'
1420
+ #
1421
+ # @param functionName [String] name of the function to return
1422
+ # from
1423
+ #
1424
+
1425
+ #
1426
+ def ethActionFinalReturn( txBlock, contractName, functionName )
1427
+
1428
+ retExpression = if isConstructor( contractName, functionName )
1429
+ alApi.tlaRecord(
1430
+ alApi.tlaRecordFieldDef(
1431
+ Constants::FIELD_NAME_ADDRESS,
1432
+ alApi.expression(refRunningContractAddress)
1433
+ ))
1434
+ else
1435
+ alApi.tlaNil
1436
+ end
1437
+ txBlock.ret true, retExpression
1438
+
1439
+ end
1440
+
1441
+ # Call txBlock.done (=to jump to end label) in function
1442
+ # 'functionName' of contract 'contractName'.
1443
+ #
1444
+ # @param txBlock [Al::BlockBuilder] block to add action into
1445
+ #
1446
+ # @param contractName [String] contract for the function
1447
+ # 'functionName'
1448
+ #
1449
+ # @param functionName [String] name of the function to return
1450
+ # from
1451
+ #
1452
+ def ethActionGotoEnd( txBlock, contractName, functionName )
1453
+ txBlock.done( nEthnInterfaceOperation(contractName, functionName))
1454
+ end
1455
+
1456
+ # Set field 'variableAst' in contact 'contractAst' to
1457
+ # 'expressionAst'.
1458
+ #
1459
+ # For example:
1460
+ # { IF a.address = contractId THEN [a EXCEPT !.variable[indexExpession] = a.field + 1] ELSE a: a \in eth_storageRoot }
1461
+ #
1462
+ # @param contractAst [AstSexp] :ContractDefinition
1463
+ #
1464
+ # @param identifierAst [AstSexp] lval to update
1465
+ #
1466
+ # @param expressionAst [AstSexp] rval expression for update
1467
+ #
1468
+ # @return [Al::Expression] result of translatating
1469
+ # 'expressionAst' to update contract state variable
1470
+ # 'identifierAst' in contract 'contractAst'
1471
+ #
1472
+ def ethAssignStatement(contractAst, identifierAst, expressionAst)
1473
+ @logger.debug "#{__method__}: identifierAst=#{identifierAst}" if @logger.debug?
1474
+
1475
+ setIteratorVariable = "a"
1476
+ # create context allowing contract to be found in variable 'a'
1477
+ expressionContext = ExpressionContext.createExpressionContext( expressionAst )
1478
+ expressionContext.setContextContract( setIteratorVariable )
1479
+
1480
+ # tranlate expresion within context
1481
+ expr = Ethereum.dispatcher( expressionDispatcher, expressionContext.sexp_type, expressionContext )
1482
+
1483
+ # translate IF THEN EXCEPT tla-expression
1484
+ alApi.tlaSetGenerate(
1485
+ alApi.tlaIF(
1486
+ ethBindToRecipient(setIteratorVariable),
1487
+ alApi.tlaEXCEPT(
1488
+ setIteratorVariable,
1489
+ alApi.equal(
1490
+ tlaRecordField(
1491
+ "!",
1492
+ Ethereum.dispatcher( lvalAccessDistpatcher,identifierAst.sexp_type, identifierAst )),
1493
+ expr )),
1494
+ setIteratorVariable),
1495
+ setIteratorVariable,
1496
+ alApi.expression(refStorageRoot) )
1497
+ end
1498
+
1499
+
1500
+ # Implement actions for EMV 0xf0, CREATE
1501
+ def ethActionCall_create( txBlock, contractAst, functionAst, functionCallStmt, parameters, contractCalled, functionCalled )
1502
+ @logger.debug "#{__method__}: functionCallStmt=#{functionCallStmt}" if @logger.debug?
1503
+
1504
+ # procedure call for local calls
1505
+ ethCallStatement(
1506
+ txBlock,
1507
+ contractCalled['name'], functionCalled['name'], parameters
1508
+ ) # ethCallStatement
1509
+
1510
+ # if call failed? then fail caller
1511
+ txBlock.ifThen(
1512
+ ethServiceFail(
1513
+ exprInterfaceOperation(
1514
+ contractCalled['name'],
1515
+ functionCalled['name'] )
1516
+ )) do
1517
+ failure nEthnInterfaceOperation(contractAst['name'], functionAst['name'] )
1518
+ end
1519
+ end
1520
+
1521
+ # Implement actions for EMV 0xf1 CALL
1522
+ def ethActionCall_call( txBlock, contractAst, functionAst, functionCallStmt, parameters, contractCalled, functionCalled )
1523
+ @logger.debug "#{__method__}: functionCallStmt=#{functionCallStmt}" if @logger.debug?
1524
+ # procedure call tx
1525
+ ethCallTransaction(
1526
+ txBlock,
1527
+ contractCalled['name'], functionCalled['name'], parameters
1528
+ )
1529
+
1530
+ # if call failed? then fail caller
1531
+ txBlock.ifThen(
1532
+ ethServiceFail(
1533
+ exprInterfaceOperation(
1534
+ contractCalled['name'],
1535
+ functionCalled['name'] )
1536
+ )) do
1537
+ failure nEthnInterfaceOperation(contractAst['name'], functionAst['name'] )
1538
+ end
1539
+
1540
+ end
1541
+
1542
+ # Implement actions for EMV 0xf2 CALLCODE
1543
+ def ethActionCall_callcode( txBlock, contractAst, functionAst, functionCallStmt, parameters, contractCalled, functionCalled )
1544
+ raise ":callcode not yet implemented for, used in #{functionCallStmt}"
1545
+ end
1546
+
1547
+ # Implement actions for EMV 0xf4 DELEGATECALL. It is local call
1548
+ #
1549
+ #
1550
+ #
1551
+ def ethActionCall_delegatecall( txBlock, contractAst, functionAst, functionCallStmt, parameters, contractCalled, functionCalled )
1552
+ ethCallStatement(
1553
+ txBlock,
1554
+ contractCalled['name'], functionCalled['name'], parameters
1555
+ ) # ethCallStatement
1556
+
1557
+ # if call failed? then fail caller
1558
+ txBlock.ifThen(
1559
+ ethServiceFail(
1560
+ exprInterfaceOperation(
1561
+ contractCalled['name'],
1562
+ functionCalled['name']
1563
+ )
1564
+ )) do
1565
+ failure nEthnInterfaceOperation(contractAst['name'], functionAst['name'] )
1566
+ end
1567
+ end
1568
+
1569
+
1570
+
1571
+ # @!endgroup
1572
+
1573
+
1574
+
1575
+ # ------------------------------------------------------------------
1576
+ # @!group Expressions
1577
+
1578
+ # Create AL language binary expression using AL language
1579
+ # 'lExpr', 'rExpr' and AL language 'operator'.
1580
+ #
1581
+ # @return [Al:BinaryExpression] binary expression 'lExpr' <<operator>> 'rExpr'
1582
+ def ethBinaryExpression( lExpr, operator, rExpr )
1583
+ @logger.debug "#{__method__}: lExpr=#{lExpr}operator=#{operator} rExpr=#{rExpr}" if @logger.debug?
1584
+ alApi.binaryExpression do
1585
+ lval lExpr
1586
+ op operator
1587
+ rval rExpr
1588
+ end
1589
+ end
1590
+
1591
+ # @return [Al::Expression] expression referencing a variable
1592
+ def ethVariableReferenceExpression( strVariable )
1593
+ alApi.expression(ethVariableReference(strVariable))
1594
+ end
1595
+
1596
+ # @return [Al::Expression] interface operation wrapped to expression
1597
+ def exprInterfaceOperation( contractName, functionName )
1598
+ alApi.expression nInterfaceOperation( contractName, functionName )
1599
+ end
1600
+
1601
+ # @return [AL::Expression] expression for thisCid (=currently
1602
+ # running contract)
1603
+ def exprRunningContractAddress
1604
+ alApi.expression(refRunningContractAddress)
1605
+ end
1606
+
1607
+
1608
+ # @!endgroup
1609
+
1610
+ # ------------------------------------------------------------------
1611
+ # @!group Init expressions
1612
+
1613
+ # Map contract member variable type to method, which initializes
1614
+ # member variable.
1615
+ def typeInitDispatcher
1616
+ @typeInitDispatcher ||= {
1617
+ :ElementaryTypeName => method(:initElementaryTypeName),
1618
+ :UserDefinedTypeName => method(:initUserDefinedTypeName),
1619
+ :ArrayTypeName => method(:initArrayTypeName),
1620
+ :Mapping => method(:initMappingTypeName),
1621
+ }
1622
+ end
1623
+
1624
+ # Create expression to initialize contract member variabable
1625
+ # 'variableAst' (where variableAst sexp_type is known to equal
1626
+ # :ElementaryTypeName).
1627
+ #
1628
+ # Sbuilder domain resolvers map domains to contract member
1629
+ # variables, and domain setup extensions determine actual domain
1630
+ # values in formal model state. This means that translator
1631
+ # cannot determine initial values at tranlation time, instead we
1632
+ # use a template call 'snippetFacadeParamsetDomainInit', which
1633
+ # to identify applicable domain nil value for the contract
1634
+ # member variable.
1635
+ #
1636
+ # @param hash [Hash] Hash with properties :contractAst and
1637
+ # :variableAst
1638
+ #
1639
+ # @return [String] mustache template expression to access domain
1640
+ # assigned for 'variableAst' in currently running setup
1641
+ def initElementaryTypeName( hash )
1642
+
1643
+ contractAst = hash[:contractAst]
1644
+ variableAst = hash[:variableAst]
1645
+ initValue = Constants.snippetFacadeParamsetDomainInit(contractAst['name'], variableAst['name'] )
1646
+ return initValue
1647
+
1648
+ end
1649
+
1650
+ # Create expression to initiali contract member variable for
1651
+ # :ArrayTypeName -type. Init as empty set
1652
+ #
1653
+ # @param hash [Hash] Hash with properties :contractAst and
1654
+ # :variableAst
1655
+ def initArrayTypeName( hash )
1656
+ initValue = alApi.tlaSet( )
1657
+ return initValue
1658
+ end
1659
+
1660
+ # Create expresion to initialize contract member variable for
1661
+ # :UserDefinedTypeName -type (=contract)
1662
+ #
1663
+ # @param hash [Hash] Hash with properties :contractAst and
1664
+ # :variableAst
1665
+ #
1666
+ # @return [TlaNil] Nil expression
1667
+ def initUserDefinedTypeName( hash )
1668
+
1669
+ contractAst = hash[:contractAst]
1670
+ variableAst = hash[:variableAst]
1671
+
1672
+ initValue = alApi.tlaNil
1673
+ return initValue
1674
+
1675
+ end
1676
+
1677
+
1678
+ # Create expression to inialize contract member variable
1679
+ # 'variableAst', where variableAst sexp_type is known to be
1680
+ # :Mapping.
1681
+ #
1682
+ # The expression interates a variable over the domain assigned
1683
+ # to 'variableAst' (found using snippet facade template
1684
+ # 'snippetFacadeParamsetDomain' ), and creates a mapping to an
1685
+ # initial value for the domain (found using snippet facade
1686
+ # template 'snippetFacadeParamsetDomainInit').
1687
+ #
1688
+ # That is
1689
+ #
1690
+ # [ p1 \in {{snippetFacadeParamsetDomain}} : {{snippetFacadeParamsetDomainInit}} ]
1691
+ #
1692
+ # gets translated to smtng line [ a \in d_eth_address |-> 0 ]
1693
+ #
1694
+ # @param hash [Hash] Hash with properties :contractAst and
1695
+ # :variableAst
1696
+ #
1697
+ # @return [Al::TlaRecord] tlaRecord expression initializing all
1698
+ # mapping source domain values to initial value in the
1699
+ # domain.
1700
+ def initMappingTypeName( hash )
1701
+ # Retrieve data from hash
1702
+ contractAst = hash[:contractAst]
1703
+ variableAst = hash[:variableAst]
1704
+
1705
+ typeAst = variableAst.variableType
1706
+
1707
+ # Type name for mapping variable
1708
+ typeDefName = nContractMappingMemberDefinition( contractAst['name'], variableAst['name'] )
1709
+
1710
+ # p1
1711
+ inputParameter = alApi.inputParameterName
1712
+
1713
+ # mustache template for access inital val in the domain
1714
+ initValue = Constants.snippetFacadeParamsetDomainInit(typeDefName, alApi.outputParameterName)
1715
+ @logger.debug( "#{__method__}: typeDefName#{typeDefName}, alApi.outputParameterName=#{alApi.outputParameterName}=initValue=#{initValue}") if @logger.debug?
1716
+
1717
+ # Translate smthng like: [ p1 \in Set : {{initValue}} ]
1718
+ alApi.tlaRecord(
1719
+ [ alApi.tlaRecordFieldDef(
1720
+ alApi.tlaSetIterate( inputParameter,
1721
+ Constants.templateSpecname(
1722
+ Constants.snippetFacadeParamsetDomain(typeDefName, inputParameter),
1723
+ Constants::META_DOMAIN)
1724
+ ),
1725
+ initValue
1726
+ )])
1727
+ end
1728
+
1729
+ # @!endgroup Init expressions
1730
+
1731
+
1732
+ # ------------------------------------------------------------------
1733
+ # @!group Short handy idioms
1734
+
1735
+ # Check whether service execution status for interface operation
1736
+ # for 'refInterfaceOperation' has failed.
1737
+ #
1738
+ # @param refInterfaceOperation [Al::Reference] reference to
1739
+ # variable of interface operation
1740
+ #
1741
+ # @return [Al::Expression] InfrastructureServiceGetStatus(
1742
+ # refInterfaceOperation ) <> TRUE
1743
+ def ethServiceFail( refInterfaceOperation )
1744
+ alApi.unequal( ethGetServiceStatus(refInterfaceOperation), true)
1745
+ end
1746
+
1747
+ # {#ethServiceConstructorCanStart}: Expression, which should
1748
+ # evaluate TRUE to allow service procedure for a contract
1749
+ # constructor to execute.
1750
+ #
1751
+ # Implemation calls directly {#ethServiceMsgCallCanStart} i.e
1752
+ # rule enabling service execution of contract creation and
1753
+ # 'normal' message calls are identical.
1754
+ #
1755
+ # @return [Al::Expression] expression to evaluate allowing
1756
+ # constructor service to be called
1757
+ def ethServiceConstructorCanStart( parameterName)
1758
+ ethServiceMsgCallCanStart( parameterName )
1759
+ end
1760
+
1761
+ # @return [Al:TlaExpression] tla-expression NextId( free_address, Nil)
1762
+ def ethNextId
1763
+ alApi.tlaOperatorCall( specNextId, [alApi.expression(refFreeAddressPool), alApi.tlaNil ] )
1764
+ end
1765
+
1766
+ # @param contractReference [String,tlaSexp] Reference to bind with
1767
+ #
1768
+ # @return [Al::Expression] expression to bind
1769
+ # 'contractReference'.address to running contract found in
1770
+ # variable 'nRunningContractAddress'
1771
+ def ethBindToRecipient( contractReference )
1772
+ alApi.equal( alApi.tlaRecordField(contractReference, Constants::SOL_ADDRESS), refRunningContractAddress )
1773
+ end
1774
+
1775
+ # Read execution status of service pointed by
1776
+ # 'interfaceOperation' paramater.
1777
+ #
1778
+ # @param refInterfaceOperation [Al::Reference] reference to
1779
+ # interface operation to read
1780
+ #
1781
+ # @return [Al::OperatorExpression] expression to access status
1782
+ def ethGetServiceStatus( refInterfaceOperation )
1783
+ alApi.tlaOperatorCall(alApi.tlaPlainname(Constants::SBUILDER_GET_STATUS), [refInterfaceOperation])
1784
+ end
1785
+
1786
+
1787
+ # Canonize :VariableDeclaration and
1788
+ # VariableDeclarationWithDomain to a hash structure. Exception
1789
+ # for unhandled data types.
1790
+ #
1791
+ # @param variableDef [SexpAst] sexp AST node variable declration
1792
+ #
1793
+ # @return [Hash] hash with :name, :type, :typeName, :domain,
1794
+ # :isArray -properties. Property :domain is set for
1795
+ # :VariableDeclarationWithDomain 'variableDef' and for
1796
+ # 'bool' :ElementaryTypeName.
1797
+ #
1798
+ def ethVariableDeclaration( variableDef )
1799
+ case variableDef.sexp_type
1800
+ when :VariableDeclarationWithDomain
1801
+ decl = {
1802
+ :name => variableDef["name"],
1803
+ :type => :VariableDeclarationWithDomain,
1804
+ :domain => variableDef["domain"],
1805
+ :isArray => false,
1806
+ }
1807
+
1808
+ when :VariableDeclaration
1809
+
1810
+ typeDecl =variableDef.nodes_with_match( :ElementaryTypeName )
1811
+ raise TranslatorException, "TODO: add more type support #{variableDef}" if typeDecl.nil? or typeDecl.length != 1
1812
+ typeDecl = typeDecl.first
1813
+ domain = Constants::DOMAIN_BOOLEAN if typeDecl["name"] == Constants::SOL_BOOLEAN_TYPE
1814
+ decl = {
1815
+ :name => variableDef["name"],
1816
+ :type => typeDecl.sexp_type,
1817
+ :typeName => typeDecl["name"],
1818
+ :domain => domain,
1819
+ :isArray => variableDef.children[0].sexp_type == :ArrayTypeName ? true : false
1820
+ }
1821
+ else
1822
+ raise TranslatorException, "TODO: unknown sexp_type #{variableDef.sexp_type} for #{variableDef}"
1823
+ end
1824
+ @logger.info "#{__method__}: varaible=#{variableDef["name"]} -> decl=#{decl}"
1825
+ decl
1826
+ end
1827
+
1828
+
1829
+ # @return [Al::Expression] Expression accessing first element in
1830
+ # sequence modeling a stack
1831
+ def ethStackTop( variableName )
1832
+ alApi.tlaOperatorCall(
1833
+ alApi.tlaPlainname("Head"),
1834
+ [
1835
+ ethVariableReferenceExpression( variableName )
1836
+ ])
1837
+ end
1838
+
1839
+ # @return [Al:Expression] Expression for tail of stack modeling
1840
+ # popping an top element from stack
1841
+ def ethStackRest( variableName )
1842
+ alApi.tlaOperatorCall(
1843
+ alApi.tlaPlainname("Tail"),
1844
+ [
1845
+ ethVariableReferenceExpression( variableName )
1846
+ ])
1847
+ end
1848
+
1849
+ # @!endgroup
1850
+
1851
+ # ------------------------------------------------------------------
1852
+ # @!group Helpers
1853
+
1854
+ # @param mapper [Hash] key=symbol to choose, value = lamba
1855
+ # @param ast [AstSext] AST node to dispatch
1856
+ def self.dispatcher( mapper, key, ast )
1857
+ # Error checks
1858
+ raise TranslatorException, "Missing dispatcher key to dispatch one #{mapper.keys.join(',')}. Error dispatching ast node: #{ast}" if key.nil?
1859
+ raise TranslatorException, "Dispatch key '#{key}' not found in known keys:#{mapper.keys.join(',')}. Error dispatching ast node: #{ast}" if mapper[key].nil?
1860
+
1861
+ # invoke lamba
1862
+ mapper[key][ast]
1863
+
1864
+ end
1865
+
1866
+ # @!endgroup
1867
+
1868
+ # ------------------------------------------------------------------
1869
+ # @!group Create names
1870
+
1871
+
1872
+ # @return [String] wrap {nInterfaceOperation} to an eth template
1873
+ def nEthnInterfaceOperation( contractName, functionName )
1874
+ Constants.templateSpecname(nInterfaceOperation( contractName, functionName ))
1875
+ end
1876
+
1877
+
1878
+ # @return [String] type name for contract e.g. for contract
1879
+ # 'Simple' this gets eventually translated to t_Simple
1880
+ def nContractType( contractName )
1881
+ contractName
1882
+ end
1883
+
1884
+ # @return [String] name mapping definition of member variable
1885
+ # 'memberName' in contract 'contracName'
1886
+ def nContractMappingMemberDefinition( contractName, memberName )
1887
+ "#{contractName}_#{memberName}"
1888
+ end
1889
+
1890
+ # @return [String] combine 'contractName' and 'functionName' to
1891
+ # name of intefaceOperation (i.e. string
1892
+ # 'contractName(operationName)' where operationName is emtpy
1893
+ # for constructors)
1894
+ def nInterfaceOperation( contractName, functionName )
1895
+ "#{contractName}(#{nContractFunctionOperation(contractName, functionName)})"
1896
+ end
1897
+
1898
+ # @return [String] name of contract response type
1899
+ def nContractFunctionResponseType( contractName, functionName )
1900
+ nInterfaceOperation( contractName, functionName )
1901
+ end
1902
+
1903
+ # @return [String] empty string for constructor, else functionName
1904
+ def nContractFunctionOperation( contractName, functionName )
1905
+ isConstructor(contractName, functionName) ? "" : functionName
1906
+ end
1907
+
1908
+ # @return [Boollean] true for constructors where contractName == functionName
1909
+ def isConstructor( contractName, functionName )
1910
+ contractName == functionName
1911
+ end
1912
+
1913
+ def nPopState
1914
+ "popState"
1915
+ end
1916
+
1917
+ # @return [String] name account variable
1918
+ def nAccount
1919
+ Constants::VARIABLE_ACCOUNTS
1920
+ end
1921
+
1922
+ # @return [String] name of storage root
1923
+ def nStorageRoot
1924
+ Constants::VARIABLE_STORAGE_ROOT
1925
+ end
1926
+
1927
+ # @return [String] name of total value mined
1928
+ def nMined
1929
+ Constants::VARIABLE_MINED
1930
+ end
1931
+
1932
+ def specNextId
1933
+ Constants.templateSpecname( Constants::RUNTIME_NEXTID)
1934
+ end
1935
+
1936
+ def specFreeAddress
1937
+ Constants.templateSpecname( Constants::VARIABLE_ADDRESS_FREE)
1938
+ end
1939
+
1940
+ # @return [Al::Expression] expression msg.recipient
1941
+ def ethMsgRecipient( contractName, functionName )
1942
+ alApi.expression( refMsgRecipient(contractName, functionName ))
1943
+ end
1944
+
1945
+ # @return [String] Name of local variable holding contract
1946
+ # 'this'
1947
+ def nRunningContractAddress #
1948
+ Constants::VARIABLE_LOCAL_CURRENT_CONTRACT
1949
+ end
1950
+
1951
+ # @!endgroup
1952
+
1953
+ # ------------------------------------------------------------------
1954
+ # @!group References
1955
+
1956
+ def ethFunctionParameterExpression( contractName, functionName, parameterName=nil )
1957
+ alApi.expression( ethFunctionParameterReference(contractName, functionName, parameterName))
1958
+ end
1959
+
1960
+ # Create a 'Reference' to input variable of contract
1961
+ # 'contractName' function 'functionName', (and to
1962
+ # 'parameterName' if given).
1963
+ #
1964
+ # @param contractName [String] name contract
1965
+ #
1966
+ # @param functionName [String] name function
1967
+ #
1968
+ # @param parameterName [String] name of parameter to refer to,
1969
+ # nil refere to variable
1970
+ #
1971
+ # @return [Al:ReferenceExpression] expression to access
1972
+ # 'parameter' in 'transaction', 'operator' or 'macro'
1973
+
1974
+ def ethFunctionParameterReference( contractName, functionName, parameterName=nil )
1975
+ operationName = nContractFunctionOperation(contractName, functionName )
1976
+ alApi.reference( [Constants::META_ETH, contractName, operationName], parameterName )
1977
+ end
1978
+
1979
+
1980
+ # @return [Al::ReferenceExpression] reference to 'parameterName'
1981
+ # in 'contractName'. Use 'RUNTIME_GET_ELEMENT_SAFE' to access
1982
+ # correct entry from 'storageRoot' (also in case when entry is
1983
+ # not found)
1984
+ #
1985
+ # @param variableRef [Al::Reference] refAccount or
1986
+ # refStorageroot specification code variable to access
1987
+ #
1988
+ # @param parameterName [String] field name of contract entry to access
1989
+ #
1990
+ # @param refAddress [Al::Reference] address idenfifying contract
1991
+ # to access
1992
+ #
1993
+ def ethContractVariableReference( variableRef, refAddress, parameterName )
1994
+ alApi.tlaOperatorCall(
1995
+ Constants.templateSpecname(Constants::RUNTIME_GET_ELEMENT_SAFE),
1996
+ [
1997
+ alApi.expression(variableRef),
1998
+ alApi.expression(Constants::FIELD_NAME_ADDRESS),
1999
+ refAddress,
2000
+ alApi.constantExpression(parameterName ),
2001
+
2002
+ ]
2003
+ )
2004
+ # alApi.reference( [Constants::META_ETH, Constants::VARIABLE_STORAGE_ROOT], parameterName )
2005
+ end
2006
+
2007
+ # Referencing local variable 'nameLocal'
2008
+ #
2009
+ # @param nameLocal [String] name of local variable to create a
2010
+ # reference to
2011
+ def ethLocalReference( nameLocal, parameterName=nil )
2012
+ alApi.reference( nameLocal )
2013
+ end
2014
+
2015
+ # @param domain [String] name domain to reference to
2016
+ def ethDomainReference( domain )
2017
+ alApi.reference( [Constants::META_DOMAIN, domain] )
2018
+ end
2019
+
2020
+ # @param strVariable [String] name of tla variable to reference
2021
+ def ethVariableReference( strVariable )
2022
+ alApi.reference( [Constants::META_ETH, strVariable] )
2023
+ end
2024
+
2025
+ def refStorageRoot
2026
+ ethVariableReference(nStorageRoot)
2027
+ end
2028
+
2029
+ # @return [Al::Reference] refence to currently running contract
2030
+ # (through local variable with the name 'nRunningContractAddress')
2031
+ def refRunningContractAddress
2032
+ ethLocalReference(nRunningContractAddress)
2033
+ end
2034
+
2035
+
2036
+ def refAccount
2037
+ ethVariableReference(nAccount)
2038
+ end
2039
+
2040
+ def refFreeAddressPool
2041
+ ethVariableReference(Constants::VARIABLE_ADDRESS_FREE)
2042
+ end
2043
+
2044
+ def refMsgRecipient( contractName, functionName )
2045
+ ethFunctionParameterReference( contractName, functionName, Constants::FIELD_NAME_RECIPIENT )
2046
+ end
2047
+
2048
+ # @!endgroup
2049
+
2050
+
2051
+ end
2052
+ end
2053
+ end
2054
+