Add handling for fallible signers (#8367) (#8374)

automerge
This commit is contained in:
mergify[bot]
2020-02-21 01:45:13 -08:00
committed by GitHub
parent c2b17c7d3f
commit c2be9fdf0e
11 changed files with 188 additions and 86 deletions

View File

@@ -1,9 +1,11 @@
//! A library for generating a message from a sequence of instructions
use crate::hash::Hash;
use crate::instruction::{AccountMeta, CompiledInstruction, Instruction};
use crate::pubkey::Pubkey;
use crate::short_vec;
use crate::{
hash::Hash,
instruction::{AccountMeta, CompiledInstruction, Instruction},
pubkey::Pubkey,
short_vec, system_instruction,
};
use itertools::Itertools;
fn position(keys: &[Pubkey], key: &Pubkey) -> u8 {
@@ -206,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()
}

View File

@@ -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 bs58;
use ed25519_dalek;
use generic_array::{typenum::U64, GenericArray};
@@ -135,11 +135,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 {
@@ -148,7 +148,7 @@ 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())
}
@@ -156,7 +156,7 @@ impl Signer for Keypair {
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))
}
}
@@ -170,6 +170,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,
@@ -187,17 +222,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 {

View File

@@ -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 {
@@ -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)
}
);
}
@@ -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())
}
}

View File

@@ -6,7 +6,7 @@ use crate::{
message::Message,
pubkey::Pubkey,
short_vec,
signature::Signature,
signature::{Signature, SignerError},
signers::Signers,
system_instruction,
};
@@ -214,23 +214,18 @@ impl Transaction {
/// Check keys and keypair lengths, then sign this transaction.
pub fn sign<T: Signers>(&mut self, keypairs: &T, recent_blockhash: Hash) {
self.partial_sign(keypairs, recent_blockhash);
assert_eq!(self.is_signed(), true, "not enough keypairs");
if let Err(e) = self.try_sign(keypairs, recent_blockhash) {
panic!("Transaction::sign failed with error {:?}", e);
}
}
/// Sign using some subset of required keys
/// if recent_blockhash is not the same as currently in the transaction,
/// clear any prior signatures and update recent_blockhash
pub fn partial_sign<T: Signers>(&mut self, keypairs: &T, recent_blockhash: Hash) {
let positions = self
.get_signing_keypair_positions(&keypairs.pubkeys())
.expect("account_keys doesn't contain num_required_signatures keys");
let positions: Vec<usize> = positions
.iter()
.map(|pos| pos.expect("keypair-pubkey mismatch"))
.collect();
self.partial_sign_unchecked(keypairs, positions, recent_blockhash)
if let Err(e) = self.try_partial_sign(keypairs, recent_blockhash) {
panic!("Transaction::partial_sign failed with error {:?}", e);
}
}
/// Sign the transaction and place the signatures in their associated positions in `signatures`
@@ -241,6 +236,55 @@ impl Transaction {
positions: Vec<usize>,
recent_blockhash: Hash,
) {
if let Err(e) = self.try_partial_sign_unchecked(keypairs, positions, recent_blockhash) {
panic!(
"Transaction::partial_sign_unchecked failed with error {:?}",
e
);
}
}
/// Check keys and keypair lengths, then sign this transaction, returning any signing errors
/// encountered
pub fn try_sign<T: Signers>(
&mut self,
keypairs: &T,
recent_blockhash: Hash,
) -> result::Result<(), SignerError> {
self.try_partial_sign(keypairs, recent_blockhash)?;
if !self.is_signed() {
Err(SignerError::NotEnoughSigners)
} else {
Ok(())
}
}
/// Sign using some subset of required keys, returning any signing errors encountered. If
/// recent_blockhash is not the same as currently in the transaction, clear any prior
/// signatures and update recent_blockhash
pub fn try_partial_sign<T: Signers>(
&mut self,
keypairs: &T,
recent_blockhash: Hash,
) -> result::Result<(), SignerError> {
let positions = self.get_signing_keypair_positions(&keypairs.pubkeys())?;
if positions.iter().any(|pos| pos.is_none()) {
return Err(SignerError::KeypairPubkeyMismatch);
}
let positions: Vec<usize> = positions.iter().map(|pos| pos.unwrap()).collect();
self.try_partial_sign_unchecked(keypairs, positions, recent_blockhash)
}
/// Sign the transaction, returning any signing errors encountered, and place the
/// signatures in their associated positions in `signatures` without checking that the
/// positions are correct.
pub fn try_partial_sign_unchecked<T: Signers>(
&mut self,
keypairs: &T,
positions: Vec<usize>,
recent_blockhash: Hash,
) -> result::Result<(), SignerError> {
// if you change the blockhash, you're re-signing...
if recent_blockhash != self.message.recent_blockhash {
self.message.recent_blockhash = recent_blockhash;
@@ -249,10 +293,11 @@ impl Transaction {
.for_each(|signature| *signature = Signature::default());
}
let signatures = keypairs.sign_message(&self.message_data());
let signatures = keypairs.try_sign_message(&self.message_data())?;
for i in 0..positions.len() {
self.signatures[positions[i]] = signatures[i];
}
Ok(())
}
/// Verify the transaction