polymesh_api_client/
block.rs

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 Encode for AdditionalSigned {
182  fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
183    self.spec_version.encode_to(output);
184    self.tx_version.encode_to(output);
185    self.genesis_hash.encode_to(output);
186    self.current_hash.encode_to(output);
187    if self.tx_version >= 8 {
188      self.metadata_hash.encode_to(output);
189    }
190  }
191}
192
193impl Decode for AdditionalSigned {
194  fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
195    let spec_version = Decode::decode(input)?;
196    let tx_version = Decode::decode(input)?;
197    let genesis_hash = Decode::decode(input)?;
198    let current_hash = Decode::decode(input)?;
199    let metadata_hash = if tx_version >= 8 {
200      Decode::decode(input)?
201    } else {
202      None
203    };
204    Ok(Self {
205      spec_version,
206      tx_version,
207      genesis_hash,
208      current_hash,
209      metadata_hash,
210    })
211  }
212}
213
214#[derive(Clone, Copy, Debug, PartialEq, Eq)]
215#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
216#[cfg_attr(all(feature = "std", feature = "type_info"), derive(TypeInfo))]
217pub enum Era {
218  Immortal,
219  Mortal(u64, u64),
220}
221
222impl Era {
223  pub fn mortal(current: BlockNumber, period: Option<u64>) -> Self {
224    let period = period.unwrap_or(64);
225    sp_runtime::generic::Era::mortal(period, current.into()).into()
226  }
227
228  pub fn immortal() -> Self {
229    Self::Immortal
230  }
231}
232
233impl Encode for Era {
234  fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
235    let runtime_era: sp_runtime::generic::Era = self.clone().into();
236    runtime_era.encode_to(output)
237  }
238}
239
240impl Decode for Era {
241  fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
242    let runtime_era = sp_runtime::generic::Era::decode(input)?;
243    Ok(runtime_era.into())
244  }
245}
246
247impl From<sp_runtime::generic::Era> for Era {
248  fn from(e: sp_runtime::generic::Era) -> Self {
249    match e {
250      sp_runtime::generic::Era::Immortal => Self::Immortal,
251      sp_runtime::generic::Era::Mortal(period, phase) => Self::Mortal(period, phase),
252    }
253  }
254}
255
256impl From<Era> for sp_runtime::generic::Era {
257  fn from(e: Era) -> Self {
258    match e {
259      Era::Immortal => Self::Immortal,
260      Era::Mortal(period, phase) => Self::Mortal(period, phase),
261    }
262  }
263}
264
265#[derive(Clone, Debug, Encode, Decode)]
266#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
267pub struct Extra {
268  era: sp_runtime::generic::Era,
269  nonce: Compact<u32>,
270  tip: Compact<u128>,
271}
272
273impl Extra {
274  pub fn new(era: Era, nonce: u32) -> Self {
275    Self {
276      era: era.into(),
277      nonce: nonce.into(),
278      tip: 0u128.into(),
279    }
280  }
281
282  pub fn nonce(&self) -> u32 {
283    self.nonce.0
284  }
285
286  pub fn tip(&self) -> u128 {
287    self.tip.0
288  }
289}
290
291/// Encoded is a wrapper for data that has already been encoded.
292///
293/// This is used to avoid double encoding.
294#[derive(Clone, Debug)]
295#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
296pub struct Encoded(
297  #[cfg_attr(feature = "serde", serde(with = "impl_serde::serialize"))] pub Vec<u8>,
298);
299
300impl Encoded {
301  pub fn decode_as<T: Decode>(&self) -> Result<T> {
302    Ok(T::decode(&mut &self.0[..])?)
303  }
304}
305
306impl<T: Encode> From<&T> for Encoded {
307  fn from(other: &T) -> Self {
308    Self(other.encode())
309  }
310}
311
312impl Encode for Encoded {
313  fn size_hint(&self) -> usize {
314    self.0.len()
315  }
316  fn encode_to<T: Output + ?Sized>(&self, dest: &mut T) {
317    dest.write(&self.0);
318  }
319}
320
321impl Decode for Encoded {
322  fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
323    if let Some(len) = input.remaining_len()? {
324      let mut data = vec![0u8; len];
325      input.read(&mut data.as_mut_slice())?;
326      Ok(Self(data))
327    } else {
328      let mut data = Vec::new();
329      while let Ok(b) = input.read_byte() {
330        data.push(b);
331      }
332      Ok(Self(data))
333    }
334  }
335}
336
337/// BytesPayload is a wrapper for signing the raw SCALE bytes of `T`.
338///
339/// The wrapped `T` type will be SCALE encoded and wrapped with a prefix & suffix `<Bytes>...T SCALE Encoded...</Bytes>` before signing.
340pub struct BytesPayload<T>(pub T);
341
342pub const BYTES_PREFIX: &[u8] = b"<Bytes>";
343pub const BYTES_SUFFIX: &[u8] = b"</Bytes>";
344
345impl<T: Encode + Clone> From<&T> for BytesPayload<T> {
346  fn from(other: &T) -> Self {
347    Self(other.clone())
348  }
349}
350
351impl<T: Encode + Clone> Encode for BytesPayload<T> {
352  fn size_hint(&self) -> usize {
353    BYTES_PREFIX.len() + self.0.size_hint() + BYTES_SUFFIX.len()
354  }
355  fn encode_to<D: Output + ?Sized>(&self, dest: &mut D) {
356    dest.write(BYTES_PREFIX);
357    self.0.encode_to(dest);
358    dest.write(BYTES_SUFFIX);
359  }
360}
361
362pub struct SignedPayload<'a>((&'a Encoded, &'a Extra, AdditionalSigned));
363
364impl<'a> SignedPayload<'a> {
365  pub fn new(call: &'a Encoded, extra: &'a Extra, additional: AdditionalSigned) -> Self {
366    Self((call, extra, additional))
367  }
368}
369
370impl<'a> Encode for SignedPayload<'a> {
371  fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
372    self.0.using_encoded(|payload| {
373      if payload.len() > 256 {
374        f(&blake2_256(payload)[..])
375      } else {
376        f(payload)
377      }
378    })
379  }
380}
381
382/// PreparedTransaction holds all data needed to sign a transaction.
383///
384/// This can be used for offline signers.
385#[derive(Clone, Debug, Encode, Decode)]
386#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
387pub struct PreparedTransaction {
388  pub call: Encoded,
389  pub extra: Extra,
390  pub additional: AdditionalSigned,
391  pub account: Option<AccountId>,
392}
393
394impl PreparedTransaction {
395  pub fn new(
396    account: AccountId,
397    additional: AdditionalSigned,
398    extra: Extra,
399    call: Encoded,
400  ) -> Self {
401    Self {
402      account: Some(account),
403      additional,
404      extra,
405      call,
406    }
407  }
408
409  /// Decode from a `SignedPayload` with optional `AccountId`.
410  pub fn decode_signed_payload<Api: ChainApi, I: codec::Input>(input: &mut I) -> Result<Self> {
411    let call = Api::RuntimeCall::decode(input)?;
412    let extra = Decode::decode(input)?;
413    let additional = Decode::decode(input)?;
414    // Try decode optional `AccountId`.
415    let account = match input.remaining_len()? {
416      Some(33) => Decode::decode(input)?,
417      _ => None,
418    };
419    Ok(Self {
420      call: Encoded(call.encode()),
421      extra,
422      additional,
423      account,
424    })
425  }
426
427  pub async fn sign(self, signer: &mut impl Signer) -> Result<ExtrinsicV4> {
428    let account = signer.account();
429    if let Some(tx_account) = &self.account {
430      // Ensure the signer's account matches the transaction.
431      if account != *tx_account {
432        use sp_core::crypto::Ss58Codec;
433        let version = 12u16.into(); // Polymesh
434        let a1 = account.to_ss58check_with_version(version);
435        let a2 = tx_account.to_ss58check_with_version(version);
436        return Err(Error::WrongSignerAccount(a1, a2));
437      }
438    }
439    let payload = SignedPayload::new(&self.call, &self.extra, self.additional);
440    let payload = payload.encode();
441    let sig = signer.sign(&payload[..]).await?;
442
443    let xt = ExtrinsicV4::signed(account, sig, self.extra, self.call);
444    Ok(xt)
445  }
446}
447
448#[derive(Clone, Debug, Encode, Decode)]
449#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
450pub struct ExtrinsicSignature {
451  pub account: GenericAddress,
452  pub signature: MultiSignature,
453  pub extra: Extra,
454}
455
456/// Current version of the `UncheckedExtrinsic` format.
457pub const EXTRINSIC_VERSION: u8 = 4;
458
459#[derive(Clone, Debug)]
460#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
461pub struct ExtrinsicV4 {
462  pub signature: Option<ExtrinsicSignature>,
463  pub call: Encoded,
464}
465
466impl ExtrinsicV4 {
467  pub fn tx_hash(tx: &[u8]) -> TxHash {
468    H256(blake2_256(tx))
469  }
470
471  pub fn signed(account: AccountId, sig: MultiSignature, extra: Extra, call: Encoded) -> Self {
472    Self {
473      signature: Some(ExtrinsicSignature {
474        account: GenericAddress::from(account),
475        signature: sig,
476        extra,
477      }),
478      call,
479    }
480  }
481
482  pub fn unsigned(call: Encoded) -> Self {
483    Self {
484      signature: None,
485      call,
486    }
487  }
488
489  pub fn as_hex_and_hash(&self) -> (String, TxHash) {
490    let tx = self.encode();
491    let tx_hash = Self::tx_hash(tx.as_slice());
492    let mut tx_hex = hex::encode(tx);
493    tx_hex.insert_str(0, "0x");
494    (tx_hex, tx_hash)
495  }
496
497  pub fn to_hex(&self) -> String {
498    let mut hex = hex::encode(self.encode());
499    hex.insert_str(0, "0x");
500    hex
501  }
502}
503
504impl Encode for ExtrinsicV4 {
505  fn encode(&self) -> Vec<u8> {
506    let mut buf = Vec::with_capacity(512);
507
508    // 1 byte version id and signature if signed.
509    match &self.signature {
510      Some(sig) => {
511        buf.push(EXTRINSIC_VERSION | 0b1000_0000);
512        sig.encode_to(&mut buf);
513      }
514      None => {
515        buf.push(EXTRINSIC_VERSION & 0b0111_1111);
516      }
517    }
518    self.call.encode_to(&mut buf);
519
520    buf.encode()
521  }
522}
523
524impl Decode for ExtrinsicV4 {
525  fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
526    // Decode Vec length.
527    let _len: Compact<u32> = Decode::decode(input)?;
528    // Version and signed flag.
529    let version = input.read_byte()?;
530    let is_signed = version & 0b1000_0000 != 0;
531    if (version & 0b0111_1111) != EXTRINSIC_VERSION {
532      Err("Invalid EXTRINSIC_VERSION")?;
533    }
534
535    let signature = if is_signed {
536      Some(ExtrinsicSignature::decode(input)?)
537    } else {
538      None
539    };
540
541    Ok(Self {
542      signature,
543      call: Decode::decode(input)?,
544    })
545  }
546}
547
548#[derive(Clone, Debug)]
549#[cfg_attr(feature = "serde", derive(Deserialize))]
550pub struct AccountInfo {
551  pub nonce: u32,
552}
553
554#[derive(Clone, Debug, PartialEq)]
555#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
556#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
557pub enum TransactionStatus {
558  Future,
559  Ready,
560  Broadcast(Vec<String>),
561  InBlock(BlockHash),
562  Retracted(BlockHash),
563  FinalityTimeout(BlockHash),
564  Finalized(BlockHash),
565  Usurped(TxHash),
566  Dropped,
567  Invalid,
568}
569
570#[derive(Clone, Debug)]
571#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
572#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
573pub struct SignedBlock {
574  pub block: Block,
575  // TODO: Add Justifications field.
576}
577
578#[derive(Clone, Debug)]
579#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
580pub struct Block {
581  extrinsics: Vec<Encoded>,
582  header: Header,
583}
584
585impl Block {
586  pub fn find_extrinsic(&self, xt_hash: TxHash) -> Option<usize> {
587    // TODO: Add caching of blocks with extrinsic hashes.
588    self
589      .extrinsics
590      .iter()
591      .position(|xt| ExtrinsicV4::tx_hash(xt.0.as_slice()) == xt_hash)
592  }
593
594  pub fn extrinsics(&self) -> &[Encoded] {
595    self.extrinsics.as_slice()
596  }
597
598  pub fn parent(&self) -> BlockHash {
599    self.header.parent_hash
600  }
601
602  pub fn state_root(&self) -> BlockHash {
603    self.header.state_root
604  }
605
606  pub fn extrinsics_root(&self) -> BlockHash {
607    self.header.extrinsics_root
608  }
609
610  pub fn block_number(&self) -> BlockNumber {
611    self.header.number
612  }
613
614  pub fn to_string(&self) -> String {
615    format!("{:?}", self)
616  }
617}
618
619#[derive(Clone, Debug, Decode, PartialEq, Eq)]
620#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
621pub enum Phase {
622  ApplyExtrinsic(u32),
623  Finalization,
624  Initialization,
625}
626
627#[derive(Clone, Debug, Decode)]
628#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
629pub struct EventRecord<Event> {
630  pub phase: Phase,
631  pub event: Event,
632  pub topics: Vec<BlockHash>,
633}
634
635impl<Event: RuntimeEnumTraits> EventRecord<Event> {
636  pub fn name(&self) -> &'static str {
637    self.event.as_name()
638  }
639
640  pub fn short_doc(&self) -> &'static str {
641    self.event.as_short_doc()
642  }
643
644  pub fn docs(&self) -> &'static [&'static str] {
645    self.event.as_docs()
646  }
647
648  pub fn to_string(&self) -> String {
649    format!("{:#?}", self)
650  }
651}
652
653#[derive(Clone, Debug, Decode, Default)]
654#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
655pub struct EventRecords<Event>(pub Vec<EventRecord<Event>>);
656
657impl<Event: RuntimeEnumTraits> EventRecords<Event> {
658  pub fn from_vec(mut events: Vec<EventRecord<Event>>, filter: Option<Phase>) -> Self {
659    if let Some(filter) = filter {
660      events.retain(|ev| ev.phase == filter);
661    }
662    Self(events)
663  }
664
665  pub fn to_string(&self) -> String {
666    format!("{:#?}", self.0)
667  }
668}
669
670#[cfg(test)]
671mod tests {
672  use anyhow::Result;
673
674  use super::*;
675
676  /// Test the `BytesPayload` signing and verification.
677  #[tokio::test]
678  async fn test_bytes_payload() -> Result<()> {
679    let data = b"Hello";
680    let payload = BytesPayload(Encoded::from(data));
681    let encoded = payload.encode();
682    assert_eq!(
683      encoded.len(),
684      BYTES_PREFIX.len() + data.len() + BYTES_SUFFIX.len()
685    );
686    assert_eq!(&encoded[..BYTES_PREFIX.len()], BYTES_PREFIX);
687    assert_eq!(
688      &encoded[BYTES_PREFIX.len()..BYTES_PREFIX.len() + data.len()],
689      data
690    );
691    assert_eq!(&encoded[BYTES_PREFIX.len() + data.len()..], BYTES_SUFFIX);
692
693    // Alice sr25519 keypair.
694    let alice = sp_core::sr25519::Pair::from_string("//Alice", None)?;
695
696    // Sign the payload using Alice.
697    let sig = alice.sign(&encoded[..]);
698
699    // Verify the signature.
700    let verified = alice.verify(&sig, &encoded[..])?;
701    assert!(verified);
702
703    Ok(())
704  }
705
706  /// Test signature from `subkey` tool.
707  #[tokio::test]
708  async fn test_subkey_signature() -> Result<()> {
709    let unwrapped_data = b"Test from subkey";
710    let payload = BytesPayload(Encoded::from(unwrapped_data));
711    let wrapped_data = payload.encode();
712
713    // Signatures from `subkey` tool.
714    let hex = hex::decode("f22a9a82306e09fefab3782f55d1981795803211e3a2ef8f90555bb96dab0d281fd98705f4c675545d1f537b90cefa6596f60617eac5dec5bd4b9306908dc687")?;
715    let unwrapped_sig = MultiSignature::Sr25519(
716      sp_core::sr25519::Signature::try_from(hex.as_slice()).expect("Invalid signature"),
717    );
718    let hex = hex::decode("8c76d5f31c5ff229a90067063a19feff0e94f28793a490d63e0abd9f5aa5a33fa58fae990b98b6d80ae1ec0085fe19a36cb5b757f46b2d7574c7fc9e35974682")?;
719    let wrapped_sig = MultiSignature::Sr25519(
720      sp_core::sr25519::Signature::try_from(hex.as_slice()).expect("Invalid signature"),
721    );
722
723    // Alice sr25519 keypair.
724    let alice = sp_core::sr25519::Pair::from_string("//Alice", None)?;
725
726    // Verify the unwrapped data signature.
727    let verified = alice.verify(&unwrapped_sig, &unwrapped_data[..])?;
728    assert!(verified);
729
730    // Verify the wrapped data signature.
731    let verified = alice.verify(&wrapped_sig, &wrapped_data[..])?;
732    assert!(verified);
733
734    Ok(())
735  }
736}