Skip to content

Commit b59e19f

Browse files
committed
Merge branch 'devnet-ready' into enable-multi-block-per-slot
2 parents b21a91f + 1ff8d4c commit b59e19f

File tree

19 files changed

+362
-45
lines changed

19 files changed

+362
-45
lines changed

.github/workflows/contract-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ permissions:
2424

2525
jobs:
2626
run:
27-
runs-on: ubuntu-latest
27+
runs-on: [self-hosted, type-ccx13]
2828
env:
2929
RUST_BACKTRACE: full
3030
steps:
@@ -52,7 +52,7 @@ jobs:
5252
- name: Run tests
5353
uses: nick-fields/retry@v3
5454
with:
55-
timeout_minutes: 90
55+
timeout_minutes: 120
5656
max_attempts: 3
5757
retry_wait_seconds: 60
5858
command: |
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export const IADDRESS_MAPPING_ADDRESS = "0x000000000000000000000000000000000000080c";
2+
3+
export const IAddressMappingABI = [
4+
{
5+
"inputs": [
6+
{
7+
"internalType": "address",
8+
"name": "target_address",
9+
"type": "address"
10+
}
11+
],
12+
"name": "addressMapping",
13+
"outputs": [
14+
{
15+
"internalType": "bytes32",
16+
"name": "",
17+
"type": "bytes32"
18+
}
19+
],
20+
"stateMutability": "view",
21+
"type": "function"
22+
}
23+
];

