From 1a0a5a51964074daf51e2edc6a4970a4a3991d0a Mon Sep 17 00:00:00 2001 From: gabiazcona <94936618+gabiazcona@users.noreply.github.com> Date: Fri, 25 Mar 2022 11:41:50 +0100 Subject: [PATCH] Batch sign messages (#16) --- src/account.rs | 38 ++++++++++++++++++++++++++- src/lib.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++--- src/simple.rs | 2 +- 3 files changed, 104 insertions(+), 5 deletions(-) diff --git a/src/account.rs b/src/account.rs index 6c23f9d..738c4d4 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,4 +1,5 @@ use crate::{CryptoType, Network, Pair}; +use core::mem; const ROOT_ACCOUNT: &str = "ROOT"; @@ -10,11 +11,13 @@ pub enum Account<'a, P> { Root { pair: P, network: Network, + pending_sign: Vec>, }, Sub { path: &'a str, name: &'a str, network: Network, + pending_sign: Vec>, }, } @@ -26,6 +29,7 @@ where Account::Root { pair, network: Network::default(), + pending_sign: Vec::new(), } } @@ -67,9 +71,41 @@ where Self::Sub { .. } => todo!(), } } + + /// Save data to be signed later + pub fn add_to_pending(&mut self, message: &[u8]) { + self.pending_sign_mut().push(message.into()); + } + + /// Sign messages from the queue returning them and their signatures + pub fn sign_pending(&mut self) -> Vec<(Vec, P::Signature)> { + let v = mem::take(self.pending_sign_mut()); + v.into_iter() + .map(|msg| { + let s = self.sign(&msg); + (msg, s) + }) + .collect() + } + + // Return an iterator over the messages pending for signature in this account + pub fn get_pending(&self) -> impl Iterator { + self.pending_sign().iter().map(|i| i.as_ref()) + } + + fn pending_sign_mut(&mut self) -> &mut Vec> { + match self { + Self::Root { pending_sign, .. } | Self::Sub { pending_sign, .. } => pending_sign, + } + } + + fn pending_sign(&self) -> &Vec> { + match self { + Self::Root { pending_sign, .. } | Self::Sub { pending_sign, .. } => pending_sign, + } + } } impl CryptoType for Account<'_, P> { type Pair = P; } - diff --git a/src/lib.rs b/src/lib.rs index c5b7828..57c7e3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,7 @@ where /// let vault: SimpleVault = "//Alice".into(); /// let mut wallet = Wallet::from(vault); /// if wallet.is_locked() { - /// wallet = wallet.unlock("").await?; + /// wallet = wallet.unlock(()).await?; /// } /// # assert_eq!(wallet.is_locked(), false); /// # Ok(()) @@ -98,15 +98,78 @@ where /// # use libwallet::{Wallet, SimpleVault, sr25519, Result}; /// # #[async_std::main] async fn main() -> Result<()> { /// - /// let wallet = Wallet::new(SimpleVault::::new()).unlock("").await?; + /// let wallet = Wallet::new(SimpleVault::::new()).unlock(()).await?; /// let signature = wallet.sign(&[0x01, 0x02, 0x03]); - /// assert!(signature.is_ok()); + /// # assert!(signature.is_ok()); /// # Ok(()) } /// ``` pub fn sign(&self, message: &[u8]) -> Result> { Ok(self.root_account()?.sign(message)) } + /// Save data to be signed later by root account + /// ``` + /// # use libwallet::{Wallet, SimpleVault, sr25519, Result}; + /// # #[async_std::main] async fn main() -> Result<()> { + /// + /// let mut wallet = Wallet::new(SimpleVault::::new()).unlock(()).await?; + /// let res = wallet.sign_later(&[0x01, 0x02, 0x03]); + /// assert!(res.is_ok()); + /// # Ok(()) } + /// ``` + pub fn sign_later(&mut self, message: &[u8]) -> Result<()> { + self.root + .as_mut() + .map(|a| a.add_to_pending(message)) + .ok_or(Error::Locked) + } + + /// Try to sign all messages in the queue of an account + /// Returns signed transactions + /// ``` + /// # use libwallet::{Wallet, SimpleVault, sr25519, Result}; + /// # #[async_std::main] async fn main() -> Result<()> { + /// + /// let mut wallet = Wallet::new(SimpleVault::::new()).unlock(()).await?; + /// wallet.sign_later(&[0x01, 0x02, 0x03]); + /// wallet.sign_later(&[0x01, 0x02]); + /// wallet.sign_pending("ROOT"); + /// let res = wallet.get_pending("ROOT").collect::>(); + /// assert!(res.is_empty()); + /// # Ok(()) } + /// ``` + pub fn sign_pending(&mut self, name: &str) -> Vec<(Vec, SignatureOf)> { + match name { + "ROOT" => self + .root + .as_mut() + .map(|a| a.sign_pending()) + .unwrap_or_default(), + _ => todo!(), //search sub-accounts + } + } + + /// Iteratate over the messages with pending signature of the named account. + /// It panics if the wallet is locked. + /// + /// ``` + /// # use libwallet::{Wallet, SimpleVault, sr25519, Result}; + /// # #[async_std::main] async fn main() -> Result<()> { + /// + /// let mut wallet = Wallet::new(SimpleVault::::new()).unlock(()).await?; + /// wallet.sign_later(&[0x01, 0x02, 0x03]); + /// wallet.sign_later(&[0x01, 0x02]); + /// let res = wallet.get_pending("ROOT").collect::>(); + /// assert_eq!(vec![vec![0x01, 0x02, 0x03], vec![0x01, 0x02]], res); + /// # Ok(()) } + /// ``` + pub fn get_pending(&self, name: &str) -> impl Iterator { + match name { + "ROOT" => self.root_account().unwrap().get_pending(), + _ => todo!(), //get sub-accounts + } + } + /// Switch the network used by the root account which is used by /// default when deriving new sub-accounts pub fn switch_default_network(&mut self, net: &str) -> Result<&Account> { diff --git a/src/simple.rs b/src/simple.rs index 8f649e8..9053734 100644 --- a/src/simple.rs +++ b/src/simple.rs @@ -11,7 +11,7 @@ impl SimpleVault

{ /// # use libwallet::{SimpleVault, Vault, Result, sr25519}; /// # #[async_std::main] async fn main() -> Result<()> { /// let vault = SimpleVault::::new(); - /// assert!(vault.unlock("").await.is_ok()); + /// assert!(vault.unlock(()).await.is_ok()); /// # Ok(()) } /// ``` #[cfg(feature = "std")]