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
use crate::serialization::HexIfHumanReadable;
use fuel_core_types::{
    fuel_tx::{
        AssetId,
        Contract,
        ContractId,
        StorageSlot,
        TxPointer,
        UtxoId,
    },
    fuel_types::{
        BlockHeight,
        Bytes32,
        Salt,
    },
};
use itertools::Itertools;
use serde::{
    Deserialize,
    Serialize,
};
use serde_with::serde_as;

#[serde_as]
#[derive(Default, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct ContractConfig {
    pub contract_id: ContractId,
    #[serde_as(as = "HexIfHumanReadable")]
    pub code: Vec<u8>,
    pub tx_id: Bytes32,
    pub output_index: u16,
    /// TxPointer: auto-generated if None
    /// used if contract is forked from another chain to preserve id & tx_pointer
    /// The block height that the contract was last used in
    pub tx_pointer_block_height: BlockHeight,
    /// TxPointer: auto-generated if None
    /// used if contract is forked from another chain to preserve id & tx_pointer
    /// The index of the originating tx within `tx_pointer_block_height`
    pub tx_pointer_tx_idx: u16,
    pub states: Vec<ContractStateConfig>,
    pub balances: Vec<ContractBalanceConfig>,
}

#[serde_as]
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct ContractStateConfig {
    pub key: Bytes32,
    #[serde_as(as = "HexIfHumanReadable")]
    pub value: Vec<u8>,
}

impl TryFrom<ContractStateConfig> for StorageSlot {
    type Error = anyhow::Error;

    fn try_from(value: ContractStateConfig) -> Result<Self, Self::Error> {
        let key = value.key;
        let value = Bytes32::try_from(value.value.as_slice())?;
        Ok(Self::new(key, value))
    }
}

#[cfg(feature = "test-helpers")]
impl crate::Randomize for ContractStateConfig {
    fn randomize(mut rng: impl ::rand::Rng) -> Self {
        Self {
            key: crate::Randomize::randomize(&mut rng),
            value: Bytes32::randomize(&mut rng).to_vec(),
        }
    }
}

#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct ContractBalanceConfig {
    pub asset_id: AssetId,
    pub amount: u64,
}

#[cfg(feature = "test-helpers")]
impl crate::Randomize for ContractBalanceConfig {
    fn randomize(mut rng: impl ::rand::Rng) -> Self {
        Self {
            asset_id: crate::Randomize::randomize(&mut rng),
            amount: crate::Randomize::randomize(&mut rng),
        }
    }
}

impl ContractConfig {
    pub fn utxo_id(&self) -> UtxoId {
        UtxoId::new(self.tx_id, self.output_index)
    }

    pub fn tx_pointer(&self) -> TxPointer {
        TxPointer::new(self.tx_pointer_block_height, self.tx_pointer_tx_idx)
    }
}

#[cfg(feature = "test-helpers")]
impl crate::Randomize for ContractConfig {
    fn randomize(mut rng: impl ::rand::Rng) -> Self {
        let code = Bytes32::randomize(&mut rng).to_vec();
        let states = vec![ContractStateConfig {
            key: Bytes32::randomize(&mut rng),
            value: Bytes32::randomize(&mut rng).to_vec(),
        }];

        let contract = Contract::from(code.clone());
        let state_root = {
            let states = states
                .iter()
                .cloned()
                .map(|state| StorageSlot::try_from(state).expect("32 bytes"))
                .collect_vec();
            Contract::initial_state_root(states.iter())
        };
        let salt = Salt::zeroed();
        let contract_id = contract.id(&salt, &contract.root(), &state_root);

        Self {
            contract_id,
            code,
            tx_id: crate::Randomize::randomize(&mut rng),
            output_index: rng.gen(),
            tx_pointer_block_height: crate::Randomize::randomize(&mut rng),
            tx_pointer_tx_idx: rng.gen(),
            states,
            balances: vec![ContractBalanceConfig {
                asset_id: crate::Randomize::randomize(&mut rng),
                amount: rng.gen(),
            }],
        }
    }
}

impl ContractConfig {
    pub fn update_contract_id(&mut self, salt: Salt) {
        let slots: Vec<_> = self
            .states
            .iter()
            .cloned()
            .map(StorageSlot::try_from)
            .try_collect()
            .unwrap();
        let state_root = Contract::initial_state_root(slots.iter());

        let contract = Contract::from(self.code.clone());
        let root = contract.root();
        let contract_id = contract.id(&salt, &root, &state_root);
        self.contract_id = contract_id;
    }
}