Planning
This commit is contained in:
+21
-13
@@ -14,8 +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';
|
||||
import { type MarketAction, type MarketInfo } from '../shared/market.ts';
|
||||
import { marketMessageV3, type MarketActionV3, type MarketActivityV3, type MarketListingV3, type MarketOrderV3 } from '../shared/market-v3.ts';
|
||||
import { StateClient } from '../shared/rpc-client.ts';
|
||||
import { createBackup,restoreBackup } from '../server/recovery.ts';
|
||||
import { Ledger } from '../server/testnet/ledger.ts';
|
||||
@@ -139,9 +139,17 @@ test('native RandomX HTTP: three blocks, mined transfer, peer gossip/sync and du
|
||||
}
|
||||
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)) });
|
||||
const atomicFromCents = (cents: number) => ((BigInt(cents) * 1_000_000_000_000n + 14999n) / 15000n).toString();
|
||||
let current: MarketActionV3;
|
||||
if (action.type === 'list') current = { type: 'list', cardHash: action.cardHash, sequence: action.sequence,
|
||||
kind: action.kind, priceAtomicXmr: atomicFromCents(action.priceCents), endsAt: action.endsAt, feeBps: action.feeBps };
|
||||
else if (action.type === 'buy') current = { type: 'buy', listingId: action.listingId,
|
||||
priceAtomicXmr: atomicFromCents(action.priceCents), feeBps: action.feeBps };
|
||||
else if (action.type === 'bid') current = { type: 'bid', listingId: action.listingId, priceAtomicXmr: atomicFromCents(action.priceCents) };
|
||||
else current = action;
|
||||
const terms = { version: 3 as const, network: NETWORK, chainId: CHAIN_ID, marketId: info.marketId, actor: who.owner,
|
||||
publicKey: who.publicKey, nonce: randomUUID(), expiresAt: Date.now() + 60000, action: current };
|
||||
return json<T>(base, '/api/market/command', 200, { ...terms, signature: who.signed(marketMessageV3(terms)) });
|
||||
}
|
||||
async function waitFor<T>(label: string, read: () => Promise<T>, predicate: (value: T) => boolean): Promise<T> {
|
||||
const until = Date.now() + 25_000;
|
||||
@@ -264,15 +272,15 @@ 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');
|
||||
const market = await json<{ info: MarketInfo; listings: MarketListingV3[] }>(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',
|
||||
const sale = await marketCommand<MarketListingV3>(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,
|
||||
assert.equal((await json<{ listings: MarketListingV3[] }>(first.base, '/api/market')).listings[0].id, sale.id);
|
||||
const order = await marketCommand<MarketOrderV3>(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');
|
||||
@@ -292,7 +300,7 @@ test('native RandomX HTTP: three blocks, mined transfer, peer gossip/sync and du
|
||||
{ ...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');
|
||||
assert.equal((await marketCommand<MarketActivityV3>(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);
|
||||
@@ -330,10 +338,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' });
|
||||
const settling = await marketCommand<MarketActivityV3>(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((await json<{ listings: MarketListingV3[] }>(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);
|
||||
@@ -383,7 +391,7 @@ test('native RandomX HTTP: three blocks, mined transfer, peer gossip/sync and du
|
||||
assert.deepEqual(await pagedClient.load(),restored);
|
||||
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.equal((await marketCommand<MarketActivityV3>(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,
|
||||
|
||||
Reference in New Issue
Block a user