Skip to content

Commit

Permalink
Add handling for fallible signers (solana-labs#8367)
Browse files Browse the repository at this point in the history
automerge
  • Loading branch information
CriesofCarrots authored Feb 21, 2020
1 parent 18fd523 commit 0b7e8d0
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 83 deletions.
6 changes: 3 additions & 3 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use solana_sdk::{
native_token::lamports_to_sol,
program_utils::DecodeError,
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, Signature, Signer},
signature::{keypair_from_seed, Keypair, Signature, Signer, SignerError},
system_instruction::{self, create_address_with_seed, SystemError, MAX_ADDRESS_SEED_LEN},
system_transaction,
transaction::{Transaction, TransactionError},
Expand Down Expand Up @@ -1980,15 +1980,15 @@ impl Signer for FaucetKeypair {
self.transaction.message().account_keys[0]
}

fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>> {
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
Ok(self.pubkey())
}

fn sign_message(&self, _msg: &[u8]) -> Signature {
self.transaction.signatures[0]
}

fn try_sign_message(&self, message: &[u8]) -> Result<Signature, Box<dyn error::Error>> {
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
Ok(self.sign_message(message))
}
}
Expand Down
48 changes: 9 additions & 39 deletions client/src/client_error.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,20 @@
use crate::rpc_request;
use solana_sdk::transaction::TransactionError;
use solana_sdk::{signature::SignerError, transaction::TransactionError};
use std::{fmt, io};
use thiserror::Error;

