Skip to content
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

Add contract creation allow lists to EVM domains #3350

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open

Conversation

teor2345
Copy link
Member

@teor2345 teor2345 commented Jan 15, 2025

How to review this PR

This PR is the second in a series of PRs which add "private EVM" functionality to subspace. The accompanying spec PR is subspace/protocol-specs#55 and user documentation is TODO.

The first PR in the series is #3359, it moves some test code around, and makes minor fixes.

The third PR in the series is #3360, it applies these changes and a storage migration to the Taurus runtime. (This storage isn't currently deployed on mainnet, so a migration isn't needed there.)

What it does

This PR adds a pallet which filters ethereum contract creation using an allow list. The allow list can be updated by the domain sudo account.

By default, all accounts can create contracts, to maintain compatibility with existing EVM domains. But the chainspec can be configured with an initial allow list of accounts. For our "private" EVM, that will be the domain sudo.

Close #3344.

This PR doesn't do a dynamic check for the domain sudo inside the pallet or runtime. We decided that isn't needed, because we can add the sudo account in the chainspec, or they can add themselves to the list using the (sudo) call provided by the pallet.

Part of #3353.

Other Changes

This PR makes it easier to add further code which:

  • has domain runtime type specific configurations (for example: EVM or Auto ID)
  • does recursion through utility pallet batches or call wrappers
  • extracts pallet calls from the RuntimeCall type (using MaybeInto*Call traits)
  • tests using ethereum transactions or EVM runtimes, via the EvmOnchainStateApi runtime API, and contract address calculation function

It also simplifies some existing code which was doing those things.

Bug Fixes

Fixes some transfer prevention extension code, which recursed through pallet-utility calls to use an iterator loop instead. The calls are checked in the same order, but references to the "Call stack" are stored on the heap instead. This avoids stack overflow for deeply nested calls.

Fixes some bugs in test-only code which made it harder to use.

TODOs

Code contributor checklist:

@teor2345 teor2345 added enhancement New feature or request execution Subspace execution labels Jan 15, 2025
@teor2345 teor2345 self-assigned this Jan 15, 2025
@teor2345

This comment was marked as resolved.

NingLin-P

This comment was marked as resolved.

teor2345

This comment was marked as resolved.

@vedhavyas

This comment was marked as resolved.

@teor2345

This comment was marked as resolved.

@vedhavyas

This comment was marked as resolved.

Copy link
Member

@vedhavyas vedhavyas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense overall. Left questions on penalizing malicious operator including these txns in bundle

