Skip to content

Add compile-time check that prevents ink! messages with a &self receiver from mutating state via ink_env #2405

@davidsemakula

Description

@davidsemakula

Overview

Following Rust semantics, ink! messages with a &self receiver (i.e. an immutable self reference receiver) shouldn't be able to mutate state/storage because the storage type is the Self type. However, this is currently possible by calling functions from ink_env directly (e.g. ink_env::set_contract_storage, ink_env::transfer e.t.c).

See #1969 for additional details.

Motivation

  • Consistency with Rust semantics makes ink! contracts easier to formally verify with Rust verification tools
  • Enforcement of claimed guarantees and consistency with Rust semantics makes ink! contracts easier to audit
  • Producing verifiable builds of ink! contracts with messages which mutate state, presented as read-only operations in verifiable ink! ABI metadata, and thus in potentially user facing tools (like wallets and explorers) likely has security implications at the application level

Design

  • Require ink! build tools (i.e. cargo-contract) to set a rustc cfg setting/flag (i.e. --cfg 'ink_check="msg_mut"') that essentially advertises there ability check/enforce (at compile-time) that ink! messages with &self receivers don't call any ink_env functions that can mutate state (i.e. they'll emit an error otherwise during the check or build process)
  • Update the ink proc-macro to emit an compile-time error if (and only if) it detects a message with a &self receiver and the aforementioned cfg setting is NOT enabled/set (i.e. #[cfg(not(ink_check = "msg_mut"))] - see also)
  • Create a MIR check that finds ink! message functions with immutable self reference receivers and emits a compiler error if it finds any calls to any ink_env functions that mutate state/storage/environment (i.e. ink_env::set_contract_storage, ink_env::transfer, ink_env::emit_event e.t.c)
  • Run the MIR check in cargo-contract during "check" and "build" commands for ink! contracts using ink! version >= 6

Additional Considerations

The current implementation of ink! linting infrastructure adds about a ~2x build-time penalty because it re-compiles the ink! contract with a fixed version of the Rust nightly compiler. Potential solutions for reducing this build-time penalty will be explored as part of the fix(es) for this issue.

Metadata

Metadata

Assignees

Labels

A-eDSLConcerning the Rust eDSL as a total.A-ink_lang[ink_lang] Work itemA-linterIssue regarding the ink! linter.B-designDesigning a new component, interface or functionality.Breaking changeThis PR of issue introduces a breaking change

Type

No type

Projects

Status

Backlog

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions