ink_e2e/
client_utils.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::log_info;
16use regex::Regex;
17use std::{
18    collections::BTreeMap,
19    path::PathBuf,
20};
21
22/// Generate a unique salt based on the system time.
23/// todo return no `Option` here, just the salt.
24pub fn salt() -> Option<[u8; 32]> {
25    use funty::Fundamental as _;
26
27    let mut arr = [0u8; 32];
28    let t: [u8; 16] = std::time::SystemTime::now()
29        .duration_since(std::time::UNIX_EPOCH)
30        .unwrap_or_else(|err| panic!("unable to get unix time: {err}"))
31        .as_millis()
32        .as_u128()
33        .to_le_bytes();
34    arr[..16].copy_from_slice(t.as_slice());
35    arr[16..].copy_from_slice(t.as_slice());
36    Some(arr)
37}
38
39/// A registry of contracts that can be loaded.
40pub struct ContractsRegistry {
41    contracts: BTreeMap<String, PathBuf>,
42}
43
44impl ContractsRegistry {
45    /// Create a new registry with the given contracts.
46    pub fn new<P: Into<PathBuf>>(contracts: impl IntoIterator<Item = P>) -> Self {
47        let contracts = contracts
48            .into_iter()
49            .map(|path| {
50                let contract_binary_path: PathBuf = path.into();
51                let contract_name =
52                    contract_binary_path.file_stem().unwrap_or_else(|| {
53                        panic!(
54                            "Invalid contract binary path `{}`",
55                            contract_binary_path.display(),
56                        )
57                    });
58                (
59                    contract_name.to_string_lossy().to_string(),
60                    contract_binary_path,
61                )
62            })
63            .collect();
64
65        Self { contracts }
66    }
67
68    /// Load the binary code for the given contract.
69    pub fn load_code(&self, contract: &str) -> Vec<u8> {
70        let contract_binary_path = self
71            .contracts
72            .iter().find_map(|(name, path)| {
73                let re = Regex::new(r"-features-.+$").expect("failed creating regex");
74                let key = re.replace_all(name, "");
75                if key == contract || key.replace('_', "-") == contract {
76                    return Some(path);
77                }
78                None
79            })
80            .unwrap_or_else(||
81                panic!(
82                    "Unknown contract {contract}. Available contracts: {:?}.\n\
83                     For a contract to be built, add it as a dependency to the `Cargo.toml`",
84                    self.contracts.keys()
85                )
86            );
87        let code = std::fs::read(contract_binary_path).unwrap_or_else(|err| {
88            panic!(
89                "Error loading '{}': {:?}",
90                contract_binary_path.display(),
91                err
92            )
93        });
94        log_info(&format!("{:?} has {} KiB", contract, code.len() / 1024));
95        code
96    }
97}
98
99/// Returns the `H256` hash of the code slice.
100pub fn code_hash(code: &[u8]) -> [u8; 32] {
101    h256_hash(code)
102}
103
104/// Returns the `H256` hash of the given `code` slice.
105fn h256_hash(code: &[u8]) -> [u8; 32] {
106    use sha3::{
107        Digest,
108        Keccak256,
109    };
110    let hash = Keccak256::digest(code);
111    let sl = hash.as_slice();
112    assert!(sl.len() == 32, "expected length of 32");
113    let mut arr = [0u8; 32];
114    arr.copy_from_slice(sl);
115    arr
116}