-
Notifications
You must be signed in to change notification settings - Fork 40
feat: Build verifiable contracts #800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Build verifiable contracts #800
Conversation
05c0d27 to
ea47304
Compare
…builds from cargo contract
Cargo.toml
Outdated
| contract-build = { git = "https://github.com/use-ink/cargo-contract", rev = "c7aba2e", default-features = false } | ||
| contract-extrinsics = { git = "https://github.com/use-ink/cargo-contract", rev = "c7aba2e", default-features = false } | ||
| contract-transcode = { git = "https://github.com/use-ink/cargo-contract", rev = "c7aba2e", default-features = false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporarily needed til these changes are released. Please note that the base branch for this PR isn't main but feat/contract-verification so we can merge as soon as the review is done
| os: | ||
| - ubuntu-latest | ||
| - macos-latest | ||
| - macos-13 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a docker daemon running for the new verifiable_contract_lifecycle test.
Apparently, GitHub Actions doesn't support virtualization for ARM chips (reference run https://github.com/r0gue-io/pop-cli/actions/runs/19732309848/job/56536182023), so we need to either:
- Use an Intel chip version.
- Skip that test on MacOs.
I prefer 1, skipping a test smells super weird. But it's true that the job is running far slower than the Linux one due to all this virtualization stuff, not the test itself. So option 2 might also be considered
19d213e to
8645ec0
Compare
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## feat/contract-verification #800 +/- ##
=============================================================
Coverage ? 76.05%
=============================================================
Files ? 115
Lines ? 26423
Branches ? 26423
=============================================================
Hits ? 20095
Misses ? 4190
Partials ? 2138
🚀 New features to boost your workflow:
|
6c8bec3 to
8472867
Compare
8472867 to
25e97d6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds support for building verifiable smart contracts using Docker-based deterministic builds, leveraging the verifiable build functionality from cargo-contract. The feature enables reproducible builds through the new --verifiable flag and optional custom Docker images via the --image flag.
Key Changes:
- Changed the build API from accepting a boolean
releaseparameter to aBuildModeenum (Debug/Release/Verifiable) - Added Docker support for macOS CI testing using Colima with QEMU
- Introduced
--manifest-pathas an alias for--pathto support direct Cargo.toml references
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
crates/pop-contracts/src/build.rs |
Updated build_smart_contract to accept BuildMode and ImageVariant; added tokio runtime handling for verifiable builds |
crates/pop-contracts/src/utils/mod.rs |
Enhanced get_manifest_path to accept either directory paths or direct Cargo.toml paths |
crates/pop-contracts/src/lib.rs |
Exported new types BuildMode and ImageVariant from the public API |
crates/pop-contracts/README.md |
Updated example code to reflect new BuildMode parameter |
crates/pop-cli/src/commands/build/mod.rs |
Added --verifiable and --image CLI flags with --manifest-path alias; updated test fixtures |
crates/pop-cli/src/commands/build/contract.rs |
Added resolve_build_mode and resolve_image helper functions with comprehensive unit tests |
crates/pop-cli/src/commands/up/contract.rs |
Updated to use BuildMode::Release instead of boolean |
crates/pop-cli/src/commands/call/contract.rs |
Updated to use BuildMode::Release instead of boolean |
crates/pop-cli/tests/contract.rs |
Added integration test for verifiable contract build with metadata validation |
Cargo.toml |
Changed contract dependencies from crates.io versions to git revision c7aba2e |
Cargo.lock |
Updated dependency sources to match Cargo.toml changes |
.github/workflows/integration-tests.yml |
Added Docker/Colima setup for macOS-13; configured TMPDIR for Docker compatibility |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| match (&args.profile, args.verifiable) { | ||
| (Some(Profile::Release), false) | (Some(Profile::Production), false) => BuildMode::Release, | ||
| (None, true) => BuildMode::Verifiable, | ||
| (None, false) if args.release => BuildMode::Release, |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The resolve_build_mode function doesn't handle the case where both verifiable=true and a profile is specified (e.g., Profile::Release + verifiable=true). Due to the clap conflicts_with_all constraint on line 89 of mod.rs, this should be prevented at the CLI level. However, if this function is called programmatically in tests or other code paths, it would silently fall through to the _ catch-all and return BuildMode::Debug, which is incorrect.
Consider adding a comment explaining that this case is prevented by CLI validation, or add an explicit match arm that clarifies the behavior:
(Some(_), true) => BuildMode::Debug, // Unreachable due to CLI conflicts_with_all| (None, false) if args.release => BuildMode::Release, | |
| (None, false) if args.release => BuildMode::Release, | |
| // This case is unreachable due to CLI `conflicts_with_all` validation, | |
| // but is included for clarity in case the function is called programmatically. | |
| (Some(_), true) => BuildMode::Debug, // Unreachable due to CLI conflicts_with_all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While the review is right, the fallback is there to reduce the casuistry and just default to the simplest mode instead of failing if an invalid combination of args is used. As the review says, clap will handle this conflict, so I wouldn't add a specific case just for tests
crates/pop-cli/tests/contract.rs
Outdated
|
|
||
| #[tokio::test] | ||
| async fn verifiable_contract_lifecycle() -> Result<()> { | ||
| // TODO: Incomplete test, we'll be adding more steps as the development of the feature advance |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment contains a typo: "advance" should be "advances" (or better yet, "progresses").
Suggested correction:
// TODO: Incomplete test, we'll be adding more steps as the development of the feature progresses| // TODO: Incomplete test, we'll be adding more steps as the development of the feature advance | |
| // TODO: Incomplete test, we'll be adding more steps as the development of the feature progresses |
crates/pop-cli/tests/contract.rs
Outdated
| async fn verifiable_contract_lifecycle() -> Result<()> { | ||
| // TODO: Incomplete test, we'll be adding more steps as the development of the feature advance |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The test name verifiable_contract_lifecycle is misleading because it only tests the build step, not a full lifecycle like the contract_lifecycle test above it. The test doesn't cover deployment, instantiation, or calling the contract.
Consider renaming to something more accurate like verifiable_contract_build or updating the TODO comment to clarify that this will eventually become a full lifecycle test.
| async fn verifiable_contract_lifecycle() -> Result<()> { | |
| // TODO: Incomplete test, we'll be adding more steps as the development of the feature advance | |
| async fn verifiable_contract_build() -> Result<()> { | |
| // TODO: This test currently only covers the verifiable build step. We'll add deployment, instantiation, and calling as the feature develops, and rename the test accordingly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a lifecycle of a verifiable contract in the sense build -> publish -> verify, we won't be including calls, deployment and so on as that's already tested by contract_lifecycle. The other steps will be added as the feature development progresses
AlexD10S
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job! I left some comments on the code.
Regarding DevEx, it's not an easy thing to fix (we have a similar issue with --deterministic for chains), but just something to keep in mind: the user needs to know that Docker must be running on their machine. If they only run pop build --verifiable, the error message is confusing:
Error: Socket not found: /var/run/docker.sock
Do you have the docker engine installed in path?
| #[cfg(feature = "contract")] | ||
| pub(crate) verifiable: bool, | ||
| /// Custom image for verifiable builds | ||
| #[clap(long, help_heading = CONTRACT_HELP_HEADER)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you can add requires = "verifiable", image is useless if verifiable is not indicated no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely, let me fix it!
| ///#[cfg(feature = "contract")] | ||
| ///pub(crate) verifiable: bool | ||
| /// Whether to build in a way that the contract is verifiable. For verifiable contracts, use | ||
| /// --manifest-path instead of --path to directly point to your contracts Cargo.toml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? --manifest-path instead of --path to directly point to your contracts Cargo.toml
Can we not abstract this to the user?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the contract-build function we're using captures what's passed to the CLI: https://github.com/use-ink/cargo-contract/blob/master/crates/build/src/docker.rs#L523 and then pass it to cargo-contract CLI inside the Docker image. If we pass --path that CLI called inside docker will simply fail as it doesn't expect --path, you can try it yourself.
I was thinking on simply disabling --path if --verifiable is used, but I think it's still worth it to don't force the user to run the command inside the contract's dir.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see. The cargo-contract is a very confusing implementation no?
Because here we already get the path to the manifest path: https://github.com/r0gue-io/pop-cli/blob/main/crates/pop-contracts/src/build.rs#L22 and then we can call the build function very clean.
Not sure the best solution here to be honest, for the user might be confusing to be forced to use --manifest-path but i see your point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah it's not the best for third party integration, but normal anyway as it's designed to call cargo-contract inside the image :/
The line you mentioned is actually useless for verifiable contracts, as if the user specifies --manifest-path it's captured within the image, not passed to contract_build::execute at all.
So yeah it's not optimal but Idk if we have something better
Agreed with @AlexD10S. This message error comes directly from |
0df52e3 to
0bf0202
Compare
That would be great, it can be done in another PR if you prefer, and the same logic implemented for the |
Closes:
pop build --verifiable#794cargo contractalready contains most of the logic, so this one should be easy to review