add Transaction::partial_sign() (#4333)

* add partial sign

* nits
This commit is contained in:
Rob Walker 2019-05-17 18:55:57 -07:00 committed by GitHub
parent 18c6729d6c
commit 431cc82032
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 5 deletions

View File

@ -30,7 +30,7 @@ declare print_free_tree=(
'programs/stake_program/src'
)
if _ git grep -n --max-depth=0 "${prints[@]/#/-e }" -- "${print_free_tree[@]}"; then
if _ git --no-pager grep -n --max-depth=0 "${prints[@]/#/-e }" -- "${print_free_tree[@]}"; then
exit 1
fi
@ -39,14 +39,14 @@ fi
# Default::default()
#
# Ref: https://github.com/solana-labs/solana/issues/2630
if _ git grep -n 'Default::default()' -- '*.rs'; then
if _ git --no-pager grep -n 'Default::default()' -- '*.rs'; then
exit 1
fi
# Let's keep a .gitignore for every crate, ensure it's got
# /target/ in it
declare gitignores_ok=true
for i in $(git ls-files \*/Cargo.toml ); do
for i in $(git --no-pager ls-files \*/Cargo.toml ); do
dir=$(dirname "$i")
if [[ ! -f $dir/.gitignore ]]; then
echo 'error: nits.sh .gitnore missing for crate '"$dir" >&2

View File

@ -67,7 +67,7 @@ pub struct Transaction {
impl Transaction {
pub fn new_unsigned(message: Message) -> Self {
Self {
signatures: Vec::with_capacity(message.num_required_signatures as usize),
signatures: vec![Signature::default(); message.num_required_signatures as usize],
message,
}
}
@ -180,10 +180,40 @@ impl Transaction {
assert_eq!(keypair.pubkey(), signed_keys[i], "keypair-pubkey mismatch");
}
assert_eq!(keypairs.len(), signed_keys.len(), "not enough keypairs");
self.sign_unchecked(keypairs, recent_blockhash);
}
/// 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: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) {
let signed_keys =
&self.message.account_keys[0..self.message.num_required_signatures as usize];
// if you change the blockhash, you're re-signing...
if recent_blockhash != self.message.recent_blockhash {
self.message.recent_blockhash = recent_blockhash;
self.signatures
.iter_mut()
.for_each(|signature| *signature = Signature::default());
}
for keypair in keypairs {
let i = signed_keys
.iter()
.position(|pubkey| pubkey == &keypair.pubkey())
.expect("keypair-pubkey mismatch");
self.signatures[i] = keypair.sign_message(&self.message_data())
}
}
pub fn is_signed(&self) -> bool {
self.signatures
.iter()
.all(|signature| *signature != Signature::default())
}
/// Verify that references in the instructions are valid
pub fn verify_refs(&self) -> bool {
let message = self.message();
@ -204,6 +234,7 @@ impl Transaction {
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::hash;
use crate::instruction::AccountMeta;
use crate::signature::Keypair;
use crate::system_instruction;
@ -397,6 +428,45 @@ mod tests {
Transaction::new_unsigned_instructions(vec![]).sign(&[&keypair], Hash::default());
}
#[test]
#[should_panic]
fn test_partial_sign_mismatched_key() {
let keypair = Keypair::new();
Transaction::new_unsigned_instructions(vec![Instruction::new(
Pubkey::default(),
&0,
vec![AccountMeta::new(Pubkey::new_rand(), true)],
)])
.partial_sign(&[&keypair], Hash::default());
}
#[test]
fn test_partial_sign() {
let keypair0 = Keypair::new();
let keypair1 = Keypair::new();
let keypair2 = Keypair::new();
let mut tx = Transaction::new_unsigned_instructions(vec![Instruction::new(
Pubkey::default(),
&0,
vec![
AccountMeta::new(keypair0.pubkey(), true),
AccountMeta::new(keypair1.pubkey(), true),
AccountMeta::new(keypair2.pubkey(), true),
],
)]);
tx.partial_sign(&[&keypair0, &keypair2], Hash::default());
assert!(!tx.is_signed());
tx.partial_sign(&[&keypair1], Hash::default());
assert!(tx.is_signed());
let hash = hash(&[1]);
tx.partial_sign(&[&keypair1], hash);
assert!(!tx.is_signed());
tx.partial_sign(&[&keypair0, &keypair2], hash);
assert!(tx.is_signed());
}
#[test]
#[should_panic]
fn test_transaction_missing_keypair() {
@ -430,5 +500,6 @@ mod tests {
tx.message.instructions[0],
CompiledInstruction::new(0, &0, vec![0])
);
assert!(tx.is_signed());
}
}