// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Open Ethereum. // Open Ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Open Ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Open Ethereum. If not, see . use std::collections::BTreeSet; use common_types::{ header::Header, errors::{EthcoreError as Error, EngineError}, }; use ethereum_types::{Address, H256}; use parity_crypto::publickey::{public_to_address, recover as ec_recover, Signature}; use lazy_static::lazy_static; use lru_cache::LruCache; use parking_lot::RwLock; use rlp::encode; use crate::{ADDRESS_LENGTH, SIGNATURE_LENGTH, VANITY_LENGTH, NULL_NONCE, NULL_MIXHASH}; /// How many recovered signature to cache in the memory. pub const CREATOR_CACHE_NUM: usize = 4096; lazy_static! { /// key: header hash /// value: creator address static ref CREATOR_BY_HASH: RwLock> = RwLock::new(LruCache::new(CREATOR_CACHE_NUM)); } /// Recover block creator from signature pub fn recover_creator(header: &Header) -> Result { // Initialization 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)? } // Split `signed_extra data` and `signature` let (signed_data_slice, signature_slice) = data.split_at(data.len() - SIGNATURE_LENGTH); // convert `&[u8]` to `[u8; 65]` let signature = { let mut s = [0; SIGNATURE_LENGTH]; s.copy_from_slice(signature_slice); s }; // modify header and hash it 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) } /// Extract signer list from extra_data. /// /// Layout of extra_data: /// ---- /// VANITY: 32 bytes /// Signers: N * 32 bytes as hex encoded (20 characters) /// Signature: 65 bytes /// -- pub fn extract_signers(header: &Header) -> Result, Error> { let data = header.extra_data(); if data.len() <= VANITY_LENGTH + SIGNATURE_LENGTH { Err(EngineError::CliqueCheckpointNoSigner)? } // extract only the portion of extra_data which includes the signer list 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
= (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) } /// Retrieve `null_seal` pub fn null_seal() -> Vec> { vec![encode(&NULL_MIXHASH.as_bytes().to_vec()), encode(&NULL_NONCE.as_bytes().to_vec())] }