This commit is contained in:
2026-10-20 03:14:08 -04:00
parent b1d2b5c10e
commit 9850fd8554
28 changed files with 1256 additions and 26 deletions
+32 -2
View File
@@ -6,7 +6,7 @@ import { constants, existsSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { delimiter, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { generateKeyPairSync, sign } from 'node:crypto';
import { generateKeyPairSync, randomUUID, sign } from 'node:crypto';
import { DatabaseSync } from 'node:sqlite';
import { setTimeout as delay } from 'node:timers/promises';
import { RandomX } from '../server/testnet/randomx.ts';
@@ -14,6 +14,8 @@ import { address, CHAIN_ID, GENESIS_HASH, MAX_TARGET, miningBlob, mintId, sha256
transferId, workFor } from '../server/testnet/consensus.ts';
import { NETWORK, CONSENSUS, blockClaimMessage, nftTransferMessage,
type MiningTemplate, type NftTransfer, type TestnetBlock, type TestnetSnapshot } from '../shared/testnet.ts';
import { marketMessage, type MarketAction, type MarketInfo, type MarketListing, type MarketOrder,
type MarketActivity } from '../shared/market.ts';
const root = fileURLToPath(new URL('../', import.meta.url));
const wikipediaMock = resolve(root, 'tests', 'mock-wikipedia.mjs');
@@ -81,7 +83,8 @@ test('native RandomX HTTP: three blocks, mined transfer, peer gossip/sync and du
env: { ...process.env, HOST: '127.0.0.1', PORT: '0', NODE_ENV: 'production',
WIKIDECK_DATA_DIR: dataDir, RANDOMX_BINARY: binary,
WIKIDECK_PEERS: peer ?? '', WIKIDECK_ALLOW_PRIVATE_PEERS: peer ? '1' : '0',
PUBLIC_ORIGIN: 'http://127.0.0.1:3001' },
PUBLIC_ORIGIN: 'http://127.0.0.1:3001', WIKIDECK_PAYMENT_MODE: 'mock',
WIKIDECK_MARKET_FEE_BPS: '250', WIKIDECK_MARKET_CONFIRMATIONS: '2' },
stdio: ['ignore', 'pipe', 'pipe'],
});
let output = '';
@@ -131,6 +134,11 @@ test('native RandomX HTTP: three blocks, mined transfer, peer gossip/sync and du
return (await request(base, path, expected, body)).json() as Promise<T>;
}
const snapshot = (base: string) => json<TestnetSnapshot>(base, '/api/chain');
async function marketCommand<T>(base: string, info: MarketInfo, who: ReturnType<typeof wallet>, action: MarketAction) {
const terms = { network: NETWORK, chainId: CHAIN_ID, marketId: info.marketId, actor: who.owner,
publicKey: who.publicKey, nonce: randomUUID(), expiresAt: Date.now() + 60000, action };
return json<T>(base, '/api/market/command', 200, { ...terms, signature: who.signed(marketMessage(terms)) });
}
async function waitFor<T>(label: string, read: () => Promise<T>, predicate: (value: T) => boolean): Promise<T> {
const until = Date.now() + 25_000;
let value: T;
@@ -248,6 +256,19 @@ test('native RandomX HTTP: three blocks, mined transfer, peer gossip/sync and du
assert.equal(aliceDeck.tip, block2.hash);
assert.equal(aliceDeck.total, 2);
assert.deepEqual(aliceDeck.nfts.map(item => item.block.tokenId), [block1.tokenId, block2.tokenId]);
const market = await json<{ info: MarketInfo; listings: MarketListing[] }>(first.base, '/api/market');
assert.equal(market.info.paymentMode, 'mock');
assert.deepEqual(market.listings, []);
await (await request(first.base, '/api/market/command', 400, {})).text();
await (await request(first.base, '/api/market/command', 403, {}, { Origin: 'https://evil.example' })).text();
const sale = await marketCommand<MarketListing>(first.base, market.info, alice, { type: 'list',
cardHash: block1.tokenId, sequence: 0, kind: 'fixed', priceCents: 1500, endsAt: null, feeBps: 250 });
assert.equal((await json<{ listings: MarketListing[] }>(first.base, '/api/market')).listings[0].id, sale.id);
const order = await marketCommand<MarketOrder>(first.base, market.info, bob, { type: 'buy', listingId: sale.id,
priceCents: 1500, feeBps: 250 });
assert.equal(order.invoice.address, null);
assert.equal(order.invoice.amountXmr, '0.100000000000');
await marketCommand(first.base, market.info, bob, { type: 'mock-pay', orderId: order.id });
await (await request(first.base, `/api/decks/${'z'.repeat(64)}`, 400)).text();
// A truly independent process reconstructs the canonical chain over HTTP and checks real work.
@@ -262,6 +283,8 @@ test('native RandomX HTTP: three blocks, mined transfer, peer gossip/sync and du
const badTransfer = await json<{ error: string }>(second.base, '/api/transfer', 400,
{ ...transfer, signature: tamperSignature(transfer.signature) });
assert.match(badTransfer.error, /signature/i);
await marketCommand(first.base, market.info, alice, { type: 'deliver', orderId: order.id, transfer });
assert.equal((await marketCommand<MarketActivity>(first.base, market.info, bob, { type: 'activity' })).orders[0].status, 'delivery-pending');
const pending = { id: transferId(transfer), status: 'pending',
message: 'Ownership changes only when this transaction is mined.' };
assert.deepEqual(await json(second.base, '/api/transfer', 202, transfer), pending);
@@ -299,6 +322,10 @@ test('native RandomX HTTP: three blocks, mined transfer, peer gossip/sync and du
assert.deepEqual(canonical.blocks, [block1, block2, block3]);
assert.deepEqual(canonical.transfers, [transfer]);
assert.deepEqual(canonical.pendingTransfers, []);
const settling = await marketCommand<MarketActivity>(first.base, market.info, bob, { type: 'activity' });
assert.equal(settling.orders[0].deliveryConfirmations, 1);
assert.equal(settling.orders[0].payout, 'not-started');
assert.equal((await json<{ listings: MarketListing[] }>(second.base, '/api/market')).listings.length, 0);
assert.equal(canonical.cumulativeWork, (3n * workFor(MAX_TARGET)).toString());
for (const node of [first, second]) {
const replay = await json<{ error: string }>(node.base, '/api/transfer', 400, transfer);
@@ -331,6 +358,9 @@ test('native RandomX HTTP: three blocks, mined transfer, peer gossip/sync and du
}
first = await start(firstDir); // Ledger.open revalidates every persisted block using native RandomX.
const restored = await snapshot(first.base);
const restoredMarket = await json<{ info: MarketInfo }>(first.base, '/api/market');
assert.equal(restoredMarket.info.marketId, market.info.marketId);
assert.equal((await marketCommand<MarketActivity>(first.base, market.info, bob, { type: 'activity' })).orders[0].id, order.id);
assert.deepEqual(restored, { ...canonical, peers: 0 });
assert.deepEqual(await json(first.base, `/api/nfts/${block1.tokenId}`), {
network: NETWORK, chainId: CHAIN_ID, tokenId: block1.tokenId, owner: bob.owner, sequence: 1,