Skip to content

Commit

Permalink
tx: move signing related code into sign mod
Browse files Browse the repository at this point in the history
  • Loading branch information
tzemanovic committed Sep 20, 2024
1 parent aa68b68 commit bbf2d34
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 153 deletions.
13 changes: 8 additions & 5 deletions crates/tx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ use data::TxType;
pub use either;
pub use event::new_tx_event;
pub use namada_core::key::SignableEthMessage;
pub use sign::SignatureIndex;
pub use sign::{
standalone_signature, verify_standalone_sig, SignatureIndex, Signed,
VerifySigError,
};
pub use types::{
standalone_signature, verify_standalone_sig, Authorization, BatchedTx,
BatchedTxRef, Code, Commitment, CompressedAuthorization, Data, DecodeError,
Header, IndexedTx, IndexedTxRange, MaspBuilder, Memo, Section, Signed,
Signer, Tx, TxCommitments, TxError, VerifySigError,
Authorization, BatchedTx, BatchedTxRef, Code, Commitment,
CompressedAuthorization, Data, DecodeError, Header, IndexedTx,
IndexedTxRange, MaspBuilder, Memo, Section, Signer, Tx, TxCommitments,
TxError,
};

/// Length of the transaction sections salt
Expand Down
150 changes: 148 additions & 2 deletions crates/tx/src/sign.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,161 @@
//! Types for signing

use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
use std::io;
use std::marker::PhantomData;

use borsh::schema::{self, Declaration, Definition};
use namada_core::address::Address;
use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use namada_core::key::common;
use namada_core::key::{common, SerializeWithBorsh, SigScheme, Signable};
use namada_macros::BorshDeserializer;
#[cfg(feature = "migrations")]
use namada_migrations::*;
use serde::{Deserialize, Serialize};
use thiserror::Error;

/// Represents an error in signature verification
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum VerifySigError {
#[error("{0}")]
VerifySig(#[from] namada_core::key::VerifySigError),
#[error("{0}")]
Gas(#[from] namada_gas::Error),
#[error("The wrapper signature is invalid.")]
InvalidWrapperSignature,
#[error("The section signature is invalid: {0}")]
InvalidSectionSignature(String),
#[error("The number of PKs overflows u8::MAX")]
PksOverflow,
#[error("An expected signature is missing.")]
MissingSignature,
}

/// A generic signed data wrapper for serialize-able types.
///
/// The default serialization method is [`BorshSerialize`].
#[derive(
Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize,
)]
pub struct Signed<T, S = SerializeWithBorsh> {
/// Arbitrary data to be signed
pub data: T,
/// The signature of the data
pub sig: common::Signature,
/// The method to serialize the data with,
/// before it being signed
_serialization: PhantomData<S>,
}

impl<S, T: Eq> Eq for Signed<T, S> {}

impl<S, T: PartialEq> PartialEq for Signed<T, S> {
fn eq(&self, other: &Self) -> bool {
self.data == other.data && self.sig == other.sig
}
}

impl<S, T: Hash> Hash for Signed<T, S> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.data.hash(state);
self.sig.hash(state);
}
}

impl<S, T: PartialOrd> PartialOrd for Signed<T, S> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.data.partial_cmp(&other.data)
}
}
impl<S, T: Ord> Ord for Signed<T, S> {
fn cmp(&self, other: &Self) -> Ordering {
self.data.cmp(&other.data)
}
}

impl<S, T: BorshSchema> BorshSchema for Signed<T, S> {
fn add_definitions_recursively(
definitions: &mut BTreeMap<Declaration, Definition>,
) {
let fields = schema::Fields::NamedFields(vec![
("data".to_string(), T::declaration()),
("sig".to_string(), <common::Signature>::declaration()),
]);
let definition = schema::Definition::Struct { fields };
schema::add_definition(Self::declaration(), definition, definitions);
T::add_definitions_recursively(definitions);
<common::Signature>::add_definitions_recursively(definitions);
}

fn declaration() -> schema::Declaration {
format!("Signed<{}>", T::declaration())
}
}

