Skip to content

Commit

Permalink
Add optional support for locking the signer.
Browse files Browse the repository at this point in the history
  • Loading branch information
Neopallium committed Jun 20, 2024
1 parent a3c0243 commit 59f2416
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 18 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ members = [
# Our crates
polymesh-api-codegen-macro = { version = "3.4.0", path = "crates/polymesh-api-codegen-macro", default-features = false }
polymesh-api-codegen = { version = "3.4.0", path = "crates/polymesh-api-codegen", default-features = false }
polymesh-api-client = { version = "3.5.1", path = "crates/polymesh-api-client", default-features = false }
polymesh-api-client = { version = "3.6.0", path = "crates/polymesh-api-client", default-features = false }
polymesh-api-client-extras = { version = "3.3.0", path = "crates/polymesh-api-client-extras", default-features = false }
polymesh-api-ink = { version = "1.3.0", path = "crates/polymesh-api-ink", default-features = false }
polymesh-api = { version = "3.7.0", path = "./", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion crates/polymesh-api-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "polymesh-api-client"
version = "3.5.1"
version = "3.6.0"
edition = "2021"
authors = ["Robert G. Jakabosky <[email protected]>"]
license = "Apache-2.0"
Expand Down
7 changes: 5 additions & 2 deletions crates/polymesh-api-client/src/basic_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,8 +623,11 @@ pub type GenericAddress = MultiAddress<AccountId, u32>;
#[cfg_attr(all(feature = "std", feature = "type_info"), derive(TypeInfo))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct IdentityId(
#[cfg_attr(feature = "utoipa", schema(example = "0x0600000000000000000000000000000000000000000000000000000000000000"))]
pub [u8; 32]
#[cfg_attr(
feature = "utoipa",
schema(example = "0x0600000000000000000000000000000000000000000000000000000000000000")
)]
pub [u8; 32],
);

impl fmt::Display for IdentityId {
Expand Down
39 changes: 39 additions & 0 deletions crates/polymesh-api-client/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use sp_core::Pair;
use sp_runtime::MultiSignature;
use sp_std::prelude::*;

use tokio::sync::{Mutex, MutexGuard};

use async_trait::async_trait;

#[cfg(not(feature = "std"))]
Expand Down Expand Up @@ -76,6 +78,39 @@ pub mod dev {
}
}

pub struct LockableSigner(Mutex<Box<dyn Signer>>);

impl LockableSigner {
pub fn new<S: Signer + 'static>(signer: S) -> Self {
Self(Mutex::new(Box::new(signer)))
}

pub async fn lock(&self) -> LockedSigner<'_> {
LockedSigner(self.0.lock().await)
}
}

pub struct LockedSigner<'a>(MutexGuard<'a, Box<dyn Signer>>);

#[async_trait]
impl<'a> Signer for LockedSigner<'a> {
fn account(&self) -> AccountId {
self.0.account()
}

async fn nonce(&self) -> Option<u32> {
self.0.nonce().await
}

async fn set_nonce(&mut self, nonce: u32) {
self.0.set_nonce(nonce).await
}

async fn sign(&self, msg: &[u8]) -> Result<MultiSignature> {
self.0.sign(msg).await
}
}

#[async_trait]
pub trait Signer: Send + Sync {
fn account(&self) -> AccountId;
Expand All @@ -92,6 +127,10 @@ pub trait Signer: Send + Sync {
async fn set_nonce(&mut self, _nonce: u32) {}

async fn sign(&self, msg: &[u8]) -> Result<MultiSignature>;

async fn lock(&self) -> Option<LockedSigner<'_>> {
None
}
}

