// 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/>.

//! Tracing datatypes.

use bytes::Bytes;
use ethereum_types::{Address, Bloom, BloomInput, U256};
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};

use super::error::Error;
use evm::CallType;
use vm::ActionParams;

/// `Call` result.
#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)]
pub struct CallResult {
    /// Gas used by call.
    pub gas_used: U256,
    /// Call Output.
    pub output: Bytes,
}

/// `Create` result.
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct CreateResult {
    /// Gas used by create.
    pub gas_used: U256,
    /// Code of the newly created contract.
    pub code: Bytes,
    /// Address of the newly created contract.
    pub address: Address,
}

impl CreateResult {
    /// Returns bloom.
    pub fn bloom(&self) -> Bloom {
        BloomInput::Raw(self.address.as_bytes()).into()
    }
}

/// Description of a _call_ action, either a `CALL` operation or a message transaction.
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct Call {
    /// The sending account.
    pub from: Address,
    /// The destination account.
    pub to: Address,
    /// The value transferred to the destination account.
    pub value: U256,
    /// The gas available for executing the call.
    pub gas: U256,
    /// The input data provided to the call.
    pub input: Bytes,
    /// The type of the call.
    pub call_type: CallType,
}

impl From<ActionParams> for Call {
    fn from(p: ActionParams) -> Self {
        match p.call_type {
            CallType::DelegateCall | CallType::CallCode => Call {
                from: p.address,
                to: p.code_address,
                value: p.value.value(),
                gas: p.gas,
                input: p.data.unwrap_or_else(Vec::new),
                call_type: p.call_type,
            },
            _ => Call {
                from: p.sender,
                to: p.address,
                value: p.value.value(),
                gas: p.gas,
                input: p.data.unwrap_or_else(Vec::new),
                call_type: p.call_type,
            },
        }
    }
}

impl Call {
    /// Returns call action bloom.
    /// The bloom contains from and to addresses.
    pub fn bloom(&self) -> Bloom {
        let mut bloom = Bloom::default();
        bloom.accrue(BloomInput::Raw(self.from.as_bytes()));
        bloom.accrue(BloomInput::Raw(self.to.as_bytes()));
        bloom
    }
}

/// Description of a _create_ action, either a `CREATE` operation or a create transaction.
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct Create {
    /// The address of the creator.
    pub from: Address,
    /// The value with which the new account is endowed.
    pub value: U256,
    /// The gas available for the creation init code.
    pub gas: U256,
    /// The init code.
    pub init: Bytes,
}

impl From<ActionParams> for Create {
    fn from(p: ActionParams) -> Self {
        Create {
            from: p.sender,
            value: p.value.value(),
            gas: p.gas,
            init: p.code.map_or_else(Vec::new, |c| (*c).clone()),
        }
    }
}

impl Create {
    /// Returns bloom create action bloom.
    /// The bloom contains only from address.
    pub fn bloom(&self) -> Bloom {
        BloomInput::Raw(self.from.as_bytes()).into()
    }
}

/// Reward type.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum RewardType {
    /// Block
    Block,
    /// Uncle
    Uncle,
    /// Empty step (AuthorityRound)
    EmptyStep,
    /// A reward directly attributed by an external protocol (e.g. block reward contract)
    External,
}

impl Encodable for RewardType {
    fn rlp_append(&self, s: &mut RlpStream) {
        let v = match *self {
            RewardType::Block => 0u32,
            RewardType::Uncle => 1,
            RewardType::EmptyStep => 2,
            RewardType::External => 3,
        };
        Encodable::rlp_append(&v, s);
    }
}

impl Decodable for RewardType {
    fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
        rlp.as_val().and_then(|v| {
            Ok(match v {
                0u32 => RewardType::Block,
                1 => RewardType::Uncle,
                2 => RewardType::EmptyStep,
                3 => RewardType::External,
                _ => return Err(DecoderError::Custom("Invalid value of RewardType item")),
            })
        })
    }
}

/// Reward action
#[derive(Debug, Clone, PartialEq)]
pub struct Reward {
    /// Author's address.
    pub author: Address,
    /// Reward amount.
    pub value: U256,
    /// Reward type.
    pub reward_type: RewardType,
}

impl Reward {
    /// Return reward action bloom.
    pub fn bloom(&self) -> Bloom {
        BloomInput::Raw(self.author.as_bytes()).into()
    }
}

impl Encodable for Reward {
    fn rlp_append(&self, s: &mut RlpStream) {
        s.begin_list(3);
        s.append(&self.author);
        s.append(&self.value);
        s.append(&self.reward_type);
    }
}

impl Decodable for Reward {
    fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
        let res = Reward {
            author: rlp.val_at(0)?,
            value: rlp.val_at(1)?,
            reward_type: rlp.val_at(2)?,
        };

        Ok(res)
    }
}

/// Suicide action.
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct Suicide {
    /// Suicided address.
    pub address: Address,
    /// Suicided contract heir.
    pub refund_address: Address,
    /// Balance of the contract just before suicide.
    pub balance: U256,
}

impl Suicide {
    /// Return suicide action bloom.
    pub fn bloom(&self) -> Bloom {
        let mut bloom = Bloom::default();
        bloom.accrue(BloomInput::Raw(self.address.as_bytes()));
        bloom.accrue(BloomInput::Raw(self.refund_address.as_bytes()));
        bloom
    }
}

