Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ export async function getPkpAuthContextAdapter(
const nodeUrls = litClientCtx.getMaxPricesForNodeProduct({
nodePrices: respondingNodePrices,
userMaxPrice: litClientCtx.getUserMaxPrice({
product: 'LIT_ACTION',
product: 'SIGN_SESSION_KEY',
}),
productId: PRODUCT_IDS['LIT_ACTION'],
productId: PRODUCT_IDS['SIGN_SESSION_KEY'],
numRequiredNodes: threshold,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import {
* - DECRYPTION (0): Used for decryption operations
* - SIGN (1): Used for signing operations
* - LA (2): Used for Lit Actions execution
* - SIGN_SESSION_KEY (3): Used for sign session key operations
*/
export const PRODUCT_IDS = {
DECRYPTION: 0n, // For decryption operations
SIGN: 1n, // For signing operations
LIT_ACTION: 2n, // For Lit Actions execution
SIGN_SESSION_KEY: 3n, // For sign session key operations
} as const;

// Schema for the request
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { PRODUCT_IDS } from '@lit-protocol/constants';

import { getMaxPricesForNodeProduct } from './getMaxPricesForNodeProduct';

describe('getMaxPricesForNodeProduct', () => {
it('uses the requested product column when ranking nodes', () => {
const nodePrices = [
{
url: 'https://node-a',
prices: [80n, 5n, 9n, 30n],
},
{
url: 'https://node-b',
prices: [70n, 4n, 8n, 10n],
},
{
url: 'https://node-c',
prices: [60n, 3n, 7n, 20n],
},
];

// Log the incoming order to show the encryption column is already sorted lowest-first.
console.log(
'incoming order',
nodePrices.map(({ url, prices }) => ({
url,
decryptionPrice: prices[PRODUCT_IDS.DECRYPTION],
signPrice: prices[PRODUCT_IDS.SIGN],
litActionPrice: prices[PRODUCT_IDS.LIT_ACTION],
signSessionKeyPrice: prices[PRODUCT_IDS.SIGN_SESSION_KEY],
}))
);

// Call the helper exactly like the SDK does: ask for SIGN_SESSION_KEY prices,
// pass the raw price feed output, and cap the request at two nodes.
const result = getMaxPricesForNodeProduct({
nodePrices,
userMaxPrice: 100n,
productId: PRODUCT_IDS.SIGN_SESSION_KEY,
numRequiredNodes: 2,
});

console.log(
'selected nodes',
result.map(({ url, price }) => ({ url, price }))
);

// After sorting the nodes by the session-key column, the helper should
// return node-b (10) and node-c (20) even though the original array was
// ordered by the decryption price column.
expect(result).toHaveLength(2);
expect(result[0].url).toBe('https://node-b');
expect(result[1].url).toBe('https://node-c');

// Base prices are taken from the SIGN_SESSION_KEY column (10 and 20)
// with the excess (100 - 30 = 70) split evenly.
expect(result[0].price).toBe(10n + 35n);
expect(result[1].price).toBe(20n + 35n);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,24 @@ export function getMaxPricesForNodeProduct({
productId,
numRequiredNodes,
}: MaxPricesForNodes): { url: string; price: bigint }[] {
// Always evaluate pricing using the product-specific column so we truly pick
// the cheapest validators for that product (the upstream feed is sorted by
// prices[0]/decryption price only).
const sortedNodes = [...nodePrices].sort((a, b) => {
const priceA = a.prices[productId];
const priceB = b.prices[productId];

if (priceA === priceB) {
return 0;
}

return priceA < priceB ? -1 : 1;
});

// If we don't need all nodes to service the request, only use the cheapest `n` of them
const nodesToConsider = numRequiredNodes
? nodePrices.slice(0, numRequiredNodes)
: nodePrices;
? sortedNodes.slice(0, numRequiredNodes)
: sortedNodes;

let totalBaseCost = 0n;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { PRODUCT_ID_VALUES } from '@lit-protocol/constants';

export const PricingContextSchema = z
.object({
product: z.enum(['DECRYPTION', 'SIGN', 'LIT_ACTION']),
product: z.enum(['DECRYPTION', 'SIGN', 'LIT_ACTION', 'SIGN_SESSION_KEY']),
userMaxPrice: z.bigint().optional(),
nodePrices: z.array(
z.object({ url: z.string(), prices: z.array(z.bigint()) })
Expand Down
Loading