Skip to content

Commit

Permalink
blockchain: add Verifier trait
Browse files Browse the repository at this point in the history
Add an Verifier trait rather than a verify method on a Signature.  The
purpose is to allow additional context to be provided when checking
signatures.  This is needed in Solana where signature checking
requires access to per-request data (specifically instructions native
program which is passed with the rest of accounts).
  • Loading branch information
mina86 committed Nov 7, 2023
1 parent 4f00326 commit 1fbc541
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 36 deletions.
22 changes: 12 additions & 10 deletions common/blockchain/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,19 @@ impl<PK: crate::PubKey> Block<PK> {


/// Signs the block.
pub fn sign<S>(&self, signer: &S) -> PK::Signature
where
S: crate::validators::Signer<Signature = PK::Signature>,
{
pub fn sign(&self, signer: &impl crate::Signer<PK>) -> PK::Signature {
signer.sign(self.calc_hash().as_slice())
}

/// Verifies signature for the block.
#[inline]
pub fn verify(&self, pk: &PK, signature: &PK::Signature) -> bool {
pk.verify(self.calc_hash().as_slice(), signature)
pub fn verify(
&self,
pubkey: &PK,
signature: &PK::Signature,
verifier: &impl crate::Verifier<PK>,
) -> bool {
verifier.verify(self.calc_hash().as_slice(), pubkey, signature)
}

/// Constructs next block.
Expand Down Expand Up @@ -179,14 +181,14 @@ fn test_block_generation() {
let signer = MockSigner(pk);
let signature = genesis.sign(&signer);
assert_eq!(MockSignature(1722674425, pk), signature);
assert!(genesis.verify(&pk, &signature));
assert!(!genesis.verify(&MockPubKey(88), &signature));
assert!(!genesis.verify(&pk, &MockSignature(0, pk)));
assert!(genesis.verify(&pk, &signature, &()));
assert!(!genesis.verify(&MockPubKey(88), &signature, &()));
assert!(!genesis.verify(&pk, &MockSignature(0, pk), &()));

let mut block = genesis.clone();
block.host_timestamp += 1;
assert_ne!(genesis_hash, block.calc_hash());
assert!(!block.verify(&pk, &signature));
assert!(!block.verify(&pk, &signature, &()));

// Try creating invalid next block.
assert_eq!(
Expand Down
2 changes: 1 addition & 1 deletion common/blockchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ pub use config::Config;
pub use epoch::Epoch;
pub use height::{BlockDelta, BlockHeight, HostDelta, HostHeight};
pub use manager::ChainManager;
pub use validators::{PubKey, Signer, Validator};
pub use validators::{PubKey, Signer, Validator, Verifier};
25 changes: 13 additions & 12 deletions common/blockchain/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,26 +199,27 @@ impl<PK: crate::PubKey> ChainManager<PK> {
&mut self,
pubkey: PK,
signature: &PK::Signature,
verifier: &impl crate::Verifier<PK>,
) -> Result<bool, AddSignatureError> {
let pending = self
.pending_block
.as_mut()
.ok_or(AddSignatureError::NoPendingBlock)?;
if pending.signers.contains(&pubkey) {
return Ok(false);
}
if !pubkey.verify(pending.hash.as_slice(), signature) {
return Err(AddSignatureError::BadSignature);
}

pending.signing_stake += self
let validator_stake = self
.next_epoch
.validator(&pubkey)
.ok_or(AddSignatureError::BadValidator)?
.stake()
.get();
assert!(pending.signers.insert(pubkey));
if !verifier.verify(pending.hash.as_slice(), &pubkey, signature) {
return Err(AddSignatureError::BadSignature);
}

if !pending.signers.insert(pubkey) {
return Ok(false);
}

pending.signing_stake += validator_stake;
if pending.signing_stake < self.next_epoch.quorum_stake().get() {
return Ok(false);
}
Expand Down Expand Up @@ -304,7 +305,7 @@ fn test_generate() {
validator: &crate::validators::Validator<MockPubKey>,
) -> Result<bool, AddSignatureError> {
let signature = mgr.head().1.sign(&validator.pubkey().make_signer());
mgr.add_signature(validator.pubkey().clone(), &signature)
mgr.add_signature(validator.pubkey().clone(), &signature, &())
}

// The head hasn’t been fully signed yet.
Expand All @@ -329,11 +330,11 @@ fn test_generate() {
let signature = mgr.head().1.sign(&pubkey.make_signer());
assert_eq!(
Err(AddSignatureError::BadValidator),
mgr.add_signature(pubkey, &signature)
mgr.add_signature(pubkey, &signature, &())
);
assert_eq!(
Err(AddSignatureError::BadSignature),
mgr.add_signature(bob.pubkey().clone(), &signature)
mgr.add_signature(bob.pubkey().clone(), &signature, &())
);

assert_eq!(
Expand Down
40 changes: 27 additions & 13 deletions common/blockchain/src/validators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,23 @@ pub trait PubKey:
{
/// Signature corresponding to this public key type.
type Signature: Clone + borsh::BorshSerialize + borsh::BorshDeserialize;

/// Verifies the signature for given message.
fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool;
}

pub trait Signer {
/// Signature created by this signer.
type Signature: Clone + borsh::BorshSerialize + borsh::BorshDeserialize;
/// Function verifying a signature.
pub trait Verifier<PK: PubKey> {
/// Verify signature for given message.
fn verify(
&self,
message: &[u8],
pubkey: &PK,
signature: &PK::Signature,
) -> bool;
}

/// Function generating signatures.
pub trait Signer<PK: PubKey> {
/// Signs given message.
fn sign(&self, message: &[u8]) -> Self::Signature;
fn sign(&self, message: &[u8]) -> PK::Signature;
}

/// A validator
Expand Down Expand Up @@ -115,16 +121,24 @@ pub(crate) mod test_utils {

impl super::PubKey for MockPubKey {
type Signature = MockSignature;
}

fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool {
signature.0 == short_hash(message) && &signature.1 == self
impl super::Verifier<MockPubKey> for () {
fn verify(
&self,
message: &[u8],
pubkey: &MockPubKey,
signature: &<MockPubKey as super::PubKey>::Signature,
) -> bool {
signature.0 == short_hash(message) && &signature.1 == pubkey
}
}

impl super::Signer for MockSigner {
type Signature = MockSignature;

fn sign(&self, message: &[u8]) -> Self::Signature {
impl super::Signer<MockPubKey> for MockSigner {
fn sign(
&self,
message: &[u8],
) -> <MockPubKey as super::PubKey>::Signature {
MockSignature(short_hash(message), self.0)
}
}
Expand Down

0 comments on commit 1fbc541

Please sign in to comment.