1
- package tiny_wasm
1
+ package tinywasm
2
2
3
3
import (
4
4
"fmt"
5
5
"github.com/tinychain/tiny-wasm/wagon/exec"
6
6
"github.com/tinychain/tinychain/common"
7
+ "github.com/tinychain/tinychain/core/types"
7
8
"math/big"
8
9
)
9
10
@@ -31,6 +32,7 @@ const (
31
32
GasCostSSet = 20000
32
33
GasCostSReset = 5000
33
34
GasRefundSClear = 15000
35
+ GasSstoreClear = 5000
34
36
GasRefundSelfDestruct = 24000
35
37
GasCostCreate = 32000
36
38
GasCostCall = 700
@@ -41,6 +43,7 @@ const (
41
43
GasCostLogTopic = 375
42
44
GasCostCopy = 3
43
45
GasCostBlockHash = 800
46
+ GasCostCreateData = 200
44
47
45
48
GasCostExtcodeSize = 700
46
49
GasCostExtcodeCopy = 700
@@ -60,14 +63,14 @@ func (*eeiApi) useGas(p *exec.Process, w *WasmIntptr, amount int64) {
60
63
61
64
func (* eeiApi ) getAddress (p * exec.Process , w * WasmIntptr , resultOffset int32 ) {
62
65
w .useGas (GasCostBase )
63
- p . WriteAt ( w .contract .Address ().Bytes (), int64 ( resultOffset ) )
66
+ writeToMem ( p , w .contract .Address ().Bytes (), resultOffset )
64
67
}
65
68
66
69
func (* eeiApi ) getExternalBalance (p * exec.Process , w * WasmIntptr , addressOffset , resultOffset int32 ) {
67
70
w .useGas (GasCostBalance )
68
71
addr := loadFromMem (p , addressOffset , common .AddressLength )
69
72
balance := w .evm .StateDB .GetBalance (common .BytesToAddress (addr ))
70
- p . WriteAt ( balance .Bytes (), int64 ( resultOffset ) )
73
+ writeToMem ( p , balance .Bytes (), resultOffset )
71
74
}
72
75
73
76
// getBlockHash gets the hash of one of the 256 most recent completed blocks.
@@ -78,7 +81,7 @@ func (*eeiApi) getBlockHash(p *exec.Process, w *WasmIntptr, number int64, result
78
81
return ErrEEICallFailure
79
82
}
80
83
hash := w .evm .Context .GetHash (uint64 (number ))
81
- p . WriteAt ( hash .Bytes (), int64 ( resultOffset ) )
84
+ writeToMem ( p , hash .Bytes (), resultOffset )
82
85
return EEICallSuccess
83
86
84
87
}
@@ -108,8 +111,8 @@ func (*eeiApi) call(p *exec.Process, w *WasmIntptr, gas int64, addressOffset, va
108
111
}
109
112
110
113
func (* eeiApi ) callDataCopy (p * exec.Process , w * WasmIntptr , resultOffset , dataOffset , length int32 ) {
111
- w .useGas (GasCostCopy * uint64 (length ))
112
- p . WriteAt ( w .contract .Input [dataOffset :dataOffset + length ], int64 ( resultOffset ) )
114
+ w .useGas (GasCostVeryLow + GasCostCopy * uint64 (length ))
115
+ writeToMem ( p , w .contract .Input [dataOffset :dataOffset + length ], resultOffset )
113
116
}
114
117
115
118
func (* eeiApi ) getCallDataSize (p * exec.Process , w * WasmIntptr ) int32 {
@@ -159,81 +162,195 @@ func (*eeiApi) callStatic(p *exec.Process, w *WasmIntptr, gas int64, addressOffs
159
162
}
160
163
161
164
func (* eeiApi ) storageStore (p * exec.Process , w * WasmIntptr , pathOffset , valueOffset int32 ) {
165
+ if w .IsReadOnly () {
166
+ panic ("Static mode violation in storageStore" )
167
+ }
168
+
169
+ key := common .BytesToHash (loadFromMem (p , pathOffset , u256Len ))
170
+ val := loadFromMem (p , pathOffset , u256Len )
171
+
172
+ oldVal := w .StateDB ().GetState (w .contract .Address (), key )
173
+
174
+ // This checks for 3 scenario's and calculates gas accordingly:
175
+ //
176
+ // 1. From a zero-value address to a non-zero value (NEW VALUE)
177
+ // 2. From a non-zero value address to a zero-value address (DELETE)
178
+ // 3. From a non-zero to a non-zero (CHANGE)
179
+ switch {
180
+ case oldVal == nil && new (big.Int ).SetBytes (val ).Sign () != 0 : // 0 => non 0
181
+ w .useGas (GasCostSSet )
182
+ case oldVal != nil && new (big.Int ).SetBytes (val ).Sign () == 0 : // non 0 => 0
183
+ w .useGas (GasSstoreClear )
184
+ default : // non 0 => non 0 (or 0 => 0)
185
+ w .useGas (GasCostSReset )
186
+ }
162
187
188
+ w .StateDB ().SetState (w .contract .Address (), key , val )
163
189
}
164
190
165
191
func (* eeiApi ) storageLoad (p * exec.Process , w * WasmIntptr , pathOffset , resultOffset int32 ) {
166
-
192
+ w .useGas (GasCostSLoad )
193
+ key := common .BytesToHash (loadFromMem (p , pathOffset , u256Len ))
194
+ val := w .StateDB ().GetState (w .contract .Address (), key )
195
+ writeToMem (p , val , resultOffset )
167
196
}
168
197
169
198
func (* eeiApi ) getCaller (p * exec.Process , w * WasmIntptr , resultOffset int32 ) {
170
-
199
+ w .useGas (GasCostBase )
200
+ addr := w .contract .CallerAddress
201
+ writeToMem (p , addr .Bytes (), resultOffset )
171
202
}
172
203
173
204
func (* eeiApi ) getCallValue (p * exec.Process , w * WasmIntptr , resultOffset int32 ) {
174
-
205
+ w .useGas (GasCostBase )
206
+ writeToMem (p , w .contract .Value ().Bytes (), resultOffset )
175
207
}
176
208
177
209
func (* eeiApi ) codeCopy (p * exec.Process , w * WasmIntptr , resultOffset , codeOffset , length int32 ) {
178
-
210
+ w .useGas (GasCostVeryLow + GasCostCopy * uint64 (length ))
211
+ writeToMem (p , w .contract .Code [codeOffset :codeOffset + length ], resultOffset )
179
212
}
180
213
181
214
func (* eeiApi ) getCodeSize (p * exec.Process , w * WasmIntptr ) int32 {
182
-
215
+ w .useGas (GasCostBase )
216
+ return int32 (len (w .contract .Code ))
183
217
}
184
218
185
219
func (* eeiApi ) getBlockCoinbase (p * exec.Process , w * WasmIntptr , resultOffset int32 ) {
186
-
220
+ w .useGas (GasCostBase )
221
+ writeToMem (p , w .evm .Coinbase ().Bytes (), resultOffset )
187
222
}
188
223
189
- func (* eeiApi ) create (p * exec.Process , w * WasmIntptr , valueOffset , dataOffset , length , resultOffset int32 ) {
224
+ func (* eeiApi ) create (p * exec.Process , w * WasmIntptr , valueOffset , dataOffset , length , resultOffset int32 ) int32 {
225
+ w .useGas (GasCostCreate )
226
+
227
+ oldVM := w .vm
228
+ oldContract := w .contract
229
+ defer func () {
230
+ w .vm = oldVM
231
+ w .contract = oldContract
232
+ }()
190
233
234
+ w .terminateType = TerminateInvalid
235
+
236
+ if int (valueOffset )+ u128Len > len (w .vm .Memory ()) {
237
+ return ErrEEICallFailure
238
+ }
239
+
240
+ if int (dataOffset + length ) > len (w .vm .Memory ()) {
241
+ return ErrEEICallFailure
242
+ }
243
+
244
+ code := loadFromMem (p , dataOffset , length )
245
+ val := loadFromMem (p , valueOffset , u128Len )
246
+
247
+ w .terminateType = TerminateFinish
248
+
249
+ // EIP150 says that the calling contract should keep 1/64th of the
250
+ // leftover gas.
251
+ gas := w .contract .Gas - w .contract .Gas / 64
252
+ _ , addr , leftGas , _ := w .evm .Create (w .contract , code , gas , new (big.Int ).SetBytes (val ))
253
+
254
+ switch w .terminateType {
255
+ case TerminateFinish :
256
+ oldContract .Gas += leftGas
257
+ p .WriteAt (addr .Bytes (), int64 (resultOffset ))
258
+ return EEICallSuccess
259
+ case TerminateRevert :
260
+ oldContract .Gas += gas
261
+ return ErrEEICallRevert
262
+ default :
263
+ oldContract .Gas += leftGas
264
+ return ErrEEICallFailure
265
+ }
191
266
}
192
267
193
268
func (* eeiApi ) getBlockDifficulty (p * exec.Process , w * WasmIntptr , resultOffset int32 ) {
194
269
195
270
}
196
271
197
272
func (* eeiApi ) externalCodeCopy (p * exec.Process , w * WasmIntptr , addressOffset , resultOffset , codeOffset , length int32 ) {
273
+ addr := common .BytesToAddress (loadFromMem (p , addressOffset , common .AddressLength ))
274
+ code := w .StateDB ().GetCode (addr )
198
275
276
+ w .useGas (GasCostVeryLow + GasCostCopy * uint64 (len (code )))
277
+ writeToMem (p , code [codeOffset :codeOffset + length ], resultOffset )
199
278
}
200
279
201
280
func (* eeiApi ) getExternalCodeSize (p * exec.Process , w * WasmIntptr , addressOffset int32 ) int32 {
202
-
281
+ w .useGas (GasCostExtCode )
282
+ addr := common .BytesToAddress (loadFromMem (p , addressOffset , common .AddressLength ))
283
+ return int32 (w .StateDB ().GetCodeSize (addr ))
203
284
}
204
285
205
286
func (* eeiApi ) getGasLeft (p * exec.Process , w * WasmIntptr ) int64 {
206
-
287
+ w .useGas (GasCostBase )
288
+ return int64 (w .contract .Gas )
207
289
}
208
290
209
291
func (* eeiApi ) getBlockGasLimit (p * exec.Process , w * WasmIntptr ) int64 {
210
-
292
+ w .useGas (GasCostBase )
293
+ return int64 (w .evm .GasLimit )
211
294
}
212
295
213
296
func (* eeiApi ) getTxGasPrice (p * exec.Process , w * WasmIntptr , valueOffset int32 ) {
214
-
297
+ w .useGas (GasCostBase )
298
+ writeToMem (p , w .evm .GasPrice .Bytes (), valueOffset )
215
299
}
216
300
217
- func (* eeiApi ) log (p * exec.Process , w * WasmIntptr , dataOffset , length , numberOfTopics , topic1 , topic2 , topic3 , topic4 int32 ) {
301
+ func (* eeiApi ) log (p * exec.Process , w * WasmIntptr , dataOffset , dataLength , numberOfTopics , topic1 , topic2 , topic3 , topic4 int32 ) {
302
+ w .useGas (GasCostLog + GasCostLogData * uint64 (dataLength ) + GasCostLogTopic * uint64 (numberOfTopics ))
218
303
304
+ if numberOfTopics > 4 || numberOfTopics < 0 {
305
+ w .terminateType = TerminateInvalid
306
+ p .Terminate ()
307
+ }
308
+
309
+ data := loadFromMem (p , dataOffset , dataLength )
310
+ topics := make ([]common.Hash , numberOfTopics )
311
+
312
+ switch numberOfTopics {
313
+ case 4 :
314
+ topics [3 ] = common .BytesToHash (loadFromMem (p , topic4 , u256Len ))
315
+ fallthrough
316
+ case 3 :
317
+ topics [2 ] = common .BytesToHash (loadFromMem (p , topic3 , u256Len ))
318
+ fallthrough
319
+ case 2 :
320
+ topics [1 ] = common .BytesToHash (loadFromMem (p , topic2 , u256Len ))
321
+ fallthrough
322
+ case 1 :
323
+ topics [0 ] = common .BytesToHash (loadFromMem (p , topic1 , u256Len ))
324
+ default :
325
+ return
326
+ }
327
+
328
+ w .StateDB ().AddLog (& types.Log {
329
+ Address : w .contract .Address (),
330
+ Topics : topics ,
331
+ Data : data ,
332
+ BlockHeight : w .evm .BlockHeight .Uint64 (),
333
+ })
219
334
}
220
335
221
336
func (* eeiApi ) getBlockNumber (p * exec.Process , w * WasmIntptr ) int64 {
222
-
337
+ w .useGas (GasCostBase )
338
+ return w .evm .BlockHeight .Int64 ()
223
339
}
224
340
225
341
func (* eeiApi ) getTxOrigin (p * exec.Process , w * WasmIntptr , resultOffset int32 ) {
226
-
342
+ w .useGas (GasCostBase )
343
+ writeToMem (p , w .evm .Origin .Bytes (), resultOffset )
227
344
}
228
345
229
346
func (* eeiApi ) finish (p * exec.Process , w * WasmIntptr , dataOffset , length int32 ) {
230
- w .returnData = loadFromMem (p , dataOffset , int ( length ) )
347
+ w .returnData = loadFromMem (p , dataOffset , length )
231
348
w .terminateType = TerminateFinish
232
349
p .Terminate ()
233
350
}
234
351
235
352
func (* eeiApi ) revert (p * exec.Process , w * WasmIntptr , dataOffset , length int32 ) {
236
- w .returnData = loadFromMem (p , dataOffset , int ( length ) )
353
+ w .returnData = loadFromMem (p , dataOffset , length )
237
354
w .terminateType = TerminateRevert
238
355
p .Terminate ()
239
356
}
@@ -245,21 +362,48 @@ func (*eeiApi) getReturnDataSize(p *exec.Process, w *WasmIntptr) int32 {
245
362
246
363
func (* eeiApi ) returnDataCopy (p * exec.Process , w * WasmIntptr , resultOffset , dataOffset , length int32 ) {
247
364
w .useGas (GasCostCopy * uint64 (length ))
248
- p . WriteAt ( loadFromMem ( p , dataOffset , int ( length )), int64 ( resultOffset ) )
365
+ writeToMem ( p , w . returnData [ dataOffset : dataOffset + length ], resultOffset )
249
366
}
250
367
251
368
func (* eeiApi ) selfDestruct (p * exec.Process , w * WasmIntptr , addressOffset int32 ) {
369
+ addr := common .BytesToAddress (loadFromMem (p , addressOffset , common .AddressLength ))
370
+ balance := w .StateDB ().GetBalance (w .contract .Address ())
371
+
372
+ totalGas := GasCostSuicide
373
+ // If the target address dose not exist, add the account creation cost
374
+ if ! w .StateDB ().Exist (addr ) {
375
+ totalGas += GasCostCreateBySuicide
376
+ }
377
+ w .StateDB ().AddBalance (addr , balance )
378
+ w .useGas (uint64 (totalGas ))
379
+ w .StateDB ().Suicide (w .contract .Address ())
252
380
381
+ w .terminateType = TerminateSuicide
382
+ p .Terminate ()
253
383
}
254
384
255
385
func (* eeiApi ) getBlockTimestamp (p * exec.Process , w * WasmIntptr ) int64 {
386
+ w .useGas (GasCostBase )
387
+ return w .evm .Time .Int64 ()
388
+ }
256
389
390
+ // swapEndian swap big endian to little endian or reverse.
391
+ func swapEndian (src []byte ) []byte {
392
+ rect := make ([]byte , len (src ))
393
+ for i , b := range src {
394
+ rect [len (src )- i - 1 ] = b
395
+ }
396
+ return rect
257
397
}
258
398
259
- func loadFromMem (p * exec.Process , offset int32 , size int ) []byte {
399
+ func loadFromMem (p * exec.Process , offset int32 , size int32 ) []byte {
260
400
b := make ([]byte , size )
261
401
p .ReadAt (b , int64 (offset ))
262
- return b
402
+ return swapEndian (b )
403
+ }
404
+
405
+ func writeToMem (p * exec.Process , data []byte , offset int32 ) (int , error ) {
406
+ return p .WriteAt (swapEndian (data ), int64 (offset ))
263
407
}
264
408
265
409
func getCallParams (p * exec.Process , w * WasmIntptr , addressOffset , valueOffset , dataOffset , dataLength int32 ) (addr common.Address , value * big.Int , input []byte ) {
@@ -277,11 +421,12 @@ func getCallParams(p *exec.Process, w *WasmIntptr, addressOffset, valueOffset, d
277
421
}
278
422
279
423
// Get the input data from mem
280
- input = loadFromMem (p , dataOffset , int ( dataLength ) )
424
+ input = loadFromMem (p , dataOffset , dataLength )
281
425
282
426
return
283
427
}
284
428
429
+ // call provides a common call function for `call`, `callCode` and `callDelegate` of EEI api.
285
430
func call (w * WasmIntptr , toContract * Contract , input []byte , snapshot int ) int32 {
286
431
if w .evm .depth > maxCallDepth {
287
432
// Clear all gas of contract
0 commit comments