From 040580c33d991274a36ad42c43e4467e16126ffe Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Thu, 23 Dec 2021 19:32:06 +0800 Subject: [PATCH] Customizable ink address (#10521) --- bin/node/runtime/src/lib.rs | 1 + frame/contracts/src/lib.rs | 71 ++++++++++++++++++++++++++++-------- frame/contracts/src/tests.rs | 4 +- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 97de54fc21e8c..a79d8997f4ce1 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -957,6 +957,7 @@ impl pallet_contracts::Config for Runtime { type DeletionQueueDepth = DeletionQueueDepth; type DeletionWeightLimit = DeletionWeightLimit; type Schedule = Schedule; + type AddressGenerator = pallet_contracts::DefaultAddressGenerator; } impl pallet_sudo::Config for Runtime { diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index b604c9618c6ae..ab7a0c254f560 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -136,6 +136,55 @@ type BalanceOf = /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(6); +/// Provides the contract address generation method. +/// +/// See [`DefaultAddressGenerator`] for the default implementation. +pub trait AddressGenerator { + /// Generate the address of a contract based on the given instantiate parameters. + /// + /// # Note for implementors + /// 1. Make sure that there are no collisions, different inputs never lead to the same output. + /// 2. Make sure that the same inputs lead to the same output. + /// 3. Changing the implementation through a runtime upgrade without a proper storage migration + /// would lead to catastrophic misbehavior. + fn generate_address( + deploying_address: &T::AccountId, + code_hash: &CodeHash, + salt: &[u8], + ) -> T::AccountId; +} + +/// Default address generator. +/// +/// This is the default address generator used by contract instantiation. Its result +/// is only dependend on its inputs. It can therefore be used to reliably predict the +/// address of a contract. This is akin to the formular of eth's CREATE2 opcode. There +/// is no CREATE equivalent because CREATE2 is strictly more powerful. +/// +/// Formula: `hash(deploying_address ++ code_hash ++ salt)` +pub struct DefaultAddressGenerator; + +impl AddressGenerator for DefaultAddressGenerator +where + T: frame_system::Config, + T::AccountId: UncheckedFrom + AsRef<[u8]>, +{ + fn generate_address( + deploying_address: &T::AccountId, + code_hash: &CodeHash, + salt: &[u8], + ) -> T::AccountId { + let buf: Vec<_> = deploying_address + .as_ref() + .iter() + .chain(code_hash.as_ref()) + .chain(salt) + .cloned() + .collect(); + UncheckedFrom::unchecked_from(T::Hashing::hash(&buf)) + } +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -227,6 +276,9 @@ pub mod pallet { /// Changing this value for an existing chain might need a storage migration. #[pallet::constant] type DepositPerItem: Get>; + + /// The address generator used to generate the addresses of contracts. + type AddressGenerator: AddressGenerator; } #[pallet::pallet] @@ -728,27 +780,16 @@ where Ok(maybe_value) } - /// Determine the address of a contract, - /// - /// This is the address generation function used by contract instantiation. Its result - /// is only dependend on its inputs. It can therefore be used to reliably predict the - /// address of a contract. This is akin to the formular of eth's CREATE2 opcode. There - /// is no CREATE equivalent because CREATE2 is strictly more powerful. + /// Determine the address of a contract. /// - /// Formula: `hash(deploying_address ++ code_hash ++ salt)` + /// This is the address generation function used by contract instantiation. See + /// [`DefaultAddressGenerator`] for the default implementation. pub fn contract_address( deploying_address: &T::AccountId, code_hash: &CodeHash, salt: &[u8], ) -> T::AccountId { - let buf: Vec<_> = deploying_address - .as_ref() - .iter() - .chain(code_hash.as_ref()) - .chain(salt) - .cloned() - .collect(); - UncheckedFrom::unchecked_from(T::Hashing::hash(&buf)) + T::AddressGenerator::generate_address(deploying_address, code_hash, salt) } /// Store code for benchmarks which does not check nor instrument the code. diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 584253aeaadfe..6ca4e34f8aeae 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -24,7 +24,8 @@ use crate::{ storage::Storage, wasm::{PrefabWasmModule, ReturnCode as RuntimeReturnCode}, weights::WeightInfo, - BalanceOf, Code, CodeStorage, Config, ContractInfoOf, Error, Pallet, Schedule, + BalanceOf, Code, CodeStorage, Config, ContractInfoOf, DefaultAddressGenerator, Error, Pallet, + Schedule, }; use assert_matches::assert_matches; use codec::Encode; @@ -285,6 +286,7 @@ impl Config for Test { type Schedule = MySchedule; type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; + type AddressGenerator = DefaultAddressGenerator; } pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);