Skip to content

Commit 9a9e7a2

Browse files
committed
nit
1 parent 15f9b89 commit 9a9e7a2

File tree

11 files changed

+154
-175
lines changed

11 files changed

+154
-175
lines changed

substrate/frame/revive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ alloy-consensus = { workspace = true, default-features = true }
7070
array-bytes = { workspace = true, default-features = true }
7171
assert_matches = { workspace = true }
7272
itertools = { workspace = true }
73+
k256 = { workspace = true, features = ["alloc", "ecdsa"] }
7374
pretty_assertions = { workspace = true }
7475
secp256k1 = { workspace = true, features = ["recovery"] }
7576
serde_json = { workspace = true }

substrate/frame/revive/src/evm/call.rs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,6 @@ impl GenericTransaction {
7272
let is_dry_run = matches!(mode, CreateCallMode::DryRun);
7373
let base_fee = <Pallet<T>>::evm_base_fee();
7474

75-
// EIP-7702: Store the authorization list for later processing
76-
let authorization_list = self.authorization_list.clone();
77-
7875
// We would like to allow for transactions without a chain id to be executed through pallet
7976
// revive. These are called unprotected transactions and they are transactions that predate
8077
// EIP-155 which do not include a Chain ID. These transactions are still useful today in
@@ -187,20 +184,8 @@ impl GenericTransaction {
187184

188185
crate::Call::eth_substrate_call::<T> { call: Box::new(call), transaction_encoded }
189186
.into()
190-
} else if self.authorization_list.is_empty() {
191-
crate::Call::eth_call::<T> {
192-
dest,
193-
value,
194-
weight_limit: Zero::zero(),
195-
eth_gas_limit: gas,
196-
data,
197-
transaction_encoded,
198-
effective_gas_price,
199-
encoded_len,
200-
}
201-
.into()
202187
} else {
203-
crate::Call::eth_call_with_authorization_list::<T> {
188+
crate::Call::eth_call::<T> {
204189
dest,
205190
value,
206191
weight_limit: Zero::zero(),

substrate/frame/revive/src/evm/eip7702.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ fn recover_authority(auth: &AuthorizationListEntry) -> Result<H160, ()> {
185185
/// - `address`: Target address to delegate to
186186
/// - `nonce`: Nonce for the authorization
187187
#[cfg(any(test, feature = "runtime-benchmarks"))]
188+
#[allow(dead_code)] // Used by benchmarks; tests use Signer::sign_authorization wrapper
188189
pub fn sign_authorization(
189190
signing_key: &k256::ecdsa::SigningKey,
190191
chain_id: U256,

substrate/frame/revive/src/exec.rs

Lines changed: 123 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,12 @@ struct Frame<T: Config> {
664664
/// The delegate call info of the currently executing frame which was spawned by
665665
/// `delegate_call`.
666666
delegate: Option<DelegateInfo<T>>,
667+
/// The address where the code (and immutable data) originates from.
668+
///
669+
/// For regular contracts, this equals the contract's own address.
670+
/// For delegated accounts (EIP-7702), this is the delegation target's address.
671+
/// For explicit delegate_call, this is the callee's address.
672+
code_address: H160,
667673
/// The output of the last executed call frame.
668674
last_frame_output: ExecReturnValue,
669675
/// The set of contracts that were created during this call stack.
@@ -1054,111 +1060,134 @@ where
10541060
input_data: &[u8],
10551061
exec_config: &ExecConfig<T>,
10561062
) -> Result<Option<(Frame<T>, ExecutableOrPrecompile<T, E, Self>)>, ExecError> {
1057-
let (account_id, contract_info, executable, delegate, entry_point) = match frame_args {
1058-
FrameArgs::Call { dest, cached_info, delegated_call } => {
1059-
let address = T::AddressMapper::to_address(&dest);
1060-
let precompile = <AllPrecompiles<T>>::get(address.as_fixed_bytes());
1061-
1062-
// which contract info to load is unaffected by the fact if this
1063-
// is a delegate call or not
1064-
let contract = match (cached_info, &precompile) {
1065-
(Some(info), _) => CachedContract::Cached(info),
1066-
(None, None) =>
1067-
if let Some(info) = AccountInfo::<T>::load_contract(&address) {
1068-
CachedContract::Cached(info)
1069-
} else {
1070-
CachedContract::None
1063+
let (account_id, contract_info, executable, delegate, code_address, entry_point) =
1064+
match frame_args {
1065+
FrameArgs::Call { dest, cached_info, delegated_call } => {
1066+
let address = T::AddressMapper::to_address(&dest);
1067+
let precompile = <AllPrecompiles<T>>::get(address.as_fixed_bytes());
1068+
1069+
// which contract info to load is unaffected by the fact if this
1070+
// is a delegate call or not
1071+
let contract = match (cached_info, &precompile) {
1072+
(Some(info), _) => CachedContract::Cached(info),
1073+
(None, None) => {
1074+
if let Some(info) = AccountInfo::<T>::load_contract(&address) {
1075+
CachedContract::Cached(info)
1076+
} else {
1077+
CachedContract::None
1078+
}
10711079
},
1072-
(None, Some(precompile)) if precompile.has_contract_info() => {
1073-
log::trace!(target: LOG_TARGET, "found precompile for address {address:?}");
1074-
if let Some(info) = AccountInfo::<T>::load_contract(&address) {
1075-
CachedContract::Cached(info)
1076-
} else {
1077-
let info = ContractInfo::new(&address, 0u32.into(), H256::zero())?;
1078-
CachedContract::Cached(info)
1079-
}
1080-
},
1081-
(None, Some(_)) => CachedContract::None,
1082-
};
1080+
(None, Some(precompile)) if precompile.has_contract_info() => {
1081+
log::trace!(target: LOG_TARGET, "found precompile for address {address:?}");
1082+
if let Some(info) = AccountInfo::<T>::load_contract(&address) {
1083+
CachedContract::Cached(info)
1084+
} else {
1085+
let info = ContractInfo::new(&address, 0u32.into(), H256::zero())?;
1086+
CachedContract::Cached(info)
1087+
}
1088+
},
1089+
(None, Some(_)) => CachedContract::None,
1090+
};
10831091

1084-
let delegated_call = delegated_call.or_else(|| {
1085-
exec_config.mock_handler.as_ref().and_then(|mock_handler| {
1086-
mock_handler.mock_delegated_caller(address, input_data)
1087-
})
1088-
});
1089-
// in case of delegate the executable is not the one at `address`
1090-
let executable = if let Some(delegated_call) = &delegated_call {
1091-
if let Some(precompile) =
1092-
<AllPrecompiles<T>>::get(delegated_call.callee.as_fixed_bytes())
1093-
{
1094-
ExecutableOrPrecompile::Precompile {
1095-
instance: precompile,
1096-
_phantom: Default::default(),
1092+
let delegated_call = delegated_call.or_else(|| {
1093+
exec_config.mock_handler.as_ref().and_then(|mock_handler| {
1094+
mock_handler.mock_delegated_caller(address, input_data)
1095+
})
1096+
});
1097+
// in case of delegate the executable is not the one at `address`
1098+
// code_address tracks where the code (and immutable data) comes from
1099+
let (executable, code_address) = if let Some(delegated_call) = &delegated_call {
1100+
if let Some(precompile) =
1101+
<AllPrecompiles<T>>::get(delegated_call.callee.as_fixed_bytes())
1102+
{
1103+
(
1104+
ExecutableOrPrecompile::Precompile {
1105+
instance: precompile,
1106+
_phantom: Default::default(),
1107+
},
1108+
delegated_call.callee,
1109+
)
1110+
} else {
1111+
let Some(info) =
1112+
AccountInfo::<T>::load_contract(&delegated_call.callee)
1113+
else {
1114+
return Ok(None);
1115+
};
1116+
let executable = E::from_storage(info.code_hash, meter)?;
1117+
(ExecutableOrPrecompile::Executable(executable), delegated_call.callee)
10971118
}
10981119
} else {
1099-
let Some(info) = AccountInfo::<T>::load_contract(&delegated_call.callee)
1100-
else {
1101-
return Ok(None);
1102-
};
1103-
let executable = E::from_storage(info.code_hash, meter)?;
1104-
ExecutableOrPrecompile::Executable(executable)
1105-
}
1106-
} else {
1107-
if let Some(precompile) = precompile {
1108-
ExecutableOrPrecompile::Precompile {
1109-
instance: precompile,
1110-
_phantom: Default::default(),
1120+
// For regular calls, check if the address is delegated (EIP-7702)
1121+
// If so, use the delegation target as the code source
1122+
let code_addr =
1123+
AccountInfo::<T>::get_delegation_target(&address).unwrap_or(address);
1124+
if let Some(precompile) = precompile {
1125+
(
1126+
ExecutableOrPrecompile::Precompile {
1127+
instance: precompile,
1128+
_phantom: Default::default(),
1129+
},
1130+
code_addr,
1131+
)
1132+
} else {
1133+
let Some(info) = AccountInfo::<T>::load_contract(&address) else {
1134+
return Ok(None);
1135+
};
1136+
let executable = E::from_storage(info.code_hash, meter)?;
1137+
(ExecutableOrPrecompile::Executable(executable), code_addr)
11111138
}
1112-
} else {
1113-
let Some(info) = AccountInfo::<T>::load_contract(&address) else {
1114-
return Ok(None);
1115-
};
1116-
let executable = E::from_storage(info.code_hash, meter)?;
1117-
ExecutableOrPrecompile::Executable(executable)
1118-
}
1119-
};
1139+
};
11201140

1121-
(dest, contract, executable, delegated_call, ExportedFunction::Call)
1122-
},
1123-
FrameArgs::Instantiate { sender, executable, salt, input_data } => {
1124-
let deployer = T::AddressMapper::to_address(&sender);
1125-
let account_nonce = <System<T>>::account_nonce(&sender);
1126-
let address = if let Some(salt) = salt {
1127-
address::create2(&deployer, executable.code(), input_data, salt)
1128-
} else {
1129-
use sp_runtime::Saturating;
1130-
address::create1(
1131-
&deployer,
1132-
// the Nonce from the origin has been incremented pre-dispatch, so we
1133-
// need to subtract 1 to get the nonce at the time of the call.
1134-
if origin_is_caller {
1135-
account_nonce.saturating_sub(1u32.into()).saturated_into()
1136-
} else {
1137-
account_nonce.saturated_into()
1138-
},
1141+
(
1142+
dest,
1143+
contract,
1144+
executable,
1145+
delegated_call,
1146+
code_address,
1147+
ExportedFunction::Call,
11391148
)
1140-
};
1141-
let contract = ContractInfo::new(
1142-
&address,
1143-
<System<T>>::account_nonce(&sender),
1144-
*executable.code_hash(),
1145-
)?;
1146-
(
1147-
T::AddressMapper::to_fallback_account_id(&address),
1148-
CachedContract::Cached(contract),
1149-
ExecutableOrPrecompile::Executable(executable),
1150-
None,
1151-
ExportedFunction::Constructor,
1152-
)
1153-
},
1154-
};
1149+
},
1150+
FrameArgs::Instantiate { sender, executable, salt, input_data } => {
1151+
let deployer = T::AddressMapper::to_address(&sender);
1152+
let account_nonce = <System<T>>::account_nonce(&sender);
1153+
let address = if let Some(salt) = salt {
1154+
address::create2(&deployer, executable.code(), input_data, salt)
1155+
} else {
1156+
use sp_runtime::Saturating;
1157+
address::create1(
1158+
&deployer,
1159+
// the Nonce from the origin has been incremented pre-dispatch, so we
1160+
// need to subtract 1 to get the nonce at the time of the call.
1161+
if origin_is_caller {
1162+
account_nonce.saturating_sub(1u32.into()).saturated_into()
1163+
} else {
1164+
account_nonce.saturated_into()
1165+
},
1166+
)
1167+
};
1168+
let contract = ContractInfo::new(
1169+
&address,
1170+
<System<T>>::account_nonce(&sender),
1171+
*executable.code_hash(),
1172+
)?;
1173+
(
1174+
T::AddressMapper::to_fallback_account_id(&address),
1175+
CachedContract::Cached(contract),
1176+
ExecutableOrPrecompile::Executable(executable),
1177+
None,
1178+
address, // For instantiate, code_address is the new contract's address
1179+
ExportedFunction::Constructor,
1180+
)
1181+
},
1182+
};
11551183

11561184
let frame = Frame {
11571185
delegate,
11581186
value_transferred,
11591187
contract_info,
11601188
account_id,
11611189
entry_point,
1190+
code_address,
11621191
frame_meter: meter.new_nested(call_resources)?,
11631192
allows_reentry: true,
11641193
read_only,
@@ -1938,13 +1967,9 @@ where
19381967
return Err(Error::<T>::InvalidImmutableAccess.into());
19391968
}
19401969

1941-
// Immutable is read from contract code being executed
1942-
let address = self
1943-
.top_frame()
1944-
.delegate
1945-
.as_ref()
1946-
.map(|d| d.callee)
1947-
.unwrap_or(T::AddressMapper::to_address(self.account_id()));
1970+
// Immutable data is read from the address where the code originates.
1971+
// This handles regular contracts, delegated accounts (EIP-7702), and delegate_call.
1972+
let address = self.top_frame().code_address;
19481973
Ok(<ImmutableDataOf<T>>::get(address).ok_or_else(|| Error::<T>::InvalidImmutableAccess)?)
19491974
}
19501975

substrate/frame/revive/src/lib.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,32 +1345,38 @@ pub mod pallet {
13451345
transaction_encoded: Vec<u8>,
13461346
effective_gas_price: U256,
13471347
encoded_len: u32,
1348-
authorization_list: Vec<evm::SignedAuthorizationListEntry>,
1348+
authorization_list: Vec<evm::AuthorizationListEntry>,
13491349
) -> DispatchResultWithPostInfo {
13501350
let signer = Self::ensure_eth_signed(origin)?;
13511351
let origin = OriginFor::<T>::signed(signer.clone());
13521352

13531353
Self::ensure_non_contract_if_signed(&origin)?;
13541354

1355+
// Process EIP-7702 authorizations with weight metering
13551356
let (eth_gas_limit, weight_limit) = if !authorization_list.is_empty() {
13561357
let chain_id = U256::from(T::ChainId::get());
1357-
let (new_accounts, existing_accounts) =
1358-
evm::eip7702::process_authorizations::<T>(&authorization_list, chain_id);
13591358

1359+
// Calculate worst-case weight for authorization processing
13601360
let auth_count = authorization_list.len() as u64;
1361-
let single_auth_weight = T::WeightInfo::process_single_authorization();
1362-
let auth_weight = single_auth_weight
1361+
let auth_weight = T::WeightInfo::validate_authorization()
13631362
.saturating_mul(auth_count)
1364-
.saturating_add(T::WeightInfo::apply_delegations_existing(
1365-
existing_accounts as u32,
1366-
))
1367-
.saturating_add(T::WeightInfo::apply_delegations_new(new_accounts as u32));
1368-
1363+
.saturating_add(T::WeightInfo::apply_delegation(1).saturating_mul(auth_count));
1364+
1365+
// Create a weight meter and process authorizations
1366+
let mut meter = frame_support::weights::WeightMeter::with_limit(auth_weight);
1367+
evm::eip7702::process_authorizations::<T>(
1368+
&authorization_list,
1369+
chain_id,
1370+
&mut meter,
1371+
)?;
1372+
1373+
// Adjust gas/weight limits by consumed weight
1374+
let consumed = meter.consumed();
13691375
let gas_scale: u64 = T::GasScale::get().into();
1370-
let auth_gas = auth_weight.ref_time().saturating_div(gas_scale);
1376+
let auth_gas = consumed.ref_time().saturating_div(gas_scale);
13711377

13721378
let adjusted_gas_limit = eth_gas_limit.saturating_sub(U256::from(auth_gas));
1373-
let adjusted_weight_limit = weight_limit.saturating_sub(auth_weight);
1379+
let adjusted_weight_limit = weight_limit.saturating_sub(consumed);
13741380

13751381
(adjusted_gas_limit, adjusted_weight_limit)
13761382
} else {
@@ -1386,7 +1392,7 @@ pub mod pallet {
13861392
transaction_encoded: transaction_encoded.clone(),
13871393
effective_gas_price,
13881394
encoded_len,
1389-
authorization_list,
1395+
authorization_list: authorization_list.clone(),
13901396
}
13911397
.into();
13921398
let info = T::FeeInfo::dispatch_info(&call);

substrate/frame/revive/src/metering/storage.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,11 +409,12 @@ where
409409
self.total_deposit = self.total_deposit.saturating_sub(&amount);
410410
last.state = ContractState::Terminated;
411411
},
412-
(ContractState::Terminated, ContractState::Terminated) =>
412+
(ContractState::Terminated, ContractState::Terminated) => {
413413
debug_assert!(
414414
false,
415415
"We never emit two terminates for the same contract."
416-
),
416+
)
417+
},
417418
}
418419
continue;
419420
}

substrate/frame/revive/src/storage.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,6 @@ impl<T: Config> AccountInfo<T> {
293293
updated.code_hash = target_code_hash;
294294
updated
295295
} else {
296-
// Not currently delegated, create new ContractInfo
297296
let account_id = T::AddressMapper::to_account_id(address);
298297
let nonce = frame_system::Pallet::<T>::account_nonce(&account_id);
299298
ContractInfo::<T>::new_for_delegation(address, nonce, target_code_hash)?

0 commit comments

Comments
 (0)