diff --git a/scarb/src/compiler/compilers/starknet_contract/artifacts_writer.rs b/scarb/src/compiler/compilers/starknet_contract/artifacts_writer.rs index 7ee0711c8..f5269f240 100644 --- a/scarb/src/compiler/compilers/starknet_contract/artifacts_writer.rs +++ b/scarb/src/compiler/compilers/starknet_contract/artifacts_writer.rs @@ -1,6 +1,6 @@ use crate::compiler::compilers::starknet_contract::{ContractFileStemCalculator, ContractSelector}; use crate::compiler::compilers::Props; -use crate::compiler::helpers::write_json; +use crate::compiler::helpers::write_json_with_byte_count; use crate::core::{PackageName, Workspace}; use crate::flock::Filesystem; use cairo_lang_compiler::db::RootDatabase; @@ -9,11 +9,17 @@ use cairo_lang_starknet::contract::ContractDeclaration; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use cairo_lang_starknet_classes::contract_class::ContractClass; use cairo_lang_utils::UpcastMut; +use indoc::formatdoc; use itertools::{izip, Itertools}; use scarb_stable_hash::short_hash; use serde::Serialize; use smol_str::SmolStr; +const MAX_SIERRA_PROGRAM_FELTS: usize = 81290; +const MAX_CASM_PROGRAM_FELTS: usize = 81290; +const MAX_CONTRACT_CLASS_BYTES: usize = 4089446; +const MAX_COMPILED_CONTRACT_CLASS_BYTES: usize = 4089446; + #[derive(Debug, Serialize)] struct StarknetArtifacts { version: usize, @@ -132,16 +138,58 @@ impl ArtifactsWriter { ); if self.sierra { + let sierra_felts = class.sierra_program.len(); + if sierra_felts > MAX_SIERRA_PROGRAM_FELTS { + ws.config().ui().warn(formatdoc! {r#" + Sierra program exceeds maximum byte-code size on Starknet: + {MAX_SIERRA_PROGRAM_FELTS} felts allowed. Actual size: {sierra_felts} felts. + "#}); + } + let file_name = format!("{file_stem}{extension_prefix}.contract_class.json"); - write_json(&file_name, "output file", &self.target_dir, ws, class)?; + + let class_size = write_json_with_byte_count( + &file_name, + "output file", + &self.target_dir, + ws, + class, + )?; + if class_size > MAX_CONTRACT_CLASS_BYTES { + ws.config().ui().warn(formatdoc! {r#" + Contract class size exceeds maximum allowed size on Starknet: + {MAX_CONTRACT_CLASS_BYTES} bytes allowed. Actual size: {class_size} bytes. + "#}); + } artifact.artifacts.sierra = Some(file_name); } if self.casm { if let Some(casm_class) = casm_class { + let casm_felts = casm_class.bytecode.len(); + if casm_felts > MAX_CASM_PROGRAM_FELTS { + ws.config().ui().warn(formatdoc! {r#" + CASM program exceeds maximum byte-code size on Starknet: + {MAX_CASM_PROGRAM_FELTS} felts allowed. Actual size: {casm_felts} felts. + "#}); + } + let file_name = format!("{file_stem}{extension_prefix}.compiled_contract_class.json"); - write_json(&file_name, "output file", &self.target_dir, ws, casm_class)?; + + let compiled_class_size = write_json_with_byte_count( + &file_name, + "output file", + &self.target_dir, + ws, + casm_class, + )?; + if compiled_class_size > MAX_COMPILED_CONTRACT_CLASS_BYTES { + ws.config().ui().warn(formatdoc! {r#" + Compiled contract class size exceeds maximum allowed size on Starknet: + {MAX_COMPILED_CONTRACT_CLASS_BYTES} bytes allowed. Actual size: {compiled_class_size} bytes. + "#}); + } artifact.artifacts.casm = Some(file_name); } } @@ -151,7 +199,7 @@ impl ArtifactsWriter { artifacts.finish(); - write_json( + write_json_with_byte_count( &format!( "{}{extension_prefix}.starknet_artifacts.json", self.target_name diff --git a/scarb/src/compiler/helpers.rs b/scarb/src/compiler/helpers.rs index bfbe15e82..38b1c303e 100644 --- a/scarb/src/compiler/helpers.rs +++ b/scarb/src/compiler/helpers.rs @@ -14,6 +14,32 @@ use itertools::Itertools; use serde::Serialize; use std::io::{BufWriter, Write}; +pub struct CountingWriter { + inner: W, + pub byte_count: usize, +} + +impl CountingWriter { + pub fn new(inner: W) -> Self { + Self { + inner, + byte_count: 0, + } + } +} + +impl Write for CountingWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let n = self.inner.write(buf)?; + self.byte_count += n; + Ok(n) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + pub fn build_compiler_config<'c>( db: &RootDatabase, unit: &CairoCompilationUnit, @@ -112,6 +138,21 @@ pub fn write_json( Ok(()) } +pub fn write_json_with_byte_count( + file_name: &str, + description: &str, + target_dir: &Filesystem, + ws: &Workspace<'_>, + value: impl Serialize, +) -> Result { + let file = target_dir.create_rw(file_name, description, ws.config())?; + let file = BufWriter::new(&*file); + let mut writer = CountingWriter::new(file); + serde_json::to_writer(&mut writer, &value) + .with_context(|| format!("failed to serialize {file_name}"))?; + Ok(writer.byte_count) +} + pub fn write_string( file_name: &str, description: &str,