@@ -4,6 +4,7 @@ use fil_actors_runtime::ActorError;
4
4
use fil_actors_runtime:: EAM_ACTOR_ADDR ;
5
5
use fil_actors_runtime:: { deserialize_block, extract_send_result} ;
6
6
use fvm_ipld_encoding:: ipld_block:: IpldBlock ;
7
+ use fvm_ipld_encoding:: BytesDe ;
7
8
use fvm_shared:: sys:: SendFlags ;
8
9
use fvm_shared:: MethodNum ;
9
10
use fvm_shared:: METHOD_SEND ;
@@ -23,32 +24,32 @@ use {
23
24
pub fn create (
24
25
state : & mut ExecutionState ,
25
26
system : & mut System < impl Runtime > ,
26
- value : U256 ,
27
+ endowment : U256 ,
27
28
offset : U256 ,
28
29
size : U256 ,
29
30
) -> Result < U256 , ActorError > {
30
31
if system. readonly {
31
32
return Err ( ActorError :: read_only ( "create called while read-only" . into ( ) ) ) ;
32
33
}
34
+ state. return_data = Vec :: new ( ) ;
33
35
34
- let ExecutionState { stack : _, memory, .. } = state;
35
-
36
- let value = TokenAmount :: from ( & value) ;
37
- if value > system. rt . current_balance ( ) {
38
- return Ok ( U256 :: zero ( ) ) ;
39
- }
40
- let input_region = get_memory_region ( memory, offset, size) ?;
36
+ let input_region = get_memory_region ( & mut state. memory , offset, size) ?;
41
37
42
38
let input_data = if let Some ( MemoryRegion { offset, size } ) = input_region {
43
- & memory[ offset..] [ ..size. get ( ) ]
39
+ & state . memory [ offset..] [ ..size. get ( ) ]
44
40
} else {
45
41
& [ ]
46
42
} ;
47
43
48
44
// We increment the nonce earlier than in the EVM. See the comment in `create2` for details.
49
- let nonce = system. increment_nonce ( ) ;
50
- let params = eam:: CreateParams { code : input_data. to_vec ( ) , nonce } ;
51
- create_init ( system, IpldBlock :: serialize_cbor ( & params) ?, eam:: CREATE_METHOD_NUM , value)
45
+ let params = eam:: CreateParams { code : input_data. to_vec ( ) , nonce : system. nonce } ;
46
+ create_common (
47
+ state,
48
+ system,
49
+ IpldBlock :: serialize_cbor ( & params) ?,
50
+ eam:: CREATE_METHOD_NUM ,
51
+ endowment,
52
+ )
52
53
}
53
54
54
55
pub fn create2 (
@@ -63,25 +64,47 @@ pub fn create2(
63
64
return Err ( ActorError :: read_only ( "create2 called while read-only" . into ( ) ) ) ;
64
65
}
65
66
66
- let ExecutionState { stack : _, memory, .. } = state;
67
-
68
- let endowment = TokenAmount :: from ( & endowment) ;
69
- if endowment > system. rt . current_balance ( ) {
70
- return Ok ( U256 :: zero ( ) ) ;
71
- }
72
-
73
- let input_region = get_memory_region ( memory, offset, size) ?;
67
+ let input_region = get_memory_region ( & mut state. memory , offset, size) ?;
74
68
75
69
// BE encoded array
76
70
let salt: [ u8 ; 32 ] = salt. into ( ) ;
77
71
78
72
let input_data = if let Some ( MemoryRegion { offset, size } ) = input_region {
79
- & memory[ offset..] [ ..size. get ( ) ]
73
+ & state . memory [ offset..] [ ..size. get ( ) ]
80
74
} else {
81
75
& [ ]
82
76
} ;
83
77
let params = eam:: Create2Params { code : input_data. to_vec ( ) , salt } ;
84
78
79
+ create_common (
80
+ state,
81
+ system,
82
+ IpldBlock :: serialize_cbor ( & params) ?,
83
+ eam:: CREATE2_METHOD_NUM ,
84
+ endowment,
85
+ )
86
+ }
87
+
88
+ /// call into Ethereum Address Manager to make the new account
89
+ #[ inline]
90
+ fn create_common (
91
+ state : & mut ExecutionState ,
92
+ system : & mut System < impl Runtime > ,
93
+ params : Option < IpldBlock > ,
94
+ method : MethodNum ,
95
+ endowment : U256 ,
96
+ ) -> Result < U256 , ActorError > {
97
+ // First, we clear the return data. We want to do this even if we, e.g., fail due to the
98
+ // endowment.
99
+ state. return_data = Vec :: new ( ) ;
100
+
101
+ // Then we explicitly check the endowment. We could just try and deal with the error, but then
102
+ // we'd need to add some logic for decrementing the nonce. It's easier to check up-front.
103
+ let endowment = TokenAmount :: from ( & endowment) ;
104
+ if endowment > system. rt . current_balance ( ) {
105
+ return Ok ( U256 :: zero ( ) ) ;
106
+ }
107
+
85
108
// We increment the nonce earlier than in the EVM, but this is unlikely to cause issues:
86
109
//
87
110
// 1. Like the EVM, we increment the nonce on address conflict (effectively "skipping" the
@@ -93,30 +116,33 @@ pub fn create2(
93
116
// stack depth. However, given that there are other ways to increment the nonce without
94
117
// deploying a contract (e.g., 2), this shouldn't be an issue.
95
118
system. increment_nonce ( ) ;
96
- create_init ( system, IpldBlock :: serialize_cbor ( & params) ?, eam:: CREATE2_METHOD_NUM , endowment)
97
- }
98
119
99
- /// call into Ethereum Address Manager to make the new account
100
- #[ inline]
101
- fn create_init (
102
- system : & mut System < impl Runtime > ,
103
- params : Option < IpldBlock > ,
104
- method : MethodNum ,
105
- value : TokenAmount ,
106
- ) -> Result < U256 , ActorError > {
107
120
// Apply EIP-150
108
121
let gas_limit = ( 63 * system. rt . gas_available ( ) ) / 64 ;
109
122
110
123
// send bytecode & params to EAM to generate the address and contract
111
- let ret =
112
- system. send ( & EAM_ACTOR_ADDR , method, params, value, Some ( gas_limit) , SendFlags :: default ( ) ) ;
124
+ let ret = system. send (
125
+ & EAM_ACTOR_ADDR ,
126
+ method,
127
+ params,
128
+ endowment,
129
+ Some ( gas_limit) ,
130
+ SendFlags :: default ( ) ,
131
+ ) ;
113
132
114
133
Ok ( match ret {
115
134
Ok ( eam_ret) => {
116
135
let ret: eam:: CreateReturn = deserialize_block ( eam_ret) ?;
117
136
ret. eth_address . as_evm_word ( )
118
137
}
119
- Err ( _) => U256 :: zero ( ) ,
138
+ Err ( mut err) => {
139
+ // Any non-empty return data here always comes from the constructor.
140
+ if let Some ( r) = err. take_data ( ) {
141
+ // As with CALL, ignore decode failures and fallback on returning the encoded value.
142
+ state. return_data = r. deserialize ( ) . map ( |BytesDe ( d) | d) . unwrap_or_else ( |_| r. data ) ;
143
+ }
144
+ U256 :: zero ( )
145
+ }
120
146
} )
121
147
}
122
148
@@ -164,12 +190,13 @@ pub fn selfdestruct(
164
190
165
191
#[ cfg( test) ]
166
192
mod tests {
167
- use crate :: evm_unit_test;
168
193
use crate :: ext:: eam;
194
+ use crate :: { evm_unit_test, EVM_CONTRACT_REVERTED } ;
169
195
170
196
use fil_actors_evm_shared:: uints:: U256 ;
171
197
use fil_actors_runtime:: EAM_ACTOR_ADDR ;
172
198
use fvm_ipld_encoding:: ipld_block:: IpldBlock ;
199
+ use fvm_ipld_encoding:: BytesSer ;
173
200
use fvm_shared:: address:: Address as FilAddress ;
174
201
use fvm_shared:: error:: { ErrorNumber , ExitCode } ;
175
202
use fvm_shared:: sys:: SendFlags ;
@@ -267,16 +294,18 @@ mod tests {
267
294
// the deed
268
295
CREATE2 ;
269
296
}
297
+ m. state. return_data = b"deadbeef" . to_vec( ) ; // stale return data
270
298
m. state. stack. push( U256 :: from( 0xDEADBEEFu64 ) ) . unwrap( ) ; // salt
271
- m. state. stack. push( U256 :: from( 4 ) ) . unwrap( ) ; // input size
272
- m. state. stack. push( U256 :: from( 28 ) ) . unwrap( ) ; // input offset
273
- m. state. stack. push( U256 :: from( 1234 ) ) . unwrap( ) ; // initial value
299
+ m. state. stack. push( U256 :: from( 4 ) ) . unwrap( ) ; // input size
300
+ m. state. stack. push( U256 :: from( 28 ) ) . unwrap( ) ; // input offset
301
+ m. state. stack. push( U256 :: from( 1234 ) ) . unwrap( ) ; // initial value
274
302
for _ in 0 ..4 {
275
303
m. step( ) . expect( "execution step failed" ) ;
276
304
}
277
305
assert_eq!( m. state. stack. len( ) , 1 ) ;
278
306
assert_eq!( m. state. stack. pop( ) . unwrap( ) , ret_addr. as_evm_word( ) ) ;
279
307
assert_eq!( m. system. nonce, 2 ) ;
308
+ assert!( m. state. return_data. is_empty( ) ) ;
280
309
} ;
281
310
}
282
311
@@ -466,6 +495,54 @@ mod tests {
466
495
// the deed
467
496
CREATE ;
468
497
}
498
+ m. state. return_data = b"deadbeef" . to_vec( ) ; // stale return data
499
+ m. state. stack. push( U256 :: from( 4 ) ) . unwrap( ) ; // input size
500
+ m. state. stack. push( U256 :: from( 28 ) ) . unwrap( ) ; // input offset
501
+ m. state. stack. push( U256 :: from( 1234 ) ) . unwrap( ) ; // initial value
502
+ for _ in 0 ..4 {
503
+ m. step( ) . expect( "execution step failed" ) ;
504
+ }
505
+ assert_eq!( m. state. stack. len( ) , 1 ) ;
506
+ assert_eq!( m. state. stack. pop( ) . unwrap( ) , U256 :: from( 0 ) ) ;
507
+ assert_eq!( m. system. nonce, 2 ) ;
508
+ assert!( m. state. return_data. is_empty( ) ) ;
509
+ } ;
510
+ }
511
+
512
+ #[ test]
513
+ fn test_create_revert ( ) {
514
+ let revert_data = & b"foobar" [ ..] ;
515
+ evm_unit_test ! {
516
+ ( rt) {
517
+ rt. set_balance( TokenAmount :: from_atto( 1_000_000 ) ) ;
518
+
519
+ let code = vec![ 0x01 , 0x02 , 0x03 , 0x04 ] ;
520
+ let nonce = 1 ;
521
+ let create_params = eam:: CreateParams { code, nonce } ;
522
+ let create_return = BytesSer ( revert_data) ;
523
+
524
+ rt. expect_gas_available( 10_000_000_000 ) ;
525
+ rt. expect_send(
526
+ EAM_ACTOR_ADDR ,
527
+ eam:: CREATE_METHOD_NUM ,
528
+ IpldBlock :: serialize_cbor( & create_params) . unwrap( ) ,
529
+ TokenAmount :: from_atto( 1234 ) ,
530
+ Some ( 63 * 10_000_000_000 / 64 ) ,
531
+ SendFlags :: empty( ) ,
532
+ IpldBlock :: serialize_cbor( & create_return) . unwrap( ) ,
533
+ EVM_CONTRACT_REVERTED ,
534
+ None ,
535
+ ) ;
536
+ }
537
+ ( m) {
538
+ // input data
539
+ PUSH4 ; 0x01 ; 0x02 ; 0x03 ; 0x04 ;
540
+ PUSH0 ;
541
+ MSTORE ;
542
+ // the deed
543
+ CREATE ;
544
+ }
545
+ m. state. return_data = b"deadbeef" . to_vec( ) ; // stale return data
469
546
m. state. stack. push( U256 :: from( 4 ) ) . unwrap( ) ; // input size
470
547
m. state. stack. push( U256 :: from( 28 ) ) . unwrap( ) ; // input offset
471
548
m. state. stack. push( U256 :: from( 1234 ) ) . unwrap( ) ; // initial value
@@ -475,6 +552,7 @@ mod tests {
475
552
assert_eq!( m. state. stack. len( ) , 1 ) ;
476
553
assert_eq!( m. state. stack. pop( ) . unwrap( ) , U256 :: from( 0 ) ) ;
477
554
assert_eq!( m. system. nonce, 2 ) ;
555
+ assert_eq!( m. state. return_data, revert_data)
478
556
} ;
479
557
}
480
558
0 commit comments