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 169 170 171 172
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT
use std::array::TryFromSliceError;
use crate::evm_shared::v10::uints::U256;
use fvm_shared3::ActorID;
use cid::Cid;
use fvm_ipld_encoding::strict_bytes;
use fvm_ipld_encoding::tuple::*;
use serde::{Deserialize, Serialize};
use serde_tuple::{Deserialize_tuple, Serialize_tuple};
/// 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 super::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)"
);
}
}