Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Customizable ink address #10521

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/node/runtime/src/lib.rs
Expand Up @@ -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 {
Expand Down
71 changes: 56 additions & 15 deletions frame/contracts/src/lib.rs
Expand Up @@ -136,6 +136,55 @@ type BalanceOf<T> =
/// 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<T: frame_system::Config> {
/// 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(
Copy link
Member

Choose a reason for hiding this comment

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

Every public item needs rust doc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Member

Choose a reason for hiding this comment

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

Thanks. I think we need to document some warnings here though:

  1. Implementer must make sure that there are no collisions (same output for different inputs).
  2. Implementer must make sure that the same inputs lead to the same output (including environmental inputs like your storage location).
  3. Changing the implementation through a runtime upgrade without a proper storage migration would be a a catastrophic event.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion. Appended them to the doc.

deploying_address: &T::AccountId,
code_hash: &CodeHash<T>,
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<T> AddressGenerator<T> for DefaultAddressGenerator
where
T: frame_system::Config,
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
fn generate_address(
deploying_address: &T::AccountId,
code_hash: &CodeHash<T>,
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::*;
Expand Down Expand Up @@ -227,6 +276,9 @@ pub mod pallet {
/// Changing this value for an existing chain might need a storage migration.
#[pallet::constant]
type DepositPerItem: Get<BalanceOf<Self>>;

/// The address generator used to generate the addresses of contracts.
type AddressGenerator: AddressGenerator<Self>;
}

#[pallet::pallet]
Expand Down Expand Up @@ -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<T>,
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.
Expand Down
4 changes: 3 additions & 1 deletion frame/contracts/src/tests.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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]);
Expand Down