stylus_tools/core/
verification.rs1use crate::core::deployment::deployer::StylusDeployer::deployCall;
5use crate::core::deployment::deployer::{stylus_constructorCall, ADDRESS};
6use crate::core::verification::VerificationError::{
7 InvalidDeployerAddress, InvalidInitData, TransactionReceiptError, TxNotSuccessful,
8};
9use crate::{
10 core::{deployment::prelude::DeploymentCalldata, project::contract::Contract, reflection},
11 utils::cargo,
12};
13
14use alloy::sol_types::SolCall;
15use alloy::{
16 consensus::Transaction,
17 primitives::{Address, TxHash},
18 providers::Provider,
19};
20
21pub async fn verify(
22 contract: &Contract,
23 tx_hash: TxHash,
24 skip_clean: bool,
25 provider: &impl Provider,
26) -> Result<VerificationStatus, VerificationError> {
27 let tx = provider
28 .get_transaction_by_hash(tx_hash)
29 .await?
30 .ok_or(VerificationError::NoCodeAtAddress)?;
31 if !skip_clean {
32 cargo::clean()?;
33 }
34 let deployment_success = provider
35 .get_transaction_receipt(tx_hash)
36 .await?
37 .map(|receipt| receipt.status())
38 .ok_or(TransactionReceiptError)?;
39 if !deployment_success {
40 return Err(TxNotSuccessful);
41 }
42 let status = contract.check(None, &Default::default(), provider).await?;
43 let deployment_data = DeploymentCalldata::new(status.code());
44
45 match tx.to() {
46 Some(deployer_address) => {
47 verify_constructor_deployment(tx.input(), &deployment_data, deployer_address)
48 }
49 _ => verify_create_deployment(&DeploymentCalldata(tx.input().to_vec()), &deployment_data),
50 }
51}
52
53fn verify_constructor_deployment(
54 tx_input: &[u8],
55 deployment_data: &DeploymentCalldata,
56 deployer_address: Address,
57) -> Result<VerificationStatus, VerificationError> {
58 let _constructor = reflection::constructor()?.ok_or(VerificationError::NoConstructor)?;
59 let deploy_call = deployCall::abi_decode(tx_input).unwrap();
60 let constructor_called = deploy_call
61 .initData
62 .starts_with(stylus_constructorCall::SELECTOR.as_slice());
63 if !constructor_called {
64 return Err(InvalidInitData);
65 }
66 if deployer_address != ADDRESS {
67 return Err(InvalidDeployerAddress);
68 }
69 verify_create_deployment(
70 &DeploymentCalldata(deploy_call.bytecode.to_vec()),
71 deployment_data,
72 )
73}
74
75fn verify_create_deployment(
76 calldata: &DeploymentCalldata,
77 deployment_data: &DeploymentCalldata,
78) -> Result<VerificationStatus, VerificationError> {
79 if deployment_data == calldata {
80 return Ok(VerificationStatus::Success);
81 }
82
83 let tx_prelude = calldata.prelude();
84 let build_prelude = deployment_data.prelude();
85 let prelude_mismatch = if tx_prelude == build_prelude {
86 None
87 } else {
88 Some(PreludeMismatch {
89 tx: hex::encode(tx_prelude),
90 build: hex::encode(build_prelude),
91 })
92 };
93
94 let tx_wasm_length = calldata.compressed_wasm().len();
95 let build_wasm_length = deployment_data.compressed_wasm().len();
96 Ok(VerificationStatus::Failure(VerificationFailure {
97 prelude_mismatch,
98 tx_wasm_length,
99 build_wasm_length,
100 }))
101}
102
103#[derive(Debug)]
104pub enum VerificationStatus {
105 Success,
106 Failure(VerificationFailure),
107}
108
109#[derive(Debug)]
110pub struct VerificationFailure {
111 pub prelude_mismatch: Option<PreludeMismatch>,
112 pub tx_wasm_length: usize,
113 pub build_wasm_length: usize,
114}
115
116#[derive(Debug)]
117pub struct PreludeMismatch {
118 pub tx: String,
119 pub build: String,
120}
121
122#[derive(Debug, thiserror::Error)]
123pub enum VerificationError {
124 #[error("RPC failed: {0}")]
125 Rpc(#[from] alloy::transports::RpcError<alloy::transports::TransportErrorKind>),
126
127 #[error("{0}")]
128 Check(#[from] crate::core::check::CheckError),
129 #[error("{0}")]
130 Reflection(#[from] crate::core::reflection::ReflectionError),
131 #[error("{0}")]
132 Command(#[from] crate::error::CommandError),
133
134 #[error("No code at address")]
135 NoCodeAtAddress,
136 #[error("Deployment transaction uses constructor but the local project doesn't have one")]
137 NoConstructor,
138 #[error("Invalid init data: Constructor not called")]
139 InvalidInitData,
140 #[error("Invalid deployer address")]
141 InvalidDeployerAddress,
142 #[error("Transaction receipt error")]
143 TransactionReceiptError,
144 #[error("Deployment transaction not successful")]
145 TxNotSuccessful,
146}