Skip to content

Commit

Permalink
fix: storing final weights on DB in nano scale
Browse files Browse the repository at this point in the history
  • Loading branch information
steinerkelvin committed Oct 14, 2024
1 parent 8041c87 commit 39d4067
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 93 deletions.
1 change: 1 addition & 0 deletions apps/commune-worker/src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export interface VotesByProposal {
export async function vote(new_vote: NewVote) {
await db.insert(daoVoteSchema).values(new_vote);
}

export async function addSeenProposal(proposal: NewNotification) {
await db.insert(governanceNotificationSchema).values(proposal);
}
Expand Down
176 changes: 85 additions & 91 deletions apps/commune-worker/src/workers/weight-aggregator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ import {
normalizeWeightsToPercent,
} from "../weights";

// TODO: subnets
// TODO: update tables on DB
type Aggregator = "module" | "subnet";
const SUBNETS_NETUID = 0;
const MODULES_NETUID = 2;

type AggregatorKind = "module" | "subnet";

export async function weightAggregatorWorker(api: ApiPromise) {
await cryptoWaitReady();
Expand All @@ -52,7 +53,9 @@ export async function weightAggregatorWorker(api: ApiPromise) {
knownLastBlock = lastBlock;

log(`Block ${lastBlock.blockNumber}: processing`);
// to avoid priority too low when casting votes

// To avoid "Priority is too low" / conflicting transactions when casting
// votes we alternate the blocks in which each type of vote is done
if (lastBlock.blockNumber % 2 === 0) {
await weightAggregatorTask(
api,
Expand All @@ -77,6 +80,50 @@ export async function weightAggregatorWorker(api: ApiPromise) {
}
}

/**
* Fetches assigned weights by users and their stakes, to calculate the final
* weights for the community validator.
*/
export async function weightAggregatorTask(
api: ApiPromise,
keypair: KeyringPair,
lastBlock: number,
aggregator: AggregatorKind,
) {
const storages: SubspaceStorageName[] = ["stakeFrom"];
const storageMap = { subspaceModule: storages };
const queryResult = await queryChain(api, storageMap, lastBlock);
const stakeFromData = STAKE_FROM_SCHEMA.parse({
stakeFromStorage: queryResult.stakeFrom,
}).stakeFromStorage;

const communityValidatorAddress = keypair.address as SS58Address;
const stakeOnCommunityValidator = stakeFromData.get(
communityValidatorAddress,
);
if (stakeOnCommunityValidator == undefined) {
throw new Error(
`Community validator ${communityValidatorAddress} not found in stake data`,
);
}

if (aggregator == "module") {
await postModuleAggregation(
stakeOnCommunityValidator,
api,
keypair,
lastBlock,
);
} else if (aggregator == "subnet") {
await postSubnetAggregation(
stakeOnCommunityValidator,
api,
keypair,
lastBlock,
);
}
}

function getNormalizedWeights(
stakeOnCommunityValidator: Map<SS58Address, bigint>,
weightMap: Map<string, Map<number, bigint>>,
Expand All @@ -85,6 +132,7 @@ function getNormalizedWeights(
const normalizedVoteWeights = normalizeWeightsForVote(finalWeights);
const normalizedPercWeights = normalizeWeightsToPercent(finalWeights);
return {
stakeWeights: finalWeights,
normalizedWeights: normalizedVoteWeights,
percWeights: normalizedPercWeights,
};
Expand All @@ -108,79 +156,71 @@ function buildNetworkVote(voteMap: Map<number, number>) {
return { uids, weights };
}

async function voteAndUpdate<T>(
async function doVote<T>(
api: ApiPromise,
keypair: KeyringPair,
uids: number[],
weights: number[],
netuid: number,
insertFunction: (data: T[]) => Promise<void>,
toInsert: T[],
voteMap: Map<number, number>,
) {
const { uids, weights } = buildNetworkVote(voteMap);
if (uids.length === 0) {
console.warn("No weights to set");
return;
}
if (uids.length !== weights.length) {
throw new Error("UIDs and weights arrays must have the same length");
}
try {
// TODO: whats this 2, netuid? should be a constant
await setChainWeights(api, keypair, netuid, uids, weights);
await insertFunction(toInsert);
} catch (err) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
console.error(`Failed to set weights on chain: ${err}`);
return;
}
}

async function postModuleAggregation(
stakeOnCommunityValidator: Map<SS58Address, bigint>,
api: ApiPromise,
keypair: KeyringPair,
lastBlock: number,
) {
const uidMap = await getModuleUids();
const moduleWeightMap = await getUserWeightMap();
const moduleWeightsInfo = getNormalizedWeights(
const { stakeWeights, normalizedWeights, percWeights } = getNormalizedWeights(
stakeOnCommunityValidator,
moduleWeightMap,
);
const uidMap = await getModuleUids();
const moduleWeights: ModuleWeight[] = Array.from(
moduleWeightsInfo.normalizedWeights,

const dbModuleWeights: ModuleWeight[] = Array.from(
stakeWeights,
)
.map(([moduleId, weight]): ModuleWeight | null => {
.map(([moduleId, stakeWeight]): ModuleWeight | null => {
const moduleActualId = uidMap.get(moduleId);
if (moduleActualId === undefined) {
console.error(`Module id ${moduleId} not found in uid map`);
return null;
}
const modulePercWeight = moduleWeightsInfo.percWeights.get(moduleId);
if (modulePercWeight === undefined) {
const percWeight = percWeights.get(moduleId);
if (percWeight === undefined) {
console.error(
`Module id ${moduleId} not found in normalizedPercWeights`,
);
return null;
}
return {
moduleId: moduleActualId,
percWeight: modulePercWeight,
stakeWeight: weight,
percWeight: percWeight,
stakeWeight: stakeWeight,
atBlock: lastBlock,
};
})
.filter((module): module is ModuleWeight => module !== null);
const { uids, weights } = buildNetworkVote(
moduleWeightsInfo.normalizedWeights,
);
await voteAndUpdate(
.filter((module) => module !== null);

await insertModuleWeight(dbModuleWeights);

await doVote(
api,
keypair,
uids,
weights,
2,
insertModuleWeight,
moduleWeights,
MODULES_NETUID,
normalizedWeights,
);
}

Expand All @@ -192,82 +232,35 @@ async function postSubnetAggregation(
) {
const subnetWeightMap = await getUserSubnetWeightMap();

const subnetWeightsInfo = getNormalizedWeights(
const { stakeWeights, normalizedWeights, percWeights } = getNormalizedWeights(
stakeOnCommunityValidator,
subnetWeightMap,
);

const subnetWeights: SubnetWeight[] = Array.from(
subnetWeightsInfo.normalizedWeights,
stakeWeights,
)
.map(([netuid, weight]): SubnetWeight | null => {
const subnetPercWeight = subnetWeightsInfo.percWeights.get(netuid);
.map(([netuid, stakeWeight]): SubnetWeight | null => {
const subnetPercWeight = percWeights.get(netuid);
if (subnetPercWeight === undefined) {
console.error(`Subnet id ${netuid} not found in normalizedPercWeights`);
return null;
}
return {
netuid: netuid,
percWeight: subnetPercWeight,
stakeWeight: weight,
stakeWeight: stakeWeight,
atBlock: lastBlock,
};
})
.filter((subnet): subnet is SubnetWeight => subnet !== null);
const { uids, weights } = buildNetworkVote(
subnetWeightsInfo.normalizedWeights,
);
await voteAndUpdate(
.filter((subnet) => subnet !== null);

await doVote(
api,
keypair,
uids,
weights,
0,
insertSubnetWeight,
subnetWeights,
);
}

/**
* Fetches assigned weights by users and their stakes, to calculate the final
* weights for the community validator.
*/
export async function weightAggregatorTask(
api: ApiPromise,
keypair: KeyringPair,
lastBlock: number,
aggregator: Aggregator,
) {
const storages: SubspaceStorageName[] = ["stakeFrom"];
const storageMap = { subspaceModule: storages };
const queryResult = await queryChain(api, storageMap, lastBlock);
const stakeFromData = STAKE_FROM_SCHEMA.parse({
stakeFromStorage: queryResult.stakeFrom,
}).stakeFromStorage;
const communityValidatorAddress = keypair.address as SS58Address;
const stakeOnCommunityValidator = stakeFromData.get(
communityValidatorAddress,
SUBNETS_NETUID,
normalizedWeights,
);
if (stakeOnCommunityValidator == undefined) {
throw new Error(
`Community validator ${communityValidatorAddress} not found in stake data`,
);
}
if (aggregator == "module") {
await postModuleAggregation(
stakeOnCommunityValidator,
api,
keypair,
lastBlock,
);
} else {
await postSubnetAggregation(
stakeOnCommunityValidator,
api,
keypair,
lastBlock,
);
}
}

async function setChainWeights(
Expand All @@ -277,6 +270,7 @@ async function setChainWeights(
uids: number[],
weights: number[],
) {
assert(uids.length === weights.length, "UIDs and weights arrays must have the same length");
assert(api.tx.subspaceModule != undefined);
assert(api.tx.subspaceModule.setWeights != undefined);
const tx = await api.tx.subspaceModule
Expand Down Expand Up @@ -351,7 +345,7 @@ async function getUserSubnetWeightMap(): Promise<
Map<string, Map<number, bigint>>
> {
const result = await db
.select({
.select({>
userKey: userSubnetDataSchema.userKey,
weight: userSubnetDataSchema.weight,
netuid: subnetDataSchema.netuid,
Expand Down
5 changes: 3 additions & 2 deletions packages/db/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,9 @@ export const computedModuleWeightsSchema = createTable(
moduleId: integer("module_id")
.notNull()
.references(() => moduleData.id),

// Aggregated weights measured in nanos
stakeWeight: integer("stake_weight").notNull(),
stakeWeight: bigint("stake_weight", { mode: "bigint" }).notNull(),
// Normalized aggregated weights (100% sum)
percWeight: real("perc_weight").notNull(),

Expand All @@ -424,7 +425,7 @@ export const computedSubnetWeights = createTable("computed_subnet_weights", {
.references(() => subnetDataSchema.netuid),

// Aggregated weights measured in nanos
stakeWeight: integer("stake_weight").notNull(),
stakeWeight: bigint("stake_weight", { mode: "bigint" }).notNull(),
// Normalized aggregated weights (100% sum)
percWeight: real("perc_weight").notNull(),

Expand Down

0 comments on commit 39d4067

Please sign in to comment.