Skip to content
Open
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ pub enum ProxyType {
SwapHotkey,
SubnetLeaseBeneficiary, // Used to operate the leased subnet
RootClaim,
Validate,
}

impl TryFrom<u8> for ProxyType {
Expand All @@ -190,6 +191,7 @@ impl TryFrom<u8> for ProxyType {
15 => Ok(Self::SwapHotkey),
16 => Ok(Self::SubnetLeaseBeneficiary),
17 => Ok(Self::RootClaim),
18 => Ok(Self::Validate),
_ => Err(()),
}
}
Expand All @@ -216,6 +218,7 @@ impl From<ProxyType> for u8 {
ProxyType::SwapHotkey => 15,
ProxyType::SubnetLeaseBeneficiary => 16,
ProxyType::RootClaim => 17,
ProxyType::Validate => 18,
}
}
}
Expand Down Expand Up @@ -453,4 +456,34 @@ mod tests {
fn netuid_has_u16_bin_repr() {
assert_eq!(NetUid(5).encode(), 5u16.encode());
}

#[test]
fn proxy_type_ids_remain_stable_and_validate_roundtrips() {
let expected_ids = [
(ProxyType::Any, 0u8),
(ProxyType::Owner, 1u8),
(ProxyType::NonCritical, 2u8),
(ProxyType::NonTransfer, 3u8),
(ProxyType::Senate, 4u8),
(ProxyType::NonFungible, 5u8),
(ProxyType::Triumvirate, 6u8),
(ProxyType::Governance, 7u8),
(ProxyType::Staking, 8u8),
(ProxyType::Registration, 9u8),
(ProxyType::Transfer, 10u8),
(ProxyType::SmallTransfer, 11u8),
(ProxyType::RootWeights, 12u8),
(ProxyType::ChildKeys, 13u8),
(ProxyType::SudoUncheckedSetCode, 14u8),
(ProxyType::SwapHotkey, 15u8),
(ProxyType::SubnetLeaseBeneficiary, 16u8),
(ProxyType::RootClaim, 17u8),
(ProxyType::Validate, 18u8),
];

for (proxy_type, id) in expected_ids {
assert_eq!(<u8 as From<ProxyType>>::from(proxy_type), id);
assert_eq!(ProxyType::try_from(id), Ok(proxy_type));
}
}
}
45 changes: 45 additions & 0 deletions contract-tests/test/pure-proxy.precompile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ async function getTransferCallCode(api: TypedApi<typeof devnet>, receiver: KeyPa
return [...data]
}

async function getRemarkCallCode(api: TypedApi<typeof devnet>) {
const unsignedTx = api.tx.System.remark({ remark: new Uint8Array([1, 2, 3]) });
const encodedCallDataBytes = await unsignedTx.getEncodedData();
return [...encodedCallDataBytes.asBytes()];
}

async function getProxies(api: TypedApi<typeof devnet>, address: string) {
const entries = await api.query.Proxy.Proxies.getEntries()
const result = []
Expand Down Expand Up @@ -207,4 +213,43 @@ describe("Test pure proxy precompile", () => {
assert.equal(Number(proxyInfo[2]), delay, "delay should match")
}
})

it("Call createPureProxy with Validate type", async () => {
const validateType = 18;
const validateWallet = generateRandomEthersWallet();
await forceSetBalanceToEthAddress(api, validateWallet.address);

const proxiesBefore = await getProxies(api, convertH160ToSS58(validateWallet.address));
const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, validateWallet);

const tx = await contract.createPureProxy(validateType, 0, 0);
await tx.wait();

const proxiesAfter = await getProxies(api, convertH160ToSS58(validateWallet.address));
assert.equal(proxiesAfter.length, proxiesBefore.length + 1, "validate pure proxy should be created");
})

it("Call addProxy with Validate type, then reject non-Validate call", async () => {
const validateType = 18;
const ownerWallet = generateRandomEthersWallet();
const delegateWallet = generateRandomEthersWallet();
await forceSetBalanceToEthAddress(api, ownerWallet.address);
await forceSetBalanceToEthAddress(api, delegateWallet.address);

const ownerContract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, ownerWallet);
const addProxyTx = await ownerContract.addProxy(convertH160ToPublicKey(delegateWallet.address), validateType, 0);
await addProxyTx.wait();

const delegateContract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, delegateWallet);
const remarkCall = await getRemarkCallCode(api);

await assert.rejects(
async () => {
const tx = await delegateContract.proxyCall(convertH160ToPublicKey(ownerWallet.address), [validateType], remarkCall);
await tx.wait();
},
undefined,
"validate proxy should reject a remark call"
);
})
});
2 changes: 1 addition & 1 deletion pallets/subtensor/src/utils/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use subtensor_runtime_common::NetUid;
const MESSAGE_PREFIX: &str = "\x19Ethereum Signed Message:\n";

impl<T: Config> Pallet<T> {
pub(crate) fn hash_message_eip191<M: AsRef<[u8]>>(message: M) -> [u8; 32] {
pub fn hash_message_eip191<M: AsRef<[u8]>>(message: M) -> [u8; 32] {
let msg_len = message.as_ref().len().to_string();
keccak_256(
&[
Expand Down
2 changes: 2 additions & 0 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ ethereum.workspace = true

[dev-dependencies]
frame-metadata.workspace = true
libsecp256k1.workspace = true
sp-io.workspace = true
sp-tracing.workspace = true
precompile-utils = { workspace = true, features = ["testing"] }
Expand Down Expand Up @@ -280,6 +281,7 @@ std = [
"pallet-shield/std",
"stp-shield/std",
"sp-weights/std",
"libsecp256k1/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
Expand Down
28 changes: 28 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,34 @@ impl InstanceFilter<RuntimeCall> for ProxyType {
c,
RuntimeCall::SubtensorModule(pallet_subtensor::Call::claim_root { .. })
),
ProxyType::Validate => {
matches!(
c,
RuntimeCall::SubtensorModule(
pallet_subtensor::Call::serve_axon { .. }
| pallet_subtensor::Call::serve_axon_tls { .. }
| pallet_subtensor::Call::associate_evm_key { .. }
| pallet_subtensor::Call::set_weights { .. }
| pallet_subtensor::Call::set_mechanism_weights { .. }
| pallet_subtensor::Call::batch_set_weights { .. }
| pallet_subtensor::Call::commit_weights { .. }
| pallet_subtensor::Call::commit_mechanism_weights { .. }
| pallet_subtensor::Call::batch_commit_weights { .. }
| pallet_subtensor::Call::reveal_weights { .. }
| pallet_subtensor::Call::reveal_mechanism_weights { .. }
| pallet_subtensor::Call::batch_reveal_weights { .. }
| pallet_subtensor::Call::commit_timelocked_weights { .. }
| pallet_subtensor::Call::commit_crv3_mechanism_weights { .. }
| pallet_subtensor::Call::commit_timelocked_mechanism_weights { .. }
) | RuntimeCall::Commitments(pallet_commitments::Call::set_commitment { .. })
) || matches!(
c,
RuntimeCall::Proxy(pallet_subtensor_proxy::Call::add_proxy {
proxy_type: ProxyType::Validate,
..
})
)
}
}
}
fn is_superset(&self, o: &Self) -> bool {
Expand Down
Loading
Loading