Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
11 changes: 11 additions & 0 deletions docs/guides/evm/precompiles/subnet.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ getCommitRevealWeightsInterval(uint16)
getDifficulty(uint16)
getImmunityPeriod(uint16)
getKappa(uint16)
getLiquidAlphaConsensusMode(uint16)
getLiquidAlphaEnabled(uint16)
getMaxBurn(uint16)
getMaxDifficulty(uint16)
Expand Down Expand Up @@ -75,6 +76,9 @@ netuid. It increments on every successful registration, allowing a contract to
distinguish a reused netuid even when it did not retain the previous
registration block.

`getLiquidAlphaConsensusMode` returns `0` for `Current`, `1` for `Previous`,
and `2` for `Auto` (the default).

`getSubnetDissolutionStatus` returns `(isDissolving, cleanupInProgress,
cleanupPhase)`. Phase `0` means that detailed cleanup has not started. Active
cleanup uses stable, append-only phase codes:
Expand Down Expand Up @@ -126,6 +130,7 @@ setCommitRevealWeightsInterval(uint16,uint64)
setDifficulty(uint16,uint64)
setImmunityPeriod(uint16,uint16)
setKappa(uint16,uint16)
setLiquidAlphaConsensusMode(uint16,uint8)
setLiquidAlphaEnabled(uint16,bool)
setMaxDifficulty(uint16,uint64)
setMinAllowedWeights(uint16,uint16)
Expand All @@ -140,6 +145,12 @@ setYuma3Enabled(uint16,bool)
toggleTransfers(uint16,bool)
```

`setLiquidAlphaConsensusMode` uses the same mode encoding as the getter and
reverts for values above `2`. It dispatches
`AdminUtils.sudo_set_liquid_alpha_consensus_mode` with the mapped EVM caller,
so subnet-owner authorization, the admin window, and the hyperparameter rate
limit remain enforced by the runtime.

## Legacy no-op functions

These released selectors remain routed but intentionally do not change state:
Expand Down
9 changes: 8 additions & 1 deletion docs/hyperparameters/liquid-alpha-enabled.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ by liquid alpha:

Subnet owners can change the mode during the subnet's admin window. The call
has the same owner/root authorization and per-hyperparameter rate limiting as
other owner-settable parameters, but it is currently exposed as a raw call:
other owner-settable parameters. It is available as a raw call:

```bash
btcli call AdminUtils.sudo_set_liquid_alpha_consensus_mode \
Expand All @@ -41,6 +41,13 @@ mode = await client.query(
)
```

EVM contracts can use the Subnet precompile at `0x0803` through
`getLiquidAlphaConsensusMode(uint16)` and
`setLiquidAlphaConsensusMode(uint16,uint8)`. The EVM encoding is `0` for
`Current`, `1` for `Previous`, and `2` for `Auto`; other values revert. The
setter exposes the signed subnet-owner path and retains the runtime's admin
window and rate-limit checks.

One important dependency: `compute_bonds` only runs on the Yuma3 path. If [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) is off, the classic bond code ignores this toggle entirely and always uses [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg). Setting `alpha_low`/`alpha_high` also requires this flag to be on first ([`LiquidAlphaDisabled`](/code/pallets/subtensor/src/macros/errors.rs#L134) error otherwise).

<HyperparamLiquidAlpha focus="liquid_alpha_enabled" />
Expand Down
2 changes: 2 additions & 0 deletions precompiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ mod address_and_selector_tests {
"setSubnetIdentity(uint16,string,string,string,string,string,string,string,string)",
"updateSubnetSymbol(uint16,string)",
"triggerEpoch(uint16)",
"setLiquidAlphaConsensusMode(uint16,uint8)",
"setBondsPenalty(uint16,uint16)",
"setMaxAllowedUids(uint16,uint16)",
"setMaxBurnV2(uint16,uint64)",
Expand Down Expand Up @@ -680,6 +681,7 @@ mod address_and_selector_tests {

for signature in [
"getRegisteredSubnetCounter(uint16)",
"getLiquidAlphaConsensusMode(uint16)",
"getSubnetDissolutionStatus(uint16)",
"getSubnetMetadata(uint16)",
"getSubnetCapacityConfig(uint16)",
Expand Down
37 changes: 37 additions & 0 deletions precompiles/src/solidity/subnet.abi
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,25 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "netuid",
"type": "uint16"
}
],
"name": "getLiquidAlphaConsensusMode",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down Expand Up @@ -871,6 +890,24 @@
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "netuid",
"type": "uint16"
},
{
"internalType": "uint8",
"name": "mode",
"type": "uint8"
}
],
"name": "setLiquidAlphaConsensusMode",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
Expand Down
17 changes: 17 additions & 0 deletions precompiles/src/solidity/subnet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ interface ISubnet {

function getLiquidAlphaEnabled(uint16 netuid) external view returns (bool);

/**
* @dev Returns the liquid-alpha consensus source: 0 Current, 1 Previous,
* 2 Auto.
*/
function getLiquidAlphaConsensusMode(
uint16 netuid
) external view returns (uint8);

function isSubnetDissolving(uint16 netuid) external view returns (bool);

/**
Expand Down Expand Up @@ -235,6 +243,15 @@ interface ISubnet {
bool liquidAlphaEnabled
) external payable;

/**
* @dev Sets the liquid-alpha consensus source: 0 Current, 1 Previous,
* 2 Auto. Reverts for any other value.
*/
function setLiquidAlphaConsensusMode(
uint16 netuid,
uint8 mode
) external payable;

function getYuma3Enabled(uint16 netuid) external view returns (bool);

function setYuma3Enabled(
Expand Down
133 changes: 132 additions & 1 deletion precompiles/src/subnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use frame_system::RawOrigin;
use pallet_evm::{AddressMapping, PrecompileHandle};
use precompile_utils::{
EvmResult,
prelude::{BoundedString, BoundedVec, UnboundedBytes},
prelude::{BoundedString, BoundedVec, UnboundedBytes, revert},
};
use sp_core::H256;
use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, UniqueSaturatedInto};
Expand Down Expand Up @@ -772,6 +772,46 @@ where
)
}

#[precompile::public("getLiquidAlphaConsensusMode(uint16)")]
#[precompile::view]
fn get_liquid_alpha_consensus_mode(
handle: &mut impl PrecompileHandle,
netuid: u16,
) -> EvmResult<u8> {
handle.record_db_reads::<R>(1)?;
let mode = match pallet_subtensor::LiquidAlphaConsensusMode::<R>::get(NetUid::from(netuid))
{
pallet_subtensor::ConsensusMode::Current => 0,
pallet_subtensor::ConsensusMode::Previous => 1,
pallet_subtensor::ConsensusMode::Auto => 2,
};
Ok(mode)
}

#[precompile::public("setLiquidAlphaConsensusMode(uint16,uint8)")]
#[precompile::payable]
fn set_liquid_alpha_consensus_mode(
handle: &mut impl PrecompileHandle,
netuid: u16,
mode: u8,
) -> EvmResult<()> {
let mode = match mode {
0 => pallet_subtensor::ConsensusMode::Current,
1 => pallet_subtensor::ConsensusMode::Previous,
2 => pallet_subtensor::ConsensusMode::Auto,
_ => return Err(revert("invalid liquid alpha consensus mode")),
};
let call = pallet_admin_utils::Call::<R>::sudo_set_liquid_alpha_consensus_mode {
netuid: netuid.into(),
mode,
};

handle.try_dispatch_runtime_call::<R, _>(
call,
RawOrigin::Signed(handle.caller_account_id::<R>()),
)
}

#[precompile::public("getYuma3Enabled(uint16)")]
#[precompile::view]
fn get_yuma3_enabled(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<bool> {
Expand Down Expand Up @@ -1847,6 +1887,97 @@ mod tests {
});
}

#[test]
fn liquid_alpha_consensus_mode_preserves_encoding_and_owner_authorization() {
new_test_ext().execute_with(|| {
let owner = addr_from_index(0x5012);
let non_owner = addr_from_index(0x5013);
let netuid = setup_owner_subnet(owner);
let precompiles = precompiles::<SubnetPrecompile<Runtime>>();
let address = addr_from_index(SubnetPrecompile::<Runtime>::INDEX);
let get_input = encode_with_selector(
selector_u32("getLiquidAlphaConsensusMode(uint16)"),
(TEST_NETUID_U16,),
);

assert!(!pallet_subtensor::LiquidAlphaConsensusMode::<Runtime>::contains_key(netuid));
precompiles
.prepare_test(owner, address, get_input.clone())
.with_static_call(true)
.expect_cost(
precompile_utils::prelude::RuntimeHelper::<Runtime>::db_read_gas_cost(),
)
.execute_returns(2_u8);

for (encoded, expected) in [
(0_u8, pallet_subtensor::ConsensusMode::Current),
(1_u8, pallet_subtensor::ConsensusMode::Previous),
(2_u8, pallet_subtensor::ConsensusMode::Auto),
] {
precompiles
.prepare_test(
owner,
address,
encode_with_selector(
selector_u32("setLiquidAlphaConsensusMode(uint16,uint8)"),
(TEST_NETUID_U16, encoded),
),
)
.execute_returns(());
assert_eq!(
pallet_subtensor::LiquidAlphaConsensusMode::<Runtime>::get(netuid),
expected
);
precompiles
.prepare_test(owner, address, get_input.clone())
.with_static_call(true)
.expect_cost(
precompile_utils::prelude::RuntimeHelper::<Runtime>::db_read_gas_cost(),
)
.execute_returns(encoded);
}

precompiles
.prepare_test(
owner,
address,
encode_with_selector(
selector_u32("setLiquidAlphaConsensusMode(uint16,uint8)"),
(TEST_NETUID_U16, 3_u8),
),
)
.execute_reverts(|output| output == b"invalid liquid alpha consensus mode");
assert_eq!(
pallet_subtensor::LiquidAlphaConsensusMode::<Runtime>::get(netuid),
pallet_subtensor::ConsensusMode::Auto
);

let set_previous = encode_with_selector(
selector_u32("setLiquidAlphaConsensusMode(uint16,uint8)"),
(TEST_NETUID_U16, 1_u8),
);
let rejected = execute_precompile(
&precompiles,
address,
non_owner,
set_previous.clone(),
U256::zero(),
);
assert!(matches!(rejected, Some(Err(_))));
assert_eq!(
pallet_subtensor::LiquidAlphaConsensusMode::<Runtime>::get(netuid),
pallet_subtensor::ConsensusMode::Auto
);

precompiles
.prepare_test(owner, address, set_previous)
.with_static_call(true)
.execute_reverts(|output| {
output == b"Can't call non-static function in static context"
});
});
}

#[test]
fn subnet_precompile_gets_network_registered_block() {
new_test_ext().execute_with(|| {
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// `spec_version`, and `authoring_version` are the same between Wasm and native.
// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
// the compatible custom types.
spec_version: 448,
spec_version: 449,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down
37 changes: 37 additions & 0 deletions sdk/python/bittensor/evm/abi/subnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,25 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "netuid",
"type": "uint16"
}
],
"name": "getLiquidAlphaConsensusMode",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down Expand Up @@ -871,6 +890,24 @@
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "netuid",
"type": "uint16"
},
{
"internalType": "uint8",
"name": "mode",
"type": "uint8"
}
],
"name": "setLiquidAlphaConsensusMode",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
Expand Down
12 changes: 12 additions & 0 deletions sdk/python/tests/unit/test_evm.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ def test_new_bounded_array_calls_encode(self):
["[1, 2]", ["0x" + "11" * 32, "0x" + "22" * 32]],
).startswith("0x")

def test_liquid_alpha_consensus_mode_calls_encode(self):
subnet = precompiles.get_precompile("subnet")
getter = subnet.function("getLiquidAlphaConsensusMode")
setter = subnet.function("setLiquidAlphaConsensusMode")

assert precompiles.encode_call(getter, [1]).startswith("0x")
assert precompiles.encode_call(setter, [1, 2]).startswith("0x")
assert getter["outputs"] == [
{"internalType": "uint8", "name": "", "type": "uint8"}
]
assert setter["stateMutability"] == "payable"


# The vendored ABIs in bittensor/evm/abi must stay in sync with the canonical
# .abi artifacts in precompiles/src/solidity (see the bittensor.evm.precompiles
Expand Down
Loading