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.
- checksums.yaml +7 -0
- data/README.org +19 -0
- data/VERSION +1 -0
- data/bin/state-explorer.sh +215 -0
- data/lib/eth/al_api.rb +12 -0
- data/lib/eth/ast_sexp.rb +519 -0
- data/lib/eth/constants.rb +354 -0
- data/lib/eth/ethereum.rb +2054 -0
- data/lib/eth/ethereum_expression.rb +476 -0
- data/lib/eth/exception.rb +9 -0
- data/lib/eth/global_scope.rb +100 -0
- data/lib/eth/module.rb +18 -0
- data/lib/eth/sexp_processor.rb +66 -0
- data/lib/eth/sexp_processor_call_separator.rb +163 -0
- data/lib/eth/sexp_processor_getter.rb +125 -0
- data/lib/eth/sexp_processor_resolve.rb +122 -0
- data/lib/eth/sexp_processor_scope.rb +355 -0
- data/lib/eth/sexp_utils.rb +214 -0
- data/lib/eth/solidity_compiler.rb +145 -0
- data/lib/eth/solidity_loader.rb +53 -0
- data/lib/eth/solidity_translator.rb +840 -0
- data/lib/mixer/reference.rb +30 -0
- data/lib/mixer/scope.rb +100 -0
- data/lib/mixer/scoped.rb +18 -0
- data/lib/plugin/controller.rb +267 -0
- data/lib/plugin/module.rb +3 -0
- data/lib/plugin/plugin.rb +33 -0
- data/lib/sbuilder-eth.rb +6 -0
- data/sbuilder-eth.gemspec +38 -0
- metadata +149 -0
@@ -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
|
data/lib/eth/ethereum.rb
ADDED
@@ -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
|
+
|