pub trait KeypairSigner: Send + Sync + Sized + Clone {
Expand Down
11 changes: 11 additions & 0 deletions crates/polymesh-api-client/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,17 @@ impl<Api: ChainApi> Call<Api> {
pub async fn submit_and_watch(
&self,
signer: &mut impl Signer,
) -> Result<TransactionResults<Api>> {
// First try using a locked signer.
if let Some(mut signer) = signer.lock().await {
return self.submit_and_watch_inner(&mut signer).await;
}
self.submit_and_watch_inner(signer).await
}

async fn submit_and_watch_inner(
&self,
signer: &mut impl Signer,
) -> Result<TransactionResults<Api>> {
let client = self.api.client();
let account = signer.account();
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/polymesh-api-tester/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "polymesh-api-tester"
version = "0.4.4"
version = "0.5.0"
edition = "2021"
authors = ["Robert G. Jakabosky <[email protected]>"]
license = "Apache-2.0"
Expand Down
65 changes: 54 additions & 11 deletions crates/polymesh-api-tester/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,64 @@ use std::sync::Arc;
use sp_keyring::{ed25519, sr25519};
use sp_runtime::MultiSignature;

use polymesh_api::client::{AccountId, KeypairSigner, PairSigner, Signer};
use polymesh_api::client::{
AccountId, KeypairSigner, LockableSigner, LockedSigner, PairSigner, Signer,
};

use crate::error::Result;
use crate::Db;

struct AccountSignerInner {
signer: Box<dyn Signer + Send + Sync>,
db: Option<Db>,
}

#[async_trait::async_trait]
impl Signer for AccountSignerInner {
fn account(&self) -> AccountId {
self.signer.account()
}

async fn nonce(&self) -> Option<u32> {
match &self.db {
Some(db) => db.get_nonce(self.account()).await.ok(),
None => None,
}
}

async fn set_nonce(&mut self, nonce: u32) {
match &self.db {
Some(db) => {
if let Err(err) = db.set_nonce(self.account(), nonce).await {
log::error!("Failed to update account nonce in DB: {err:?}");
}
}
None => (),
}
}

async fn sign(&self, msg: &[u8]) -> polymesh_api::client::Result<MultiSignature> {
Ok(self.signer.sign(msg).await?)
}
}

/// AccountSigner is wrapper for signing keys (sr25519, ed25519, etc...).
#[derive(Clone)]
pub struct AccountSigner {
signer: Arc<dyn Signer + Send + Sync>,
signer: Arc<LockableSigner>,
account: AccountId,
db: Option<Db>,
}

impl AccountSigner {
pub fn new<P: KeypairSigner + 'static>(db: Option<Db>, pair: P) -> Self {
let signer = PairSigner::new(pair);
let account = signer.account();
Self {
signer: Arc::new(signer),
signer: Arc::new(LockableSigner::new(AccountSignerInner {
signer: Box::new(signer),
db: db.clone(),
})),
account,
db,
}
}

Expand Down Expand Up @@ -63,15 +100,21 @@ impl Signer for AccountSigner {
}

async fn nonce(&self) -> Option<u32> {
match &self.db {
Some(db) => db.get_nonce(self.account).await.ok(),
None => None,
}
let inner = self.signer.lock().await;
inner.nonce().await
}

async fn set_nonce(&mut self, _nonce: u32) {}
async fn set_nonce(&mut self, nonce: u32) {
let mut inner = self.signer.lock().await;
inner.set_nonce(nonce).await
}

async fn sign(&self, msg: &[u8]) -> polymesh_api::client::Result<MultiSignature> {
Ok(self.signer.sign(msg).await?)
let inner = self.signer.lock().await;
Ok(inner.sign(msg).await?)
}

async fn lock(&self) -> Option<LockedSigner<'_>> {
Some(self.signer.lock().await)
}
}
17 changes: 17 additions & 0 deletions crates/polymesh-api-tester/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,21 @@ impl Db {

Ok(rec.nonce as u32)
}

pub async fn set_nonce(&self, account: AccountId, nonce: u32) -> Result<bool> {
let id = account.to_string();
// Save the nonce to the database.
let rows = sqlx::query!(
r#"
UPDATE accounts SET nonce = ? WHERE account = ?
"#,
nonce,
id
)
.execute(&self.pool)
.await?
.rows_affected();

Ok(rows > 0)
}
}

0 comments on commit 59f2416

Please sign in to comment.