impl<T, S> Signed<T, S> {
/// Initialize a new [`Signed`] instance from an existing signature.
#[inline]
pub fn new_from(data: T, sig: common::Signature) -> Self {
Self {
data,
sig,
_serialization: PhantomData,
}
}
}

impl<T, S: Signable<T>> Signed<T, S> {
/// Initialize a new [`Signed`] instance.
pub fn new(keypair: &common::SecretKey, data: T) -> Self {
let to_sign = S::as_signable(&data);
let sig =
common::SigScheme::sign_with_hasher::<S::Hasher>(keypair, to_sign);
Self::new_from(data, sig)
}

/// Verify that the data has been signed by the secret key
/// counterpart of the given public key.
pub fn verify(
&self,
pk: &common::PublicKey,
) -> std::result::Result<(), VerifySigError> {
let signed_bytes = S::as_signable(&self.data);
common::SigScheme::verify_signature_with_hasher::<S::Hasher>(
pk,
&signed_bytes,
&self.sig,
)
.map_err(Into::into)
}
}

/// Get a signature for data
pub fn standalone_signature<T, S: Signable<T>>(
keypair: &common::SecretKey,
data: &T,
) -> common::Signature {
let to_sign = S::as_signable(data);
common::SigScheme::sign_with_hasher::<S::Hasher>(keypair, to_sign)
}

/// Verify that the input data has been signed by the secret key
/// counterpart of the given public key.
pub fn verify_standalone_sig<T, S: Signable<T>>(
data: &T,
pk: &common::PublicKey,
sig: &common::Signature,
) -> std::result::Result<(), VerifySigError> {
let signed_data = S::as_signable(data);
common::SigScheme::verify_signature_with_hasher::<S::Hasher>(
pk,
&signed_data,
sig,
)
.map_err(Into::into)
}

#[derive(
Clone,
Expand Down Expand Up @@ -91,7 +237,7 @@ mod test {
let sk = key::testing::keypair_1();
let pubkey = sk.to_public();
let data = [0_u8];
let signature = key::common::SigScheme::sign(&sk, &data);
let signature = key::common::SigScheme::sign(&sk, data);
let sig_index = SignatureIndex {
pubkey,
index: Some((address::testing::established_address_1(), 1)),
Expand Down
148 changes: 2 additions & 146 deletions crates/tx/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::hash::Hash;
use std::ops::{Bound, RangeBounds};

use data_encoding::HEXUPPER;
Expand All @@ -12,7 +10,6 @@ use masp_primitives::transaction::Transaction;
use masp_primitives::zip32::ExtendedFullViewingKey;
use namada_account::AccountPublicKeysMap;
use namada_core::address::Address;
use namada_core::borsh::schema::{add_definition, Declaration, Definition};
use namada_core::borsh::{
self, BorshDeserialize, BorshSchema, BorshSerialize, BorshSerializeExt,
};
Expand All @@ -32,27 +29,9 @@ use thiserror::Error;

use crate::data::protocol::ProtocolTx;
use crate::data::{hash_tx, Fee, GasLimit, TxType, WrapperTx};
use crate::sign::SignatureIndex;
use crate::sign::{SignatureIndex, VerifySigError};
use crate::{hex_data_serde, hex_salt_serde, proto, SALT_LENGTH};

/// Represents an error in signature verification
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum VerifySigError {
#[error("{0}")]
VerifySig(#[from] namada_core::key::VerifySigError),
#[error("{0}")]
Gas(#[from] namada_gas::Error),
#[error("The wrapper signature is invalid.")]
InvalidWrapperSignature,
#[error("The section signature is invalid: {0}")]
InvalidSectionSignature(String),
#[error("The number of PKs overflows u8::MAX")]
PksOverflow,
#[error("An expected signature is missing.")]
MissingSignature,
}

#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum DecodeError {
Expand All @@ -72,129 +51,6 @@ pub enum DecodeError {
InvalidJSONDeserialization(String),
}

/// A generic signed data wrapper for serialize-able types.
///
/// The default serialization method is [`BorshSerialize`].
#[derive(
Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize,
)]
pub struct Signed<T, S = SerializeWithBorsh> {
/// Arbitrary data to be signed
pub data: T,
/// The signature of the data
pub sig: common::Signature,
/// The method to serialize the data with,
/// before it being signed
_serialization: PhantomData<S>,
}

impl<S, T: Eq> Eq for Signed<T, S> {}

impl<S, T: PartialEq> PartialEq for Signed<T, S> {
fn eq(&self, other: &Self) -> bool {
self.data == other.data && self.sig == other.sig
}
}

impl<S, T: Hash> Hash for Signed<T, S> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.data.hash(state);
self.sig.hash(state);
}
}

