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
use super::error::DaemonError;
use crate::{contract::ContractCodeReference, BootError, Contract, Daemon, TxResponse};
use cosmwasm_std::CustomQuery;
use schemars::JsonSchema;
use serde::{de::DeserializeOwned, Serialize};
use std::{
env,
fmt::{self, Debug},
fs,
path::Path,
};
impl Contract<Daemon> {
pub fn upload_if_needed(&mut self) -> Result<Option<TxResponse<Daemon>>, BootError> {
if self.latest_is_uploaded()? {
log::info!("{} is already uploaded", self.id);
Ok(None)
} else {
Some(self.upload()).transpose()
}
}
pub fn latest_is_uploaded(&self) -> Result<bool, BootError> {
let latest_uploaded_code_id = self.code_id()?;
let chain = self.get_chain();
let on_chain_hash = chain
.runtime
.block_on(super::querier::DaemonQuerier::code_id_hash(
chain.sender.channel(),
latest_uploaded_code_id,
))?;
let local_hash = self.source.checksum(&self.id)?;
Ok(local_hash == on_chain_hash)
}
pub fn migrate_if_needed(
&mut self,
migrate_msg: &(impl Serialize + Debug),
) -> Result<Option<TxResponse<Daemon>>, BootError> {
if self.is_running_latest()? {
log::info!("{} is already running the latest code", self.id);
Ok(None)
} else {
Some(self.migrate(migrate_msg, self.code_id()?)).transpose()
}
}
pub fn is_running_latest(&self) -> Result<bool, BootError> {
let latest_uploaded_code_id = self.code_id()?;
let chain = self.get_chain();
let info = chain
.runtime
.block_on(super::querier::DaemonQuerier::contract_info(
chain.sender.channel(),
self.address()?,
))?;
Ok(latest_uploaded_code_id == info.code_id)
}
}
impl<ExecT, QueryT> ContractCodeReference<ExecT, QueryT>
where
ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
QueryT: CustomQuery + DeserializeOwned + 'static,
{
pub fn get_wasm_code_path(&self) -> Result<String, DaemonError> {
let wasm_code_path = self.wasm_code_path.as_ref().ok_or_else(|| {
DaemonError::StdErr("Wasm file is required to determine hash.".into())
})?;
let wasm_code_path = if wasm_code_path.contains(".wasm") {
wasm_code_path.to_string()
} else {
format!(
"{}/{}.wasm",
env::var("ARTIFACTS_DIR").expect("ARTIFACTS_DIR is not set"),
wasm_code_path
)
};
Ok(wasm_code_path)
}
pub fn checksum(&self, id: &str) -> Result<String, DaemonError> {
let wasm_code_path = &self.get_wasm_code_path()?;
if wasm_code_path.contains("artifacts") {
let checksum_path = format!("{wasm_code_path}/checksums.txt");
let contents =
fs::read_to_string(checksum_path).expect("Something went wrong reading the file");
let parsed: Vec<&str> = contents.rsplit(".wasm").collect();
let name = id.split(':').last().unwrap();
let containing_line = parsed.iter().find(|line| line.contains(name)).unwrap();
log::debug!("{:?}", containing_line);
let local_hash = containing_line
.trim_start_matches('\n')
.split_whitespace()
.next()
.unwrap();
return Ok(local_hash.into());
}
let wasm_code = Path::new(wasm_code_path);
let checksum = sha256::try_digest(wasm_code)?;
Ok(checksum)
}
}