use std::collections::{HashMap, HashSet, VecDeque};
use transact::protocol::batch::BatchPair;
use transact::protocol::transaction::{HashMethod, TransactionHeader};
use transact::protos::FromBytes;
use crate::consensus::ProposalId;
use crate::hex::parse_hex;
use crate::service::ServiceNetworkSender;
use crate::signing::hash::HashVerifier;
use crate::signing::SignatureVerifier;
use super::error::ScabbardError;
pub struct ScabbardShared {
batch_queue: VecDeque<BatchPair>,
network_sender: Option<Box<dyn ServiceNetworkSender>>,
peer_services: HashSet<String>,
proposed_batches: HashMap<ProposalId, BatchPair>,
signature_verifier: Box<dyn SignatureVerifier>,
}
impl ScabbardShared {
pub fn new(
batch_queue: VecDeque<BatchPair>,
network_sender: Option<Box<dyn ServiceNetworkSender>>,
peer_services: HashSet<String>,
signature_verifier: Box<dyn SignatureVerifier>,
) -> Self {
ScabbardShared {
batch_queue,
network_sender,
peer_services,
proposed_batches: HashMap::new(),
signature_verifier,
}
}
pub fn add_batch_to_queue(&mut self, batch: BatchPair) {
self.batch_queue.push_back(batch)
}
pub fn pop_batch_from_queue(&mut self) -> Option<BatchPair> {
self.batch_queue.pop_front()
}
pub fn network_sender(&self) -> Option<&dyn ServiceNetworkSender> {
self.network_sender.as_ref().map(|b| &**b)
}
pub fn set_network_sender(&mut self, sender: Box<dyn ServiceNetworkSender>) {
self.network_sender = Some(sender)
}
pub fn take_network_sender(&mut self) -> Option<Box<dyn ServiceNetworkSender>> {
self.network_sender.take()
}
pub fn peer_services(&self) -> &HashSet<String> {
&self.peer_services
}
pub fn add_proposed_batch(
&mut self,
proposal_id: ProposalId,
batch: BatchPair,
) -> Option<BatchPair> {
self.proposed_batches.insert(proposal_id, batch)
}
pub fn get_proposed_batch(&self, proposal_id: &ProposalId) -> Option<&BatchPair> {
self.proposed_batches.get(proposal_id)
}
pub fn remove_proposed_batch(&mut self, proposal_id: &ProposalId) -> Option<BatchPair> {
self.proposed_batches.remove(&proposal_id)
}
pub fn verify_batches(&self, batches: &[BatchPair]) -> Result<bool, ScabbardError> {
for batch in batches {
let batch_pub_key = batch.header().signer_public_key();
if !self
.signature_verifier
.verify(
batch.batch().header(),
parse_hex(batch.batch().header_signature())
.map_err(|err| ScabbardError::BatchVerificationFailed(Box::new(err)))?
.as_slice(),
batch_pub_key,
)
.map_err(|err| ScabbardError::BatchVerificationFailed(Box::new(err)))?
{
warn!(
"Batch failed signature verification: {}",
batch.batch().header_signature()
);
return Ok(false);
}
if batch.header().transaction_ids().len() != batch.batch().transactions().len() {
warn!(
"Number of transactions in batch header does not match number of transactions
in batch: {}",
batch.batch().header_signature(),
);
return Ok(false);
}
for (i, txn) in batch.batch().transactions().iter().enumerate() {
let header = TransactionHeader::from_bytes(txn.header())
.map_err(|err| ScabbardError::BatchVerificationFailed(Box::new(err)))?;
let header_signature_bytes = parse_hex(txn.header_signature())
.map_err(|err| ScabbardError::BatchVerificationFailed(Box::new(err)))?;
if header_signature_bytes != batch.header().transaction_ids()[i] {
warn!(
"Transaction at index {} does not match corresponding transaction ID in
batch header: {}",
i,
batch.batch().header_signature(),
);
return Ok(false);
}
if header.batcher_public_key() != batch_pub_key {
warn!(
"Transaction batcher public key does not match batch signer public key -
txn: {}, batch: {}",
txn.header_signature(),
batch.batch().header_signature(),
);
return Ok(false);
}
if !self
.signature_verifier
.verify(
txn.header(),
parse_hex(txn.header_signature())
.map_err(|err| ScabbardError::BatchVerificationFailed(Box::new(err)))?
.as_slice(),
header.signer_public_key(),
)
.map_err(|err| ScabbardError::BatchVerificationFailed(Box::new(err)))?
{
warn!(
"Transaction failed signature verification - txn: {}, batch: {}",
txn.header_signature(),
batch.batch().header_signature()
);
return Ok(false);
}
if !match header.payload_hash_method() {
HashMethod::SHA512 => HashVerifier
.verify(txn.payload(), header.payload_hash(), &[])
.map_err(|err| ScabbardError::BatchVerificationFailed(Box::new(err)))?,
} {
warn!(
"Transaction payload hash doesn't match payload - txn: {}, batch: {}",
txn.header_signature(),
batch.batch().header_signature()
);
return Ok(false);
}
}
}
Ok(true)
}
}