impl<S, T: PartialOrd> PartialOrd for Signed<T, S> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.data.partial_cmp(&other.data)
}
}
impl<S, T: Ord> Ord for Signed<T, S> {
fn cmp(&self, other: &Self) -> Ordering {
self.data.cmp(&other.data)
}
}

impl<S, T: BorshSchema> BorshSchema for Signed<T, S> {
fn add_definitions_recursively(
definitions: &mut BTreeMap<Declaration, Definition>,
) {
let fields = borsh::schema::Fields::NamedFields(vec![
("data".to_string(), T::declaration()),
("sig".to_string(), <common::Signature>::declaration()),
]);
let definition = borsh::schema::Definition::Struct { fields };
add_definition(Self::declaration(), definition, definitions);
T::add_definitions_recursively(definitions);
<common::Signature>::add_definitions_recursively(definitions);
}

fn declaration() -> borsh::schema::Declaration {
format!("Signed<{}>", T::declaration())
}
}

impl<T, S> Signed<T, S> {
/// Initialize a new [`Signed`] instance from an existing signature.
#[inline]
pub fn new_from(data: T, sig: common::Signature) -> Self {
Self {
data,
sig,
_serialization: PhantomData,
}
}
}

impl<T, S: Signable<T>> Signed<T, S> {
/// Initialize a new [`Signed`] instance.
pub fn new(keypair: &common::SecretKey, data: T) -> Self {
let to_sign = S::as_signable(&data);
let sig =
common::SigScheme::sign_with_hasher::<S::Hasher>(keypair, to_sign);
Self::new_from(data, sig)
}

/// Verify that the data has been signed by the secret key
/// counterpart of the given public key.
pub fn verify(
&self,
pk: &common::PublicKey,
) -> std::result::Result<(), VerifySigError> {
let signed_bytes = S::as_signable(&self.data);
common::SigScheme::verify_signature_with_hasher::<S::Hasher>(
pk,
&signed_bytes,
&self.sig,
)
.map_err(Into::into)
}
}

/// Get a signature for data
pub fn standalone_signature<T, S: Signable<T>>(
keypair: &common::SecretKey,
data: &T,
) -> common::Signature {
let to_sign = S::as_signable(data);
common::SigScheme::sign_with_hasher::<S::Hasher>(keypair, to_sign)
}

/// Verify that the input data has been signed by the secret key
/// counterpart of the given public key.
pub fn verify_standalone_sig<T, S: Signable<T>>(
data: &T,
pk: &common::PublicKey,
sig: &common::Signature,
) -> std::result::Result<(), VerifySigError> {
let signed_data = S::as_signable(data);
common::SigScheme::verify_signature_with_hasher::<S::Hasher>(
pk,
&signed_data,
sig,
)
.map_err(Into::into)
}

/// A section representing transaction data
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(
Expand Down

0 comments on commit bbf2d34

Please sign in to comment.