@@ -20,6 +20,7 @@ use fvm_shared::event::{ActorEvent, Entry, Flags};
2020use fvm_shared:: piece:: { zero_piece_commitment, PaddedPieceSize } ;
2121use fvm_shared:: sector:: { RegisteredPoStProof , SectorInfo } ;
2222use fvm_shared:: sys:: out:: vm:: ContextFlags ;
23+ use fvm_shared:: upgrade:: UpgradeInfo ;
2324use fvm_shared:: { commcid, ActorID } ;
2425use lazy_static:: lazy_static;
2526use multihash:: MultihashDigest ;
@@ -139,7 +140,7 @@ where
139140
140141 // Send.
141142 let result = self . call_manager . with_transaction ( |cm| {
142- cm. send :: < K > (
143+ cm. call_actor :: < K > (
143144 from,
144145 * recipient,
145146 Entrypoint :: Invoke ( method) ,
@@ -873,7 +874,7 @@ where
873874 . create_actor ( code_id, actor_id, delegated_address)
874875 }
875876
876- fn upgrade_actor ( & mut self , new_code_cid : Cid , params_id : BlockId ) -> Result < u32 > {
877+ fn upgrade_actor < K : Kernel > ( & mut self , new_code_cid : Cid , params_id : BlockId ) -> Result < u32 > {
877878 if self . read_only {
878879 return Err (
879880 syscall_error ! ( ReadOnly , "upgrade_actor cannot be called while read-only" ) . into ( ) ,
@@ -888,7 +889,52 @@ where
888889 } ;
889890
890891 let result = self . call_manager . with_transaction ( |cm| {
891- cm. upgrade_actor :: < Self > ( self . caller , self . actor_id , new_code_cid, params)
892+ let origin = cm. origin ( ) ;
893+
894+ let state = cm
895+ . get_actor ( self . actor_id ) ?
896+ . ok_or_else ( || syscall_error ! ( NotFound ; "actor not found" ) ) ?;
897+
898+ // store the code cid of the calling actor before running the upgrade entrypoint
899+ // in case it was changed (which could happen if the target upgrade entrypoint
900+ // sent a message to this actor which in turn called upgrade)
901+ let code = state. code ;
902+
903+ // update the code cid of the actor to new_code_cid
904+ cm. set_actor (
905+ origin,
906+ ActorState :: new (
907+ new_code_cid,
908+ state. state ,
909+ state. balance ,
910+ state. sequence ,
911+ None ,
912+ ) ,
913+ ) ?;
914+
915+ // run the upgrade entrypoint
916+ let result = cm. call_actor :: < Self > (
917+ self . caller ,
918+ Address :: new_id ( self . actor_id ) ,
919+ Entrypoint :: Upgrade ( UpgradeInfo { old_code_cid : code } ) ,
920+ params,
921+ & TokenAmount :: from_whole ( 0 ) ,
922+ None ,
923+ false ,
924+ ) ?;
925+
926+ if result. exit_code == ExitCode :: OK {
927+ // after running the upgrade, our code cid must not have changed
928+ let code_after_upgrade = cm
929+ . get_actor ( self . actor_id ) ?
930+ . ok_or_else ( || syscall_error ! ( NotFound ; "actor not found" ) ) ?
931+ . code ;
932+ if code != code_after_upgrade {
933+ return Err ( syscall_error ! ( Forbidden ; "re-entrant upgrade detected" ) . into ( ) ) ;
934+ }
935+ }
936+
937+ Ok ( result)
892938 } ) ;
893939
894940 match result {
0 commit comments