openethereum/crates/ethcore/src/engines/authority_round/util.rs

120 lines
4.2 KiB
Rust

// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum 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.
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Utility functions.
//!
//! Contains small functions used by the AuRa engine that are not strictly limited to that scope.
use std::fmt;
use client::{traits::EngineClient, BlockChainClient};
use ethabi::{self, FunctionOutputDecoder};
use ethabi_contract::use_contract;
use ethereum_types::{Address, U256};
use log::{debug, error};
use types::{header::Header, ids::BlockId};
/// A contract bound to a client and block number.
///
/// A bound contract is a combination of a `Client` reference, a `BlockId` and a contract `Address`.
/// These three parts are enough to call a contract's function; return values are automatically
/// decoded.
pub struct BoundContract<'a> {
client: &'a dyn EngineClient,
block_id: BlockId,
contract_addr: Address,
}
/// Contract call failed error.
#[derive(Debug)]
pub enum CallError {
/// The call itself failed.
CallFailed(String),
/// Decoding the return value failed or the decoded value was a failure.
DecodeFailed(ethabi::Error),
/// The passed in client reference could not be upgraded to a `BlockchainClient`.
NotFullClient,
}
impl<'a> fmt::Debug for BoundContract<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("BoundContract")
.field("client", &(self.client as *const dyn EngineClient))
.field("block_id", &self.block_id)
.field("contract_addr", &self.contract_addr)
.finish()
}
}
impl<'a> BoundContract<'a> {
/// Create a new `BoundContract`.
pub fn new(
client: &dyn EngineClient,
block_id: BlockId,
contract_addr: Address,
) -> BoundContract {
BoundContract {
client,
block_id,
contract_addr,
}
}
/// Perform a function call to an Ethereum machine that doesn't create a transaction or change the state.
///
/// Runs a constant function call on `client`. The `call` value can be serialized by calling any
/// api function generated by the `use_contract!` macro. This does not create any transactions, it only produces a
/// result based on the state at the current block: It is constant in the sense that it does not alter the EVM
/// state.
pub fn call_const<D>(&self, call: (ethabi::Bytes, D)) -> Result<D::Output, CallError>
where
D: ethabi::FunctionOutputDecoder,
{
let (data, output_decoder) = call;
let call_return = self
.client
.as_full_client()
.ok_or(CallError::NotFullClient)?
.call_contract(self.block_id, self.contract_addr, data)
.map_err(CallError::CallFailed)?;
// Decode the result and return it.
output_decoder
.decode(call_return.as_slice())
.map_err(CallError::DecodeFailed)
}
}
use_contract!(contract, "res/contracts/block_gas_limit.json");
pub fn block_gas_limit(
full_client: &dyn BlockChainClient,
header: &Header,
address: Address,
) -> Option<U256> {
let (data, decoder) = contract::functions::block_gas_limit::call();
let value = full_client.call_contract(BlockId::Hash(*header.parent_hash()), address, data).map_err(|err| {
error!(target: "block_gas_limit", "Contract call failed. Not changing the block gas limit. {:?}", err);
}).ok()?;
if value.is_empty() {
debug!(target: "block_gas_limit", "Contract call returned nothing. Not changing the block gas limit.");
None
} else {
decoder.decode(&value).ok()
}
}