1#![warn(missing_docs)]
77#![cfg_attr(not(feature = "std"), no_std)]
78#![cfg_attr(enable_alloc_error_handler, feature(alloc_error_handler))]
79
80extern crate alloc;
81
82use alloc::vec::Vec;
83
84#[cfg(feature = "std")]
85use tracing;
86
87#[cfg(feature = "std")]
88use sp_core::{
89 crypto::Pair,
90 hexdisplay::HexDisplay,
91 offchain::{OffchainDbExt, OffchainWorkerExt, TransactionPoolExt},
92 storage::ChildInfo,
93};
94#[cfg(feature = "std")]
95use sp_keystore::KeystoreExt;
96
97#[cfg(feature = "bandersnatch-experimental")]
98use sp_core::bandersnatch;
99use sp_core::{
100 crypto::KeyTypeId,
101 ecdsa, ed25519,
102 offchain::{
103 HttpError, HttpRequestId, HttpRequestStatus, OpaqueNetworkState, StorageKind, Timestamp,
104 },
105 sr25519,
106 storage::StateVersion,
107 LogLevel, LogLevelFilter, OpaquePeerId, H256,
108};
109
110#[cfg(feature = "bls-experimental")]
111use sp_core::{bls381, ecdsa_bls381};
112
113#[cfg(feature = "std")]
114use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration};
115
116use sp_runtime_interface::{
117 pass_by::{PassBy, PassByCodec},
118 runtime_interface, Pointer,
119};
120
121use codec::{Decode, Encode};
122
123#[cfg(feature = "std")]
124use secp256k1::{
125 ecdsa::{RecoverableSignature, RecoveryId},
126 Message, SECP256K1,
127};
128
129#[cfg(feature = "std")]
130use sp_externalities::{Externalities, ExternalitiesExt};
131
132pub use sp_externalities::MultiRemovalResults;
133
134#[cfg(all(not(feature = "disable_allocator"), substrate_runtime, target_family = "wasm"))]
135mod global_alloc_wasm;
136
137#[cfg(all(
138 not(feature = "disable_allocator"),
139 substrate_runtime,
140 any(target_arch = "riscv32", target_arch = "riscv64")
141))]
142mod global_alloc_riscv;
143
144#[cfg(feature = "std")]
145const LOG_TARGET: &str = "runtime::io";
146
147#[derive(Encode, Decode)]
149pub enum EcdsaVerifyError {
150 BadRS,
152 BadV,
154 BadSignature,
156}
157
158#[derive(PassByCodec, Encode, Decode)]
161pub enum KillStorageResult {
162 AllRemoved(u32),
165 SomeRemaining(u32),
168}
169
170impl From<MultiRemovalResults> for KillStorageResult {
171 fn from(r: MultiRemovalResults) -> Self {
172 match r.maybe_cursor {
176 None => Self::AllRemoved(r.loops),
177 Some(..) => Self::SomeRemaining(r.loops),
178 }
179 }
180}
181
182#[runtime_interface]
184pub trait Storage {
185 fn get(&mut self, key: &[u8]) -> Option<bytes::Bytes> {
187 self.storage(key).map(bytes::Bytes::from)
188 }
189
190 fn read(&mut self, key: &[u8], value_out: &mut [u8], value_offset: u32) -> Option<u32> {
196 self.storage(key).map(|value| {
197 let value_offset = value_offset as usize;
198 let data = &value[value_offset.min(value.len())..];
199 let written = std::cmp::min(data.len(), value_out.len());
200 value_out[..written].copy_from_slice(&data[..written]);
201 data.len() as u32
202 })
203 }
204
205 fn set(&mut self, key: &[u8], value: &[u8]) {
207 self.set_storage(key.to_vec(), value.to_vec());
208 }
209
210 fn clear(&mut self, key: &[u8]) {
212 self.clear_storage(key)
213 }
214
215 fn exists(&mut self, key: &[u8]) -> bool {
217 self.exists_storage(key)
218 }
219
220 fn clear_prefix(&mut self, prefix: &[u8]) {
222 let _ = Externalities::clear_prefix(*self, prefix, None, None);
223 }
224
225 #[version(2)]
251 fn clear_prefix(&mut self, prefix: &[u8], limit: Option<u32>) -> KillStorageResult {
252 Externalities::clear_prefix(*self, prefix, limit, None).into()
253 }
254
255 #[version(3, register_only)]
287 fn clear_prefix(
288 &mut self,
289 maybe_prefix: &[u8],
290 maybe_limit: Option<u32>,
291 maybe_cursor: Option<Vec<u8>>, ) -> MultiRemovalResults {
293 Externalities::clear_prefix(
294 *self,
295 maybe_prefix,
296 maybe_limit,
297 maybe_cursor.as_ref().map(|x| &x[..]),
298 )
299 .into()
300 }
301
302 fn append(&mut self, key: &[u8], value: Vec<u8>) {
311 self.storage_append(key.to_vec(), value);
312 }
313
314 fn root(&mut self) -> Vec<u8> {
320 self.storage_root(StateVersion::V0)
321 }
322
323 #[version(2)]
329 fn root(&mut self, version: StateVersion) -> Vec<u8> {
330 self.storage_root(version)
331 }
332
333 fn changes_root(&mut self, _parent_hash: &[u8]) -> Option<Vec<u8>> {
335 None
336 }
337
338 fn next_key(&mut self, key: &[u8]) -> Option<Vec<u8>> {
340 self.next_storage_key(key)
341 }
342
343 fn start_transaction(&mut self) {
356 self.storage_start_transaction();
357 }
358
359 fn rollback_transaction(&mut self) {
367 self.storage_rollback_transaction()
368 .expect("No open transaction that can be rolled back.");
369 }
370
371 fn commit_transaction(&mut self) {
379 self.storage_commit_transaction()
380 .expect("No open transaction that can be committed.");
381 }
382}
383
384#[runtime_interface]
387pub trait DefaultChildStorage {
388 fn get(&mut self, storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
393 let child_info = ChildInfo::new_default(storage_key);
394 self.child_storage(&child_info, key).map(|s| s.to_vec())
395 }
396
397 fn read(
405 &mut self,
406 storage_key: &[u8],
407 key: &[u8],
408 value_out: &mut [u8],
409 value_offset: u32,
410 ) -> Option<u32> {
411 let child_info = ChildInfo::new_default(storage_key);
412 self.child_storage(&child_info, key).map(|value| {
413 let value_offset = value_offset as usize;
414 let data = &value[value_offset.min(value.len())..];
415 let written = std::cmp::min(data.len(), value_out.len());
416 value_out[..written].copy_from_slice(&data[..written]);
417 data.len() as u32
418 })
419 }
420
421 fn set(&mut self, storage_key: &[u8], key: &[u8], value: &[u8]) {
425 let child_info = ChildInfo::new_default(storage_key);
426 self.set_child_storage(&child_info, key.to_vec(), value.to_vec());
427 }
428
429 fn clear(&mut self, storage_key: &[u8], key: &[u8]) {
433 let child_info = ChildInfo::new_default(storage_key);
434 self.clear_child_storage(&child_info, key);
435 }
436
437 fn storage_kill(&mut self, storage_key: &[u8]) {
442 let child_info = ChildInfo::new_default(storage_key);
443 let _ = self.kill_child_storage(&child_info, None, None);
444 }
445
446 #[version(2)]
450 fn storage_kill(&mut self, storage_key: &[u8], limit: Option<u32>) -> bool {
451 let child_info = ChildInfo::new_default(storage_key);
452 let r = self.kill_child_storage(&child_info, limit, None);
453 r.maybe_cursor.is_none()
454 }
455
456 #[version(3)]
460 fn storage_kill(&mut self, storage_key: &[u8], limit: Option<u32>) -> KillStorageResult {
461 let child_info = ChildInfo::new_default(storage_key);
462 self.kill_child_storage(&child_info, limit, None).into()
463 }
464
465 #[version(4, register_only)]
469 fn storage_kill(
470 &mut self,
471 storage_key: &[u8],
472 maybe_limit: Option<u32>,
473 maybe_cursor: Option<Vec<u8>>,
474 ) -> MultiRemovalResults {
475 let child_info = ChildInfo::new_default(storage_key);
476 self.kill_child_storage(&child_info, maybe_limit, maybe_cursor.as_ref().map(|x| &x[..]))
477 .into()
478 }
479
480 fn exists(&mut self, storage_key: &[u8], key: &[u8]) -> bool {
484 let child_info = ChildInfo::new_default(storage_key);
485 self.exists_child_storage(&child_info, key)
486 }
487
488 fn clear_prefix(&mut self, storage_key: &[u8], prefix: &[u8]) {
492 let child_info = ChildInfo::new_default(storage_key);
493 let _ = self.clear_child_prefix(&child_info, prefix, None, None);
494 }
495
496 #[version(2)]
500 fn clear_prefix(
501 &mut self,
502 storage_key: &[u8],
503 prefix: &[u8],
504 limit: Option<u32>,
505 ) -> KillStorageResult {
506 let child_info = ChildInfo::new_default(storage_key);
507 self.clear_child_prefix(&child_info, prefix, limit, None).into()
508 }
509
510 #[version(3, register_only)]
514 fn clear_prefix(
515 &mut self,
516 storage_key: &[u8],
517 prefix: &[u8],
518 maybe_limit: Option<u32>,
519 maybe_cursor: Option<Vec<u8>>,
520 ) -> MultiRemovalResults {
521 let child_info = ChildInfo::new_default(storage_key);
522 self.clear_child_prefix(
523 &child_info,
524 prefix,
525 maybe_limit,
526 maybe_cursor.as_ref().map(|x| &x[..]),
527 )
528 .into()
529 }
530
531 fn root(&mut self, storage_key: &[u8]) -> Vec<u8> {
538 let child_info = ChildInfo::new_default(storage_key);
539 self.child_storage_root(&child_info, StateVersion::V0)
540 }
541
542 #[version(2)]
549 fn root(&mut self, storage_key: &[u8], version: StateVersion) -> Vec<u8> {
550 let child_info = ChildInfo::new_default(storage_key);
551 self.child_storage_root(&child_info, version)
552 }
553
554 fn next_key(&mut self, storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
558 let child_info = ChildInfo::new_default(storage_key);
559 self.next_child_storage_key(&child_info, key)
560 }
561}
562
563#[runtime_interface]
565pub trait Trie {
566 fn blake2_256_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> H256 {
568 LayoutV0::<sp_core::Blake2Hasher>::trie_root(input)
569 }
570
571 #[version(2)]
573 fn blake2_256_root(input: Vec<(Vec<u8>, Vec<u8>)>, version: StateVersion) -> H256 {
574 match version {
575 StateVersion::V0 => LayoutV0::<sp_core::Blake2Hasher>::trie_root(input),
576 StateVersion::V1 => LayoutV1::<sp_core::Blake2Hasher>::trie_root(input),
577 }
578 }
579
580 fn blake2_256_ordered_root(input: Vec<Vec<u8>>) -> H256 {
582 LayoutV0::<sp_core::Blake2Hasher>::ordered_trie_root(input)
583 }
584
585 #[version(2)]
587 fn blake2_256_ordered_root(input: Vec<Vec<u8>>, version: StateVersion) -> H256 {
588 match version {
589 StateVersion::V0 => LayoutV0::<sp_core::Blake2Hasher>::ordered_trie_root(input),
590 StateVersion::V1 => LayoutV1::<sp_core::Blake2Hasher>::ordered_trie_root(input),
591 }
592 }
593
594 fn keccak_256_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> H256 {
596 LayoutV0::<sp_core::KeccakHasher>::trie_root(input)
597 }
598
599 #[version(2)]
601 fn keccak_256_root(input: Vec<(Vec<u8>, Vec<u8>)>, version: StateVersion) -> H256 {
602 match version {
603 StateVersion::V0 => LayoutV0::<sp_core::KeccakHasher>::trie_root(input),
604 StateVersion::V1 => LayoutV1::<sp_core::KeccakHasher>::trie_root(input),
605 }
606 }
607
608 fn keccak_256_ordered_root(input: Vec<Vec<u8>>) -> H256 {
610 LayoutV0::<sp_core::KeccakHasher>::ordered_trie_root(input)
611 }
612
613 #[version(2)]
615 fn keccak_256_ordered_root(input: Vec<Vec<u8>>, version: StateVersion) -> H256 {
616 match version {
617 StateVersion::V0 => LayoutV0::<sp_core::KeccakHasher>::ordered_trie_root(input),
618 StateVersion::V1 => LayoutV1::<sp_core::KeccakHasher>::ordered_trie_root(input),
619 }
620 }
621
622 fn blake2_256_verify_proof(root: H256, proof: &[Vec<u8>], key: &[u8], value: &[u8]) -> bool {
624 sp_trie::verify_trie_proof::<LayoutV0<sp_core::Blake2Hasher>, _, _, _>(
625 &root,
626 proof,
627 &[(key, Some(value))],
628 )
629 .is_ok()
630 }
631
632 #[version(2)]
634 fn blake2_256_verify_proof(
635 root: H256,
636 proof: &[Vec<u8>],
637 key: &[u8],
638 value: &[u8],
639 version: StateVersion,
640 ) -> bool {
641 match version {
642 StateVersion::V0 => sp_trie::verify_trie_proof::<
643 LayoutV0<sp_core::Blake2Hasher>,
644 _,
645 _,
646 _,
647 >(&root, proof, &[(key, Some(value))])
648 .is_ok(),
649 StateVersion::V1 => sp_trie::verify_trie_proof::<
650 LayoutV1<sp_core::Blake2Hasher>,
651 _,
652 _,
653 _,
654 >(&root, proof, &[(key, Some(value))])
655 .is_ok(),
656 }
657 }
658
659 fn keccak_256_verify_proof(root: H256, proof: &[Vec<u8>], key: &[u8], value: &[u8]) -> bool {
661 sp_trie::verify_trie_proof::<LayoutV0<sp_core::KeccakHasher>, _, _, _>(
662 &root,
663 proof,
664 &[(key, Some(value))],
665 )
666 .is_ok()
667 }
668
669 #[version(2)]
671 fn keccak_256_verify_proof(
672 root: H256,
673 proof: &[Vec<u8>],
674 key: &[u8],
675 value: &[u8],
676 version: StateVersion,
677 ) -> bool {
678 match version {
679 StateVersion::V0 => sp_trie::verify_trie_proof::<
680 LayoutV0<sp_core::KeccakHasher>,
681 _,
682 _,
683 _,
684 >(&root, proof, &[(key, Some(value))])
685 .is_ok(),
686 StateVersion::V1 => sp_trie::verify_trie_proof::<
687 LayoutV1<sp_core::KeccakHasher>,
688 _,
689 _,
690 _,
691 >(&root, proof, &[(key, Some(value))])
692 .is_ok(),
693 }
694 }
695}
696
697#[runtime_interface]
700pub trait Misc {
701 fn print_num(val: u64) {
706 log::debug!(target: "runtime", "{}", val);
707 }
708
709 fn print_utf8(utf8: &[u8]) {
711 if let Ok(data) = std::str::from_utf8(utf8) {
712 log::debug!(target: "runtime", "{}", data)
713 }
714 }
715
716 fn print_hex(data: &[u8]) {
718 log::debug!(target: "runtime", "{}", HexDisplay::from(&data));
719 }
720
721 fn runtime_version(&mut self, wasm: &[u8]) -> Option<Vec<u8>> {
737 use sp_core::traits::ReadRuntimeVersionExt;
738
739 let mut ext = sp_state_machine::BasicExternalities::default();
740
741 match self
742 .extension::<ReadRuntimeVersionExt>()
743 .expect("No `ReadRuntimeVersionExt` associated for the current context!")
744 .read_runtime_version(wasm, &mut ext)
745 {
746 Ok(v) => Some(v),
747 Err(err) => {
748 log::debug!(
749 target: LOG_TARGET,
750 "cannot read version from the given runtime: {}",
751 err,
752 );
753 None
754 },
755 }
756 }
757}
758
759#[cfg(feature = "std")]
760sp_externalities::decl_extension! {
761 pub struct UseDalekExt;
778}
779
780#[cfg(feature = "std")]
781impl Default for UseDalekExt {
782 fn default() -> Self {
783 Self
784 }
785}
786
787#[runtime_interface]
789pub trait Crypto {
790 fn ed25519_public_keys(&mut self, id: KeyTypeId) -> Vec<ed25519::Public> {
792 self.extension::<KeystoreExt>()
793 .expect("No `keystore` associated for the current context!")
794 .ed25519_public_keys(id)
795 }
796
797 fn ed25519_generate(&mut self, id: KeyTypeId, seed: Option<Vec<u8>>) -> ed25519::Public {
804 let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!"));
805 self.extension::<KeystoreExt>()
806 .expect("No `keystore` associated for the current context!")
807 .ed25519_generate_new(id, seed)
808 .expect("`ed25519_generate` failed")
809 }
810
811 fn ed25519_sign(
816 &mut self,
817 id: KeyTypeId,
818 pub_key: &ed25519::Public,
819 msg: &[u8],
820 ) -> Option<ed25519::Signature> {
821 self.extension::<KeystoreExt>()
822 .expect("No `keystore` associated for the current context!")
823 .ed25519_sign(id, pub_key, msg)
824 .ok()
825 .flatten()
826 }
827
828 fn ed25519_verify(sig: &ed25519::Signature, msg: &[u8], pub_key: &ed25519::Public) -> bool {
832 if sp_externalities::with_externalities(|mut e| e.extension::<UseDalekExt>().is_some())
836 .unwrap_or_default()
837 {
838 use ed25519_dalek::Verifier;
839
840 let Ok(public_key) = ed25519_dalek::VerifyingKey::from_bytes(&pub_key.0) else {
841 return false
842 };
843
844 let sig = ed25519_dalek::Signature::from_bytes(&sig.0);
845
846 public_key.verify(msg, &sig).is_ok()
847 } else {
848 ed25519::Pair::verify(sig, msg, pub_key)
849 }
850 }
851
852 #[version(1, register_only)]
866 fn ed25519_batch_verify(
867 &mut self,
868 sig: &ed25519::Signature,
869 msg: &[u8],
870 pub_key: &ed25519::Public,
871 ) -> bool {
872 let res = ed25519_verify(sig, msg, pub_key);
873
874 if let Some(ext) = self.extension::<VerificationExtDeprecated>() {
875 ext.0 &= res;
876 }
877
878 res
879 }
880
881 #[version(2)]
885 fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pub_key: &sr25519::Public) -> bool {
886 sr25519::Pair::verify(sig, msg, pub_key)
887 }
888
889 #[version(1, register_only)]
903 fn sr25519_batch_verify(
904 &mut self,
905 sig: &sr25519::Signature,
906 msg: &[u8],
907 pub_key: &sr25519::Public,
908 ) -> bool {
909 let res = sr25519_verify(sig, msg, pub_key);
910
911 if let Some(ext) = self.extension::<VerificationExtDeprecated>() {
912 ext.0 &= res;
913 }
914
915 res
916 }
917
918 #[version(1, register_only)]
925 fn start_batch_verify(&mut self) {
926 self.register_extension(VerificationExtDeprecated(true))
927 .expect("Failed to register required extension: `VerificationExt`");
928 }
929
930 #[version(1, register_only)]
942 fn finish_batch_verify(&mut self) -> bool {
943 let result = self
944 .extension::<VerificationExtDeprecated>()
945 .expect("`finish_batch_verify` should only be called after `start_batch_verify`")
946 .0;
947
948 self.deregister_extension::<VerificationExtDeprecated>()
949 .expect("No verification extension in current context!");
950
951 result
952 }
953
954 fn sr25519_public_keys(&mut self, id: KeyTypeId) -> Vec<sr25519::Public> {
956 self.extension::<KeystoreExt>()
957 .expect("No `keystore` associated for the current context!")
958 .sr25519_public_keys(id)
959 }
960
961 fn sr25519_generate(&mut self, id: KeyTypeId, seed: Option<Vec<u8>>) -> sr25519::Public {
968 let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!"));
969 self.extension::<KeystoreExt>()
970 .expect("No `keystore` associated for the current context!")
971 .sr25519_generate_new(id, seed)
972 .expect("`sr25519_generate` failed")
973 }
974
975 fn sr25519_sign(
980 &mut self,
981 id: KeyTypeId,
982 pub_key: &sr25519::Public,
983 msg: &[u8],
984 ) -> Option<sr25519::Signature> {
985 self.extension::<KeystoreExt>()
986 .expect("No `keystore` associated for the current context!")
987 .sr25519_sign(id, pub_key, msg)
988 .ok()
989 .flatten()
990 }
991
992 fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pubkey: &sr25519::Public) -> bool {
997 sr25519::Pair::verify_deprecated(sig, msg, pubkey)
998 }
999
1000 fn ecdsa_public_keys(&mut self, id: KeyTypeId) -> Vec<ecdsa::Public> {
1002 self.extension::<KeystoreExt>()
1003 .expect("No `keystore` associated for the current context!")
1004 .ecdsa_public_keys(id)
1005 }
1006
1007 fn ecdsa_generate(&mut self, id: KeyTypeId, seed: Option<Vec<u8>>) -> ecdsa::Public {
1014 let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!"));
1015 self.extension::<KeystoreExt>()
1016 .expect("No `keystore` associated for the current context!")
1017 .ecdsa_generate_new(id, seed)
1018 .expect("`ecdsa_generate` failed")
1019 }
1020
1021 fn ecdsa_sign(
1026 &mut self,
1027 id: KeyTypeId,
1028 pub_key: &ecdsa::Public,
1029 msg: &[u8],
1030 ) -> Option<ecdsa::Signature> {
1031 self.extension::<KeystoreExt>()
1032 .expect("No `keystore` associated for the current context!")
1033 .ecdsa_sign(id, pub_key, msg)
1034 .ok()
1035 .flatten()
1036 }
1037
1038 fn ecdsa_sign_prehashed(
1043 &mut self,
1044 id: KeyTypeId,
1045 pub_key: &ecdsa::Public,
1046 msg: &[u8; 32],
1047 ) -> Option<ecdsa::Signature> {
1048 self.extension::<KeystoreExt>()
1049 .expect("No `keystore` associated for the current context!")
1050 .ecdsa_sign_prehashed(id, pub_key, msg)
1051 .ok()
1052 .flatten()
1053 }
1054
1055 fn ecdsa_verify(sig: &ecdsa::Signature, msg: &[u8], pub_key: &ecdsa::Public) -> bool {
1060 #[allow(deprecated)]
1061 ecdsa::Pair::verify_deprecated(sig, msg, pub_key)
1062 }
1063
1064 #[version(2)]
1068 fn ecdsa_verify(sig: &ecdsa::Signature, msg: &[u8], pub_key: &ecdsa::Public) -> bool {
1069 ecdsa::Pair::verify(sig, msg, pub_key)
1070 }
1071
1072 fn ecdsa_verify_prehashed(
1076 sig: &ecdsa::Signature,
1077 msg: &[u8; 32],
1078 pub_key: &ecdsa::Public,
1079 ) -> bool {
1080 ecdsa::Pair::verify_prehashed(sig, msg, pub_key)
1081 }
1082
1083 #[version(1, register_only)]
1097 fn ecdsa_batch_verify(
1098 &mut self,
1099 sig: &ecdsa::Signature,
1100 msg: &[u8],
1101 pub_key: &ecdsa::Public,
1102 ) -> bool {
1103 let res = ecdsa_verify(sig, msg, pub_key);
1104
1105 if let Some(ext) = self.extension::<VerificationExtDeprecated>() {
1106 ext.0 &= res;
1107 }
1108
1109 res
1110 }
1111
1112 fn secp256k1_ecdsa_recover(
1121 sig: &[u8; 65],
1122 msg: &[u8; 32],
1123 ) -> Result<[u8; 64], EcdsaVerifyError> {
1124 let rid = libsecp256k1::RecoveryId::parse(
1125 if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8,
1126 )
1127 .map_err(|_| EcdsaVerifyError::BadV)?;
1128 let sig = libsecp256k1::Signature::parse_overflowing_slice(&sig[..64])
1129 .map_err(|_| EcdsaVerifyError::BadRS)?;
1130 let msg = libsecp256k1::Message::parse(msg);
1131 let pubkey =
1132 libsecp256k1::recover(&msg, &sig, &rid).map_err(|_| EcdsaVerifyError::BadSignature)?;
1133 let mut res = [0u8; 64];
1134 res.copy_from_slice(&pubkey.serialize()[1..65]);
1135 Ok(res)
1136 }
1137
1138 #[version(2)]
1146 fn secp256k1_ecdsa_recover(
1147 sig: &[u8; 65],
1148 msg: &[u8; 32],
1149 ) -> Result<[u8; 64], EcdsaVerifyError> {
1150 let rid = RecoveryId::from_i32(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as i32)
1151 .map_err(|_| EcdsaVerifyError::BadV)?;
1152 let sig = RecoverableSignature::from_compact(&sig[..64], rid)
1153 .map_err(|_| EcdsaVerifyError::BadRS)?;
1154 let msg = Message::from_digest_slice(msg).expect("Message is 32 bytes; qed");
1155 let pubkey = SECP256K1
1156 .recover_ecdsa(&msg, &sig)
1157 .map_err(|_| EcdsaVerifyError::BadSignature)?;
1158 let mut res = [0u8; 64];
1159 res.copy_from_slice(&pubkey.serialize_uncompressed()[1..]);
1160 Ok(res)
1161 }
1162
1163 fn secp256k1_ecdsa_recover_compressed(
1170 sig: &[u8; 65],
1171 msg: &[u8; 32],
1172 ) -> Result<[u8; 33], EcdsaVerifyError> {
1173 let rid = libsecp256k1::RecoveryId::parse(
1174 if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8,
1175 )
1176 .map_err(|_| EcdsaVerifyError::BadV)?;
1177 let sig = libsecp256k1::Signature::parse_overflowing_slice(&sig[0..64])
1178 .map_err(|_| EcdsaVerifyError::BadRS)?;
1179 let msg = libsecp256k1::Message::parse(msg);
1180 let pubkey =
1181 libsecp256k1::recover(&msg, &sig, &rid).map_err(|_| EcdsaVerifyError::BadSignature)?;
1182 Ok(pubkey.serialize_compressed())
1183 }
1184
1185 #[version(2)]
1192 fn secp256k1_ecdsa_recover_compressed(
1193 sig: &[u8; 65],
1194 msg: &[u8; 32],
1195 ) -> Result<[u8; 33], EcdsaVerifyError> {
1196 let rid = RecoveryId::from_i32(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as i32)
1197 .map_err(|_| EcdsaVerifyError::BadV)?;
1198 let sig = RecoverableSignature::from_compact(&sig[..64], rid)
1199 .map_err(|_| EcdsaVerifyError::BadRS)?;
1200 let msg = Message::from_digest_slice(msg).expect("Message is 32 bytes; qed");
1201 let pubkey = SECP256K1
1202 .recover_ecdsa(&msg, &sig)
1203 .map_err(|_| EcdsaVerifyError::BadSignature)?;
1204 Ok(pubkey.serialize())
1205 }
1206
1207 #[cfg(feature = "bls-experimental")]
1214 fn bls381_generate(&mut self, id: KeyTypeId, seed: Option<Vec<u8>>) -> bls381::Public {
1215 let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!"));
1216 self.extension::<KeystoreExt>()
1217 .expect("No `keystore` associated for the current context!")
1218 .bls381_generate_new(id, seed)
1219 .expect("`bls381_generate` failed")
1220 }
1221
1222 #[cfg(feature = "bls-experimental")]
1229 fn ecdsa_bls381_generate(
1230 &mut self,
1231 id: KeyTypeId,
1232 seed: Option<Vec<u8>>,
1233 ) -> ecdsa_bls381::Public {
1234 let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!"));
1235 self.extension::<KeystoreExt>()
1236 .expect("No `keystore` associated for the current context!")
1237 .ecdsa_bls381_generate_new(id, seed)
1238 .expect("`ecdsa_bls381_generate` failed")
1239 }
1240
1241 #[cfg(feature = "bandersnatch-experimental")]
1248 fn bandersnatch_generate(
1249 &mut self,
1250 id: KeyTypeId,
1251 seed: Option<Vec<u8>>,
1252 ) -> bandersnatch::Public {
1253 let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!"));
1254 self.extension::<KeystoreExt>()
1255 .expect("No `keystore` associated for the current context!")
1256 .bandersnatch_generate_new(id, seed)
1257 .expect("`bandernatch_generate` failed")
1258 }
1259}
1260
1261#[runtime_interface]
1263pub trait Hashing {
1264 fn keccak_256(data: &[u8]) -> [u8; 32] {
1266 sp_crypto_hashing::keccak_256(data)
1267 }
1268
1269 fn keccak_512(data: &[u8]) -> [u8; 64] {
1271 sp_crypto_hashing::keccak_512(data)
1272 }
1273
1274 fn sha2_256(data: &[u8]) -> [u8; 32] {
1276 sp_crypto_hashing::sha2_256(data)
1277 }
1278
1279 fn blake2_128(data: &[u8]) -> [u8; 16] {
1281 sp_crypto_hashing::blake2_128(data)
1282 }
1283
1284 fn blake2_256(data: &[u8]) -> [u8; 32] {
1286 sp_crypto_hashing::blake2_256(data)
1287 }
1288
1289 fn twox_256(data: &[u8]) -> [u8; 32] {
1291 sp_crypto_hashing::twox_256(data)
1292 }
1293
1294 fn twox_128(data: &[u8]) -> [u8; 16] {
1296 sp_crypto_hashing::twox_128(data)
1297 }
1298
1299 fn twox_64(data: &[u8]) -> [u8; 8] {
1301 sp_crypto_hashing::twox_64(data)
1302 }
1303}
1304
1305#[runtime_interface]
1307pub trait TransactionIndex {
1308 fn index(&mut self, extrinsic: u32, size: u32, context_hash: [u8; 32]) {
1310 self.storage_index_transaction(extrinsic, &context_hash, size);
1311 }
1312
1313 fn renew(&mut self, extrinsic: u32, context_hash: [u8; 32]) {
1315 self.storage_renew_transaction_index(extrinsic, &context_hash);
1316 }
1317}
1318
1319#[runtime_interface]
1321pub trait OffchainIndex {
1322 fn set(&mut self, key: &[u8], value: &[u8]) {
1324 self.set_offchain_storage(key, Some(value));
1325 }
1326
1327 fn clear(&mut self, key: &[u8]) {
1329 self.set_offchain_storage(key, None);
1330 }
1331}
1332
1333#[cfg(feature = "std")]
1334sp_externalities::decl_extension! {
1335 struct VerificationExtDeprecated(bool);
1339}
1340
1341#[runtime_interface]
1345pub trait Offchain {
1346 fn is_validator(&mut self) -> bool {
1351 self.extension::<OffchainWorkerExt>()
1352 .expect("is_validator can be called only in the offchain worker context")
1353 .is_validator()
1354 }
1355
1356 fn submit_transaction(&mut self, data: Vec<u8>) -> Result<(), ()> {
1360 self.extension::<TransactionPoolExt>()
1361 .expect(
1362 "submit_transaction can be called only in the offchain call context with
1363 TransactionPool capabilities enabled",
1364 )
1365 .submit_transaction(data)
1366 }
1367
1368 fn network_state(&mut self) -> Result<OpaqueNetworkState, ()> {
1370 self.extension::<OffchainWorkerExt>()
1371 .expect("network_state can be called only in the offchain worker context")
1372 .network_state()
1373 }
1374
1375 fn timestamp(&mut self) -> Timestamp {
1377 self.extension::<OffchainWorkerExt>()
1378 .expect("timestamp can be called only in the offchain worker context")
1379 .timestamp()
1380 }
1381
1382 fn sleep_until(&mut self, deadline: Timestamp) {
1384 self.extension::<OffchainWorkerExt>()
1385 .expect("sleep_until can be called only in the offchain worker context")
1386 .sleep_until(deadline)
1387 }
1388
1389 fn random_seed(&mut self) -> [u8; 32] {
1394 self.extension::<OffchainWorkerExt>()
1395 .expect("random_seed can be called only in the offchain worker context")
1396 .random_seed()
1397 }
1398
1399 fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
1404 self.extension::<OffchainDbExt>()
1405 .expect(
1406 "local_storage_set can be called only in the offchain call context with
1407 OffchainDb extension",
1408 )
1409 .local_storage_set(kind, key, value)
1410 }
1411
1412 fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
1417 self.extension::<OffchainDbExt>()
1418 .expect(
1419 "local_storage_clear can be called only in the offchain call context with
1420 OffchainDb extension",
1421 )
1422 .local_storage_clear(kind, key)
1423 }
1424
1425 fn local_storage_compare_and_set(
1435 &mut self,
1436 kind: StorageKind,
1437 key: &[u8],
1438 old_value: Option<Vec<u8>>,
1439 new_value: &[u8],
1440 ) -> bool {
1441 self.extension::<OffchainDbExt>()
1442 .expect(
1443 "local_storage_compare_and_set can be called only in the offchain call context
1444 with OffchainDb extension",
1445 )
1446 .local_storage_compare_and_set(kind, key, old_value.as_deref(), new_value)
1447 }
1448
1449 fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
1455 self.extension::<OffchainDbExt>()
1456 .expect(
1457 "local_storage_get can be called only in the offchain call context with
1458 OffchainDb extension",
1459 )
1460 .local_storage_get(kind, key)
1461 }
1462
1463 fn http_request_start(
1468 &mut self,
1469 method: &str,
1470 uri: &str,
1471 meta: &[u8],
1472 ) -> Result<HttpRequestId, ()> {
1473 self.extension::<OffchainWorkerExt>()
1474 .expect("http_request_start can be called only in the offchain worker context")
1475 .http_request_start(method, uri, meta)
1476 }
1477
1478 fn http_request_add_header(
1480 &mut self,
1481 request_id: HttpRequestId,
1482 name: &str,
1483 value: &str,
1484 ) -> Result<(), ()> {
1485 self.extension::<OffchainWorkerExt>()
1486 .expect("http_request_add_header can be called only in the offchain worker context")
1487 .http_request_add_header(request_id, name, value)
1488 }
1489
1490 fn http_request_write_body(
1497 &mut self,
1498 request_id: HttpRequestId,
1499 chunk: &[u8],
1500 deadline: Option<Timestamp>,
1501 ) -> Result<(), HttpError> {
1502 self.extension::<OffchainWorkerExt>()
1503 .expect("http_request_write_body can be called only in the offchain worker context")
1504 .http_request_write_body(request_id, chunk, deadline)
1505 }
1506
1507 fn http_response_wait(
1515 &mut self,
1516 ids: &[HttpRequestId],
1517 deadline: Option<Timestamp>,
1518 ) -> Vec<HttpRequestStatus> {
1519 self.extension::<OffchainWorkerExt>()
1520 .expect("http_response_wait can be called only in the offchain worker context")
1521 .http_response_wait(ids, deadline)
1522 }
1523
1524 fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec<u8>, Vec<u8>)> {
1529 self.extension::<OffchainWorkerExt>()
1530 .expect("http_response_headers can be called only in the offchain worker context")
1531 .http_response_headers(request_id)
1532 }
1533
1534 fn http_response_read_body(
1543 &mut self,
1544 request_id: HttpRequestId,
1545 buffer: &mut [u8],
1546 deadline: Option<Timestamp>,
1547 ) -> Result<u32, HttpError> {
1548 self.extension::<OffchainWorkerExt>()
1549 .expect("http_response_read_body can be called only in the offchain worker context")
1550 .http_response_read_body(request_id, buffer, deadline)
1551 .map(|r| r as u32)
1552 }
1553
1554 fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
1556 self.extension::<OffchainWorkerExt>()
1557 .expect("set_authorized_nodes can be called only in the offchain worker context")
1558 .set_authorized_nodes(nodes, authorized_only)
1559 }
1560}
1561
1562#[runtime_interface(wasm_only)]
1564pub trait Allocator {
1565 fn malloc(&mut self, size: u32) -> Pointer<u8> {
1567 self.allocate_memory(size).expect("Failed to allocate memory")
1568 }
1569
1570 fn free(&mut self, ptr: Pointer<u8>) {
1572 self.deallocate_memory(ptr).expect("Failed to deallocate memory")
1573 }
1574}
1575
1576#[runtime_interface(wasm_only)]
1579pub trait PanicHandler {
1580 #[trap_on_return]
1582 fn abort_on_panic(&mut self, message: &str) {
1583 self.register_panic_error_message(message);
1584 }
1585}
1586
1587#[runtime_interface]
1589pub trait Logging {
1590 fn log(level: LogLevel, target: &str, message: &[u8]) {
1597 if let Ok(message) = std::str::from_utf8(message) {
1598 log::log!(target: target, log::Level::from(level), "{}", message)
1599 }
1600 }
1601
1602 fn max_level() -> LogLevelFilter {
1604 log::max_level().into()
1605 }
1606}
1607
1608#[derive(Encode, Decode)]
1609pub struct Crossing<T: Encode + Decode>(T);
1612
1613impl<T: Encode + Decode> PassBy for Crossing<T> {
1614 type PassBy = sp_runtime_interface::pass_by::Codec<Self>;
1615}
1616
1617impl<T: Encode + Decode> Crossing<T> {
1618 pub fn into_inner(self) -> T {
1620 self.0
1621 }
1622}
1623
1624impl<T> core::default::Default for Crossing<T>
1626where
1627 T: core::default::Default + Encode + Decode,
1628{
1629 fn default() -> Self {
1630 Self(Default::default())
1631 }
1632}
1633
1634#[runtime_interface(wasm_only, no_tracing)]
1637pub trait WasmTracing {
1638 fn enabled(&mut self, metadata: Crossing<sp_tracing::WasmMetadata>) -> bool {
1648 let metadata: &tracing_core::metadata::Metadata<'static> = (&metadata.into_inner()).into();
1649 tracing::dispatcher::get_default(|d| d.enabled(metadata))
1650 }
1651
1652 fn enter_span(&mut self, span: Crossing<sp_tracing::WasmEntryAttributes>) -> u64 {
1659 let span: tracing::Span = span.into_inner().into();
1660 match span.id() {
1661 Some(id) => tracing::dispatcher::get_default(|d| {
1662 let final_id = d.clone_span(&id);
1665 d.enter(&final_id);
1666 final_id.into_u64()
1667 }),
1668 _ => 0,
1669 }
1670 }
1671
1672 fn event(&mut self, event: Crossing<sp_tracing::WasmEntryAttributes>) {
1674 event.into_inner().emit();
1675 }
1676
1677 fn exit(&mut self, span: u64) {
1680 tracing::dispatcher::get_default(|d| {
1681 let id = tracing_core::span::Id::from_u64(span);
1682 d.exit(&id);
1683 });
1684 }
1685}
1686
1687#[cfg(all(not(feature = "std"), feature = "with-tracing"))]
1688mod tracing_setup {
1689 use super::{wasm_tracing, Crossing};
1690 use core::sync::atomic::{AtomicBool, Ordering};
1691 use tracing_core::{
1692 dispatcher::{set_global_default, Dispatch},
1693 span::{Attributes, Id, Record},
1694 Event, Metadata,
1695 };
1696
1697 static TRACING_SET: AtomicBool = AtomicBool::new(false);
1698
1699 struct PassingTracingSubscriber;
1702
1703 impl tracing_core::Subscriber for PassingTracingSubscriber {
1704 fn enabled(&self, metadata: &Metadata<'_>) -> bool {
1705 wasm_tracing::enabled(Crossing(metadata.into()))
1706 }
1707 fn new_span(&self, attrs: &Attributes<'_>) -> Id {
1708 Id::from_u64(wasm_tracing::enter_span(Crossing(attrs.into())))
1709 }
1710 fn enter(&self, _: &Id) {
1711 }
1713 fn record(&self, _: &Id, _: &Record<'_>) {
1716 unimplemented! {} }
1718 fn record_follows_from(&self, _: &Id, _: &Id) {
1721 unimplemented! {} }
1723 fn event(&self, event: &Event<'_>) {
1724 wasm_tracing::event(Crossing(event.into()))
1725 }
1726 fn exit(&self, span: &Id) {
1727 wasm_tracing::exit(span.into_u64())
1728 }
1729 }
1730
1731 pub fn init_tracing() {
1735 if TRACING_SET.load(Ordering::Relaxed) == false {
1736 set_global_default(Dispatch::new(PassingTracingSubscriber {}))
1737 .expect("We only ever call this once");
1738 TRACING_SET.store(true, Ordering::Relaxed);
1739 }
1740 }
1741}
1742
1743#[cfg(not(all(not(feature = "std"), feature = "with-tracing")))]
1744mod tracing_setup {
1745 pub fn init_tracing() {}
1748}
1749
1750pub use tracing_setup::init_tracing;
1751
1752pub fn unreachable() -> ! {
1757 #[cfg(target_family = "wasm")]
1758 {
1759 core::arch::wasm32::unreachable();
1760 }
1761
1762 #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
1763 unsafe {
1764 core::arch::asm!("unimp", options(noreturn));
1765 }
1766
1767 #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64", target_family = "wasm")))]
1768 unreachable!();
1769}
1770
1771#[cfg(all(not(feature = "disable_panic_handler"), substrate_runtime))]
1773#[panic_handler]
1774#[no_mangle]
1775pub fn panic(info: &core::panic::PanicInfo) -> ! {
1776 let message = alloc::format!("{}", info);
1777 #[cfg(feature = "improved_panic_error_reporting")]
1778 {
1779 panic_handler::abort_on_panic(&message);
1780 }
1781 #[cfg(not(feature = "improved_panic_error_reporting"))]
1782 {
1783 logging::log(LogLevel::Error, "runtime", message.as_bytes());
1784 unreachable();
1785 }
1786}
1787
1788#[cfg(all(not(feature = "disable_oom"), enable_alloc_error_handler))]
1790#[alloc_error_handler]
1791pub fn oom(_: core::alloc::Layout) -> ! {
1792 #[cfg(feature = "improved_panic_error_reporting")]
1793 {
1794 panic_handler::abort_on_panic("Runtime memory exhausted.");
1795 }
1796 #[cfg(not(feature = "improved_panic_error_reporting"))]
1797 {
1798 logging::log(LogLevel::Error, "runtime", b"Runtime memory exhausted. Aborting");
1799 unreachable();
1800 }
1801}
1802
1803#[cfg(feature = "std")]
1805pub type TestExternalities = sp_state_machine::TestExternalities<sp_core::Blake2Hasher>;
1806
1807#[docify::export]
1811#[cfg(feature = "std")]
1812pub type SubstrateHostFunctions = (
1813 storage::HostFunctions,
1814 default_child_storage::HostFunctions,
1815 misc::HostFunctions,
1816 wasm_tracing::HostFunctions,
1817 offchain::HostFunctions,
1818 crypto::HostFunctions,
1819 hashing::HostFunctions,
1820 allocator::HostFunctions,
1821 panic_handler::HostFunctions,
1822 logging::HostFunctions,
1823 crate::trie::HostFunctions,
1824 offchain_index::HostFunctions,
1825 transaction_index::HostFunctions,
1826);
1827
1828#[cfg(test)]
1829mod tests {
1830 use super::*;
1831 use sp_core::{crypto::UncheckedInto, map, storage::Storage};
1832 use sp_state_machine::BasicExternalities;
1833
1834 #[test]
1835 fn storage_works() {
1836 let mut t = BasicExternalities::default();
1837 t.execute_with(|| {
1838 assert_eq!(storage::get(b"hello"), None);
1839 storage::set(b"hello", b"world");
1840 assert_eq!(storage::get(b"hello"), Some(b"world".to_vec().into()));
1841 assert_eq!(storage::get(b"foo"), None);
1842 storage::set(b"foo", &[1, 2, 3][..]);
1843 });
1844
1845 t = BasicExternalities::new(Storage {
1846 top: map![b"foo".to_vec() => b"bar".to_vec()],
1847 children_default: map![],
1848 });
1849
1850 t.execute_with(|| {
1851 assert_eq!(storage::get(b"hello"), None);
1852 assert_eq!(storage::get(b"foo"), Some(b"bar".to_vec().into()));
1853 });
1854
1855 let value = vec![7u8; 35];
1856 let storage =
1857 Storage { top: map![b"foo00".to_vec() => value.clone()], children_default: map![] };
1858 t = BasicExternalities::new(storage);
1859
1860 t.execute_with(|| {
1861 assert_eq!(storage::get(b"hello"), None);
1862 assert_eq!(storage::get(b"foo00"), Some(value.clone().into()));
1863 });
1864 }
1865
1866 #[test]
1867 fn read_storage_works() {
1868 let value = b"\x0b\0\0\0Hello world".to_vec();
1869 let mut t = BasicExternalities::new(Storage {
1870 top: map![b":test".to_vec() => value.clone()],
1871 children_default: map![],
1872 });
1873
1874 t.execute_with(|| {
1875 let mut v = [0u8; 4];
1876 assert_eq!(storage::read(b":test", &mut v[..], 0).unwrap(), value.len() as u32);
1877 assert_eq!(v, [11u8, 0, 0, 0]);
1878 let mut w = [0u8; 11];
1879 assert_eq!(storage::read(b":test", &mut w[..], 4).unwrap(), value.len() as u32 - 4);
1880 assert_eq!(&w, b"Hello world");
1881 });
1882 }
1883
1884 #[test]
1885 fn clear_prefix_works() {
1886 let mut t = BasicExternalities::new(Storage {
1887 top: map![
1888 b":a".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
1889 b":abcd".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
1890 b":abc".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
1891 b":abdd".to_vec() => b"\x0b\0\0\0Hello world".to_vec()
1892 ],
1893 children_default: map![],
1894 });
1895
1896 t.execute_with(|| {
1897 assert!(matches!(
1903 storage::clear_prefix(b":abc", None),
1904 KillStorageResult::AllRemoved(2),
1905 ));
1906
1907 assert!(storage::get(b":a").is_some());
1908 assert!(storage::get(b":abdd").is_some());
1909 assert!(storage::get(b":abcd").is_none());
1910 assert!(storage::get(b":abc").is_none());
1911
1912 assert!(matches!(
1918 storage::clear_prefix(b":abc", None),
1919 KillStorageResult::AllRemoved(0),
1920 ));
1921 });
1922 }
1923
1924 fn zero_ed_pub() -> ed25519::Public {
1925 [0u8; 32].unchecked_into()
1926 }
1927
1928 fn zero_ed_sig() -> ed25519::Signature {
1929 ed25519::Signature::from_raw([0u8; 64])
1930 }
1931
1932 #[test]
1933 fn use_dalek_ext_works() {
1934 let mut ext = BasicExternalities::default();
1935 ext.register_extension(UseDalekExt::default());
1936
1937 ext.execute_with(|| {
1939 assert!(!crypto::ed25519_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub()));
1940 });
1941
1942 BasicExternalities::default().execute_with(|| {
1944 assert!(crypto::ed25519_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub()));
1945 })
1946 }
1947
1948 #[test]
1949 fn dalek_should_not_panic_on_invalid_signature() {
1950 let mut ext = BasicExternalities::default();
1951 ext.register_extension(UseDalekExt::default());
1952
1953 ext.execute_with(|| {
1954 let mut bytes = [0u8; 64];
1955 bytes[63] = 0b1110_0000;
1957
1958 assert!(!crypto::ed25519_verify(
1959 &ed25519::Signature::from_raw(bytes),
1960 &Vec::new(),
1961 &zero_ed_pub()
1962 ));
1963 });
1964 }
1965}