Skip to main content

pop_contracts/
verify.rs

1// SPDX-License-Identifier: GPL-3.0
2
3mod helpers;
4
5use crate::{BuildMode, Error, ImageVariant, Verbosity};
6use pop_common::Docker;
7use std::path::PathBuf;
8
9/// Contract deployed on-chain.
10pub struct DeployedContract {
11	/// An endpoint to the chain where the contract is deployed.
12	pub rpc_endpoint: String,
13	/// The contract address.
14	pub contract_address: String,
15	/// The image used to build the deployed contract.
16	pub build_image: ImageVariant,
17}
18
19/// The reference contract, either local or deployed on chain.
20enum ReferenceContract {
21	Local(PathBuf),
22	Deployed(DeployedContract),
23}
24
25/// Used to verify a contract
26pub struct VerifyContract {
27	/// The path containing the source contract to be verified against `reference_contract`.
28	verifying_path: PathBuf,
29	/// The reference contract to verify against
30	reference_contract: ReferenceContract,
31}
32
33impl VerifyContract {
34	/// Creates a new `VerifyContract` instance used to verify against a local bundle.
35	///
36	/// # Arguments
37	/// - `verifying_path` - The path to the local project being verified.
38	/// - `reference_contract_bundle_path` - The path to the ".contract" bundle to verify against.
39	pub fn new_local(verifying_path: PathBuf, reference_contract_bundle_path: PathBuf) -> Self {
40		Self {
41			verifying_path,
42			reference_contract: ReferenceContract::Local(reference_contract_bundle_path),
43		}
44	}
45
46	/// Creates a new `VerifyContract` instance used to verify against a deployed contract.
47	///
48	/// # Arguments
49	/// - `verifying_path` - The path to the local project being verified.
50	/// - `reference_deployed_contract` - The deployed contract info.
51	pub fn new_deployed(
52		verifying_path: PathBuf,
53		reference_deployed_contract: DeployedContract,
54	) -> Self {
55		Self {
56			verifying_path,
57			reference_contract: ReferenceContract::Deployed(reference_deployed_contract),
58		}
59	}
60
61	/// Verify the contract
62	pub async fn execute(self) -> Result<(), Error> {
63		let (build_mode, image, reference_code_hash) = match self.reference_contract {
64			ReferenceContract::Local(reference_path) => {
65				// Parse the contract bundle
66				let build_info_parsed =
67					helpers::get_build_info_parsed_from_contract_bundle(&reference_path)?;
68
69				// If  the reference contract was built deterministically, just ensure Docker is
70				// running so we can run the image. Otherwise check that the local toolchain is
71				// the same one used to compile the reference contract.
72				if let BuildMode::Verifiable = &build_info_parsed.build_mode {
73					Docker::ensure_running().await?;
74				} else {
75					helpers::compare_local_toolchain(&build_info_parsed.build_info)?;
76				}
77
78				(
79					build_info_parsed.build_mode,
80					build_info_parsed.image,
81					build_info_parsed.polkavm_code_hash,
82				)
83			},
84			ReferenceContract::Deployed(deployed_contract) => {
85				let reference_code_hash = helpers::get_deployed_polkavm_code_hash(
86					&deployed_contract.rpc_endpoint,
87					&deployed_contract.contract_address,
88				)
89				.await?;
90
91				// All verifications against live contracts use Docker images
92				Docker::ensure_running().await?;
93
94				(BuildMode::Verifiable, Some(deployed_contract.build_image), reference_code_hash)
95			},
96		};
97
98		let mut build_results = crate::build_smart_contract(
99			&self.verifying_path,
100			build_mode,
101			Verbosity::default(),
102			None,
103			image,
104		)?;
105
106		if build_results.len() == 1 {
107			helpers::verify_polkavm_code_hash_against_build_result(
108				reference_code_hash,
109				build_results.pop().expect("There's one build result; qed;"),
110			)?;
111		} else {
112			return Err(Error::Verification(format!(
113				"Only can run verification against 1 contract, found {} in the worsskpace",
114				build_results.len()
115			)));
116		}
117
118		Ok(())
119	}
120}