contract-tests/src/contracts/staking.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,30 @@ export const IStakingV2ABI = [
233233
"stateMutability": "view",
234234
"type": "function"
235235
},
236+
{
237+
"inputs": [
238+
{
239+
"internalType": "bytes32",
240+
"name": "coldkey",
241+
"type": "bytes32"
242+
},
243+
{
244+
"internalType": "uint256",
245+
"name": "netuid",
246+
"type": "uint256"
247+
}
248+
],
249+
"name": "getTotalColdkeyStakeOnSubnet",
250+
"outputs": [
251+
{
252+
"internalType": "uint256",
253+
"name": "",
254+
"type": "uint256"
255+
}
256+
],
257+
"stateMutability": "view",
258+
"type": "function"
259+
},
236260
{
237261
"inputs": [
238262
{
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import * as assert from "assert";
2+
import { ethers } from "ethers";
3+
import { generateRandomEthersWallet } from "../src/utils";
4+
import { IADDRESS_MAPPING_ADDRESS, IAddressMappingABI } from "../src/contracts/addressMapping";
5+
import { convertH160ToPublicKey } from "../src/address-utils";
6+
import { u8aToHex } from "@polkadot/util";
7+
8+
describe("Test address mapping precompile", () => {
9+
const wallet1 = generateRandomEthersWallet();
10+
const wallet2 = generateRandomEthersWallet();
11+
12+
it("Address mapping converts H160 to AccountId32 correctly", async () => {
13+
const contract = new ethers.Contract(
14+
IADDRESS_MAPPING_ADDRESS,
15+
IAddressMappingABI,
16+
wallet1
17+
);
18+
19+
// Test with wallet1's address
20+
const evmAddress = wallet1.address;
21+
const accountId32 = await contract.addressMapping(evmAddress);
22+
const expectedAcccountId32 = convertH160ToPublicKey(evmAddress);
23+
24+
// Verify the result is a valid bytes32 (32 bytes)
25+
assert.ok(accountId32.length === 66, "AccountId32 should be 32 bytes (66 hex chars with 0x)");
26+
assert.ok(accountId32.startsWith("0x"), "AccountId32 should start with 0x");
27+
28+
// Verify it's not all zeros
29+
assert.notEqual(
30+
accountId32,
31+
"0x0000000000000000000000000000000000000000000000000000000000000000",
32+
"AccountId32 should not be all zeros"
33+
);
34+
35+
console.log("accountId32: {}", accountId32);
36+
console.log("expectedAcccountId32: {}", expectedAcccountId32);
37+
38+
assert.equal(accountId32, u8aToHex(expectedAcccountId32), "AccountId32 should be the same as the expected AccountId32");
39+
});
40+
41+
it("Address mapping works with different addresses", async () => {
42+
const contract = new ethers.Contract(
43+
IADDRESS_MAPPING_ADDRESS,
44+
IAddressMappingABI,
45+
wallet1
46+
);
47+
48+
// Test with wallet2's address
49+
const evmAddress1 = wallet1.address;
50+
const evmAddress2 = wallet2.address;
51+
52+
const accountId1 = await contract.addressMapping(evmAddress1);
53+
const accountId2 = await contract.addressMapping(evmAddress2);
54+
55+
// Different addresses should map to different AccountIds
56+
assert.notEqual(
57+
accountId1,
58+
accountId2,
59+
"Different EVM addresses should map to different AccountIds"
60+
);
61+
62+
// Both should be valid bytes32
63+
assert.ok(accountId1.length === 66, "AccountId1 should be 32 bytes");
64+
assert.ok(accountId2.length === 66, "AccountId2 should be 32 bytes");
65+
});
66+
67+
it("Address mapping is deterministic", async () => {
68+
const contract = new ethers.Contract(
69+
IADDRESS_MAPPING_ADDRESS,
70+
IAddressMappingABI,
71+
wallet1
72+
);
73+
74+
const evmAddress = wallet1.address;
75+
76+
// Call multiple times with the same address
77+
const accountId1 = await contract.addressMapping(evmAddress);
78+
const accountId2 = await contract.addressMapping(evmAddress);
79+
80+
// All calls should return the same result
81+
assert.equal(
82+
accountId1,
83+
accountId2,
84+
"First and second calls should return the same AccountId"
85+
);
86+
});
87+
});

contract-tests/test/staking.precompile.add-remove.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ describe("Test neuron precompile add remove stake", () => {
162162

163163
assert.equal(stakeFromContractV1, tao(stakeFromContractV2))
164164

165+
const totalColdkeyStakeOnSubnet = Number(
166+
await contractV2.getTotalColdkeyStakeOnSubnet(convertH160ToPublicKey(wallet1.address), netuid)
167+
);
168+
169+
// check the value is not undefined and is greater than or equal to the stake from contract V2
170+
assert.ok(totalColdkeyStakeOnSubnet != undefined)
171+
// is greater than or equal to the stake from contract V2 because of emission
172+
assert.ok(totalColdkeyStakeOnSubnet >= stakeFromContractV2)
173+
165174
})
166175

167176
it("Can remove stake", async () => {

node/src/chain_spec/localnet.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,5 @@ fn localnet_genesis(
124124
"evmChainId": {
125125
"chainId": 42,
126126
},
127-
"subtensorModule": {
128-
"startCallDelay": 10,
129-
},
130127
})
131128
}

pallets/admin-utils/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ pub mod pallet {
143143
Proxy,
144144
/// Leasing precompile
145145
Leasing,
146+
/// Address mapping precompile
147+
AddressMapping,
146148
}
147149

148150
#[pallet::type_value]

pallets/admin-utils/src/tests/mock.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ parameter_types! {
145145
pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days
146146
pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight.
147147
pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks
148-
pub const InitialStartCallDelay: u64 = 7 * 24 * 60 * 60 / 12; // 7 days
148+
pub const InitialStartCallDelay: u64 = 0; // 0 days
149149
pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000;
150150
pub const HotkeySwapOnSubnetInterval: u64 = 7 * 24 * 60 * 60 / 12; // 7 days
151151
pub const LeaseDividendsDistributionInterval: u32 = 100; // 100 blocks

pallets/admin-utils/src/tests/mod.rs

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2898,10 +2898,7 @@ fn test_sudo_set_start_call_delay_permissions_and_zero_delay() {
28982898

28992899
// Get initial delay value (should be non-zero)
29002900
let initial_delay = pallet_subtensor::StartCallDelay::<Test>::get();
2901-
assert!(
2902-
initial_delay > 0,
2903-
"Initial delay should be greater than zero"
2904-
);
2901+
assert_eq!(initial_delay, 0);
29052902

29062903
// Test 1: Non-root account should fail to set delay
29072904
assert_noop!(
@@ -2925,20 +2922,16 @@ fn test_sudo_set_start_call_delay_permissions_and_zero_delay() {
29252922
"Default owner should be account 0"
29262923
);
29272924

2928-
// Test 3: Try to start the subnet immediately - should FAIL (delay not passed)
2929-
assert_err!(
2930-
pallet_subtensor::Pallet::<Test>::start_call(
2931-
<<Test as Config>::RuntimeOrigin>::signed(coldkey_account_id),
2932-
netuid
2933-
),
2934-
pallet_subtensor::Error::<Test>::NeedWaitingMoreBlocksToStarCall
2935-
);
2925+
// Test 3: Can successfully start the subnet immediately
2926+
assert_ok!(pallet_subtensor::Pallet::<Test>::start_call(
2927+
<<Test as Config>::RuntimeOrigin>::signed(coldkey_account_id),
2928+
netuid
2929+
));
29362930

2937-
// Verify emission has not been set
2938-
assert_eq!(
2939-
pallet_subtensor::FirstEmissionBlockNumber::<Test>::get(netuid),
2940-
None,
2941-
"Emission should not be set yet"
2931+
// Verify emission has been set
2932+
assert!(
2933+
pallet_subtensor::FirstEmissionBlockNumber::<Test>::get(netuid).is_some(),
2934+
"Emission should be set"
29422935
);
29432936

29442937
// Test 4: Root sets delay to zero
@@ -2957,12 +2950,15 @@ fn test_sudo_set_start_call_delay_permissions_and_zero_delay() {
29572950
pallet_subtensor::Event::StartCallDelaySet(0),
29582951
));
29592952

2960-
// Test 5: Try to start the subnet again - should SUCCEED (delay is now zero)
2953+
// Test 5: Try to start the subnet again - should be FAILED (first emission block already set)
29612954
let current_block = frame_system::Pallet::<Test>::block_number();
2962-
assert_ok!(pallet_subtensor::Pallet::<Test>::start_call(
2963-
<<Test as Config>::RuntimeOrigin>::signed(coldkey_account_id),
2964-
netuid
2965-
));
2955+
assert_err!(
2956+
pallet_subtensor::Pallet::<Test>::start_call(
2957+
<<Test as Config>::RuntimeOrigin>::signed(coldkey_account_id),
2958+
netuid
2959+
),
2960+
pallet_subtensor::Error::<Test>::FirstEmissionBlockNumberAlreadySet
2961+
);
29662962

29672963
assert_eq!(
29682964
pallet_subtensor::FirstEmissionBlockNumber::<Test>::get(netuid),

pallets/subtensor/src/staking/helpers.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,43 @@ impl<T: Config> Pallet<T> {
9191
.into()
9292
}
9393

94+
// Returns the total amount of stake under a coldkey on a subnet
95+
//
96+
pub fn get_total_stake_for_coldkey_on_subnet(
97+
coldkey: &T::AccountId,
98+
netuid: NetUid,
99+
) -> TaoCurrency {
100+
let hotkeys = StakingHotkeys::<T>::get(coldkey);
101+
hotkeys
102+
.iter()
103+
.map(|hotkey| {
104+
Alpha::<T>::iter_prefix((hotkey, coldkey))
105+
.map(|(netuid_on_storage, _)| {
106+
if netuid_on_storage == netuid {
107+
let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet(
108+
hotkey, coldkey, netuid,
109+
);
110+
let order = GetTaoForAlpha::<T>::with_amount(alpha_stake);
111+
T::SwapInterface::sim_swap(netuid.into(), order)
112+
.map(|r| {
113+
let fee: u64 = U96F32::saturating_from_num(r.fee_paid)
114+
.saturating_mul(T::SwapInterface::current_alpha_price(
115+
netuid.into(),
116+
))
117+
.saturating_to_num();
118+
r.amount_paid_out.to_u64().saturating_add(fee)
119+
})
120+
.unwrap_or_default()
121+
} else {
122+
0
123+
}
124+
})
125+
.sum::<u64>()
126+
})
127+
.sum::<u64>()
128+
.into()
129+
}
130+
94131
// Creates a cold - hot pairing account if the hotkey is not already an active account.
95132
//
96133
pub fn create_account_if_non_existent(coldkey: &T::AccountId, hotkey: &T::AccountId) {

0 commit comments

Comments
 (0)