1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use rlp::{UntrustedRlp, DecoderError, RlpStream, Encodable, Decodable};
use bigint::{Address, U256, M256, H256};
use sha3::{Digest, Keccak256};

#[cfg(not(feature = "std"))] use alloc::vec::Vec;
#[cfg(not(feature = "std"))] use alloc::rc::Rc;
#[cfg(feature = "std")] use std::rc::Rc;

// Use transaction action so we can keep most of the common fields
// without creating a large enum.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TransactionAction {
    Call(Address),
    Create,
    /// CREATE2 transaction action with salt and code hash
    Create2(H256, M256),
}

impl TransactionAction {
    pub fn address(&self, caller: Address, nonce: U256) -> Address {
        match self {
            &TransactionAction::Call(address) => address,
            &TransactionAction::Create => {
                let mut rlp = RlpStream::new_list(2);
                rlp.append(&caller);
                rlp.append(&nonce);

                Address::from(M256::from(Keccak256::digest(rlp.out().as_slice()).as_slice()))
            },
            &TransactionAction::Create2(salt, code_hash) => {
                let mut digest = Keccak256::new();
                digest.input(&[0xff]);
                digest.input(&caller);
                digest.input(&salt);
                digest.input(&H256::from(code_hash));
                let hash = digest.result();
                Address::from(M256::from(&hash[12..]))
            }
        }
    }
}

const CREATE2_TAG: u8 = 0xc2;

impl Encodable for TransactionAction {
    fn rlp_append(&self, s: &mut RlpStream) {
        match self {
            &TransactionAction::Call(address) => {
                s.encoder().encode_value(&address);
            },
            &TransactionAction::Create => {
                s.encoder().encode_value(&[])
            },
            &TransactionAction::Create2(salt, code_hash) => {
                s.begin_list(3)
                    .append(&CREATE2_TAG)
                    .append(&salt)
                    .append(&H256::from(code_hash));
            }
        }
    }
}

impl Decodable for TransactionAction {
    fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
        let action = if rlp.is_empty() {
            TransactionAction::Create
        } else if let Ok(CREATE2_TAG) = rlp.val_at(0) {
            let salt: H256 = rlp.val_at(1)?;
            let code_hash: H256 = rlp.val_at(2)?;
            TransactionAction::Create2(salt, M256::from(code_hash))
        } else {
            TransactionAction::Call(rlp.as_val()?)
        };

        Ok(action)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rlp;

    #[test]
    fn rlp_roundtrip_call() {
        let address = Address::from(M256::from(0xDEADBEEFDEADBEEFDEADBEEF_u64));
        let action = TransactionAction::Call(address);
        let encoded = rlp::encode(&action);
        let decoded: TransactionAction = rlp::decode(&encoded);
        assert_eq!(action, decoded);
    }

    #[test]
    fn rlp_roundtrip_create() {
        let action = TransactionAction::Create;
        let encoded = rlp::encode(&action);
        let decoded: TransactionAction = rlp::decode(&encoded);
        assert_eq!(action, decoded);
    }

    #[test]
    fn rlp_roundtrip_create2() {
        let salt = H256::from(M256::from(0xDEADBEEF));
        let code_hash = M256::from(1024);
        let action = TransactionAction::Create2(salt, code_hash);
        let encoded = rlp::encode(&action);
        let decoded: TransactionAction = rlp::decode(&encoded);
        assert_eq!(action, decoded);
    }
}