use std::collections::BTreeSet;
use common_types::{
header::Header,
errors::{VapcoreError as Error, EngineError},
};
use vapory_types::{Address, H256};
use tetsy_crypto::publickey::{public_to_address, recover as ec_recover, Signature};
use lazy_static::lazy_static;
use lru_cache::LruCache;
use parking_lot::RwLock;
use tetsy_rlp::encode;
use crate::{ADDRESS_LENGTH, SIGNATURE_LENGTH, VANITY_LENGTH, NULL_NONCE, NULL_MIXHASH};
pub const CREATOR_CACHE_NUM: usize = 4096;
lazy_static! {
static ref CREATOR_BY_HASH: RwLock<LruCache<H256, Address>> = RwLock::new(LruCache::new(CREATOR_CACHE_NUM));
}
pub fn recover_creator(header: &Header) -> Result<Address, Error> {
let mut cache = CREATOR_BY_HASH.write();
if let Some(creator) = cache.get_mut(&header.hash()) {
return Ok(*creator);
}
let data = header.extra_data();
if data.len() < VANITY_LENGTH {
Err(EngineError::CliqueMissingVanity)?
}
if data.len() < VANITY_LENGTH + SIGNATURE_LENGTH {
Err(EngineError::CliqueMissingSignature)?
}
let (signed_data_slice, signature_slice) = data.split_at(data.len() - SIGNATURE_LENGTH);
let signature = {
let mut s = [0; SIGNATURE_LENGTH];
s.copy_from_slice(signature_slice);
s
};
let unsigned_header = &mut header.clone();
unsigned_header.set_extra_data(signed_data_slice.to_vec());
let msg = unsigned_header.hash();
let pubkey = ec_recover(&Signature::from(signature), &msg)?;
let creator = public_to_address(&pubkey);
cache.insert(header.hash(), creator.clone());
Ok(creator)
}
pub fn extract_signers(header: &Header) -> Result<BTreeSet<Address>, Error> {
let data = header.extra_data();
if data.len() <= VANITY_LENGTH + SIGNATURE_LENGTH {
Err(EngineError::CliqueCheckpointNoSigner)?
}
let signers_raw = &data[(VANITY_LENGTH)..data.len() - (SIGNATURE_LENGTH)];
if signers_raw.len() % ADDRESS_LENGTH != 0 {
Err(EngineError::CliqueCheckpointInvalidSigners(signers_raw.len()))?
}
let num_signers = signers_raw.len() / 20;
let signers: BTreeSet<Address> = (0..num_signers)
.map(|i| {
let start = i * ADDRESS_LENGTH;
let end = start + ADDRESS_LENGTH;
Address::from_slice(&signers_raw[start..end])
})
.collect();
Ok(signers)
}
pub fn null_seal() -> Vec<Vec<u8>> {
vec![encode(&NULL_MIXHASH.as_bytes().to_vec()), encode(&NULL_NONCE.as_bytes().to_vec())]
}