/// Description of an action that we trace; will be either a call or a create.
#[derive(Debug, Clone, PartialEq)]
pub enum Action {
    /// It's a call action.
    Call(Call),
    /// It's a create action.
    Create(Create),
    /// Suicide.
    Suicide(Suicide),
    /// Reward
    Reward(Reward),
}

impl Encodable for Action {
    fn rlp_append(&self, s: &mut RlpStream) {
        s.begin_list(2);
        match *self {
            Action::Call(ref call) => {
                s.append(&0u8);
                s.append(call);
            }
            Action::Create(ref create) => {
                s.append(&1u8);
                s.append(create);
            }
            Action::Suicide(ref suicide) => {
                s.append(&2u8);
                s.append(suicide);
            }
            Action::Reward(ref reward) => {
                s.append(&3u8);
                s.append(reward);
            }
        }
    }
}

impl Decodable for Action {
    fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
        let action_type: u8 = rlp.val_at(0)?;
        match action_type {
            0 => rlp.val_at(1).map(Action::Call),
            1 => rlp.val_at(1).map(Action::Create),
            2 => rlp.val_at(1).map(Action::Suicide),
            3 => rlp.val_at(1).map(Action::Reward),
            _ => Err(DecoderError::Custom("Invalid action type.")),
        }
    }
}

impl Action {
    /// Returns action bloom.
    pub fn bloom(&self) -> Bloom {
        match *self {
            Action::Call(ref call) => call.bloom(),
            Action::Create(ref create) => create.bloom(),
            Action::Suicide(ref suicide) => suicide.bloom(),
            Action::Reward(ref reward) => reward.bloom(),
        }
    }
}

/// The result of the performed action.
#[derive(Debug, Clone, PartialEq)]
pub enum Res {
    /// Successful call action result.
    Call(CallResult),
    /// Successful create action result.
    Create(CreateResult),
    /// Failed call.
    FailedCall(Error),
    /// Failed create.
    FailedCreate(Error),
    /// None
    None,
}

impl Encodable for Res {
    fn rlp_append(&self, s: &mut RlpStream) {
        match *self {
            Res::Call(ref call) => {
                s.begin_list(2);
                s.append(&0u8);
                s.append(call);
            }
            Res::Create(ref create) => {
                s.begin_list(2);
                s.append(&1u8);
                s.append(create);
            }
            Res::FailedCall(ref err) => {
                s.begin_list(2);
                s.append(&2u8);
                s.append(err);
            }
            Res::FailedCreate(ref err) => {
                s.begin_list(2);
                s.append(&3u8);
                s.append(err);
            }
            Res::None => {
                s.begin_list(1);
                s.append(&4u8);
            }
        }
    }
}

impl Decodable for Res {
    fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
        let action_type: u8 = rlp.val_at(0)?;
        match action_type {
            0 => rlp.val_at(1).map(Res::Call),
            1 => rlp.val_at(1).map(Res::Create),
            2 => rlp.val_at(1).map(Res::FailedCall),
            3 => rlp.val_at(1).map(Res::FailedCreate),
            4 => Ok(Res::None),
            _ => Err(DecoderError::Custom("Invalid result type.")),
        }
    }
}

impl Res {
    /// Returns result bloom.
    pub fn bloom(&self) -> Bloom {
        match *self {
            Res::Create(ref create) => create.bloom(),
            Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => {
                Default::default()
            }
        }
    }

    /// Did this call fail?
    pub fn succeeded(&self) -> bool {
        match *self {
            Res::Call(_) | Res::Create(_) => true,
            _ => false,
        }
    }
}

#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
/// A diff of some chunk of memory.
pub struct MemoryDiff {
    /// Offset into memory the change begins.
    pub offset: usize,
    /// The changed data.
    pub data: Bytes,
}

#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
/// A diff of some storage value.
pub struct StorageDiff {
    /// Which key in storage is changed.
    pub location: U256,
    /// What the value has been changed to.
    pub value: U256,
}

#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
/// A record of an executed VM operation.
pub struct VMExecutedOperation {
    /// The total gas used.
    pub gas_used: U256,
    /// The stack item placed, if any.
    pub stack_push: Vec<U256>,
    /// If altered, the memory delta.
    pub mem_diff: Option<MemoryDiff>,
    /// The altered storage value, if any.
    pub store_diff: Option<StorageDiff>,
}

#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)]
/// A record of the execution of a single VM operation.
pub struct VMOperation {
    /// The program counter.
    pub pc: usize,
    /// The instruction executed.
    pub instruction: u8,
    /// The gas cost for this instruction.
    pub gas_cost: U256,
    /// Information concerning the execution of the operation.
    pub executed: Option<VMExecutedOperation>,
}

#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)]
/// A record of a full VM trace for a CALL/CREATE.
pub struct VMTrace {
    /// The step (i.e. index into operations) at which this trace corresponds.
    pub parent_step: usize,
    /// The code to be executed.
    pub code: Bytes,
    /// The operations executed.
    pub operations: Vec<VMOperation>,
    /// The sub traces for each interior action performed as part of this call/create.
    /// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction.
    pub subs: Vec<VMTrace>,
}
