Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 64 additions & 26 deletions precompiles/src/alpha.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use core::marker::PhantomData;

use crate::PrecompileExt;
use fp_evm::{ExitError, PrecompileFailure};
use pallet_evm::{BalanceConverter, PrecompileHandle, SubstrateBalance};
use precompile_utils::EvmResult;
use sp_runtime::{SaturatedConversion, Vec};

use crate::PrecompileHandleExt;
use sp_core::U256;
use sp_std::vec::Vec;
use substrate_fixed::types::U64F64;
use subtensor_runtime_common::{NetUid, Token};
use subtensor_swap_interface::{Order, SwapHandler};

use crate::PrecompileExt;

pub struct AlphaPrecompile<R>(PhantomData<R>);

impl<R> PrecompileExt<R::AccountId> for AlphaPrecompile<R>
Expand All @@ -34,7 +34,9 @@ where
{
#[precompile::public("getAlphaPrice(uint16)")]
#[precompile::view]
fn get_alpha_price(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
fn get_alpha_price(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
// SubnetMechanism + SubnetAlphaIn + SubnetTAO + SwapBalancer reads
handle.record_db_reads::<R>(4)?;
let current_alpha_price =
<pallet_subtensor_swap::Pallet<R> as SwapHandler>::current_alpha_price(netuid.into());
let price = current_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000));
Expand All @@ -48,7 +50,9 @@ where

#[precompile::public("getMovingAlphaPrice(uint16)")]
#[precompile::view]
fn get_moving_alpha_price(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
fn get_moving_alpha_price(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
// SubnetMechanism + SubnetMovingPrice reads
handle.record_db_reads::<R>(2)?;
let moving_alpha_price: U64F64 =
pallet_subtensor::Pallet::<R>::get_moving_alpha_price(netuid.into());
let price = moving_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000));
Expand All @@ -62,49 +66,59 @@ where

#[precompile::public("getTaoInPool(uint16)")]
#[precompile::view]
fn get_tao_in_pool(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
fn get_tao_in_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
handle.record_db_reads::<R>(1)?;
Ok(pallet_subtensor::SubnetTAO::<R>::get(NetUid::from(netuid)).to_u64())
}

#[precompile::public("getAlphaInPool(uint16)")]
#[precompile::view]
fn get_alpha_in_pool(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
fn get_alpha_in_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
handle.record_db_reads::<R>(1)?;
Ok(pallet_subtensor::SubnetAlphaIn::<R>::get(NetUid::from(netuid)).into())
}

#[precompile::public("getAlphaOutPool(uint16)")]
#[precompile::view]
fn get_alpha_out_pool(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
fn get_alpha_out_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
handle.record_db_reads::<R>(1)?;
Ok(pallet_subtensor::SubnetAlphaOut::<R>::get(NetUid::from(netuid)).into())
}

#[precompile::public("getAlphaIssuance(uint16)")]
#[precompile::view]
fn get_alpha_issuance(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
fn get_alpha_issuance(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
// SubnetAlphaIn + SubnetAlphaOut reads
handle.record_db_reads::<R>(2)?;
Ok(pallet_subtensor::Pallet::<R>::get_alpha_issuance(netuid.into()).into())
}

#[precompile::public("getTaoWeight()")]
#[precompile::view]
fn get_tao_weight(_handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
fn get_tao_weight(handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
handle.record_db_reads::<R>(1)?;
let tao_weight = pallet_subtensor::TaoWeight::<R>::get();
Ok(U256::from(tao_weight))
}

#[precompile::public("getCKBurn()")]
#[precompile::view]
fn get_ck_burn(_handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
fn get_ck_burn(handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
handle.record_db_reads::<R>(1)?;
let ck_burn = pallet_subtensor::CKBurn::<R>::get();
Ok(U256::from(ck_burn))
}

#[precompile::public("simSwapTaoForAlpha(uint16,uint64)")]
#[precompile::view]
fn sim_swap_tao_for_alpha(
_handle: &mut impl PrecompileHandle,
handle: &mut impl PrecompileHandle,
netuid: u16,
tao: u64,
) -> EvmResult<U256> {
// SubnetMechanism + swap simulation reads
handle.record_db_reads::<R>(9)?;
Comment thread
open-junius marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Simulated tao-for-alpha swap omits rollback-write gas

sim_swap runs through SwapEngine::swap(..., should_rollback = true), and swap_inner calls maybe_initialize_palswap before the transaction is rolled back. On a subnet where PalSwapInitialized is false, that path writes SwapBalancer and PalSwapInitialized, but this precompile charges only reads. Because rollback leaves the subnet uninitialized, callers can repeat the undercharged write work through this view call. Charge the rollback writes as well.

Suggested change
handle.record_db_reads::<R>(9)?;
handle.record_db_reads::<R>(9)?;
handle.record_db_writes::<R>(2)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Simulated tao-for-alpha swap omits rollback-write gas

sim_swap is not a pure read path for uninitialized PalSwap subnets: it invokes the swap engine with rollback, and maybe_initialize_palswap can write both SwapBalancer and PalSwapInitialized before the transaction rolls back. Charging only reads lets callers make the EVM execute two storage writes without paying write gas. Charge the worst-case rollback writes before the simulated swap.

Suggested change
handle.record_db_reads::<R>(9)?;
handle.record_db_reads::<R>(9)?;
handle.record_db_writes::<R>(2)?;

Comment on lines +119 to +120

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Simulated tao-for-alpha swap omits rollback-write gas

sim_swap calls the swap engine with should_rollback = true, but mechanism-1 swaps still run maybe_initialize_palswap inside that rollbacked transaction. On an uninitialized subnet that writes SwapBalancer and PalSwapInitialized, so this precompile path can perform rollbacked storage writes while charging only read gas. Charge the worst-case write cost before entering sim_swap.

Suggested change
// SubnetMechanism + swap simulation reads
handle.record_db_reads::<R>(9)?;
// SubnetMechanism + swap simulation reads + rollbacked PalSwap initialization writes
handle.record_db_reads::<R>(9)?;
handle.record_db_writes::<R>(2)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Simulated tao-for-alpha swap omits rollback-write gas

sim_swap still routes through the dynamic-subnet swap path, where swap_inner calls maybe_initialize_palswap. On an uninitialized subnet that inserts both SwapBalancer and PalSwapInitialized before the surrounding transaction rolls back, so this precompile can perform write-cost storage work while charging only 9 reads. Charge the rollback writes too, or refactor simulation into a strictly read-only quote path.

Suggested change
handle.record_db_reads::<R>(9)?;
handle.record_db_reads::<R>(9)?;
handle.record_db_writes::<R>(2)?;


let order = pallet_subtensor::GetAlphaForTao::<R>::with_amount(tao);
let swap_result =
<pallet_subtensor_swap::Pallet<R> as SwapHandler>::sim_swap(netuid.into(), order)
Expand All @@ -117,10 +131,13 @@ where
#[precompile::public("simSwapAlphaForTao(uint16,uint64)")]
#[precompile::view]
fn sim_swap_alpha_for_tao(
_handle: &mut impl PrecompileHandle,
handle: &mut impl PrecompileHandle,
netuid: u16,
alpha: u64,
) -> EvmResult<U256> {
// SubnetMechanism + swap simulation reads
handle.record_db_reads::<R>(9)?;
Comment thread
open-junius marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Simulated alpha-for-tao swap omits rollback-write gas

This simulation path has the same rollback-write gap: sim_swap can initialize PalSwap inside a rolled-back transaction, writing SwapBalancer and PalSwapInitialized while the precompile records only read gas. Since the rollback means the initialization is not persisted, the write work can be repeatedly forced at a read-only gas price. Charge the two possible rollback writes before entering the simulation.

Suggested change
handle.record_db_reads::<R>(9)?;
handle.record_db_reads::<R>(9)?;
handle.record_db_writes::<R>(2)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Simulated alpha-for-tao swap omits rollback-write gas

This simulated swap uses the same rollbacked swap engine path as the tao-for-alpha case. On an uninitialized PalSwap subnet, maybe_initialize_palswap can insert SwapBalancer and PalSwapInitialized before rollback, but this precompile charges only read gas. Charge the two possible rollback writes as well.

Suggested change
handle.record_db_reads::<R>(9)?;
handle.record_db_reads::<R>(9)?;
handle.record_db_writes::<R>(2)?;

Comment on lines +138 to +139

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Simulated alpha-for-tao swap omits rollback-write gas

This simulation path has the same undercharge as the tao-for-alpha path: sim_swap can initialize PalSwap inside a rollbacked transaction, causing SwapBalancer and PalSwapInitialized writes that are not reflected in the recorded gas. Charge the worst-case write cost before the simulation call.

Suggested change
// SubnetMechanism + swap simulation reads
handle.record_db_reads::<R>(9)?;
// SubnetMechanism + swap simulation reads + rollbacked PalSwap initialization writes
handle.record_db_reads::<R>(9)?;
handle.record_db_writes::<R>(2)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Simulated alpha-for-tao swap omits rollback-write gas

This path has the same undercharge as simSwapTaoForAlpha: SwapHandler::sim_swap can initialize PalSwap state inside the rollback transaction, writing SwapBalancer and PalSwapInitialized, but the precompile records only read gas before entering the call. Charge those possible writes or make the simulation path read-only.

Suggested change
handle.record_db_reads::<R>(9)?;
handle.record_db_reads::<R>(9)?;
handle.record_db_writes::<R>(2)?;


let order = pallet_subtensor::GetTaoForAlpha::<R>::with_amount(alpha);
let swap_result =
<pallet_subtensor_swap::Pallet<R> as SwapHandler>::sim_swap(netuid.into(), order)
Expand All @@ -132,7 +149,8 @@ where

#[precompile::public("getSubnetMechanism(uint16)")]
#[precompile::view]
fn get_subnet_mechanism(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u16> {
fn get_subnet_mechanism(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u16> {
handle.record_db_reads::<R>(1)?;
Ok(pallet_subtensor::SubnetMechanism::<R>::get(NetUid::from(
netuid,
)))
Expand All @@ -147,58 +165,78 @@ where
#[precompile::public("getEMAPriceHalvingBlocks(uint16)")]
#[precompile::view]
fn get_ema_price_halving_blocks(
_handle: &mut impl PrecompileHandle,
handle: &mut impl PrecompileHandle,
netuid: u16,
) -> EvmResult<u64> {
handle.record_db_reads::<R>(1)?;
Ok(pallet_subtensor::EMAPriceHalvingBlocks::<R>::get(
NetUid::from(netuid),
))
}

#[precompile::public("getSubnetVolume(uint16)")]
#[precompile::view]
fn get_subnet_volume(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
fn get_subnet_volume(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
handle.record_db_reads::<R>(1)?;
Ok(U256::from(pallet_subtensor::SubnetVolume::<R>::get(
NetUid::from(netuid),
)))
}

#[precompile::public("getTaoInEmission(uint16)")]
#[precompile::view]
fn get_tao_in_emission(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
fn get_tao_in_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
handle.record_db_reads::<R>(1)?;
Ok(U256::from(
pallet_subtensor::SubnetTaoInEmission::<R>::get(NetUid::from(netuid)).to_u64(),
))
}

#[precompile::public("getAlphaInEmission(uint16)")]
#[precompile::view]
fn get_alpha_in_emission(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
fn get_alpha_in_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
handle.record_db_reads::<R>(1)?;
Ok(U256::from(
pallet_subtensor::SubnetAlphaInEmission::<R>::get(NetUid::from(netuid)).to_u64(),
))
}

#[precompile::public("getAlphaOutEmission(uint16)")]
#[precompile::view]
fn get_alpha_out_emission(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
fn get_alpha_out_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
handle.record_db_reads::<R>(1)?;
Ok(U256::from(
pallet_subtensor::SubnetAlphaOutEmission::<R>::get(NetUid::from(netuid)).to_u64(),
))
}

#[precompile::public("getSumAlphaPrice()")]
#[precompile::view]
fn get_sum_alpha_price(_handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
fn get_sum_alpha_price(handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
// NetworksAdded iteration + current_alpha_price reads
handle.record_db_reads::<R>(1)?;
let subnet_limit = pallet_subtensor::SubnetLimit::<R>::get().saturated_into::<u64>();

handle.record_db_reads::<R>(subnet_limit)?;

let mut sum_alpha_price: U64F64 = U64F64::from_num(0);
let netuids = pallet_subtensor::NetworksAdded::<R>::iter()
.filter(|(netuid, _)| *netuid != NetUid::ROOT)
.map(|(netuid, _)| netuid)
.collect::<Vec<_>>();

let mut sum_alpha_price: U64F64 = U64F64::from_num(0);
for (netuid, _) in netuids {
let price = <pallet_subtensor_swap::Pallet<R> as SwapHandler>::current_alpha_price(
netuid.into(),
);
// NetworksAdded entry + current_alpha_price reads
handle.record_db_reads::<R>(
netuids
.len()
.saturated_into::<u64>()
.saturating_mul(5)
.saturating_sub(subnet_limit),
)?;

for netuid in netuids.iter() {
let price =
<pallet_subtensor_swap::Pallet<R> as SwapHandler>::current_alpha_price(*netuid);

if price < U64F64::from_num(1) {
sum_alpha_price = sum_alpha_price.saturating_add(price);
Expand Down
6 changes: 4 additions & 2 deletions precompiles/src/crowdloan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ where
#[precompile::public("getCrowdloan(uint32)")]
#[precompile::view]
fn get_crowdloan(
_handle: &mut impl PrecompileHandle,
handle: &mut impl PrecompileHandle,
crowdloan_id: u32,
) -> EvmResult<CrowdloanInfo> {
handle.record_db_reads::<R>(1)?;
let crowdloan = pallet_crowdloan::Crowdloans::<R>::get(crowdloan_id).ok_or(
PrecompileFailure::Error {
exit_status: ExitError::Other("Crowdloan not found".into()),
Expand Down Expand Up @@ -105,10 +106,11 @@ where
#[precompile::public("getContribution(uint32,bytes32)")]
#[precompile::view]
fn get_contribution(
_handle: &mut impl PrecompileHandle,
handle: &mut impl PrecompileHandle,
crowdloan_id: u32,
coldkey: H256,
) -> EvmResult<u64> {
handle.record_db_reads::<R>(1)?;
let coldkey = R::AccountId::from(coldkey.0);
let contribution = pallet_crowdloan::Contributions::<R>::get(crowdloan_id, coldkey).ok_or(
PrecompileFailure::Error {
Expand Down
18 changes: 18 additions & 0 deletions precompiles/src/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use pallet_evm::{
};
use pallet_subtensor::SubtensorTransactionExtension;
use precompile_utils::EvmResult;
use precompile_utils::prelude::RuntimeHelper;
use scale_info::TypeInfo;
use sp_core::{H160, U256, blake2_256};
use sp_runtime::{
Expand All @@ -34,6 +35,23 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle {
<R as pallet_evm::Config>::AddressMapping::into_account_id(self.context().caller)
}

fn record_db_reads<R>(&mut self, reads: u64) -> EvmResult<()>
where
R: frame_system::Config + pallet_evm::Config,
{
self.record_cost(RuntimeHelper::<R>::db_read_gas_cost().saturating_mul(reads))?;
Ok(())
}

fn record_db_writes<R>(&mut self, writes: u64) -> EvmResult<()>
where
R: frame_system::Config + pallet_evm::Config,
{
self.record_cost(RuntimeHelper::<R>::db_write_gas_cost().saturating_mul(writes))?;

Ok(())
}

fn try_convert_apparent_value<R>(&self) -> EvmResult<U256>
where
R: pallet_evm::Config,
Expand Down
9 changes: 6 additions & 3 deletions precompiles/src/leasing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ where
{
#[precompile::public("getLease(uint32)")]
#[precompile::view]
fn get_lease(_handle: &mut impl PrecompileHandle, lease_id: u32) -> EvmResult<LeaseInfo> {
fn get_lease(handle: &mut impl PrecompileHandle, lease_id: u32) -> EvmResult<LeaseInfo> {
handle.record_db_reads::<R>(1)?;
let lease =
pallet_subtensor::SubnetLeases::<R>::get(lease_id).ok_or(PrecompileFailure::Error {
exit_status: ExitError::Other("Lease not found".into()),
Expand All @@ -97,10 +98,11 @@ where
#[precompile::public("getContributorShare(uint32,bytes32)")]
#[precompile::view]
fn get_contributor_share(
_handle: &mut impl PrecompileHandle,
handle: &mut impl PrecompileHandle,
lease_id: u32,
contributor: H256,
) -> EvmResult<(u128, u128)> {
handle.record_db_reads::<R>(1)?;
let contributor = R::AccountId::from(contributor.0);
let share = pallet_subtensor::SubnetLeaseShares::<R>::get(lease_id, contributor);

Expand All @@ -109,7 +111,8 @@ where

#[precompile::public("getLeaseIdForSubnet(uint16)")]
#[precompile::view]
fn get_lease_id_for_subnet(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u32> {
fn get_lease_id_for_subnet(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u32> {
handle.record_db_reads::<R>(1)?;
let lease_id = pallet_subtensor::SubnetUidToLeaseId::<R>::get(NetUid::from(netuid)).ok_or(
PrecompileFailure::Error {
exit_status: ExitError::Other("Lease not found for netuid".into()),
Expand Down
26 changes: 12 additions & 14 deletions precompiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,22 @@ extern crate alloc;

use core::marker::PhantomData;

use crate::extensions::*;
pub use address_mapping::AddressMappingPrecompile;
pub use alpha::AlphaPrecompile;
pub use balance_transfer::BalanceTransferPrecompile;
pub use crowdloan::CrowdloanPrecompile;
pub use ed25519::Ed25519Verify;
pub use extensions::PrecompileExt;
use fp_evm::{ExitError, PrecompileFailure};
use frame_support::traits::IsSubType;
use frame_support::{
dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo},
pallet_prelude::Decode,
};
pub use leasing::LeasingPrecompile;
pub use metagraph::MetagraphPrecompile;
pub use neuron::NeuronPrecompile;
use pallet_admin_utils::PrecompileEnum;
use pallet_evm::{
AddressMapping, IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult,
Expand All @@ -21,26 +31,14 @@ use pallet_evm_precompile_modexp::Modexp;
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
use pallet_subtensor_proxy as pallet_proxy;
pub use proxy::ProxyPrecompile;
use sp_core::{H160, U256, crypto::ByteArray};
use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, StaticLookup};
use subtensor_runtime_common::ProxyType;

use crate::extensions::*;

pub use address_mapping::AddressMappingPrecompile;
pub use alpha::AlphaPrecompile;
pub use balance_transfer::BalanceTransferPrecompile;
pub use crowdloan::CrowdloanPrecompile;
pub use ed25519::Ed25519Verify;
pub use extensions::PrecompileExt;
pub use leasing::LeasingPrecompile;
pub use metagraph::MetagraphPrecompile;
pub use neuron::NeuronPrecompile;
pub use proxy::ProxyPrecompile;
pub use sr25519::Sr25519Verify;
pub use staking::{StakingPrecompile, StakingPrecompileV2};
pub use storage_query::StorageQueryPrecompile;
pub use subnet::SubnetPrecompile;
use subtensor_runtime_common::ProxyType;
pub use uid_lookup::UidLookupPrecompile;
pub use voting_power::VotingPowerPrecompile;

Expand Down
Loading
Loading