@@ -8,17 +8,17 @@ use casper_executor_wasm_common::{
88 ENTRY_POINT_PAYMENT_SELF_ONWARD ,
99 } ,
1010 error:: {
11- CallError , TrapCode , HOST_ERROR_INVALID_DATA , HOST_ERROR_INVALID_INPUT ,
12- HOST_ERROR_MAX_MESSAGES_PER_BLOCK_EXCEEDED , HOST_ERROR_MESSAGE_TOPIC_FULL ,
13- HOST_ERROR_NOT_FOUND , HOST_ERROR_PAYLOAD_TOO_LONG , HOST_ERROR_SUCCESS ,
14- HOST_ERROR_TOO_MANY_TOPICS , HOST_ERROR_TOPIC_TOO_LONG ,
11+ CallError , CALLEE_NOT_CALLABLE , CALLEE_SUCCEEDED , CALLEE_TRAPPED , HOST_ERROR_INVALID_DATA ,
12+ HOST_ERROR_INVALID_INPUT , HOST_ERROR_MAX_MESSAGES_PER_BLOCK_EXCEEDED ,
13+ HOST_ERROR_MESSAGE_TOPIC_FULL , HOST_ERROR_NOT_FOUND , HOST_ERROR_PAYLOAD_TOO_LONG ,
14+ HOST_ERROR_SUCCESS , HOST_ERROR_TOO_MANY_TOPICS , HOST_ERROR_TOPIC_TOO_LONG ,
1515 } ,
1616 flags:: ReturnFlags ,
1717 keyspace:: { Keyspace , KeyspaceTag } ,
1818} ;
1919use casper_executor_wasm_interface:: {
2020 executor:: { ExecuteError , ExecuteRequestBuilder , ExecuteResult , ExecutionKind , Executor } ,
21- u32_from_host_result, Caller , HostResult , VMError , VMResult ,
21+ u32_from_host_result, Caller , VMError , VMResult ,
2222} ;
2323use casper_storage:: {
2424 global_state:: GlobalStateReader ,
@@ -129,7 +129,7 @@ pub fn casper_write<S: GlobalStateReader, E: Executor>(
129129 Ok ( key_name) => key_name,
130130 Err ( _) => {
131131 // TODO: Invalid key name encoding
132- return Ok ( HOST_ERROR_NOT_FOUND ) ;
132+ return Ok ( HOST_ERROR_INVALID_DATA ) ;
133133 }
134134 } ;
135135
@@ -139,7 +139,7 @@ pub fn casper_write<S: GlobalStateReader, E: Executor>(
139139 let key_name = match std:: str:: from_utf8 ( & key_payload_bytes) {
140140 Ok ( key_name) => key_name,
141141 Err ( _) => {
142- return Ok ( 1 ) ;
142+ return Ok ( HOST_ERROR_INVALID_DATA ) ;
143143 }
144144 } ;
145145
@@ -197,6 +197,104 @@ pub fn casper_write<S: GlobalStateReader, E: Executor>(
197197 Ok ( HOST_ERROR_SUCCESS )
198198}
199199
200+ /// Remove value under a key.
201+ ///
202+ /// This produces a transformation of Prune to the global state. Keep in mind that technically the
203+ /// data is not removed from the global state as it still there, it's just not reachable anymore
204+ /// from the newly created tip.
205+ ///
206+ /// The name for this host function is `remove` to keep it simple and consistent with read/write
207+ /// verbs, and also consistent with the rust stdlib vocabulary i.e. `V`
208+ pub fn casper_remove < S : GlobalStateReader , E : Executor > (
209+ mut caller : impl Caller < Context = Context < S , E > > ,
210+ key_space : u64 ,
211+ key_ptr : u32 ,
212+ key_size : u32 ,
213+ ) -> VMResult < u32 > {
214+ let write_cost = caller. context ( ) . config . host_function_costs ( ) . remove ;
215+ charge_host_function_call (
216+ & mut caller,
217+ & write_cost,
218+ [ key_space as u32 , key_ptr, key_size] ,
219+ ) ?;
220+
221+ let keyspace_tag = match KeyspaceTag :: from_u64 ( key_space) {
222+ Some ( keyspace_tag) => keyspace_tag,
223+ None => {
224+ // Unknown keyspace received, return error
225+ return Ok ( HOST_ERROR_NOT_FOUND ) ;
226+ }
227+ } ;
228+
229+ let key_payload_bytes = caller. memory_read ( key_ptr, key_size. try_into ( ) . unwrap ( ) ) ?;
230+
231+ let keyspace = match keyspace_tag {
232+ KeyspaceTag :: State => Keyspace :: State ,
233+ KeyspaceTag :: Context => Keyspace :: Context ( & key_payload_bytes) ,
234+ KeyspaceTag :: NamedKey => {
235+ let key_name = match std:: str:: from_utf8 ( & key_payload_bytes) {
236+ Ok ( key_name) => key_name,
237+ Err ( _) => {
238+ // TODO: Invalid key name encoding
239+ return Ok ( HOST_ERROR_INVALID_DATA ) ;
240+ }
241+ } ;
242+
243+ Keyspace :: NamedKey ( key_name)
244+ }
245+ KeyspaceTag :: PaymentInfo => {
246+ let key_name = match std:: str:: from_utf8 ( & key_payload_bytes) {
247+ Ok ( key_name) => key_name,
248+ Err ( _) => {
249+ return Ok ( HOST_ERROR_INVALID_DATA ) ;
250+ }
251+ } ;
252+
253+ if !caller. has_export ( key_name) {
254+ // Missing wasm export, unable to perform global state write
255+ return Ok ( HOST_ERROR_NOT_FOUND ) ;
256+ }
257+
258+ Keyspace :: PaymentInfo ( key_name)
259+ }
260+ } ;
261+
262+ let global_state_key = match keyspace_to_global_state_key ( caller. context ( ) , keyspace) {
263+ Some ( global_state_key) => global_state_key,
264+ None => {
265+ // Unknown keyspace received, return error
266+ return Ok ( HOST_ERROR_NOT_FOUND ) ;
267+ }
268+ } ;
269+
270+ let global_state_read_result = caller. context_mut ( ) . tracking_copy . read ( & global_state_key) ;
271+ match global_state_read_result {
272+ Ok ( Some ( _stored_value) ) => {
273+ // Produce a prune transform only if value under a given key exists in the global state
274+ caller. context_mut ( ) . tracking_copy . prune ( global_state_key) ;
275+ }
276+ Ok ( None ) => {
277+ // Entry does not exists, and we can't proceed with the prune operation
278+ return Ok ( HOST_ERROR_NOT_FOUND ) ;
279+ }
280+ Err ( error) => {
281+ // To protect the network against potential non-determinism (i.e. one validator runs out
282+ // of space or just faces I/O issues that other validators may not have) we're simply
283+ // aborting the process, hoping that once the node goes back online issues are resolved
284+ // on the validator side. TODO: We should signal this to the contract
285+ // runtime somehow, and let validator nodes skip execution.
286+ error ! (
287+ ?error,
288+ ?global_state_key,
289+ "Error while attempting a read before removing value; aborting"
290+ ) ;
291+ panic ! ( "Error while attempting a read before removing value; aborting key={global_state_key:?} error={error:?}" )
292+ }
293+ }
294+
295+ Ok ( HOST_ERROR_SUCCESS )
296+ }
297+
200298pub fn casper_print < S : GlobalStateReader , E : Executor > (
201299 mut caller : impl Caller < Context = Context < S , E > > ,
202300 message_ptr : u32 ,
@@ -220,7 +318,7 @@ pub fn casper_read<S: GlobalStateReader, E: Executor>(
220318 info_ptr : u32 ,
221319 cb_alloc : u32 ,
222320 alloc_ctx : u32 ,
223- ) -> Result < u32 , VMError > {
321+ ) -> VMResult < u32 > {
224322 let read_cost = caller. context ( ) . config . host_function_costs ( ) . read ;
225323 charge_host_function_call (
226324 & mut caller,
@@ -439,7 +537,7 @@ pub fn casper_create<S: GlobalStateReader + 'static, E: Executor + 'static>(
439537 seed_ptr : u32 ,
440538 seed_len : u32 ,
441539 result_ptr : u32 ,
442- ) -> VMResult < HostResult > {
540+ ) -> VMResult < u32 > {
443541 let create_cost = caller. context ( ) . config . host_function_costs ( ) . create ;
444542 charge_host_function_call (
445543 & mut caller,
@@ -474,7 +572,7 @@ pub fn casper_create<S: GlobalStateReader + 'static, E: Executor + 'static>(
474572
475573 let seed = if seed_ptr != 0 {
476574 if seed_len != 32 {
477- return Ok ( Err ( CallError :: NotCallable ) ) ;
575+ return Ok ( CALLEE_NOT_CALLABLE ) ;
478576 }
479577 let seed_bytes = caller. memory_read ( seed_ptr, seed_len as usize ) ?;
480578 let seed_bytes: [ u8 ; 32 ] = seed_bytes. try_into ( ) . unwrap ( ) ; // SAFETY: We checked for length.
@@ -494,7 +592,7 @@ pub fn casper_create<S: GlobalStateReader + 'static, E: Executor + 'static>(
494592 Ok ( entry_point) => Some ( entry_point) ,
495593 Err ( utf8_error) => {
496594 error ! ( %utf8_error, "entry point name is not a valid utf-8 string; unable to call" ) ;
497- return Ok ( Err ( CallError :: NotCallable ) ) ;
595+ return Ok ( CALLEE_NOT_CALLABLE ) ;
498596 }
499597 }
500598 }
@@ -589,9 +687,7 @@ pub fn casper_create<S: GlobalStateReader + 'static, E: Executor + 'static>(
589687 Ok ( uref) => uref,
590688 Err ( mint_error) => {
591689 error ! ( ?mint_error, "Failed to create a purse" ) ;
592- return Ok ( Err ( CallError :: CalleeTrapped (
593- TrapCode :: UnreachableCodeReached ,
594- ) ) ) ;
690+ return Ok ( CALLEE_TRAPPED ) ;
595691 }
596692 } ;
597693
@@ -660,7 +756,7 @@ pub fn casper_create<S: GlobalStateReader + 'static, E: Executor + 'static>(
660756 caller. consume_gas ( gas_usage. gas_spent ( ) ) ?;
661757
662758 if let Some ( host_error) = host_error {
663- return Ok ( Err ( host_error) ) ;
759+ return Ok ( host_error. into_u32 ( ) ) ;
664760 }
665761
666762 caller
@@ -694,7 +790,7 @@ pub fn casper_create<S: GlobalStateReader + 'static, E: Executor + 'static>(
694790
695791 caller. memory_write ( result_ptr, create_result_bytes) ?;
696792
697- Ok ( Ok ( ( ) ) )
793+ Ok ( CALLEE_SUCCEEDED )
698794}
699795
700796#[ allow( clippy:: too_many_arguments) ]
@@ -709,7 +805,7 @@ pub fn casper_call<S: GlobalStateReader + 'static, E: Executor + 'static>(
709805 input_len : u32 ,
710806 cb_alloc : u32 ,
711807 cb_ctx : u32 ,
712- ) -> VMResult < HostResult > {
808+ ) -> VMResult < u32 > {
713809 let call_cost = caller. context ( ) . config . host_function_costs ( ) . call ;
714810 charge_host_function_call (
715811 & mut caller,
@@ -748,7 +844,7 @@ pub fn casper_call<S: GlobalStateReader + 'static, E: Executor + 'static>(
748844 Ok ( entry_point) => entry_point,
749845 Err ( utf8_error) => {
750846 error ! ( %utf8_error, "entry point name is not a valid utf-8 string; unable to call" ) ;
751- return Ok ( Err ( CallError :: NotCallable ) ) ;
847+ return Ok ( CALLEE_NOT_CALLABLE ) ;
752848 }
753849 }
754850 } ;
@@ -842,7 +938,7 @@ pub fn casper_call<S: GlobalStateReader + 'static, E: Executor + 'static>(
842938
843939 caller. consume_gas ( gas_spent) ?;
844940
845- Ok ( host_result)
941+ Ok ( u32_from_host_result ( host_result) )
846942}
847943
848944pub fn casper_env_caller < S : GlobalStateReader , E : Executor > (
@@ -881,7 +977,7 @@ pub fn casper_env_caller<S: GlobalStateReader, E: Executor>(
881977pub fn casper_env_transferred_value < S : GlobalStateReader , E : Executor > (
882978 mut caller : impl Caller < Context = Context < S , E > > ,
883979 output : u32 ,
884- ) -> Result < ( ) , VMError > {
980+ ) -> VMResult < ( ) > {
885981 let transferred_value_cost = caller
886982 . context ( )
887983 . config
@@ -1175,7 +1271,7 @@ pub fn casper_upgrade<S: GlobalStateReader + 'static, E: Executor>(
11751271 entry_point_size : u32 ,
11761272 input_ptr : u32 ,
11771273 input_size : u32 ,
1178- ) -> VMResult < HostResult > {
1274+ ) -> VMResult < u32 > {
11791275 let upgrade_cost = caller. context ( ) . config . host_function_costs ( ) . upgrade ;
11801276 charge_host_function_call (
11811277 & mut caller,
@@ -1203,7 +1299,7 @@ pub fn casper_upgrade<S: GlobalStateReader + 'static, E: Executor>(
12031299 Ok ( entry_point) => Some ( entry_point) ,
12041300 Err ( utf8_error) => {
12051301 error ! ( %utf8_error, "entry point name is not a valid utf-8 string; unable to call" ) ;
1206- return Ok ( Err ( CallError :: NotCallable ) ) ;
1302+ return Ok ( CALLEE_NOT_CALLABLE ) ;
12071303 }
12081304 }
12091305 }
@@ -1224,7 +1320,7 @@ pub fn casper_upgrade<S: GlobalStateReader + 'static, E: Executor>(
12241320 let ( smart_contract_addr, callee_addressable_entity_key) = match caller. context ( ) . callee {
12251321 Key :: Account ( _account_hash) => {
12261322 error ! ( "Account upgrade is not possible" ) ;
1227- return Ok ( Err ( CallError :: NotCallable ) ) ;
1323+ return Ok ( CALLEE_NOT_CALLABLE ) ;
12281324 }
12291325 addressable_entity_key @ Key :: SmartContract ( smart_contract_addr) => {
12301326 let smart_contract_key = addressable_entity_key;
@@ -1242,12 +1338,12 @@ pub fn casper_upgrade<S: GlobalStateReader + 'static, E: Executor>(
12421338 ?smart_contract_key,
12431339 "Unable to find latest addressible entity hash for contract"
12441340 ) ;
1245- return Ok ( Err ( CallError :: NotCallable ) ) ;
1341+ return Ok ( CALLEE_NOT_CALLABLE ) ;
12461342 }
12471343 }
12481344 }
12491345 Ok ( Some ( other) ) => panic ! ( "should be smart contract but got {other:?}" ) ,
1250- Ok ( None ) => return Ok ( Err ( CallError :: NotCallable ) ) ,
1346+ Ok ( None ) => return Ok ( CALLEE_NOT_CALLABLE ) ,
12511347 Err ( error) => {
12521348 error ! (
12531349 ?error,
@@ -1270,7 +1366,7 @@ pub fn casper_upgrade<S: GlobalStateReader + 'static, E: Executor>(
12701366 Ok ( Some ( other_entity) ) => {
12711367 panic ! ( "Unexpected entity type: {other_entity:?}" )
12721368 }
1273- Ok ( None ) => return Ok ( Err ( CallError :: NotCallable ) ) ,
1369+ Ok ( None ) => return Ok ( CALLEE_NOT_CALLABLE ) ,
12741370 Err ( error) => {
12751371 panic ! ( "Error while reading from storage; aborting key={callee_addressable_entity_key:?} error={error:?}" )
12761372 }
@@ -1347,7 +1443,7 @@ pub fn casper_upgrade<S: GlobalStateReader + 'static, E: Executor>(
13471443 caller. consume_gas ( gas_usage. gas_spent ( ) ) ?;
13481444
13491445 if let Some ( host_error) = host_error {
1350- return Ok ( Err ( host_error) ) ;
1446+ return Ok ( host_error. into_u32 ( ) ) ;
13511447 }
13521448
13531449 caller
@@ -1369,12 +1465,12 @@ pub fn casper_upgrade<S: GlobalStateReader + 'static, E: Executor>(
13691465 ?preparation_error,
13701466 "Wasm preparation error while performing upgrade"
13711467 ) ;
1372- return Ok ( Err ( CallError :: NotCallable ) ) ;
1468+ return Ok ( CALLEE_NOT_CALLABLE ) ;
13731469 }
13741470 }
13751471 }
13761472
1377- Ok ( Ok ( ( ) ) )
1473+ Ok ( CALLEE_SUCCEEDED )
13781474}
13791475
13801476// TODO: Should this be blocktime instead of block_time? [Consistency]
0 commit comments