domains/runtime/evm/src/lib.rs Outdated Show resolved Hide resolved
domains/pallets/evm_nonce_tracker/src/lib.rs Outdated Show resolved Hide resolved
domains/pallets/evm_nonce_tracker/src/lib.rs Outdated Show resolved Hide resolved
domains/runtime/evm/src/lib.rs Outdated Show resolved Hide resolved
domains/runtime/evm/src/lib.rs Outdated Show resolved Hide resolved
domains/runtime/evm/src/lib.rs Outdated Show resolved Hide resolved
crates/pallet-domains/src/domain_registry.rs Outdated Show resolved Hide resolved
@@ -633,6 +634,7 @@ fn test_bundle_format_verification() {
bundle_slot_probability: (1, 1),
operator_allow_list: OperatorAllowList::Anyone,
initial_balances: Default::default(),
initial_evm_contract_creation_allow_list: None,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add another test with some address included ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to do this after the API is stable, so I don't have to rework the tests multiple times.

Copy link
Member Author

@teor2345 teor2345 Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test matrix:

  • Instantiate domain with allow list, using all allow list combinations below
    • Instantiate domain without allow list (existing tests)
  • Update allow list using call, with all allow list combinations below
  • Migration v2 to v3 storage format (Taurus only)

Test contract allow/deny for all combinations of:

  • Anyone vs Empty vs 1 Account vs Multiple Accounts (First vs not First)
    • Signer vs Not Signer vs Unsigned
  • Contract vs Non-Contract vs Deeply nested (contract vs non-contract)
    • Ethereum (self-contained) vs EVM (signed extension) calls
      • legacy vs vs eip1559 vs eip2930

domains/runtime/evm/src/lib.rs Outdated Show resolved Hide resolved
domains/pallets/evm_nonce_tracker/src/lib.rs Outdated Show resolved Hide resolved
domains/runtime/evm/src/lib.rs Outdated Show resolved Hide resolved
crates/pallet-domains/src/runtime_registry.rs Outdated Show resolved Hide resolved
crates/pallet-domains/src/domain_registry.rs Outdated Show resolved Hide resolved
domains/runtime/evm/src/lib.rs Outdated Show resolved Hide resolved
teor2345

This comment was marked as resolved.

@teor2345

This comment was marked as resolved.

@nazar-pc

This comment was marked as resolved.

@@ -161,7 +162,7 @@ pub type BlockTreeNodeFor<T> = crate::block_tree::BlockTreeNode<
>;

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this due to Taurus being on storage version 2 already ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if that is the case, we can manually bump right before we do taurus upgrade no ?

crates/subspace-runtime/src/lib.rs Show resolved Hide resolved
Copy link
Member Author

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've finished about half the tests, see the checklist in the comments

@@ -161,7 +162,7 @@ pub type BlockTreeNodeFor<T> = crate::block_tree::BlockTreeNode<
>;

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crates/subspace-runtime/src/lib.rs Show resolved Hide resolved
@@ -633,6 +634,7 @@ fn test_bundle_format_verification() {
bundle_slot_probability: (1, 1),
operator_allow_list: OperatorAllowList::Anyone,
initial_balances: Default::default(),
initial_evm_contract_creation_allow_list: None,
};
Copy link
Member Author

@teor2345 teor2345 Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test matrix:

  • Instantiate domain with allow list, using all allow list combinations below
    • Instantiate domain without allow list (existing tests)
  • Update allow list using call, with all allow list combinations below
  • Migration v2 to v3 storage format (Taurus only)

Test contract allow/deny for all combinations of:

  • Anyone vs Empty vs 1 Account vs Multiple Accounts (First vs not First)
    • Signer vs Not Signer vs Unsigned
  • Contract vs Non-Contract vs Deeply nested (contract vs non-contract)
    • Ethereum (self-contained) vs EVM (signed extension) calls
      • legacy vs vs eip1559 vs eip2930

@teor2345

This comment was marked as resolved.

@teor2345 teor2345 added the bug Something isn't working label Jan 24, 2025
@teor2345

This comment was marked as outdated.

Base automatically changed from private-evm-prep to main January 30, 2025 09:50
@teor2345 teor2345 marked this pull request as ready for review January 30, 2025 20:11
@teor2345 teor2345 enabled auto-merge January 30, 2025 20:12
@teor2345 teor2345 disabled auto-merge January 30, 2025 20:14
Copy link
Member

@vedhavyas vedhavyas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some questions.

there are a lot of changes between the commits that is very confusing. Not a blocker for this PR

origin: OriginFor<T>,
contract_creation_allowed_by: PermissionedActionAllowedBy<T::AccountId>,
) -> DispatchResult {
ensure_root(origin)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine to expect as sudo but ideally a domain owner should be able to update this allowlist instead of sudo so that Consensus sudo is not always required and is also a longer process.

Currently domain owner can update the XDM allow list that is initiated from the Consensus chain so I see no reason why the same domain owner should not be able to update this list as to who can create contracts as well.
Thoughts @dariolina

Apologies for not bringing this earlier.

/// Rejects contracts that can't be created under the current allow list.
/// Returns false if the call is a contract call, and the account is *not* allowed to call it.
/// Otherwise, returns true.
pub fn is_create_contract_allowed(call: &RuntimeCall, signer: &AccountId) -> bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fn can be simplied but no string opinion if it increases readability

ContractCreationAllowedBy::<T>::get()
.map(|allowed_by| allowed_by.is_allowed(signer))
.unwrap_or_default()
.unwrap_or(true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be false instead ?

// Unlike domain instantiation, no storage value means "anyone can create contracts".
ContractCreationAllowedBy::<T>::get()
.map(|allowed_by| allowed_by.is_anyone_allowed())
.unwrap_or(true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. Shouldn't this be false ?

@@ -207,6 +207,774 @@ pub fn generate_evm_account_list(
}
}

#[tokio::test(flavor = "multi_thread")]
async fn test_evm_domain_create_contracts_with_allow_list() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a very big test. Can we split it to test multiple cases in different tests ?

produce_blocks!(ferdie, alice, 3).await.unwrap();

// add domain to consensus chain allowlist
ferdie
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to open a channel between consensus and domain for this tests ?

@@ -161,7 +162,7 @@ pub type BlockTreeNodeFor<T> = crate::block_tree::BlockTreeNode<
>;

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if that is the case, we can manually bump right before we do taurus upgrade no ?

@@ -121,7 +121,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: Cow::Borrowed("subspace"),
impl_name: Cow::Borrowed("subspace"),
authoring_version: 0,
spec_version: 2,
// TODO: before deploying the next runtime version to mainnet or taurus, bump this version
spec_version: 11,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even this one, let not bump it.
when wwe create a taurus upgrade branch, we can bump it then.
TODO might be noise here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
audit-P2 Medium audit priority bug Something isn't working enhancement New feature or request execution Subspace execution
Projects
None yet
Development

Successfully merging this pull request may close these issues.

"Private" EVM
4 participants