1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))]
3#![doc = include_str!("../README.md")]
4
5#[macro_use]
6extern crate alloc;
7
8use alloc::string::String;
9use alloc::vec::Vec;
10
11use ink::env::{emit_event, topics::state::HasRemainingTopics, Environment, Topics};
12
13use ink::EnvAccess;
14use scale::{Decode, Encode};
15
16pub use pink_extension_macro::{contract, driver};
17
18pub mod chain_extension;
19pub use chain_extension::pink_extension_instance as ext;
20pub mod logger;
21pub mod system;
22
23#[cfg(all(not(feature = "std"), feature = "dlmalloc"))]
24mod allocator_dlmalloc;
25
26pub use logger::ResultExt;
27use serde::{Deserialize, Serialize};
28
29const PINK_EVENT_TOPIC: &[u8] = b"phala.pink.event";
30
31pub type WorkerId = [u8; 32];
32pub type EcdhPublicKey = [u8; 32];
33pub type Hash = [u8; 32];
34pub type EcdsaPublicKey = [u8; 33];
35pub type EcdsaSignature = [u8; 65];
36pub type AccountId = <PinkEnvironment as Environment>::AccountId;
37pub type Balance = <PinkEnvironment as Environment>::Balance;
38pub type BlockNumber = <PinkEnvironment as Environment>::BlockNumber;
39
40pub trait ConvertTo<To> {
41 fn convert_to(&self) -> To;
42}
43
44impl<F, T> ConvertTo<T> for F
45where
46 F: AsRef<[u8; 32]>,
47 T: From<[u8; 32]>,
48{
49 fn convert_to(&self) -> T {
50 (*self.as_ref()).into()
51 }
52}
53
54#[derive(Encode, Decode, Debug)]
56#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
57pub struct Message {
58 pub payload: Vec<u8>,
59 pub topic: Vec<u8>,
60}
61
62#[derive(Encode, Decode, Debug)]
64#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
65pub struct OspMessage {
66 pub message: Message,
67 pub remote_pubkey: Option<EcdhPublicKey>,
68}
69
70#[derive(Encode, Decode, Debug, Clone)]
72#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
73pub enum HookPoint {
74 OnBlockEnd,
76}
77
78#[derive(Encode, Decode, Debug, Clone)]
80#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
81pub enum PinkEvent {
82 #[codec(index = 2)]
89 SetHook {
90 hook: HookPoint,
92 contract: AccountId,
94 selector: u32,
96 gas_limit: u64,
98 },
99 #[codec(index = 3)]
106 DeploySidevmTo {
107 contract: AccountId,
109 code_hash: Hash,
111 },
112 #[codec(index = 4)]
119 SidevmMessage(Vec<u8>),
120 #[codec(index = 5)]
125 CacheOp(CacheOp),
126 #[codec(index = 6)]
133 StopSidevm,
134 #[codec(index = 7)]
141 ForceStopSidevm {
142 contract: AccountId,
144 },
145 #[codec(index = 8)]
152 SetLogHandler(AccountId),
153 #[codec(index = 9)]
160 SetContractWeight { contract: AccountId, weight: u32 },
161 #[codec(index = 10)]
168 UpgradeRuntimeTo { version: (u32, u32) },
169 #[codec(index = 11)]
174 SidevmOperation(SidevmOperation),
175 #[codec(index = 12)]
182 SetJsRuntime(Hash),
183}
184
185#[derive(Encode, Decode, Debug, Clone)]
186#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
187pub enum SidevmOperation {
188 Start {
189 contract: AccountId,
191 code_hash: Hash,
193 workers: Workers,
195 config: SidevmConfig,
196 },
197 SetDeadline {
198 contract: AccountId,
200 deadline: u32,
202 },
203}
204
205#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
206#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
207pub struct SidevmConfig {
208 pub max_code_size: u32,
210 pub max_memory_pages: u32,
212 pub vital_capacity: u64,
214 pub deadline: u32,
216}
217
218impl Default for SidevmConfig {
219 fn default() -> Self {
220 Self {
221 max_code_size: 1024 * 1024 * 10,
222 max_memory_pages: 1024,
224 vital_capacity: 50_000_000_000_u64,
226 deadline: u32::MAX,
227 }
228 }
229}
230
231#[derive(Encode, Decode, Debug, Clone)]
232#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
233pub enum Workers {
234 All,
235 List(Vec<WorkerId>),
236}
237
238impl PinkEvent {
239 pub fn allowed_in_query(&self) -> bool {
240 match self {
241 PinkEvent::SetHook { .. } => false,
242 PinkEvent::DeploySidevmTo { .. } => true,
243 PinkEvent::SidevmMessage(_) => true,
244 PinkEvent::CacheOp(_) => true,
245 PinkEvent::StopSidevm => true,
246 PinkEvent::ForceStopSidevm { .. } => true,
247 PinkEvent::SetLogHandler(_) => false,
248 PinkEvent::SetContractWeight { .. } => false,
249 PinkEvent::UpgradeRuntimeTo { .. } => false,
250 PinkEvent::SidevmOperation(_) => true,
251 PinkEvent::SetJsRuntime(_) => false,
252 }
253 }
254
255 pub fn name(&self) -> &'static str {
256 match self {
257 PinkEvent::SetHook { .. } => "SetHook",
258 PinkEvent::DeploySidevmTo { .. } => "DeploySidevmTo",
259 PinkEvent::SidevmMessage(_) => "SidevmMessage",
260 PinkEvent::CacheOp(_) => "CacheOp",
261 PinkEvent::StopSidevm => "StopSidevm",
262 PinkEvent::ForceStopSidevm { .. } => "ForceStopSidevm",
263 PinkEvent::SetLogHandler(_) => "SetLogHandler",
264 PinkEvent::SetContractWeight { .. } => "SetContractWeight",
265 PinkEvent::UpgradeRuntimeTo { .. } => "UpgradeRuntimeTo",
266 PinkEvent::SidevmOperation(_) => "SidevmOperation",
267 PinkEvent::SetJsRuntime(_) => "SetJsRuntime",
268 }
269 }
270
271 pub fn is_private(&self) -> bool {
272 match self {
273 PinkEvent::SetHook { .. } => false,
274 PinkEvent::DeploySidevmTo { .. } => false,
275 PinkEvent::SidevmMessage(_) => true,
276 PinkEvent::CacheOp(_) => true,
277 PinkEvent::StopSidevm => false,
278 PinkEvent::ForceStopSidevm { .. } => false,
279 PinkEvent::SetLogHandler(_) => false,
280 PinkEvent::SetContractWeight { .. } => false,
281 PinkEvent::UpgradeRuntimeTo { .. } => false,
282 PinkEvent::SidevmOperation(_) => false,
283 PinkEvent::SetJsRuntime(_) => false,
284 }
285 }
286}
287
288#[derive(Encode, Decode, Debug, Clone)]
290#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
291pub enum CacheOp {
292 Set { key: Vec<u8>, value: Vec<u8> },
294 SetExpiration { key: Vec<u8>, expiration: u64 },
296 Remove { key: Vec<u8> },
298}
299
300impl Topics for PinkEvent {
301 type RemainingTopics = [HasRemainingTopics; 1];
302
303 fn topics<E, B>(
304 &self,
305 builder: ink::env::topics::TopicsBuilder<ink::env::topics::state::Uninit, E, B>,
306 ) -> <B as ink::env::topics::TopicsBuilderBackend<E>>::Output
307 where
308 E: Environment,
309 B: ink::env::topics::TopicsBuilderBackend<E>,
310 {
311 builder
312 .build::<Self>()
313 .push_topic(&PINK_EVENT_TOPIC)
314 .finish()
315 }
316}
317
318#[cfg(feature = "runtime_utils")]
319impl PinkEvent {
320 pub fn event_topic() -> Hash {
321 use std::convert::TryFrom;
322 let topics = topic::topics_for(Self::StopSidevm);
323 let topic: &[u8] = topics[0].as_ref();
324 Hash::try_from(topic).expect("Should not failed")
325 }
326}
327
328pub fn set_hook(hook: HookPoint, contract: AccountId, selector: u32, gas_limit: u64) {
348 emit_event::<PinkEnvironment, _>(PinkEvent::SetHook {
349 hook,
350 contract,
351 selector,
352 gas_limit,
353 })
354}
355
356pub fn start_sidevm(code_hash: Hash) -> Result<(), system::DriverError> {
372 let driver =
373 crate::system::SidevmOperationRef::instance().ok_or(system::Error::DriverNotFound)?;
374 driver.deploy(code_hash)
375}
376
377pub fn deploy_sidevm_to(contract: AccountId, code_hash: Hash) {
379 emit_event::<PinkEnvironment, _>(PinkEvent::DeploySidevmTo {
380 contract,
381 code_hash,
382 });
383}
384
385pub fn stop_sidevm_at(contract: AccountId) {
387 emit_event::<PinkEnvironment, _>(PinkEvent::ForceStopSidevm { contract });
388}
389
390pub fn force_stop_sidevm() {
395 emit_event::<PinkEnvironment, _>(PinkEvent::StopSidevm)
396}
397
398pub fn push_sidevm_message(message: Vec<u8>) {
411 emit_event::<PinkEnvironment, _>(PinkEvent::SidevmMessage(message))
412}
413
414pub fn set_log_handler(contract: AccountId) {
416 emit_event::<PinkEnvironment, _>(PinkEvent::SetLogHandler(contract))
417}
418
419pub fn set_js_runtime(code_hash: Hash) {
421 emit_event::<PinkEnvironment, _>(PinkEvent::SetJsRuntime(code_hash))
422}
423
424pub fn set_contract_weight(contract: AccountId, weight: u32) {
426 emit_event::<PinkEnvironment, _>(PinkEvent::SetContractWeight { contract, weight });
427}
428
429pub fn upgrade_runtime(version: (u32, u32)) {
433 emit_event::<PinkEnvironment, _>(PinkEvent::UpgradeRuntimeTo { version });
434}
435
436pub fn vrf(salt: &[u8]) -> Vec<u8> {
444 let mut key_salt = b"vrf:".to_vec();
445 key_salt.extend_from_slice(salt);
446 ext().derive_sr25519_key(key_salt.into())
447}
448
449pub fn query_local_sidevm(address: AccountId, payload: Vec<u8>) -> Result<Vec<u8>, String> {
451 let url = format!("sidevm://{}", hex::encode(address));
452 let response = http_post!(url, payload);
453 if response.status_code != 200 {
454 return Err(format!(
455 "SideVM query failed: {} {}: {}",
456 response.status_code,
457 response.reason_phrase,
458 String::from_utf8_lossy(&response.body)
459 ));
460 }
461 Ok(response.body)
462}
463
464#[derive(Debug, Clone, PartialEq, Eq)]
487#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
488pub enum PinkEnvironment {}
489
490impl Environment for PinkEnvironment {
491 const MAX_EVENT_TOPICS: usize = <ink::env::DefaultEnvironment as Environment>::MAX_EVENT_TOPICS;
492
493 type AccountId = <ink::env::DefaultEnvironment as Environment>::AccountId;
494 type Balance = <ink::env::DefaultEnvironment as Environment>::Balance;
495 type Hash = <ink::env::DefaultEnvironment as Environment>::Hash;
496 type BlockNumber = <ink::env::DefaultEnvironment as Environment>::BlockNumber;
497 type Timestamp = <ink::env::DefaultEnvironment as Environment>::Timestamp;
498
499 type ChainExtension = chain_extension::PinkExt;
500}
501
502pub fn env() -> EnvAccess<'static, PinkEnvironment> {
504 Default::default()
505}
506
507#[cfg(feature = "runtime_utils")]
508mod topic;
509
510#[cfg(test)]
511mod tests {
512 #[test]
513 fn test_event_topics() {
514 insta::assert_debug_snapshot!(super::PinkEvent::event_topic());
515 }
516}