Skip to content

Commit 678f29b

Browse files
committed
merge with target
2 parents e3bdadc + 1ff8d4c commit 678f29b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1460
-383
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: |

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

chain-extensions/src/mock.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ parameter_types! {
331331
pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days
332332
pub const InitialTaoWeight: u64 = 0; // 100% global weight.
333333
pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks
334-
pub const DurationOfStartCall: u64 = 7 * 24 * 60 * 60 / 12; // Default as 7 days
334+
pub const InitialStartCallDelay: u64 = 7 * 24 * 60 * 60 / 12; // Default as 7 days
335335
pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000;
336336
pub const HotkeySwapOnSubnetInterval: u64 = 15; // 15 block, should be bigger than subnet number, then trigger clean up for all subnets
337337
pub const MaxContributorsPerLeaseToRemove: u32 = 3;
@@ -402,7 +402,7 @@ impl pallet_subtensor::Config for Test {
402402
type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration;
403403
type InitialTaoWeight = InitialTaoWeight;
404404
type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod;
405-
type DurationOfStartCall = DurationOfStartCall;
405+
type InitialStartCallDelay = InitialStartCallDelay;
406406
type SwapInterface = pallet_subtensor_swap::Pallet<Self>;
407407
type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost;
408408
type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval;

contract-tests/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"scripts": {
3-
"test": "mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register test/*test.ts"
3+
"test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/**/*.ts\""
44
},
55
"keywords": [],
66
"author": "",
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
{

contract-tests/src/subtensor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ export async function setSubtokenEnable(api: TypedApi<typeof devnet>, netuid: nu
296296
export async function startCall(api: TypedApi<typeof devnet>, netuid: number, keypair: KeyPair) {
297297
const registerBlock = Number(await api.query.SubtensorModule.NetworkRegisteredAt.getValue(netuid))
298298
let currentBlock = await api.query.System.Number.getValue()
299-
const duration = Number(await api.constants.SubtensorModule.DurationOfStartCall)
299+
const duration = Number(await api.constants.SubtensorModule.InitialStartCallDelay)
300300

301301
while (currentBlock - registerBlock <= duration) {
302302
await new Promise((resolve) => setTimeout(resolve, 2000));
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/neuron.precompile.reveal-weights.test.ts

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as assert from "assert";
2-
import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"
2+
import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair, waitForTransactionWithRetry } from "../src/substrate"
33
import { devnet } from "@polkadot-api/descriptors"
44
import { PolkadotSigner, TypedApi } from "polkadot-api";
55
import { convertPublicKeyToSs58, convertH160ToSS58 } from "../src/address-utils"
@@ -23,6 +23,16 @@ const values = [5];
2323
const salt = [9];
2424
const version_key = 0;
2525

26+
async function setStakeThreshold(
27+
api: TypedApi<typeof devnet>,
28+
alice: PolkadotSigner,
29+
minStake: bigint,
30+
) {
31+
const internalCall = api.tx.AdminUtils.sudo_set_stake_threshold({ min_stake: minStake })
32+
const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall })
33+
await waitForTransactionWithRetry(api, tx, alice)
34+
}
35+
2636
function getCommitHash(netuid: number, address: string) {
2737
const registry = new TypeRegistry();
2838
let publicKey = convertH160ToPublicKey(address);
@@ -53,7 +63,7 @@ describe("Test neuron precompile reveal weights", () => {
5363
const coldkey = getRandomSubstrateKeypair();
5464

5565
let api: TypedApi<typeof devnet>
56-
let commitEpoch: number;
66+
let commitEpoch: number | undefined;
5767

5868
// sudo account alice as signer
5969
let alice: PolkadotSigner;
@@ -86,11 +96,52 @@ describe("Test neuron precompile reveal weights", () => {
8696
assert.equal(uid, uids[0])
8797
})
8898

99+
async function ensureCommitEpoch(netuid: number, contract: ethers.Contract) {
100+
if (commitEpoch !== undefined) {
101+
return
102+
}
103+
104+
const ss58Address = convertH160ToSS58(wallet.address)
105+
const existingCommits = await api.query.SubtensorModule.WeightCommits.getValue(
106+
netuid,
107+
ss58Address
108+
)
109+
if (Array.isArray(existingCommits) && existingCommits.length > 0) {
110+
const entry = existingCommits[0]
111+
const commitBlockRaw =
112+
Array.isArray(entry) && entry.length > 1 ? entry[1] : undefined
113+
const commitBlock =
114+
typeof commitBlockRaw === "bigint"
115+
? Number(commitBlockRaw)
116+
: Number(commitBlockRaw ?? NaN)
117+
if (Number.isFinite(commitBlock)) {
118+
commitEpoch = Math.trunc(commitBlock / (100 + 1))
119+
return
120+
}
121+
}
122+
123+
await setStakeThreshold(api, alice, BigInt(0))
124+
const commitHash = getCommitHash(netuid, wallet.address)
125+
const tx = await contract.commitWeights(netuid, commitHash)
126+
await tx.wait()
127+
128+
const commitBlock = await api.query.System.Number.getValue()
129+
commitEpoch = Math.trunc(commitBlock / (100 + 1))
130+
}
131+
89132
it("EVM neuron commit weights via call precompile", async () => {
90133
let totalNetworks = await api.query.SubtensorModule.TotalNetworks.getValue()
91134
const subnetId = totalNetworks - 1
92135
const commitHash = getCommitHash(subnetId, wallet.address)
93136
const contract = new ethers.Contract(INEURON_ADDRESS, INeuronABI, wallet);
137+
138+
await setStakeThreshold(api, alice, BigInt(1))
139+
await assert.rejects(async () => {
140+
const tx = await contract.commitWeights(subnetId, commitHash)
141+
await tx.wait()
142+
})
143+
await setStakeThreshold(api, alice, BigInt(0))
144+
94145
try {
95146
const tx = await contract.commitWeights(subnetId, commitHash)
96147
await tx.wait()
@@ -120,6 +171,11 @@ describe("Test neuron precompile reveal weights", () => {
120171
// set interval epoch as 1, it is the minimum value now
121172
await setCommitRevealWeightsInterval(api, netuid, BigInt(1))
122173

174+
await ensureCommitEpoch(netuid, contract)
175+
if (commitEpoch === undefined) {
176+
throw new Error("commitEpoch should be set before revealing weights")
177+
}
178+
123179
while (true) {
124180
const currentBlock = await api.query.System.Number.getValue()
125181
const currentEpoch = Math.trunc(currentBlock / (100 + 1))
@@ -130,6 +186,19 @@ describe("Test neuron precompile reveal weights", () => {
130186
await new Promise(resolve => setTimeout(resolve, 1000))
131187
}
132188

189+
await setStakeThreshold(api, alice, BigInt(1))
190+
await assert.rejects(async () => {
191+
const tx = await contract.revealWeights(
192+
netuid,
193+
uids,
194+
values,
195+
salt,
196+
version_key
197+
);
198+
await tx.wait()
199+
})
200+
await setStakeThreshold(api, alice, BigInt(0))
201+
133202
const tx = await contract.revealWeights(
134203
netuid,
135204
uids,

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 () => {

0 commit comments

Comments
 (0)