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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use std::array::TryFromSliceError;

use crate::evm_shared::v13::uints::U256;
use fvm_shared4::ActorID;

use cid::Cid;
use fvm_ipld_encoding::strict_bytes;
use fvm_ipld_encoding::tuple::*;
use serde::{Deserialize, Serialize};

/// A tombstone indicating that the contract has been self-destructed.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize_tuple, Deserialize_tuple)]
pub struct Tombstone {
    /// The message origin when this actor was self-destructed.
    pub origin: ActorID,
    /// The message nonce when this actor was self-destructed.
    pub nonce: u64,
}

/// A Keccak256 digest of EVM bytecode.
#[derive(Deserialize, Serialize, Clone, Copy, Eq, PartialEq)]
#[serde(transparent)]
pub struct BytecodeHash(#[serde(with = "strict_bytes")] [u8; 32]);

impl std::fmt::Debug for BytecodeHash {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("BytecodeHash")
            .field(&format_args!("{}", self))
            .finish()
    }
}

impl std::fmt::Display for BytecodeHash {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if f.alternate() {
            write!(f, "0x")?;
        }
        for b in self.0 {
            write!(f, "{b:02X}")?;
        }
        Ok(())
    }
}

impl BytecodeHash {
    pub const ZERO: Self = Self([0; 32]);

    /// Keccak256 hash of `[0xfe]`, "native bytecode"
    pub const NATIVE_ACTOR: Self = Self(hex_literal::hex!(
        "bcc90f2d6dada5b18e155c17a1c0a55920aae94f39857d39d0d8ed07ae8f228b"
    ));

    /// Keccak256 hash of `[]`, empty bytecode
    pub const EMPTY: Self = Self(hex_literal::hex!(
        "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
    ));

    pub fn as_slice(&self) -> &[u8] {
        &self.0
    }
}

impl From<[u8; 32]> for BytecodeHash {
    fn from(digest: [u8; 32]) -> Self {
        BytecodeHash(digest)
    }
}

impl From<BytecodeHash> for [u8; 32] {
    fn from(digest: BytecodeHash) -> Self {
        digest.0
    }
}

impl From<BytecodeHash> for Vec<u8> {
    fn from(digest: BytecodeHash) -> Self {
        digest.0.into()
    }
}

impl From<BytecodeHash> for U256 {
    fn from(bytecode: BytecodeHash) -> Self {
        let bytes: [u8; 32] = bytecode.into();
        Self::from(bytes)
    }
}

impl TryFrom<&[u8]> for BytecodeHash {
    type Error = TryFromSliceError;

    fn try_from(value: &[u8]) -> Result<Self, TryFromSliceError> {
        Ok(Self(value.try_into()?))
    }
}

/// Data stored by an EVM contract.
/// This runs on the fvm-evm-runtime actor code cid.
#[derive(Debug, Serialize_tuple, Deserialize_tuple)]
pub struct State {
    /// The EVM contract bytecode resulting from calling the
    /// initialization code by the constructor.
    pub bytecode: Cid,

    /// The EVM contract bytecode hash keccak256(bytecode)
    pub bytecode_hash: BytecodeHash,

    /// The EVM contract state dictionary.
    /// All eth contract state is a map of U256 -> U256 values.
    ///
    /// KAMT<U256, U256>
    pub contract_state: Cid,

    /// The EVM nonce used to track how many times CREATE or CREATE2 have been called.
    pub nonce: u64,

    /// Possibly a tombstone if this actor has been self-destructed.
    ///
    /// In the EVM, self-destructed contracts are "alive" until the current top-level transaction
    /// ends. We track this by recording the origin and nonce.
    ///
    /// Specifically:
    ///
    /// 1. On SELFDESTRUCT, they mark themselves as "deleted" (by setting a tombstone with the
    ///    current origin/nonce), send away all funds, and return immediately.
    /// 2. For the rest of the current transaction (as long as the tombstone's origin/nonce matches
    ///    the currently executing top-level transaction) , the contract continues to behave
    ///    normally.
    /// 3. After the current transaction ends, the contract behaves as if it were an "empty"
    ///    contract, kind of like an embryo. At this point, the contract can be "resurrected"
    ///    (recreated) by via CREATE/CREATE2.
    ///
    /// See https://github.com/filecoin-project/ref-fvm/issues/1174 for some context.
    pub tombstone: Option<Tombstone>,
}

#[cfg(test)]
mod test {
    use fvm_ipld_encoding::{from_slice, to_vec, BytesDe};

    use crate::v13::BytecodeHash;

    #[test]
    fn test_bytecode_hash_serde() {
        let encoded = to_vec(&BytecodeHash::EMPTY).unwrap();
        let BytesDe(decoded) = from_slice(&encoded).unwrap();
        assert_eq!(
            BytecodeHash::try_from(&decoded[..]).unwrap(),
            BytecodeHash::EMPTY
        );
    }

    #[test]
    fn test_bytecode_hash_format() {
        assert_eq!(
            BytecodeHash::ZERO.to_string(),
            "0000000000000000000000000000000000000000000000000000000000000000"
        );
        assert_eq!(
            format!("{:#}", BytecodeHash::ZERO),
            "0x0000000000000000000000000000000000000000000000000000000000000000"
        );

        assert_eq!(
            format!("{:?}", BytecodeHash::ZERO),
            "BytecodeHash(0000000000000000000000000000000000000000000000000000000000000000)"
        );
    }
}