Skip to content

Commit c3a5921

Browse files
sanityclaude
andcommitted
refactor: split contract_interface.rs into logical modules
Split the 1992-line contract_interface.rs into 12 focused modules: - error.rs: ContractError type - state.rs: State, StateDelta, StateSummary - key.rs: ContractInstanceId, ContractKey - code.rs: ContractCode - update.rs: Update-related types (UpdateModification, RelatedContracts, etc.) - contract.rs: Contract type - wrapped.rs: WrappedState, WrappedContract - trait_def.rs: ContractInterface trait - wasm_interface.rs: WASM FFI boundary (pub(crate)) - encoding.rs: Typed contract helpers (pub) - tests.rs: Unit tests - mod.rs: Re-exports maintaining public API Benefits: - Improved code navigation and maintainability - Clearer separation of concerns - Easier to understand dependencies between types - No breaking changes - all public APIs preserved via re-exports All tests pass. No functional changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 882648c commit c3a5921

File tree

13 files changed

+2089
-1992
lines changed

13 files changed

+2089
-1992
lines changed

rust/src/contract_interface.rs

Lines changed: 0 additions & 1992 deletions
This file was deleted.
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//! Contract executable code representation.
2+
//!
3+
//! This module provides the `ContractCode` type which represents the executable
4+
//! WASM bytecode for a contract, along with its hash.
5+
6+
use std::borrow::Cow;
7+
use std::fs::File;
8+
use std::io::Read;
9+
use std::path::Path;
10+
11+
use blake3::{traits::digest::Digest, Hasher as Blake3};
12+
use serde::{Deserialize, Serialize};
13+
use serde_with::serde_as;
14+
15+
use crate::code_hash::CodeHash;
16+
17+
use super::key::internal_fmt_key;
18+
use super::CONTRACT_KEY_SIZE;
19+
20+
/// The executable contract.
21+
///
22+
/// It is the part of the executable belonging to the full specification
23+
/// and does not include any other metadata (like the parameters).
24+
#[serde_as]
25+
#[derive(Serialize, Deserialize, Clone)]
26+
#[cfg_attr(
27+
any(feature = "testing", all(test, any(unix, windows))),
28+
derive(arbitrary::Arbitrary)
29+
)]
30+
pub struct ContractCode<'a> {
31+
// TODO: conver this to Arc<[u8]> instead
32+
#[serde_as(as = "serde_with::Bytes")]
33+
#[serde(borrow)]
34+
pub(crate) data: Cow<'a, [u8]>,
35+
// todo: skip serializing and instead compute it
36+
pub(crate) code_hash: CodeHash,
37+
}
38+
39+
impl ContractCode<'static> {
40+
/// Loads the contract raw wasm module, without any version.
41+
pub fn load_raw(path: &Path) -> Result<Self, std::io::Error> {
42+
let contract_data = Self::load_bytes(path)?;
43+
Ok(ContractCode::from(contract_data))
44+
}
45+
46+
pub(crate) fn load_bytes(path: &Path) -> Result<Vec<u8>, std::io::Error> {
47+
let mut contract_file = File::open(path)?;
48+
let mut contract_data = if let Ok(md) = contract_file.metadata() {
49+
Vec::with_capacity(md.len() as usize)
50+
} else {
51+
Vec::new()
52+
};
53+
contract_file.read_to_end(&mut contract_data)?;
54+
Ok(contract_data)
55+
}
56+
}
57+
58+
impl ContractCode<'_> {
59+
/// Contract code hash.
60+
pub fn hash(&self) -> &CodeHash {
61+
&self.code_hash
62+
}
63+
64+
/// Returns the `Base58` string representation of the contract key.
65+
pub fn hash_str(&self) -> String {
66+
Self::encode_hash(&self.code_hash.0)
67+
}
68+
69+
/// Reference to contract code.
70+
pub fn data(&self) -> &[u8] {
71+
&self.data
72+
}
73+
74+
/// Extracts the owned contract code data as a `Vec<u8>`.
75+
pub fn into_bytes(self) -> Vec<u8> {
76+
self.data.to_vec()
77+
}
78+
79+
/// Returns the `Base58` string representation of a hash.
80+
pub fn encode_hash(hash: &[u8; CONTRACT_KEY_SIZE]) -> String {
81+
bs58::encode(hash)
82+
.with_alphabet(bs58::Alphabet::BITCOIN)
83+
.into_string()
84+
}
85+
86+
/// Copies the data if not owned and returns an owned version of self.
87+
pub fn into_owned(self) -> ContractCode<'static> {
88+
ContractCode {
89+
data: self.data.into_owned().into(),
90+
code_hash: self.code_hash,
91+
}
92+
}
93+
94+
fn gen_hash(data: &[u8]) -> CodeHash {
95+
let mut hasher = Blake3::new();
96+
hasher.update(data);
97+
let key_arr = hasher.finalize();
98+
debug_assert_eq!(key_arr[..].len(), CONTRACT_KEY_SIZE);
99+
let mut key = [0; CONTRACT_KEY_SIZE];
100+
key.copy_from_slice(&key_arr);
101+
CodeHash(key)
102+
}
103+
}
104+
105+
impl From<Vec<u8>> for ContractCode<'static> {
106+
fn from(data: Vec<u8>) -> Self {
107+
let key = ContractCode::gen_hash(&data);
108+
ContractCode {
109+
data: Cow::from(data),
110+
code_hash: key,
111+
}
112+
}
113+
}
114+
115+
impl<'a> From<&'a [u8]> for ContractCode<'a> {
116+
fn from(data: &'a [u8]) -> ContractCode<'a> {
117+
let hash = ContractCode::gen_hash(data);
118+
ContractCode {
119+
data: Cow::from(data),
120+
code_hash: hash,
121+
}
122+
}
123+
}
124+
125+
impl PartialEq for ContractCode<'_> {
126+
fn eq(&self, other: &Self) -> bool {
127+
self.code_hash == other.code_hash
128+
}
129+
}
130+
131+
impl Eq for ContractCode<'_> {}
132+
133+
impl std::fmt::Display for ContractCode<'_> {
134+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135+
write!(f, "Contract( key: ")?;
136+
internal_fmt_key(&self.code_hash.0, f)?;
137+
let data: String = if self.data.len() > 8 {
138+
self.data[..4]
139+
.iter()
140+
.map(|b| char::from(*b))
141+
.chain("...".chars())
142+
.chain(self.data[4..].iter().map(|b| char::from(*b)))
143+
.collect()
144+
} else {
145+
self.data.iter().copied().map(char::from).collect()
146+
};
147+
write!(f, ", data: [{data}])")
148+
}
149+
}
150+
151+
impl std::fmt::Debug for ContractCode<'_> {
152+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153+
f.debug_struct("ContractCode")
154+
.field("hash", &self.code_hash);
155+
Ok(())
156+
}
157+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//! Complete contract specification combining code and parameters.
2+
//!
3+
//! This module provides the `Contract` type which represents a complete
4+
//! contract with both executable code and runtime parameters.
5+
6+
use std::io::{Cursor, Read};
7+
8+
use byteorder::{LittleEndian, ReadBytesExt};
9+
use serde::{Deserialize, Serialize};
10+
11+
use crate::parameters::Parameters;
12+
13+
use super::code::ContractCode;
14+
use super::key::{internal_fmt_key, ContractKey};
15+
16+
/// A complete contract specification requires a `parameters` section
17+
/// and a `contract` section.
18+
#[derive(Debug, Serialize, Deserialize, Clone)]
19+
#[cfg_attr(
20+
any(feature = "testing", all(test, any(unix, windows))),
21+
derive(arbitrary::Arbitrary)
22+
)]
23+
pub struct Contract<'a> {
24+
#[serde(borrow)]
25+
pub parameters: Parameters<'a>,
26+
#[serde(borrow)]
27+
pub data: ContractCode<'a>,
28+
// todo: skip serializing and instead compute it
29+
key: ContractKey,
30+
}
31+
32+
impl<'a> Contract<'a> {
33+
/// Returns a contract from [contract code](ContractCode) and given [parameters](Parameters).
34+
pub fn new(contract: ContractCode<'a>, parameters: Parameters<'a>) -> Contract<'a> {
35+
let key = ContractKey::from_params_and_code(&parameters, &contract);
36+
Contract {
37+
parameters,
38+
data: contract,
39+
key,
40+
}
41+
}
42+
43+
/// Key portion of the specification.
44+
pub fn key(&self) -> &ContractKey {
45+
&self.key
46+
}
47+
48+
/// Code portion of the specification.
49+
pub fn into_code(self) -> ContractCode<'a> {
50+
self.data
51+
}
52+
}
53+
54+
impl TryFrom<Vec<u8>> for Contract<'static> {
55+
type Error = std::io::Error;
56+
57+
fn try_from(data: Vec<u8>) -> Result<Self, Self::Error> {
58+
let mut reader = Cursor::new(data);
59+
60+
let params_len = reader.read_u64::<LittleEndian>()?;
61+
let mut params_buf = vec![0; params_len as usize];
62+
reader.read_exact(&mut params_buf)?;
63+
let parameters = Parameters::from(params_buf);
64+
65+
let contract_len = reader.read_u64::<LittleEndian>()?;
66+
let mut contract_buf = vec![0; contract_len as usize];
67+
reader.read_exact(&mut contract_buf)?;
68+
let contract = ContractCode::from(contract_buf);
69+
70+
let key = ContractKey::from_params_and_code(&parameters, &contract);
71+
72+
Ok(Contract {
73+
parameters,
74+
data: contract,
75+
key,
76+
})
77+
}
78+
}
79+
80+
impl PartialEq for Contract<'_> {
81+
fn eq(&self, other: &Self) -> bool {
82+
self.key == other.key
83+
}
84+
}
85+
86+
impl Eq for Contract<'_> {}
87+
88+
impl std::fmt::Display for Contract<'_> {
89+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90+
write!(f, "ContractSpec( key: ")?;
91+
internal_fmt_key(&self.key, f)?;
92+
let data: String = if self.data.data.len() > 8 {
93+
self.data.data[..4]
94+
.iter()
95+
.map(|b| char::from(*b))
96+
.chain("...".chars())
97+
.chain(self.data.data[4..].iter().map(|b| char::from(*b)))
98+
.collect()
99+
} else {
100+
self.data.data.iter().copied().map(char::from).collect()
101+
};
102+
write!(f, ", data: [{data}])")
103+
}
104+
}

0 commit comments

Comments
 (0)