1use codec::{Compact, Decode, Encode, Output};
2
3#[cfg(all(feature = "std", feature = "type_info"))]
4use scale_info::TypeInfo;
5
6#[cfg(not(feature = "std"))]
7use alloc::{format, string::String};
8use sp_core::{hashing::blake2_256, H256};
9use sp_runtime::{ConsensusEngineId, MultiSignature};
10use sp_std::prelude::*;
11
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15use crate::basic_types::{AccountId, GenericAddress};
16use crate::*;
17
18pub type TxHash = H256;
19pub type BlockHash = H256;
20pub type BlockNumber = u32;
21
22#[cfg(feature = "serde")]
23pub mod block_number {
24 use super::BlockNumber;
25 use sp_core::U256;
26
27 pub fn serialize<S>(num: &BlockNumber, s: S) -> Result<S::Ok, S::Error>
28 where
29 S: serde::Serializer,
30 {
31 let num = U256::from(*num);
32 serde::Serialize::serialize(&num, s)
33 }
34
35 pub fn deserialize<'de, D>(d: D) -> Result<BlockNumber, D::Error>
36 where
37 D: serde::Deserializer<'de>,
38 {
39 let num: U256 = serde::Deserialize::deserialize(d)?;
40 Ok(num.as_u32())
41 }
42}
43
44#[derive(Clone, Debug, Encode, Decode)]
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
47pub struct Header {
48 pub parent_hash: BlockHash,
49 #[cfg_attr(feature = "serde", serde(with = "block_number"))]
50 #[codec(compact)]
51 pub number: BlockNumber,
52 pub state_root: BlockHash,
53 pub extrinsics_root: BlockHash,
54 pub digest: Digest,
55}
56
57impl Header {
58 pub fn hash(&self) -> BlockHash {
59 H256(self.using_encoded(blake2_256))
60 }
61}
62
63impl From<Header> for sp_runtime::generic::Header<BlockNumber, sp_runtime::traits::BlakeTwo256> {
64 fn from(header: Header) -> Self {
65 let logs = header
66 .digest
67 .logs
68 .into_iter()
69 .map(|item| item.into())
70 .collect();
71 Self {
72 parent_hash: header.parent_hash,
73 number: header.number,
74 state_root: header.state_root,
75 extrinsics_root: header.extrinsics_root,
76 digest: sp_runtime::generic::Digest { logs },
77 }
78 }
79}
80
81#[derive(Clone, Debug, Default, Encode, Decode)]
82#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
83pub struct Digest {
84 pub logs: Vec<DigestItem>,
85}
86
87#[derive(Clone, Debug)]
88#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
89#[cfg_attr(feature = "serde", serde(try_from = "RawDigestItem"))]
90#[cfg_attr(feature = "serde", serde(into = "RawDigestItem"))]
91pub enum DigestItem {
92 PreRuntime(ConsensusEngineId, Vec<u8>),
93 Consensus(ConsensusEngineId, Vec<u8>),
94 Seal(ConsensusEngineId, Vec<u8>),
95 Other(Vec<u8>),
96 RuntimeEnvironmentUpdated,
97}
98
99impl Encode for DigestItem {
100 fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
101 let runtime_era: sp_runtime::generic::DigestItem = self.clone().into();
102 runtime_era.encode_to(output)
103 }
104}
105
106impl Decode for DigestItem {
107 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
108 let runtime_era = sp_runtime::generic::DigestItem::decode(input)?;
109 Ok(runtime_era.into())
110 }
111}
112
113impl From<sp_runtime::generic::DigestItem> for DigestItem {
114 fn from(r_item: sp_runtime::generic::DigestItem) -> Self {
115 use sp_runtime::generic::DigestItem::*;
116 match r_item {
117 PreRuntime(id, data) => Self::PreRuntime(id, data),
118 Consensus(id, data) => Self::Consensus(id, data),
119 Seal(id, data) => Self::Seal(id, data),
120 Other(data) => Self::Other(data),
121 RuntimeEnvironmentUpdated => Self::RuntimeEnvironmentUpdated,
122 }
123 }
124}
125
126impl From<DigestItem> for sp_runtime::generic::DigestItem {
127 fn from(item: DigestItem) -> Self {
128 match item {
129 DigestItem::PreRuntime(id, data) => Self::PreRuntime(id, data),
130 DigestItem::Consensus(id, data) => Self::Consensus(id, data),
131 DigestItem::Seal(id, data) => Self::Seal(id, data),
132 DigestItem::Other(data) => Self::Other(data),
133 DigestItem::RuntimeEnvironmentUpdated => Self::RuntimeEnvironmentUpdated,
134 }
135 }
136}
137
138impl TryFrom<RawDigestItem> for DigestItem {
139 type Error = crate::Error;
140
141 fn try_from(raw: RawDigestItem) -> Result<Self, Self::Error> {
142 let item = DigestItem::decode(&mut &raw.0[..])?;
143 Ok(item.into())
144 }
145}
146
147#[derive(Clone, Debug)]
148#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
149pub struct RawDigestItem(
150 #[cfg_attr(feature = "serde", serde(with = "impl_serde::serialize"))] pub Vec<u8>,
151);
152
153impl From<DigestItem> for RawDigestItem {
154 fn from(item: DigestItem) -> Self {
155 Self(item.encode())
156 }
157}
158
159#[derive(Clone, Debug)]
160#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
161pub struct StorageData(
162 #[cfg_attr(feature = "serde", serde(with = "impl_serde::serialize"))] pub Vec<u8>,
163);
164
165#[derive(Clone, Debug)]
166#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
167pub struct StorageKey(
168 #[cfg_attr(feature = "serde", serde(with = "impl_serde::serialize"))] pub Vec<u8>,
169);
170
171#[derive(Clone, Debug, Default)]
172#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
173pub struct AdditionalSigned {
174 pub spec_version: u32,
175 pub tx_version: u32,
176 pub genesis_hash: BlockHash,
177 pub current_hash: BlockHash,
178 pub metadata_hash: Option<H256>,
179}
180
181impl AdditionalSigned {
182 pub fn encode_metadata_hash(&self) -> Option<Option<H256>> {
183 if self.tx_version >= 8 {
184 Some(self.metadata_hash.clone())
185 } else {
186 None
187 }
188 }
189}
190
191impl Encode for AdditionalSigned {
192 fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
193 self.spec_version.encode_to(output);
194 self.tx_version.encode_to(output);
195 self.genesis_hash.encode_to(output);
196 self.current_hash.encode_to(output);
197 if self.tx_version >= 8 {
198 self.metadata_hash.encode_to(output);
199 }
200 }
201}
202
203impl Decode for AdditionalSigned {
204 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
205 let spec_version = Decode::decode(input)?;
206 let tx_version = Decode::decode(input)?;
207 let genesis_hash = Decode::decode(input)?;
208 let current_hash = Decode::decode(input)?;
209 let metadata_hash = if tx_version >= 8 {
210 Decode::decode(input)?
211 } else {
212 None
213 };
214 Ok(Self {
215 spec_version,
216 tx_version,
217 genesis_hash,
218 current_hash,
219 metadata_hash,
220 })
221 }
222}
223
224#[derive(Clone, Copy, Debug, PartialEq, Eq)]
225#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
226#[cfg_attr(all(feature = "std", feature = "type_info"), derive(TypeInfo))]
227pub enum Era {
228 Immortal,
229 Mortal(u64, u64),
230}
231
232impl Era {
233 pub fn mortal(current: BlockNumber, period: Option<u64>) -> Self {
234 let period = period.unwrap_or(64);
235 sp_runtime::generic::Era::mortal(period, current.into()).into()
236 }
237
238 pub fn immortal() -> Self {
239 Self::Immortal
240 }
241}
242
243impl Encode for Era {
244 fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
245 let runtime_era: sp_runtime::generic::Era = self.clone().into();
246 runtime_era.encode_to(output)
247 }
248}
249
250impl Decode for Era {
251 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
252 let runtime_era = sp_runtime::generic::Era::decode(input)?;
253 Ok(runtime_era.into())
254 }
255}
256
257impl From<sp_runtime::generic::Era> for Era {
258 fn from(e: sp_runtime::generic::Era) -> Self {
259 match e {
260 sp_runtime::generic::Era::Immortal => Self::Immortal,
261 sp_runtime::generic::Era::Mortal(period, phase) => Self::Mortal(period, phase),
262 }
263 }
264}
265
266impl From<Era> for sp_runtime::generic::Era {
267 fn from(e: Era) -> Self {
268 match e {
269 Era::Immortal => Self::Immortal,
270 Era::Mortal(period, phase) => Self::Mortal(period, phase),
271 }
272 }
273}
274
275#[derive(Clone, Debug)]
276#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
277pub struct Extra {
278 era: sp_runtime::generic::Era,
279 nonce: Compact<u32>,
280 tip: Compact<u128>,
281 metadata_hash: Option<Option<H256>>,
282}
283
284impl Extra {
285 pub fn new(era: Era, nonce: u32, metadata_hash: Option<Option<H256>>) -> Self {
286 Self {
287 era: era.into(),
288 nonce: nonce.into(),
289 tip: 0u128.into(),
290 metadata_hash,
291 }
292 }
293
294 pub fn nonce(&self) -> u32 {
295 self.nonce.0
296 }
297
298 pub fn tip(&self) -> u128 {
299 self.tip.0
300 }
301}
302
303impl Encode for Extra {
304 fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
305 self.era.encode_to(output);
306 self.nonce.encode_to(output);
307 self.tip.encode_to(output);
308 if let Some(metadata_hash) = &self.metadata_hash {
309 metadata_hash.encode_to(output);
310 }
311 }
312}
313
314impl Decode for Extra {
315 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
316 let era = Decode::decode(input)?;
317 let nonce = Decode::decode(input)?;
318 let tip = Decode::decode(input)?;
319 #[cfg(feature = "polymesh_v8")]
320 let metadata_hash = Some(Decode::decode(input)?);
321 #[cfg(not(feature = "polymesh_v8"))]
322 let metadata_hash = None;
323 Ok(Self {
324 era,
325 nonce,
326 tip,
327 metadata_hash,
328 })
329 }
330}
331
332#[derive(Clone, Debug)]
336#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
337pub struct Encoded(
338 #[cfg_attr(feature = "serde", serde(with = "impl_serde::serialize"))] pub Vec<u8>,
339);
340
341impl Encoded {
342 pub fn decode_as<T: Decode>(&self) -> Result<T> {
343 Ok(T::decode(&mut &self.0[..])?)
344 }
345}
346
347impl<T: Encode> From<&T> for Encoded {
348 fn from(other: &T) -> Self {
349 Self(other.encode())
350 }
351}
352
353impl Encode for Encoded {
354 fn size_hint(&self) -> usize {
355 self.0.len()
356 }
357 fn encode_to<T: Output + ?Sized>(&self, dest: &mut T) {
358 dest.write(&self.0);
359 }
360}
361
362impl Decode for Encoded {
363 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
364 if let Some(len) = input.remaining_len()? {
365 let mut data = vec![0u8; len];
366 input.read(&mut data.as_mut_slice())?;
367 Ok(Self(data))
368 } else {
369 let mut data = Vec::new();
370 while let Ok(b) = input.read_byte() {
371 data.push(b);
372 }
373 Ok(Self(data))
374 }
375 }
376}
377
378pub struct BytesPayload<T>(pub T);
382
383pub const BYTES_PREFIX: &[u8] = b"<Bytes>";
384pub const BYTES_SUFFIX: &[u8] = b"</Bytes>";
385
386impl<T: Encode + Clone> From<&T> for BytesPayload<T> {
387 fn from(other: &T) -> Self {
388 Self(other.clone())
389 }
390}
391
392impl<T: Encode + Clone> Encode for BytesPayload<T> {
393 fn size_hint(&self) -> usize {
394 BYTES_PREFIX.len() + self.0.size_hint() + BYTES_SUFFIX.len()
395 }
396 fn encode_to<D: Output + ?Sized>(&self, dest: &mut D) {
397 dest.write(BYTES_PREFIX);
398 self.0.encode_to(dest);
399 dest.write(BYTES_SUFFIX);
400 }
401}
402
403pub struct SignedPayload<'a>((&'a Encoded, &'a Extra, AdditionalSigned));
404
405impl<'a> SignedPayload<'a> {
406 pub fn new(call: &'a Encoded, extra: &'a Extra, additional: AdditionalSigned) -> Self {
407 Self((call, extra, additional))
408 }
409}
410
411impl<'a> Encode for SignedPayload<'a> {
412 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
413 self.0.using_encoded(|payload| {
414 if payload.len() > 256 {
415 f(&blake2_256(payload)[..])
416 } else {
417 f(payload)
418 }
419 })
420 }
421}
422
423#[derive(Clone, Debug, Encode, Decode)]
427#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
428pub struct PreparedTransaction {
429 pub call: Encoded,
430 pub extra: Extra,
431 pub additional: AdditionalSigned,
432 pub account: Option<AccountId>,
433}
434
435impl PreparedTransaction {
436 pub fn new(
437 account: AccountId,
438 additional: AdditionalSigned,
439 extra: Extra,
440 call: Encoded,
441 ) -> Self {
442 Self {
443 account: Some(account),
444 additional,
445 extra,
446 call,
447 }
448 }
449
450 pub fn decode_signed_payload<Api: ChainApi, I: codec::Input>(input: &mut I) -> Result<Self> {
452 let call = Api::RuntimeCall::decode(input)?;
453 let extra = Decode::decode(input)?;
454 let additional = Decode::decode(input)?;
455 let account = match input.remaining_len()? {
457 Some(33) => Decode::decode(input)?,
458 _ => None,
459 };
460 Ok(Self {
461 call: Encoded(call.encode()),
462 extra,
463 additional,
464 account,
465 })
466 }
467
468 pub async fn sign(self, signer: &mut impl Signer) -> Result<ExtrinsicV4> {
469 let account = signer.account();
470 if let Some(tx_account) = &self.account {
471 if account != *tx_account {
473 use sp_core::crypto::Ss58Codec;
474 let version = 12u16.into(); let a1 = account.to_ss58check_with_version(version);
476 let a2 = tx_account.to_ss58check_with_version(version);
477 return Err(Error::WrongSignerAccount(a1, a2));
478 }
479 }
480 let payload = SignedPayload::new(&self.call, &self.extra, self.additional);
481 let payload = payload.encode();
482 let sig = signer.sign(&payload[..]).await?;
483
484 let xt = ExtrinsicV4::signed(account, sig, self.extra, self.call);
485 Ok(xt)
486 }
487}
488
489#[derive(Clone, Debug, Encode, Decode)]
490#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
491pub struct ExtrinsicSignature {
492 pub account: GenericAddress,
493 pub signature: MultiSignature,
494 pub extra: Extra,
495}
496
497pub const EXTRINSIC_VERSION: u8 = 4;
499
500#[derive(Clone, Debug)]
501#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
502pub struct ExtrinsicV4 {
503 pub signature: Option<ExtrinsicSignature>,
504 pub call: Encoded,
505}
506
507impl ExtrinsicV4 {
508 pub fn tx_hash(tx: &[u8]) -> TxHash {
509 H256(blake2_256(tx))
510 }
511
512 pub fn signed(account: AccountId, sig: MultiSignature, extra: Extra, call: Encoded) -> Self {
513 Self {
514 signature: Some(ExtrinsicSignature {
515 account: GenericAddress::from(account),
516 signature: sig,
517 extra,
518 }),
519 call,
520 }
521 }
522
523 pub fn unsigned(call: Encoded) -> Self {
524 Self {
525 signature: None,
526 call,
527 }
528 }
529
530 pub fn as_hex_and_hash(&self) -> (String, TxHash) {
531 let tx = self.encode();
532 let tx_hash = Self::tx_hash(tx.as_slice());
533 let mut tx_hex = hex::encode(tx);
534 tx_hex.insert_str(0, "0x");
535 (tx_hex, tx_hash)
536 }
537
538 pub fn to_hex(&self) -> String {
539 let mut hex = hex::encode(self.encode());
540 hex.insert_str(0, "0x");
541 hex
542 }
543}
544
545impl Encode for ExtrinsicV4 {
546 fn encode(&self) -> Vec<u8> {
547 let mut buf = Vec::with_capacity(512);
548
549 match &self.signature {
551 Some(sig) => {
552 buf.push(EXTRINSIC_VERSION | 0b1000_0000);
553 sig.encode_to(&mut buf);
554 }
555 None => {
556 buf.push(EXTRINSIC_VERSION & 0b0111_1111);
557 }
558 }
559 self.call.encode_to(&mut buf);
560
561 buf.encode()
562 }
563}
564
565impl Decode for ExtrinsicV4 {
566 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
567 let _len: Compact<u32> = Decode::decode(input)?;
569 let version = input.read_byte()?;
571 let is_signed = version & 0b1000_0000 != 0;
572 if (version & 0b0111_1111) != EXTRINSIC_VERSION {
573 Err("Invalid EXTRINSIC_VERSION")?;
574 }
575
576 let signature = if is_signed {
577 Some(ExtrinsicSignature::decode(input)?)
578 } else {
579 None
580 };
581
582 Ok(Self {
583 signature,
584 call: Decode::decode(input)?,
585 })
586 }
587}
588
589#[derive(Clone, Debug)]
590#[cfg_attr(feature = "serde", derive(Deserialize))]
591pub struct AccountInfo {
592 pub nonce: u32,
593}
594
595#[derive(Clone, Debug, PartialEq)]
596#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
597#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
598pub enum TransactionStatus {
599 Future,
600 Ready,
601 Broadcast(Vec<String>),
602 InBlock(BlockHash),
603 Retracted(BlockHash),
604 FinalityTimeout(BlockHash),
605 Finalized(BlockHash),
606 Usurped(TxHash),
607 Dropped,
608 Invalid,
609}
610
611#[derive(Clone, Debug)]
612#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
613#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
614pub struct SignedBlock {
615 pub block: Block,
616 }
618
619#[derive(Clone, Debug)]
620#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
621pub struct Block {
622 extrinsics: Vec<Encoded>,
623 header: Header,
624}
625
626impl Block {
627 pub fn find_extrinsic(&self, xt_hash: TxHash) -> Option<usize> {
628 self
630 .extrinsics
631 .iter()
632 .position(|xt| ExtrinsicV4::tx_hash(xt.0.as_slice()) == xt_hash)
633 }
634
635 pub fn extrinsics(&self) -> &[Encoded] {
636 self.extrinsics.as_slice()
637 }
638
639 pub fn parent(&self) -> BlockHash {
640 self.header.parent_hash
641 }
642
643 pub fn state_root(&self) -> BlockHash {
644 self.header.state_root
645 }
646
647 pub fn extrinsics_root(&self) -> BlockHash {
648 self.header.extrinsics_root
649 }
650
651 pub fn block_number(&self) -> BlockNumber {
652 self.header.number
653 }
654
655 pub fn to_string(&self) -> String {
656 format!("{:?}", self)
657 }
658}
659
660#[derive(Clone, Debug, Decode, PartialEq, Eq)]
661#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
662pub enum Phase {
663 ApplyExtrinsic(u32),
664 Finalization,
665 Initialization,
666}
667
668#[derive(Clone, Debug, Decode)]
669#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
670pub struct EventRecord<Event> {
671 pub phase: Phase,
672 pub event: Event,
673 pub topics: Vec<BlockHash>,
674}
675
676impl<Event: RuntimeEnumTraits> EventRecord<Event> {
677 pub fn name(&self) -> &'static str {
678 self.event.as_name()
679 }
680
681 pub fn short_doc(&self) -> &'static str {
682 self.event.as_short_doc()
683 }
684
685 pub fn docs(&self) -> &'static [&'static str] {
686 self.event.as_docs()
687 }
688
689 pub fn to_string(&self) -> String {
690 format!("{:#?}", self)
691 }
692}
693
694#[derive(Clone, Debug, Decode, Default)]
695#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
696pub struct EventRecords<Event>(pub Vec<EventRecord<Event>>);
697
698impl<Event: RuntimeEnumTraits> EventRecords<Event> {
699 pub fn from_vec(mut events: Vec<EventRecord<Event>>, filter: Option<Phase>) -> Self {
700 if let Some(filter) = filter {
701 events.retain(|ev| ev.phase == filter);
702 }
703 Self(events)
704 }
705
706 pub fn to_string(&self) -> String {
707 format!("{:#?}", self.0)
708 }
709}
710
711#[cfg(test)]
712mod tests {
713 use anyhow::Result;
714
715 use super::*;
716
717 #[tokio::test]
719 async fn test_bytes_payload() -> Result<()> {
720 let data = b"Hello";
721 let payload = BytesPayload(Encoded::from(data));
722 let encoded = payload.encode();
723 assert_eq!(
724 encoded.len(),
725 BYTES_PREFIX.len() + data.len() + BYTES_SUFFIX.len()
726 );
727 assert_eq!(&encoded[..BYTES_PREFIX.len()], BYTES_PREFIX);
728 assert_eq!(
729 &encoded[BYTES_PREFIX.len()..BYTES_PREFIX.len() + data.len()],
730 data
731 );
732 assert_eq!(&encoded[BYTES_PREFIX.len() + data.len()..], BYTES_SUFFIX);
733
734 let alice = sp_core::sr25519::Pair::from_string("//Alice", None)?;
736
737 let sig = alice.sign(&encoded[..]);
739
740 let verified = alice.verify(&sig, &encoded[..])?;
742 assert!(verified);
743
744 Ok(())
745 }
746
747 #[tokio::test]
749 async fn test_subkey_signature() -> Result<()> {
750 let unwrapped_data = b"Test from subkey";
751 let payload = BytesPayload(Encoded::from(unwrapped_data));
752 let wrapped_data = payload.encode();
753
754 let hex = hex::decode("f22a9a82306e09fefab3782f55d1981795803211e3a2ef8f90555bb96dab0d281fd98705f4c675545d1f537b90cefa6596f60617eac5dec5bd4b9306908dc687")?;
756 let unwrapped_sig = MultiSignature::Sr25519(
757 sp_core::sr25519::Signature::try_from(hex.as_slice()).expect("Invalid signature"),
758 );
759 let hex = hex::decode("8c76d5f31c5ff229a90067063a19feff0e94f28793a490d63e0abd9f5aa5a33fa58fae990b98b6d80ae1ec0085fe19a36cb5b757f46b2d7574c7fc9e35974682")?;
760 let wrapped_sig = MultiSignature::Sr25519(
761 sp_core::sr25519::Signature::try_from(hex.as_slice()).expect("Invalid signature"),
762 );
763
764 let alice = sp_core::sr25519::Pair::from_string("//Alice", None)?;
766
767 let verified = alice.verify(&unwrapped_sig, &unwrapped_data[..])?;
769 assert!(verified);
770
771 let verified = alice.verify(&wrapped_sig, &wrapped_data[..])?;
773 assert!(verified);
774
775 Ok(())
776 }
777}