1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
use core::fmt::Debug;
use std::io;

use zeroize::Zeroize;
use thiserror::Error;

use blake2::{Digest, Blake2b512};

use ciphersuite::{
  group::{Group, GroupEncoding},
  Ciphersuite, Ristretto,
};
use schnorr::SchnorrSignature;

use crate::{TRANSACTION_SIZE_LIMIT, ReadWrite};

#[derive(Clone, PartialEq, Eq, Debug, Error)]
pub enum TransactionError {
  /// Transaction exceeded the size limit.
  #[error("transaction is too large")]
  TooLargeTransaction,
  /// Transaction's signer isn't a participant.
  #[error("invalid signer")]
  InvalidSigner,
  /// Transaction's nonce isn't the prior nonce plus one.
  #[error("invalid nonce")]
  InvalidNonce,
  /// Transaction's signature is invalid.
  #[error("invalid signature")]
  InvalidSignature,
  /// Transaction's content is invalid.
  #[error("transaction content is invalid")]
  InvalidContent,
  /// Transaction's signer has too many transactions in the mempool.
  #[error("signer has too many transactions in the mempool")]
  TooManyInMempool,
  /// Provided Transaction added to mempool.
  #[error("provided transaction added to mempool")]
  ProvidedAddedToMempool,
}

/// Data for a signed transaction.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Signed {
  pub signer: <Ristretto as Ciphersuite>::G,
  pub nonce: u32,
  pub signature: SchnorrSignature<Ristretto>,
}

impl ReadWrite for Signed {
  fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
    let signer = Ristretto::read_G(reader)?;

    let mut nonce = [0; 4];
    reader.read_exact(&mut nonce)?;
    let nonce = u32::from_le_bytes(nonce);
    if nonce >= (u32::MAX - 1) {
      Err(io::Error::other("nonce exceeded limit"))?;
    }

    let mut signature = SchnorrSignature::<Ristretto>::read(reader)?;
    if signature.R.is_identity().into() {
      // Anyone malicious could remove this and try to find zero signatures
      // We should never produce zero signatures though meaning this should never come up
      // If it does somehow come up, this is a decent courtesy
      signature.zeroize();
      Err(io::Error::other("signature nonce was identity"))?;
    }

    Ok(Signed { signer, nonce, signature })
  }

  fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
    // This is either an invalid signature or a private key leak
    if self.signature.R.is_identity().into() {
      Err(io::Error::other("signature nonce was identity"))?;
    }
    writer.write_all(&self.signer.to_bytes())?;
    writer.write_all(&self.nonce.to_le_bytes())?;
    self.signature.write(writer)
  }
}

impl Signed {
  pub fn read_without_nonce<R: io::Read>(reader: &mut R, nonce: u32) -> io::Result<Self> {
    let signer = Ristretto::read_G(reader)?;

    let mut signature = SchnorrSignature::<Ristretto>::read(reader)?;
    if signature.R.is_identity().into() {
      // Anyone malicious could remove this and try to find zero signatures
      // We should never produce zero signatures though meaning this should never come up
      // If it does somehow come up, this is a decent courtesy
      signature.zeroize();
      Err(io::Error::other("signature nonce was identity"))?;
    }

    Ok(Signed { signer, nonce, signature })
  }

  pub fn write_without_nonce<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
    // This is either an invalid signature or a private key leak
    if self.signature.R.is_identity().into() {
      Err(io::Error::other("signature nonce was identity"))?;
    }
    writer.write_all(&self.signer.to_bytes())?;
    self.signature.write(writer)
  }
}

#[allow(clippy::large_enum_variant)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum TransactionKind<'a> {
  /// This transaction should be provided by every validator, in an exact order.
  ///
  /// The contained static string names the orderer to use. This allows two distinct provided
  /// transaction kinds, without a synchronized order, to be ordered within their own kind without
  /// requiring ordering with each other.
  ///
  /// The only malleability is in when this transaction appears on chain. The block producer will
  /// include it when they have it. Block verification will fail for validators without it.
  ///
  /// If a supermajority of validators produce a commit for a block with a provided transaction
  /// which isn't locally held, the block will be added to the local chain. When the transaction is
  /// locally provided, it will be compared for correctness to the on-chain version
  ///
  /// In order to ensure TXs aren't accidentally provided multiple times, all provided transactions
  /// must have a unique hash which is also unique to all Unsigned transactions.
  Provided(&'static str),

  /// An unsigned transaction, only able to be included by the block producer.
  ///
  /// Once an Unsigned transaction is included on-chain, it may not be included again. In order to
  /// have multiple Unsigned transactions with the same values included on-chain, some distinct
  /// nonce must be included in order to cause a distinct hash.
  ///
  /// The hash must also be unique with all Provided transactions.
  Unsigned,

  /// A signed transaction.
  Signed(Vec<u8>, &'a Signed),
}

// TODO: Should this be renamed TransactionTrait now that a literal Transaction exists?
// Or should the literal Transaction be renamed to Event?
pub trait Transaction: 'static + Send + Sync + Clone + Eq + Debug + ReadWrite {
  /// Return what type of transaction this is.
  fn kind(&self) -> TransactionKind<'_>;

  /// Return the hash of this transaction.
  ///
  /// The hash must NOT commit to the signature.
  fn hash(&self) -> [u8; 32];

  /// Perform transaction-specific verification.
  fn verify(&self) -> Result<(), TransactionError>;

  /// Obtain the challenge for this transaction's signature.
  ///
  /// Do not override this unless you know what you're doing.
  ///
  /// Panics if called on non-signed transactions.
  fn sig_hash(&self, genesis: [u8; 32]) -> <Ristretto as Ciphersuite>::F {
    match self.kind() {
      TransactionKind::Signed(order, Signed { signature, .. }) => {
        <Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide(
          &Blake2b512::digest(
            [
              b"Tributary Signed Transaction",
              genesis.as_ref(),
              &self.hash(),
              order.as_ref(),
              signature.R.to_bytes().as_ref(),
            ]
            .concat(),
          )
          .into(),
        )
      }
      _ => panic!("sig_hash called on non-signed transaction"),
    }
  }
}

pub trait GAIN: FnMut(&<Ristretto as Ciphersuite>::G, &[u8]) -> Option<u32> {}
impl<F: FnMut(&<Ristretto as Ciphersuite>::G, &[u8]) -> Option<u32>> GAIN for F {}

pub(crate) fn verify_transaction<F: GAIN, T: Transaction>(
  tx: &T,
  genesis: [u8; 32],
  get_and_increment_nonce: &mut F,
) -> Result<(), TransactionError> {
  if tx.serialize().len() > TRANSACTION_SIZE_LIMIT {
    Err(TransactionError::TooLargeTransaction)?;
  }

  tx.verify()?;

  match tx.kind() {
    TransactionKind::Provided(_) | TransactionKind::Unsigned => {}
    TransactionKind::Signed(order, Signed { signer, nonce, signature }) => {
      if let Some(next_nonce) = get_and_increment_nonce(signer, &order) {
        if *nonce != next_nonce {
          Err(TransactionError::InvalidNonce)?;
        }
      } else {
        // Not a participant
        Err(TransactionError::InvalidSigner)?;
      }

      // TODO: Use a batch verification here
      if !signature.verify(*signer, tx.sig_hash(genesis)) {
        Err(TransactionError::InvalidSignature)?;
      }
    }
  }

  Ok(())
}