#[derive(Debug)]
#[derive(Error, Debug)]
pub enum ClientError {
Io(io::Error),
Reqwest(reqwest::Error),
RpcError(rpc_request::RpcError),
SerdeJson(serde_json::error::Error),
TransactionError(TransactionError),
Io(#[from] io::Error),
Reqwest(#[from] reqwest::Error),
RpcError(#[from] rpc_request::RpcError),
SerdeJson(#[from] serde_json::error::Error),
SigningError(#[from] SignerError),
TransactionError(#[from] TransactionError),
}

impl fmt::Display for ClientError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "solana client error")
}
}

impl std::error::Error for ClientError {}

impl From<io::Error> for ClientError {
fn from(err: io::Error) -> ClientError {
ClientError::Io(err)
}
}

impl From<reqwest::Error> for ClientError {
fn from(err: reqwest::Error) -> ClientError {
ClientError::Reqwest(err)
}
}

impl From<rpc_request::RpcError> for ClientError {
fn from(err: rpc_request::RpcError) -> ClientError {
ClientError::RpcError(err)
}
}

impl From<serde_json::error::Error> for ClientError {
fn from(err: serde_json::error::Error) -> ClientError {
ClientError::SerdeJson(err)
}
}

impl From<TransactionError> for ClientError {
fn from(err: TransactionError) -> ClientError {
ClientError::TransactionError(err)
}
}
14 changes: 6 additions & 8 deletions client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,13 +517,11 @@ impl RpcClient {
// Re-sign any failed transactions with a new blockhash and retry
let (blockhash, _fee_calculator) =
self.get_new_blockhash(&transactions_signatures[0].0.message().recent_blockhash)?;
transactions = transactions_signatures
.into_iter()
.map(|(mut transaction, _)| {
transaction.sign(signer_keys, blockhash);
transaction
})
.collect();
transactions = vec![];
for (mut transaction, _) in transactions_signatures.into_iter() {
transaction.try_sign(signer_keys, blockhash)?;
transactions.push(transaction);
}
}
}

Expand All @@ -534,7 +532,7 @@ impl RpcClient {
) -> Result<(), ClientError> {
let (blockhash, _fee_calculator) =
self.get_new_blockhash(&tx.message().recent_blockhash)?;
tx.sign(signer_keys, blockhash);
tx.try_sign(signer_keys, blockhash)?;
Ok(())
}

Expand Down
7 changes: 3 additions & 4 deletions remote-wallet/src/remote_keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ use crate::{
};
use solana_sdk::{
pubkey::Pubkey,
signature::{Signature, Signer},
signature::{Signature, Signer, SignerError},
};
use std::error;

pub struct RemoteKeypair {
pub wallet_type: RemoteWalletType,
Expand All @@ -25,15 +24,15 @@ impl RemoteKeypair {
}

impl Signer for RemoteKeypair {
fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>> {
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
match &self.wallet_type {
RemoteWalletType::Ledger(wallet) => wallet
.get_pubkey(&self.derivation_path)
.map_err(|e| e.into()),
}
}

fn try_sign_message(&self, message: &[u8]) -> Result<Signature, Box<dyn error::Error>> {
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
match &self.wallet_type {
RemoteWalletType::Ledger(wallet) => wallet
.sign_message(&self.derivation_path, message)
Expand Down
22 changes: 21 additions & 1 deletion remote-wallet/src/remote_wallet.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use crate::ledger::{is_valid_ledger, LedgerWallet};
use log::*;
use parking_lot::{Mutex, RwLock};
use solana_sdk::{pubkey::Pubkey, signature::Signature};
use solana_sdk::{
pubkey::Pubkey,
signature::{Signature, SignerError},
};
use std::{
fmt,
str::FromStr,
Expand Down Expand Up @@ -47,6 +50,23 @@ pub enum RemoteWalletError {
UserCancel,
}

impl From<RemoteWalletError> for SignerError {
fn from(err: RemoteWalletError) -> SignerError {
match err {
RemoteWalletError::Hid(hid_error) => {
SignerError::ConnectionError(hid_error.to_string())
}
RemoteWalletError::DeviceTypeMismatch => SignerError::ConnectionError(err.to_string()),
RemoteWalletError::InvalidDevice => SignerError::ConnectionError(err.to_string()),
RemoteWalletError::InvalidInput(input) => SignerError::InvalidInput(input),
RemoteWalletError::NoDeviceFound => SignerError::NoDeviceFound,
RemoteWalletError::Protocol(e) => SignerError::Protocol(e.to_string()),
RemoteWalletError::UserCancel => SignerError::UserCancel,
_ => SignerError::CustomError(err.to_string()),
}
}
}

/// Collection of conntected RemoteWallets
pub struct RemoteWalletManager {
usb: Arc<Mutex<hidapi::HidApi>>,
Expand Down
16 changes: 15 additions & 1 deletion sdk/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
hash::Hash,
instruction::{AccountMeta, CompiledInstruction, Instruction},
pubkey::Pubkey,
short_vec,
short_vec, system_instruction,
};
use itertools::Itertools;

Expand Down Expand Up @@ -208,6 +208,20 @@ impl Message {
)
}

pub fn new_with_nonce(
mut instructions: Vec<Instruction>,
payer: Option<&Pubkey>,
nonce_account_pubkey: &Pubkey,
nonce_authority_pubkey: &Pubkey,
) -> Self {
let nonce_ix = system_instruction::advance_nonce_account(
&nonce_account_pubkey,
&nonce_authority_pubkey,
);
instructions.insert(0, nonce_ix);
Self::new_with_payer(instructions, payer)
}

pub fn serialize(&self) -> Vec<u8> {
bincode::serialize(self).unwrap()
}
Expand Down
51 changes: 43 additions & 8 deletions sdk/src/signature.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! The `signature` module provides functionality for public, and private keys.
use crate::pubkey::Pubkey;
use crate::{pubkey::Pubkey, transaction::TransactionError};
use generic_array::{typenum::U64, GenericArray};
use hmac::Hmac;
use rand::{rngs::OsRng, CryptoRng, RngCore};
Expand Down Expand Up @@ -132,11 +132,11 @@ pub trait Signer {
fn pubkey(&self) -> Pubkey {
self.try_pubkey().unwrap_or_default()
}
fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>>;
fn try_pubkey(&self) -> Result<Pubkey, SignerError>;
fn sign_message(&self, message: &[u8]) -> Signature {
self.try_sign_message(message).unwrap_or_default()
}
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, Box<dyn error::Error>>;
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError>;
}

impl Signer for Keypair {
Expand All @@ -145,15 +145,15 @@ impl Signer for Keypair {
Pubkey::new(self.0.public.as_ref())
}

fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>> {
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
Ok(self.pubkey())
}

fn sign_message(&self, message: &[u8]) -> Signature {
Signature::new(&self.0.sign(message).to_bytes())
}

fn try_sign_message(&self, message: &[u8]) -> Result<Signature, Box<dyn error::Error>> {
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
Ok(self.sign_message(message))
}
}
Expand All @@ -167,6 +167,41 @@ where
}
}

#[derive(Debug, Error, PartialEq)]
pub enum SignerError {
#[error("keypair-pubkey mismatch")]
KeypairPubkeyMismatch,

#[error("not enough signers")]
NotEnoughSigners,

#[error("transaction error")]
TransactionError(#[from] TransactionError),

#[error("custom error: {0}")]
CustomError(String),

// Presigner-specific Errors
#[error("presigner error")]
PresignerError(#[from] PresignerError),

// Remote Keypair-specific Errors
#[error("connection error: {0}")]
ConnectionError(String),

#[error("invalid input: {0}")]
InvalidInput(String),

#[error("no device found")]
NoDeviceFound,

#[error("device protocol error: {0}")]
Protocol(String),

#[error("operation has been cancelled")]
UserCancel,
}

#[derive(Debug, Default)]
pub struct Presigner {
pubkey: Pubkey,
Expand All @@ -184,17 +219,17 @@ impl Presigner {
}

#[derive(Debug, Error, PartialEq)]
enum PresignerError {
pub enum PresignerError {
#[error("pre-generated signature cannot verify data")]
VerificationFailure,
}

impl Signer for Presigner {
fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>> {
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
Ok(self.pubkey)
}

fn try_sign_message(&self, message: &[u8]) -> Result<Signature, Box<dyn error::Error>> {
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
if self.signature.verify(self.pubkey.as_ref(), message) {
Ok(self.signature)
} else {
Expand Down
29 changes: 23 additions & 6 deletions sdk/src/signers.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::{
pubkey::Pubkey,
signature::{Signature, Signer},
signature::{Signature, Signer, SignerError},
};

pub trait Signers {
fn pubkeys(&self) -> Vec<Pubkey>;
fn try_pubkeys(&self) -> Result<Vec<Pubkey>, SignerError>;
fn sign_message(&self, message: &[u8]) -> Vec<Signature>;
fn try_sign_message(&self, message: &[u8]) -> Result<Vec<Signature>, SignerError>;
}

macro_rules! default_keypairs_impl {
Expand All @@ -14,11 +16,27 @@ macro_rules! default_keypairs_impl {
self.iter().map(|keypair| keypair.pubkey()).collect()
}

fn try_pubkeys(&self) -> Result<Vec<Pubkey>, SignerError> {
let mut pubkeys = Vec::new();
for keypair in self.iter() {
pubkeys.push(keypair.try_pubkey()?);
}
Ok(pubkeys)
}

fn sign_message(&self, message: &[u8]) -> Vec<Signature> {
self.iter()
.map(|keypair| keypair.sign_message(message))
.collect()
}

fn try_sign_message(&self, message: &[u8]) -> Result<Vec<Signature>, SignerError> {
let mut signatures = Vec::new();
for keypair in self.iter() {
signatures.push(keypair.try_sign_message(message)?);
}
Ok(signatures)
}
);
}

Expand Down Expand Up @@ -57,24 +75,23 @@ impl<T: Signer> Signers for Vec<&T> {
#[cfg(test)]
mod tests {
use super::*;
use std::error;

struct Foo;
impl Signer for Foo {
fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>> {
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
Ok(Pubkey::default())
}
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, Box<dyn error::Error>> {
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
Ok(Signature::default())
}
}

struct Bar;
impl Signer for Bar {
fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>> {
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
Ok(Pubkey::default())
}
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, Box<dyn error::Error>> {
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
Ok(Signature::default())
}
}
Expand Down
Loading

0 comments on commit 0b7e8d0

Please sign in to comment.