Skip to content

Commit

Permalink
Merge pull request Phala-Network#990 from Phala-Network/upload-sidevm…
Browse files Browse the repository at this point in the history
…code

Upload sidevm code via pRPC
  • Loading branch information
kvinwang authored Oct 13, 2022
2 parents 1592904 + 0d95215 commit d1c18f1
Show file tree
Hide file tree
Showing 22 changed files with 561 additions and 32 deletions.
1 change: 0 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

env:
CARGO_TERM_COLOR: always
Expand Down
2 changes: 1 addition & 1 deletion crates/phactory/api/proto
Submodule proto updated 1 files
+13 −0 pruntime_rpc.proto
2 changes: 1 addition & 1 deletion crates/phactory/src/contracts/pink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ impl Pink {
.as_ref()
.ok_or(QueryError::SidevmNotFound)?;
let cmd_sender = match handle {
contracts::SidevmHandle::Terminated(_) => {
contracts::SidevmHandle::Stopped(_) => {
return Err(QueryError::SidevmNotFound)
}
contracts::SidevmHandle::Running(sender) => sender,
Expand Down
73 changes: 55 additions & 18 deletions crates/phactory/src/contracts/support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl Decode for RawData {
#[derive(Clone)]
pub enum SidevmHandle {
Running(CommandSender),
Terminated(ExitReason),
Stopped(ExitReason),
}

impl Serialize for SidevmHandle {
Expand All @@ -88,7 +88,7 @@ impl Serialize for SidevmHandle {
{
match self {
SidevmHandle::Running(_) => ExitReason::Restore.serialize(serializer),
SidevmHandle::Terminated(r) => r.serialize(serializer),
SidevmHandle::Stopped(r) => r.serialize(serializer),
}
}
}
Expand All @@ -99,7 +99,7 @@ impl<'de> Deserialize<'de> for SidevmHandle {
D: serde::Deserializer<'de>,
{
let reason = ExitReason::deserialize(deserializer)?;
Ok(SidevmHandle::Terminated(reason))
Ok(SidevmHandle::Stopped(reason))
}
}

Expand All @@ -112,6 +112,11 @@ struct SidevmInfo {
handle: Arc<Mutex<SidevmHandle>>,
}

pub(crate) enum SidevmCode {
Hash(H256),
Code(Vec<u8>),
}

#[derive(Serialize, Deserialize)]
pub struct FatContract {
#[serde(with = "more::scale_bytes")]
Expand Down Expand Up @@ -232,24 +237,55 @@ impl FatContract {
pub(crate) fn start_sidevm(
&mut self,
spawner: &sidevm::service::Spawner,
code: Vec<u8>,
auto_restart: bool,
code: SidevmCode,
ensure_waiting_code: bool,
) -> Result<()> {
if let Some(info) = &self.sidevm_info {
if let SidevmHandle::Running(_) = &*info.handle.lock().unwrap() {
bail!("Sidevm can only be started once");
}
let handle = self.sidevm_handle();
if let Some(SidevmHandle::Running(_)) = &handle {
bail!("Sidevm can only be started once");
}
let handle = do_start_sidevm(spawner, &code, self.contract_id.0, self.weight)?;

let code_hash = sp_core::blake2_256(&code).into();
let (code, code_hash) = match code {
SidevmCode::Hash(hash) => (vec![], hash),
SidevmCode::Code(code) => {
let actual_hash = sp_core::blake2_256(&code).into();
if ensure_waiting_code {
if !matches!(
&handle,
Some(SidevmHandle::Stopped(ExitReason::WaitingForCode))
) {
bail!("The sidevm isn't waiting for code");
}
let expected_hash = self
.sidevm_info
.as_ref()
.ok_or(anyhow!("No sidevm info"))?
.code_hash;
if actual_hash != expected_hash {
bail!(
"Code hash mismatch, expected: {expected_hash:?}, actual: {actual_hash:?}"
);
}
}
(code, actual_hash)
}
};

let handle = if code.is_empty() {
Arc::new(Mutex::new(SidevmHandle::Stopped(
ExitReason::WaitingForCode,
)))
} else {
do_start_sidevm(spawner, &code, self.contract_id.0, self.weight)?
};

let start_time = chrono::Utc::now().to_rfc3339();
self.sidevm_info = Some(SidevmInfo {
code,
code_hash,
start_time,
handle,
auto_restart,
auto_restart: true,
});
Ok(())
}
Expand All @@ -260,7 +296,7 @@ impl FatContract {
) -> Result<()> {
if let Some(sidevm_info) = &mut self.sidevm_info {
let guard = sidevm_info.handle.lock().unwrap();
let handle = if let SidevmHandle::Terminated(reason) = &*guard {
let handle = if let SidevmHandle::Stopped(reason) = &*guard {
let need_restart = match reason {
ExitReason::Exited(_) => false,
ExitReason::Stopped => false,
Expand All @@ -272,6 +308,7 @@ impl FatContract {
ExitReason::OcallAborted(OcallAborted::GasExhausted) => false,
ExitReason::OcallAborted(OcallAborted::Stifled) => true,
ExitReason::Restore => true,
ExitReason::WaitingForCode => false,
};
if !need_restart {
return Ok(());
Expand All @@ -298,7 +335,7 @@ impl FatContract {
let vmid = sidevm::ShortId(&self.contract_id.0);

let tx = match &*handle.lock().unwrap() {
SidevmHandle::Terminated(_) => {
SidevmHandle::Stopped(_) => {
error!(target: "sidevm", "[{vmid}] PM to sidevm failed, instance terminated");
return Err(anyhow!(
"Push message to sidevm failed, instance terminated"
Expand All @@ -324,15 +361,15 @@ impl FatContract {
pub(crate) fn get_system_message_handler(&self) -> Option<CommandSender> {
let guard = self.sidevm_info.as_ref()?.handle.lock().unwrap();
match &*guard {
SidevmHandle::Terminated(_) => None,
SidevmHandle::Stopped(_) => None,
SidevmHandle::Running(tx) => Some(tx.clone()),
}
}

pub(crate) fn destroy(self, spawner: &sidevm::service::Spawner) {
if let Some(sidevm_info) = &self.sidevm_info {
match sidevm_info.handle.lock().unwrap().clone() {
SidevmHandle::Terminated(_) => {}
SidevmHandle::Stopped(_) => {}
SidevmHandle::Running(tx) => {
spawner.spawn(async move {
if let Err(err) = tx.send(SidevmCommand::Stop).await {
Expand Down Expand Up @@ -373,7 +410,7 @@ impl FatContract {
start_time,
..Default::default()
},
SidevmHandle::Terminated(reason) => pb::SidevmInfo {
SidevmHandle::Stopped(reason) => pb::SidevmInfo {
state: "stopped".into(),
code_hash,
start_time,
Expand Down Expand Up @@ -410,7 +447,7 @@ fn do_start_sidevm(
let vmid = sidevm::ShortId(&id);
let reason = join_handle.await.unwrap_or(ExitReason::Cancelled);
error!(target: "sidevm", "[{vmid}] Sidevm process terminated with reason: {:?}", reason);
*cloned_handle.lock().unwrap() = SidevmHandle::Terminated(reason);
*cloned_handle.lock().unwrap() = SidevmHandle::Stopped(reason);
});
Ok(handle)
}
Expand Down
21 changes: 20 additions & 1 deletion crates/phactory/src/prpc_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,12 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> Phactory<Platform>
.collect();
Ok(pb::GetClusterInfoResponse { clusters })
}

pub fn upload_sidevm_code(&mut self, contract_id: ContractId, code: Vec<u8>) -> RpcResult<()> {
self.system()?
.upload_sidevm_code(contract_id, code)
.map_err(from_display)
}
}

#[derive(Clone)]
Expand Down Expand Up @@ -1388,7 +1394,8 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> PhactoryApi for Rpc
&mut self,
request: pb::GetContractInfoRequest,
) -> Result<pb::GetContractInfoResponse, prpc::server::Error> {
self.lock_phactory().get_contract_info(&request.contract_ids)
self.lock_phactory()
.get_contract_info(&request.contract_ids)
}

async fn get_cluster_info(
Expand All @@ -1397,6 +1404,18 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> PhactoryApi for Rpc
) -> Result<pb::GetClusterInfoResponse, prpc::server::Error> {
self.lock_phactory().get_cluster_info()
}

async fn upload_sidevm_code(
&mut self,
request: pb::SidevmCode,
) -> Result<(), prpc::server::Error> {
let contract_id: [u8; 32] = request
.contract
.try_into()
.or(Err(from_display("Invalid contract id")))?;
self.lock_phactory()
.upload_sidevm_code(contract_id.into(), request.code)
}
}

fn try_decode_hex(hex_str: &str) -> Result<Vec<u8>, hex::FromHexError> {
Expand Down
25 changes: 17 additions & 8 deletions crates/phactory/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod side_tasks;

use crate::{
benchmark,
contracts::{pink::cluster::Cluster, AnyContract, ContractsKeeper, ExecuteEnv},
contracts::{pink::cluster::Cluster, AnyContract, ContractsKeeper, ExecuteEnv, SidevmCode},
pink::{cluster::ClusterKeeper, ContractEventCallback, Pink},
secret_channel::{ecdh_serde, SecretReceiver},
types::{BlockInfo, OpaqueError, OpaqueQuery, OpaqueReply},
Expand Down Expand Up @@ -1626,6 +1626,18 @@ impl<P: pal::Platform> System<P> {
&self.sidevm_spawner,
);
}

pub(crate) fn upload_sidevm_code(
&mut self,
contract_id: ContractId,
code: Vec<u8>,
) -> Result<()> {
let contract = self
.contracts
.get_mut(&contract_id)
.ok_or_else(|| anyhow!("Contract not found"))?;
contract.start_sidevm(&self.sidevm_spawner, SidevmCode::Code(code), true)
}
}

pub fn handle_contract_command_result(
Expand Down Expand Up @@ -1809,14 +1821,11 @@ pub(crate) fn apply_pink_events(
let vmid = sidevm::ShortId(target_contract.as_ref());
let target_contract = get_contract!(&target_contract);
let code_hash = code_hash.into();
let wasm_code = match cluster.get_resource(ResourceType::SidevmCode, &code_hash) {
Some(code) => code,
None => {
error!(target: "sidevm", "[{vmid}] Start sidevm failed: code not found, code_hash={code_hash:?}");
continue;
}
let code = match cluster.get_resource(ResourceType::SidevmCode, &code_hash) {
Some(code) => SidevmCode::Code(code),
None => SidevmCode::Hash(code_hash),
};
if let Err(err) = target_contract.start_sidevm(&spawner, wasm_code, true) {
if let Err(err) = target_contract.start_sidevm(&spawner, code, false) {
error!(target: "sidevm", "[{vmid}] Start sidevm failed: {:?}", err);
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/sidevm/host-runtime/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum ExitReason {
OcallAborted(OcallAborted),
/// When a previous running instance restored from a checkpoint.
Restore,
/// The sidevm was deployed without code, so it it waiting to a custom code uploading.
WaitingForCode,
}

pub enum Command {
Expand Down
2 changes: 1 addition & 1 deletion e2e/res/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ $(SYSTEM_CONTRACT_BIN):
cd "${SYSTEM_CONTRACT_DIR}" && cargo contract build --release

${CHECK_SYSTEM_BIN}:
cd check_system && cargo contract build --release
make -C check_system

3 changes: 3 additions & 0 deletions e2e/res/check_system/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ scale-info = { version = "2", default-features = false, features = ["derive"], o

pink-extension = { version = "0.1", default-features = false, path = "../../../crates/pink/pink-extension" }

[build-dependencies]
sp-core = "6.0"

[lib]
name = "check_system"
path = "lib.rs"
Expand Down
23 changes: 23 additions & 0 deletions e2e/res/check_system/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
SIDE_PROG_DIR=sleep
SIDE_PROG=sleep
TARGET=wasm32-wasi

SIDE_WASM=${SIDE_PROG_DIR}/target/${TARGET}/release/${SIDE_PROG}.wasm

target/ink/start_sidevm.contract: sideprog.wasm
cargo contract build --release

sideprog.wasm: ${SIDE_WASM}
cp ${SIDE_WASM} ./sideprog.wasm
wasm-strip sideprog.wasm

.PHONY: ${SIDE_WASM}

${SIDE_WASM}:
cargo build --manifest-path ${SIDE_PROG_DIR}/Cargo.toml --release --target ${TARGET}

.PHONY: clean
clean:
rm -rf sideprog.wasm
rm -rf target/ ${SIDE_PROG_DIR}/target

4 changes: 4 additions & 0 deletions e2e/res/check_system/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() {
let hash = sp_core::blake2_256(include_bytes!("./sideprog.wasm"));
std::fs::write("./sideprog.wasm.hash", hash).unwrap();
}
10 changes: 10 additions & 0 deletions e2e/res/check_system/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ mod check_system {
}
self.on_block_end_called = true
}

#[ink(message)]
pub fn start_sidevm(&self) -> bool {
let hash = *include_bytes!("./sideprog.wasm.hash");
let system = pink::system::SystemRef::instance();
system
.deploy_sidevm_to(self.env().account_id(), hash)
.expect("Failed to deploy sidevm");
true
}
}

impl ContractDeposit for CheckSystem {
Expand Down
Binary file added e2e/res/check_system/sideprog.wasm
Binary file not shown.
Binary file added e2e/res/check_system/sideprog.wasm.hash
Binary file not shown.
17 changes: 17 additions & 0 deletions e2e/res/check_system/sleep/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# workaround for https://github.com/rust-lang/cargo/issues/6745
[workspace]

[package]
edition = "2021"
name = "sleep"
version = "0.1.0"

[lib]
crate-type = ["cdylib"]

[dependencies]
log = "0.4.16"
once_cell = "1.10.0"
sidevm = "0.1"
tokio = { version = "1", features = ["macros"] }
futures = "0.3"
6 changes: 6 additions & 0 deletions e2e/res/check_system/sleep/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[sidevm::main]
async fn main() {
loop {
sidevm::time::sleep(std::time::Duration::from_secs(10)).await
}
}
Loading

0 comments on commit d1c18f1

Please sign in to comment.