Remove SlashVote per #349

This commit is contained in:
Luke Parker 2023-08-21 23:48:33 -04:00
parent 718c44d50e
commit c65bb70741
No known key found for this signature in database
9 changed files with 59 additions and 369 deletions

View file

@ -76,20 +76,6 @@ async fn handle_block<
// TODO: disconnect the node from network/ban from further participation in Tributary
}
TributaryTransaction::Tendermint(TendermintTx::SlashVote(vote)) => {
// TODO: make sure same signer doesn't vote twice
// increment the counter for this vote
let vote_key = TributaryDb::<D>::slash_vote_key(genesis, vote.id, vote.target);
let mut count = txn.get(&vote_key).map_or(0, |c| u32::from_le_bytes(c.try_into().unwrap()));
count += 1; // TODO: Increase by weight, not by 1
txn.put(vote_key, count.to_le_bytes());
// TODO: Check if a supermajority of validators, by weight, voted, and increment slash
// points if so
// If a node has a certain number more than the median slash points, the node should be
// removed
}
TributaryTransaction::Application(tx) => {
handle_application_tx::<D, _, _, _>(
tx,

View file

@ -251,7 +251,7 @@ impl<T: TransactionTrait> Block<T> {
// use this pattern of verifying tendermint Txs and app txs differently?
match tx {
Transaction::Tendermint(tx) => {
match verify_tendermint_tx::<N>(tx, genesis, schema.clone(), &commit) {
match verify_tendermint_tx::<N>(tx, schema.clone(), &commit) {
Ok(()) => {}
Err(e) => Err(BlockError::TransactionError(e))?,
}

View file

@ -112,7 +112,7 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
}
// verify the tx
if verify_tendermint_tx::<N>(tendermint_tx, self.genesis, schema, commit).is_err() {
if verify_tendermint_tx::<N>(tendermint_tx, schema, commit).is_err() {
return false;
}
}

View file

@ -6,7 +6,7 @@ use async_trait::async_trait;
use subtle::ConstantTimeEq;
use zeroize::{Zeroize, Zeroizing};
use rand::{SeedableRng, seq::SliceRandom, rngs::OsRng};
use rand::{SeedableRng, seq::SliceRandom};
use rand_chacha::ChaCha12Rng;
use transcript::{Transcript, RecommendedTranscript};
@ -43,11 +43,11 @@ use tokio::{
use crate::{
TENDERMINT_MESSAGE, TRANSACTION_MESSAGE, BLOCK_MESSAGE, ReadWrite,
transaction::Transaction as TransactionTrait, Transaction, BlockHeader, Block, BlockError,
Blockchain, P2p, tendermint::tx::SlashVote,
Blockchain, P2p,
};
pub mod tx;
use tx::{TendermintTx, VoteSignature};
use tx::TendermintTx;
const DST: &[u8] = b"Tributary Tendermint Commit Aggregator";
@ -312,21 +312,17 @@ impl<D: Db, T: TransactionTrait, P: P2p> Network for TendermintNetwork<D, T, P>
);
let signer = self.signer();
let tx = match slash_event {
let Some(tx) = (match slash_event {
SlashEvent::WithEvidence(m1, m2) => {
// create an unsigned evidence tx
TendermintTx::SlashEvidence((m1, m2).encode())
Some(TendermintTx::SlashEvidence((m1, m2).encode()))
}
SlashEvent::Id(reason, block, round) => {
// create a signed vote tx
let mut tx = TendermintTx::SlashVote(SlashVote {
id: (reason, block, round).encode().try_into().unwrap(),
target: validator.encode().try_into().unwrap(),
sig: VoteSignature::default(),
});
tx.sign(&mut OsRng, signer.genesis, &signer.key);
tx
SlashEvent::Id(_reason, _block, _round) => {
// TODO: Increase locally observed slash points
None
}
}) else {
return;
};
// add tx to blockchain and broadcast to peers

View file

@ -1,19 +1,10 @@
use core::ops::Deref;
use std::{io, vec, default::Default};
use std::io;
use scale::Decode;
use zeroize::Zeroizing;
use blake2::{Digest, Blake2s256};
use blake2::{Digest, Blake2s256, Blake2b512};
use rand::{RngCore, CryptoRng};
use ciphersuite::{
group::{GroupEncoding, ff::Field},
Ciphersuite, Ristretto,
};
use schnorr::SchnorrSignature;
use ciphersuite::{Ciphersuite, Ristretto};
use crate::{
transaction::{Transaction, TransactionKind, TransactionError},
@ -28,72 +19,10 @@ use tendermint::{
ext::{Network, Commit, RoundNumber, SignatureScheme},
};
/// Signing data for a slash vote.
///
/// The traditional Signed uses a nonce, whereas votes aren't required/expected to be ordered.
/// Accordingly, a simple uniqueness check works instead.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct VoteSignature {
pub signer: <Ristretto as Ciphersuite>::G,
pub signature: SchnorrSignature<Ristretto>,
}
impl ReadWrite for VoteSignature {
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
let signer = Ristretto::read_G(reader)?;
let signature = SchnorrSignature::<Ristretto>::read(reader)?;
Ok(VoteSignature { signer, signature })
}
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&self.signer.to_bytes())?;
self.signature.write(writer)
}
}
impl Default for VoteSignature {
fn default() -> Self {
VoteSignature {
signer: Ristretto::generator(),
signature: SchnorrSignature::<Ristretto>::read(&mut [0; 64].as_slice()).unwrap(),
}
}
}
/// A vote to slash a malicious validator.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct SlashVote {
pub id: [u8; 13], // vote id(slash event id)
pub target: [u8; 32], // who to slash
pub sig: VoteSignature, // signature
}
impl ReadWrite for SlashVote {
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
let mut id = [0; 13];
let mut target = [0; 32];
reader.read_exact(&mut id)?;
reader.read_exact(&mut target)?;
let sig = VoteSignature::read(reader)?;
Ok(SlashVote { id, target, sig })
}
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&self.id)?;
writer.write_all(&self.target)?;
self.sig.write(writer)
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum TendermintTx {
SlashEvidence(Vec<u8>),
// TODO: should the SlashVote.sig be directly in the enum
// like as in (SlashVote, sig) since the sig is sig of the tx.
SlashVote(SlashVote),
}
impl ReadWrite for TendermintTx {
@ -126,10 +55,6 @@ impl ReadWrite for TendermintTx {
}
Ok(TendermintTx::SlashEvidence(data))
}
1 => {
let vote = SlashVote::read(reader)?;
Ok(TendermintTx::SlashVote(vote))
}
_ => Err(io::Error::new(io::ErrorKind::Other, "invalid transaction type")),
}
}
@ -141,10 +66,6 @@ impl ReadWrite for TendermintTx {
writer.write_all(&u32::try_from(ev.len()).unwrap().to_le_bytes())?;
writer.write_all(ev)
}
TendermintTx::SlashVote(vote) => {
writer.write_all(&[1])?;
vote.write(writer)
}
}
}
}
@ -157,32 +78,12 @@ impl Transaction for TendermintTx {
}
fn hash(&self) -> [u8; 32] {
let mut tx = self.serialize();
if let TendermintTx::SlashVote(vote) = self {
// Make sure the part we're cutting off is the signature
assert_eq!(tx.drain((tx.len() - 64) ..).collect::<Vec<_>>(), vote.sig.signature.serialize());
}
Blake2s256::digest(tx).into()
Blake2s256::digest(self.serialize()).into()
}
fn sig_hash(&self, genesis: [u8; 32]) -> <Ristretto as Ciphersuite>::F {
fn sig_hash(&self, _genesis: [u8; 32]) -> <Ristretto as Ciphersuite>::F {
match self {
TendermintTx::SlashEvidence(_) => panic!("sig_hash called on slash evidence transaction"),
TendermintTx::SlashVote(vote) => {
let signature = &vote.sig.signature;
<Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide(
&Blake2b512::digest(
[
b"Tributary Slash Vote",
genesis.as_ref(),
&self.hash(),
signature.R.to_bytes().as_ref(),
]
.concat(),
)
.into(),
)
}
}
}
@ -191,34 +92,6 @@ impl Transaction for TendermintTx {
}
}
impl TendermintTx {
// Sign a transaction
pub fn sign<R: RngCore + CryptoRng>(
&mut self,
rng: &mut R,
genesis: [u8; 32],
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
) {
fn signature(tx: &mut TendermintTx) -> Option<&mut VoteSignature> {
match tx {
TendermintTx::SlashVote(vote) => Some(&mut vote.sig),
_ => None,
}
}
signature(self).unwrap().signer = Ristretto::generator() * key.deref();
let sig_nonce = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(rng));
signature(self).unwrap().signature.R =
<Ristretto as Ciphersuite>::generator() * sig_nonce.deref();
let sig_hash = self.sig_hash(genesis);
signature(self).unwrap().signature =
SchnorrSignature::<Ristretto>::sign(key, sig_nonce, sig_hash);
}
}
pub fn decode_evidence<N: Network>(
mut ev: &[u8],
) -> Result<(SignedMessageFor<N>, Option<SignedMessageFor<N>>), TransactionError> {
@ -234,13 +107,13 @@ pub fn decode_evidence<N: Network>(
// re-implements an entire foreign library's checks for malicious behavior).
pub(crate) fn verify_tendermint_tx<N: Network>(
tx: &TendermintTx,
genesis: [u8; 32],
schema: N::SignatureScheme,
commit: impl Fn(u32) -> Option<Commit<N::SignatureScheme>>,
) -> Result<(), TransactionError> {
tx.verify()?;
match tx {
// TODO: Only allow one evidence per validator, since evidence is fatal
TendermintTx::SlashEvidence(ev) => {
let (first, second) = decode_evidence::<N>(ev)?;
@ -332,21 +205,6 @@ pub(crate) fn verify_tendermint_tx<N: Network>(
_ => Err(TransactionError::InvalidContent)?,
}
}
TendermintTx::SlashVote(vote) => {
// TODO: verify the target is actually one of our validators?
// this shouldn't be a problem because if the target isn't valid, no one else
// gonna vote on it. But we still have to think about spam votes.
// TODO: we need to check signer is a participant
// TODO: Move this into the standalone TendermintTx verify
let sig = &vote.sig;
// verify the tx signature
// TODO: Use Schnorr half-aggregation and a batch verification here
if !sig.signature.verify(sig.signer, tx.sig_hash(genesis)) {
Err(TransactionError::InvalidSignature)?;
}
}
}
Ok(())

View file

@ -19,10 +19,10 @@ use crate::{
transaction::Transaction as TransactionTrait,
TransactionError, Transaction, ProvidedError, ProvidedTransactions, merkle, BlockError, Block,
Blockchain,
tendermint::{TendermintNetwork, Validators, tx::TendermintTx, Signer, TendermintBlock},
tendermint::{TendermintNetwork, Validators, Signer, TendermintBlock},
tests::{
ProvidedTransaction, SignedTransaction, random_provided_transaction, p2p::DummyP2p,
new_genesis, random_vote_tx, random_evidence_tx,
new_genesis, random_evidence_tx,
},
};
@ -149,34 +149,6 @@ fn invalid_block() {
// signature (which it explicitly isn't allowed to anyways)
assert_eq!(block.header.transactions, merkle(&[block.transactions[0].hash()]));
}
{
// Invalid vote signature
let mut blockchain = blockchain.clone();
let vote_tx = random_vote_tx(&mut OsRng, genesis);
assert!(blockchain.add_transaction::<N>(
true,
Transaction::Tendermint(vote_tx),
validators.clone()
));
let mut block = blockchain.build_block::<N>(validators.clone());
blockchain.verify_block::<N>(&block, validators.clone()).unwrap();
match &mut block.transactions[0] {
Transaction::Tendermint(tx) => match tx {
TendermintTx::SlashVote(vote) => {
vote.sig.signature.s += <Ristretto as Ciphersuite>::F::ONE;
}
_ => panic!("non-vote tx found"),
},
_ => panic!("non-tendermint tx found"),
}
assert!(blockchain.verify_block::<N>(&block, validators.clone()).is_err());
// Make sure this isn't because the merkle changed due to the transaction hash including the
// signature (which it explicitly isn't allowed to anyways)
assert_eq!(block.header.transactions, merkle(&[block.transactions[0].hash()]));
}
}
#[test]
@ -281,61 +253,6 @@ fn provided_transaction() {
assert!(blockchain.add_block::<N>(&block, vec![], validators.clone()).is_err());
}
#[test]
fn tendermint_vote_tx() {
let genesis = new_genesis();
let validators = Arc::new(Validators::new(genesis, vec![]).unwrap());
let (_, mut blockchain) = new_blockchain::<SignedTransaction>(genesis, &[]);
let test = |blockchain: &mut Blockchain<MemDb, SignedTransaction>,
mempool: Vec<Transaction<SignedTransaction>>| {
let tip = blockchain.tip();
for tx in mempool.clone() {
let Transaction::Tendermint(tx) = tx else {
panic!("non-tendermint tx found");
};
assert!(blockchain.add_transaction::<N>(
true,
Transaction::Tendermint(tx),
validators.clone()
));
}
let block = blockchain.build_block::<N>(validators.clone());
assert_eq!(blockchain.tip(), tip);
assert_eq!(block.header.parent, tip);
// Make sure all transactions were included
for bt in &block.transactions {
assert!(mempool.contains(bt));
}
// Make sure the merkle was correct
// Uses block.transactions instead of mempool as order may differ between the two
assert_eq!(
block.header.transactions,
merkle(&block.transactions.iter().map(Transaction::hash).collect::<Vec<_>>()),
);
// Verify and add the block
blockchain.verify_block::<N>(&block, validators.clone()).unwrap();
assert!(blockchain.add_block::<N>(&block, vec![], validators.clone()).is_ok());
assert_eq!(blockchain.tip(), block.hash());
};
// test with single tx
let tx = random_vote_tx(&mut OsRng, genesis);
test(&mut blockchain, vec![Transaction::Tendermint(tx)]);
// test with multiple txs
let mut mempool: Vec<Transaction<SignedTransaction>> = vec![];
for _ in 0 .. 5 {
mempool.push(Transaction::Tendermint(random_vote_tx(&mut OsRng, genesis)));
}
test(&mut blockchain, mempool);
}
#[tokio::test]
async fn tendermint_evidence_tx() {
let genesis = new_genesis();
@ -397,8 +314,8 @@ async fn tendermint_evidence_tx() {
test(&mut blockchain, mempool, validators);
}
#[test]
fn block_tx_ordering() {
#[tokio::test]
async fn block_tx_ordering() {
#[derive(Debug, PartialEq, Eq, Clone)]
enum SignedTx {
Signed(Box<SignedTransaction>),
@ -451,10 +368,10 @@ fn block_tx_ordering() {
let genesis = new_genesis();
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
let validators = Arc::new(Validators::new(genesis, vec![]).unwrap());
// signer
let signer = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0).1.signer;
let validators = Arc::new(Validators::new(genesis, vec![(signer, 1)]).unwrap());
let (_, mut blockchain) = new_blockchain::<SignedTx>(genesis, &[signer]);
let tip = blockchain.tip();
@ -469,7 +386,13 @@ fn block_tx_ordering() {
assert!(blockchain.add_transaction::<N>(true, signed_tx.clone(), validators.clone()));
mempool.push(signed_tx);
let unsigned_tx = Transaction::Tendermint(random_vote_tx(&mut OsRng, genesis));
let unsigned_tx = Transaction::Tendermint(
random_evidence_tx::<N>(
Signer::new(genesis, key.clone()).into(),
TendermintBlock(vec![u8::try_from(i).unwrap()]),
)
.await,
);
assert!(blockchain.add_transaction::<N>(true, unsigned_tx.clone(), validators.clone()));
mempool.push(unsigned_tx);

View file

@ -13,9 +13,7 @@ use crate::{
transaction::Transaction as TransactionTrait,
tendermint::{TendermintBlock, Validators, Signer, TendermintNetwork},
ACCOUNT_MEMPOOL_LIMIT, Transaction, Mempool,
tests::{
SignedTransaction, signed_transaction, p2p::DummyP2p, random_vote_tx, random_evidence_tx,
},
tests::{SignedTransaction, signed_transaction, p2p::DummyP2p, random_evidence_tx},
};
type N = TendermintNetwork<MemDb, SignedTransaction, DummyP2p>;
@ -55,17 +53,6 @@ async fn mempool_addition() {
));
assert_eq!(mempool.next_nonce(&signer), Some(1));
// add a tendermint vote tx
let vote_tx = random_vote_tx(&mut OsRng, genesis);
assert!(mempool.add::<N>(
&blockchain_next_nonces,
true,
Transaction::Tendermint(vote_tx.clone()),
validators.clone(),
unsigned_in_chain,
commit,
));
// add a tendermint evidence tx
let evidence_tx =
random_evidence_tx::<N>(Signer::new(genesis, key.clone()).into(), TendermintBlock(vec![]))
@ -91,14 +78,6 @@ async fn mempool_addition() {
unsigned_in_chain,
commit,
));
assert!(!mempool.add::<N>(
&blockchain_next_nonces,
true,
Transaction::Tendermint(vote_tx.clone()),
validators.clone(),
unsigned_in_chain,
commit,
));
assert!(!mempool.add::<N>(
&blockchain_next_nonces,
true,
@ -146,18 +125,17 @@ async fn mempool_addition() {
assert_eq!(mempool.next_nonce(&second_signer), Some(3));
// Getting a block should work
assert_eq!(mempool.block(&blockchain_next_nonces, unsigned_in_chain).len(), 5);
assert_eq!(mempool.block(&blockchain_next_nonces, unsigned_in_chain).len(), 4);
// If the blockchain says an account had its nonce updated, it should cause a prune
blockchain_next_nonces.insert(signer, 1);
let mut block = mempool.block(&blockchain_next_nonces, unsigned_in_chain);
assert_eq!(block.len(), 4);
assert_eq!(block.len(), 3);
assert!(!block.iter().any(|tx| tx.hash() == first_tx.hash()));
assert_eq!(mempool.txs(), &block.drain(..).map(|tx| (tx.hash(), tx)).collect::<HashMap<_, _>>());
// Removing should also successfully prune
mempool.remove(&tx.hash());
mempool.remove(&vote_tx.hash());
assert_eq!(
mempool.txs(),

View file

@ -22,10 +22,7 @@ use ::tendermint::{
use crate::{
transaction::{Signed, TransactionError, TransactionKind, Transaction, verify_transaction},
ReadWrite,
tendermint::{
tx::{SlashVote, VoteSignature, TendermintTx},
Validators, Signer,
},
tendermint::{tx::TendermintTx, Validators, Signer},
};
#[cfg(test)]
@ -205,19 +202,3 @@ pub async fn random_evidence_tx<N: Network>(
let signed = signed_from_data::<N>(signer, signer_id, 0, 0, data).await;
TendermintTx::SlashEvidence((signed, None::<SignedMessageFor<N>>).encode())
}
pub fn random_vote_tx<R: RngCore + CryptoRng>(rng: &mut R, genesis: [u8; 32]) -> TendermintTx {
// private key
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut *rng));
// vote data
let mut id = [0u8; 13];
let mut target = [0u8; 32];
rng.fill_bytes(&mut id);
rng.fill_bytes(&mut target);
let mut tx = TendermintTx::SlashVote(SlashVote { id, target, sig: VoteSignature::default() });
tx.sign(rng, genesis, &key);
tx
}

View file

@ -4,7 +4,6 @@ use zeroize::Zeroizing;
use rand::{RngCore, rngs::OsRng};
use ciphersuite::{Ristretto, Ciphersuite, group::ff::Field};
use schnorr::SchnorrSignature;
use scale::Encode;
@ -24,54 +23,25 @@ use crate::{
TendermintBlock, Signer, Validators, TendermintNetwork,
},
tests::{
p2p::DummyP2p, SignedTransaction, new_genesis, random_evidence_tx, random_vote_tx,
tendermint_meta, signed_from_data,
p2p::DummyP2p, SignedTransaction, random_evidence_tx, tendermint_meta, signed_from_data,
},
};
type N = TendermintNetwork<MemDb, SignedTransaction, DummyP2p>;
#[test]
fn vote_tx() {
let genesis = new_genesis();
let mut tx = random_vote_tx(&mut OsRng, genesis);
let commit = |_: u32| -> Option<Commit<Arc<Validators>>> {
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
};
let validators = Arc::new(Validators::new(genesis, vec![]).unwrap());
// should pass
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap();
if let TendermintTx::SlashVote(vote) = &mut tx {
vote.sig.signature = SchnorrSignature::read(&mut [0; 64].as_slice()).unwrap();
} else {
panic!("SlashVote TX wasn't SlashVote");
}
// should fail
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators, commit).is_err());
}
#[tokio::test]
async fn serialize_tendermint() {
// make a tendermint tx with random evidence
let (genesis, signer, _, _) = tendermint_meta().await;
let (_, signer, _, _) = tendermint_meta().await;
let tx = random_evidence_tx::<N>(signer.into(), TendermintBlock(vec![])).await;
let res = TendermintTx::read::<&[u8]>(&mut tx.serialize().as_ref()).unwrap();
assert_eq!(res, tx);
// with vote tx
let vote_tx = random_vote_tx(&mut OsRng, genesis);
let vote_res = TendermintTx::read::<&[u8]>(&mut vote_tx.serialize().as_ref()).unwrap();
assert_eq!(vote_res, vote_tx);
}
#[tokio::test]
async fn invalid_valid_round() {
// signer
let (genesis, signer, signer_id, validators) = tendermint_meta().await;
let (_, signer, signer_id, validators) = tendermint_meta().await;
let commit = |_: u32| -> Option<Commit<Arc<Validators>>> {
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
};
@ -87,13 +57,13 @@ async fn invalid_valid_round() {
// This should be invalid evidence if a valid valid round is specified
let (_, tx) = valid_round_tx(None).await;
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).is_err());
assert!(verify_tendermint_tx::<N>(&tx, validators.clone(), commit).is_err());
// If an invalid valid round is specified (>= current), this should be invalid evidence
let (mut signed, tx) = valid_round_tx(Some(RoundNumber(0))).await;
// should pass
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap();
verify_tendermint_tx::<N>(&tx, validators.clone(), commit).unwrap();
// change the signature
let mut random_sig = [0u8; 64];
@ -102,12 +72,12 @@ async fn invalid_valid_round() {
let tx = TendermintTx::SlashEvidence((signed.clone(), None::<SignedMessageFor<N>>).encode());
// should fail
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators, commit).is_err());
assert!(verify_tendermint_tx::<N>(&tx, validators, commit).is_err());
}
#[tokio::test]
async fn invalid_precommit_signature() {
let (genesis, signer, signer_id, validators) = tendermint_meta().await;
let (_, signer, signer_id, validators) = tendermint_meta().await;
let commit = |i: u32| -> Option<Commit<Arc<Validators>>> {
assert_eq!(i, 0);
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
@ -124,8 +94,7 @@ async fn invalid_precommit_signature() {
};
// Empty Precommit should fail.
assert!(verify_tendermint_tx::<N>(&precommit(None).await.1, genesis, validators.clone(), commit)
.is_err());
assert!(verify_tendermint_tx::<N>(&precommit(None).await.1, validators.clone(), commit).is_err());
// valid precommit signature should fail.
let block_id = [0x22u8; 32];
@ -136,7 +105,6 @@ async fn invalid_precommit_signature() {
assert!(verify_tendermint_tx::<N>(
&precommit(Some((block_id, signer.clone().sign(&commit_msg).await))).await.1,
genesis,
validators.clone(),
commit
)
@ -145,20 +113,20 @@ async fn invalid_precommit_signature() {
// any other signature can be used as evidence.
{
let (mut signed, tx) = precommit(Some((block_id, signer.sign(&[]).await))).await;
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap();
verify_tendermint_tx::<N>(&tx, validators.clone(), commit).unwrap();
// So long as we can authenticate where it came from
let mut random_sig = [0u8; 64];
OsRng.fill_bytes(&mut random_sig);
signed.sig = random_sig;
let tx = TendermintTx::SlashEvidence((signed.clone(), None::<SignedMessageFor<N>>).encode());
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators, commit).is_err());
assert!(verify_tendermint_tx::<N>(&tx, validators, commit).is_err());
}
}
#[tokio::test]
async fn evidence_with_prevote() {
let (genesis, signer, signer_id, validators) = tendermint_meta().await;
let (_, signer, signer_id, validators) = tendermint_meta().await;
let commit = |_: u32| -> Option<Commit<Arc<Validators>>> {
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
};
@ -179,7 +147,7 @@ async fn evidence_with_prevote() {
// No prevote message should be valid as slash evidence at this time
for prevote in [prevote(None).await, prevote(Some([0x22u8; 32])).await] {
assert!(verify_tendermint_tx::<N>(&prevote, genesis, validators.clone(), commit).is_err());
assert!(verify_tendermint_tx::<N>(&prevote, validators.clone(), commit).is_err());
}
}
@ -202,23 +170,23 @@ async fn conflicting_msgs_evidence_tx() {
// non-conflicting data should fail
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x11]))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(&signed_1)).encode());
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).is_err());
assert!(verify_tendermint_tx::<N>(&tx, validators.clone(), commit).is_err());
// conflicting data should pass
let signed_2 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(signed_2)).encode());
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap();
verify_tendermint_tx::<N>(&tx, validators.clone(), commit).unwrap();
// Except if it has a distinct round number, as we don't check cross-round conflicts
// (except for Precommit)
let signed_2 = signed_for_b_r(0, 1, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(signed_2)).encode());
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap_err();
verify_tendermint_tx::<N>(&tx, validators.clone(), commit).unwrap_err();
// Proposals for different block numbers should also fail as evidence
let signed_2 = signed_for_b_r(1, 0, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(signed_2)).encode());
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap_err();
verify_tendermint_tx::<N>(&tx, validators.clone(), commit).unwrap_err();
}
// Prevote
@ -226,23 +194,23 @@ async fn conflicting_msgs_evidence_tx() {
// non-conflicting data should fail
let signed_1 = signed_for_b_r(0, 0, Data::Prevote(Some([0x11; 32]))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(&signed_1)).encode());
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).is_err());
assert!(verify_tendermint_tx::<N>(&tx, validators.clone(), commit).is_err());
// conflicting data should pass
let signed_2 = signed_for_b_r(0, 0, Data::Prevote(Some([0x22; 32]))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(signed_2)).encode());
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap();
verify_tendermint_tx::<N>(&tx, validators.clone(), commit).unwrap();
// Except if it has a distinct round number, as we don't check cross-round conflicts
// (except for Precommit)
let signed_2 = signed_for_b_r(0, 1, Data::Prevote(Some([0x22; 32]))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(signed_2)).encode());
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap_err();
verify_tendermint_tx::<N>(&tx, validators.clone(), commit).unwrap_err();
// Proposals for different block numbers should also fail as evidence
let signed_2 = signed_for_b_r(1, 0, Data::Prevote(Some([0x22; 32]))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(signed_2)).encode());
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap_err();
verify_tendermint_tx::<N>(&tx, validators.clone(), commit).unwrap_err();
}
// Precommit
@ -251,17 +219,17 @@ async fn conflicting_msgs_evidence_tx() {
let signed_1 = signed_for_b_r(0, 0, Data::Precommit(Some(([0x11; 32], sig)))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(&signed_1)).encode());
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).is_err());
assert!(verify_tendermint_tx::<N>(&tx, validators.clone(), commit).is_err());
// For precommit, the round number is ignored
let signed_2 = signed_for_b_r(0, 1, Data::Precommit(Some(([0x22; 32], sig)))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(signed_2)).encode());
verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).unwrap();
verify_tendermint_tx::<N>(&tx, validators.clone(), commit).unwrap();
// Yet the block number isn't
let signed_2 = signed_for_b_r(1, 0, Data::Precommit(Some(([0x22; 32], sig)))).await;
let tx = TendermintTx::SlashEvidence((&signed_1, Some(signed_2)).encode());
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).is_err());
assert!(verify_tendermint_tx::<N>(&tx, validators.clone(), commit).is_err());
}
// msgs from different senders should fail
@ -290,7 +258,7 @@ async fn conflicting_msgs_evidence_tx() {
let validators =
Arc::new(Validators::new(genesis, vec![(signer_pub, 1), (signer_pub_2, 1)]).unwrap());
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators, commit).is_err());
assert!(verify_tendermint_tx::<N>(&tx, validators, commit).is_err());
}
// msgs with different steps should fail
@ -298,6 +266,6 @@ async fn conflicting_msgs_evidence_tx() {
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![]))).await;
let signed_2 = signed_for_b_r(0, 0, Data::Prevote(None)).await;
let tx = TendermintTx::SlashEvidence((signed_1, Some(signed_2)).encode());
assert!(verify_tendermint_tx::<N>(&tx, genesis, validators.clone(), commit).is_err());
assert!(verify_tendermint_tx::<N>(&tx, validators.clone(), commit).is_err());
}
}