Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1,412 changes: 984 additions & 428 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,11 @@
}
},
"dependencies": {
"@fastify/cors": "9.0.1",
"@fastify/formbody": "7.4.0",
"@fastify/http-proxy": "9.5.0",
"@fastify/swagger": "8.15.0",
"@fastify/type-provider-typebox": "4.0.0",
"@sinclair/typebox": "0.32.35",
"@fastify/cors": "11.2.0",
"@fastify/formbody": "8.0.2",
"@fastify/http-proxy": "11.4.1",
"@fastify/type-provider-typebox": "5.2.0",
"@sinclair/typebox": "0.34.48",
"@stacks/api-toolkit": "1.12.2",
"@stacks/codec": "1.6.0",
"@stacks/common": "6.10.0",
Expand All @@ -102,8 +101,8 @@
"cross-env": "7.0.3",
"ecpair": "2.1.0",
"env-schema": "7.0.0",
"fastify": "4.29.1",
"fastify-metrics": "11.0.0",
"fastify": "5.8.2",
"fastify-metrics": "12.1.0",
"getopts": "2.3.0",
"ioredis": "5.6.1",
"jsonrpc-lite": "2.2.0",
Expand All @@ -127,6 +126,7 @@
"devDependencies": {
"@commitlint/cli": "19.2.2",
"@commitlint/config-conventional": "10.0.0",
"@fastify/swagger": "9.7.0",
"@redocly/cli": "1.34.1",
"@stacks/eslint-config": "1.2.0",
"@types/dockerode": "3.3.39",
Expand Down
1 change: 1 addition & 0 deletions src/api/routes/bns/names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ export const BnsNameRoutes: FastifyPluginAsync<
],
}
),
'4xx': Type.Record(Type.String(), Type.String()),
},
},
},
Expand Down
1 change: 1 addition & 0 deletions src/api/routes/bns/namespaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export const BnsNamespaceRoutes: FastifyPluginAsync<
],
],
}),
'4xx': Type.Record(Type.String(), Type.String()),
},
},
},
Expand Down
8 changes: 4 additions & 4 deletions src/api/routes/core-node-rpc-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as path from 'path';
import fetch, { RequestInit } from 'node-fetch';
import { FastifyPluginAsync } from 'fastify';
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
import { Server, ServerResponse } from 'node:http';
import { IncomingMessage, Server } from 'node:http';
import { fastifyHttpProxy } from '@fastify/http-proxy';
import { StacksCoreRpcClient } from '../../core-rpc/client';
import { logger } from '@stacks/api-toolkit';
Expand Down Expand Up @@ -119,7 +119,7 @@ export const CoreNodeRpcProxyRouter: FastifyPluginAsync<
/**
* Reads an http request stream into a Buffer.
*/
async function readRequestBody(req: ServerResponse, maxSizeBytes = Infinity): Promise<Buffer> {
async function readRequestBody(req: IncomingMessage, maxSizeBytes = Infinity): Promise<Buffer> {
return new Promise((resolve, reject) => {
let resultBuffer: Buffer = Buffer.alloc(0);
req.on('data', chunk => {
Expand Down Expand Up @@ -330,7 +330,7 @@ export const CoreNodeRpcProxyRouter: FastifyPluginAsync<
onResponse: async (req, reply, response) => {
// Log the transaction id broadcast
if (getReqUrl(req).pathname === '/v2/transactions' && reply.statusCode === 200) {
const responseBuffer = await readRequestBody(response as ServerResponse);
const responseBuffer = await readRequestBody(response.stream);
const txId = responseBuffer.toString();
await logTxBroadcast(txId);
await reply.send(responseBuffer);
Expand All @@ -353,7 +353,7 @@ export const CoreNodeRpcProxyRouter: FastifyPluginAsync<
reqBody.transaction_payload.length / 2
);
const minFee = txSize * feeOpts.minTxFeeRatePerByte;
const responseBuffer = await readRequestBody(response as ServerResponse);
const responseBuffer = await readRequestBody(response.stream);
const responseJson = JSON.parse(responseBuffer.toString()) as FeeEstimateResponse;

if (await shouldUseTransactionMinimumFee()) {
Expand Down
10 changes: 8 additions & 2 deletions src/api/routes/faucets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,10 @@ export const FaucetRoutes: FastifyPluginAsync<
success: false,
});
}
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const forwardedFor = req.headers['x-forwarded-for'];
const ip =
(Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor?.split(',')[0])?.trim() ??
req.ip;

// Guard condition: requests are limited to 5 times per 5 minutes.
// Only based on address for now, but we're keeping the IP in case
Expand Down Expand Up @@ -429,7 +432,10 @@ export const FaucetRoutes: FastifyPluginAsync<
// Guard condition: requests are limited to x times per y minutes.
// Only based on address for now, but we're keeping the IP in case
// we want to escalate and implement a per IP policy
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const forwardedFor = req.headers['x-forwarded-for'];
const ip =
(Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor?.split(',')[0])?.trim() ??
req.ip;
const lastRequests = await fastify.db.getSTXFaucetRequests(recipientAddress);
const now = Date.now();
const isStackingReq = req.query.stacking ?? false;
Expand Down
4 changes: 2 additions & 2 deletions src/api/routes/stx-supply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export const StxSupplyRoutes: FastifyPluginAsync<
200: {
content: {
'text/plain': {
type: 'string',
schema: Type.String(),
},
},
},
Expand Down Expand Up @@ -157,7 +157,7 @@ export const StxSupplyRoutes: FastifyPluginAsync<
200: {
content: {
'text/plain': {
type: 'string',
schema: Type.String(),
},
},
},
Expand Down
63 changes: 58 additions & 5 deletions tests/bns-e2e/bns-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import { StacksMocknet } from '@stacks/network';
import {
broadcastTransaction,
bufferCV,
ClarityAbi,
FungibleConditionCode,
getAddressFromPrivateKey,
makeContractCall,
makeStandardSTXPostCondition,
standardPrincipalCV,
uintCV,
SignedContractCallOptions,
noneCV,
StacksTransaction,
TransactionVersion,
} from '@stacks/transactions';
import { PgWriteStore } from '../../src/datastore/pg-write-store';
import { standByForTx as standByForTxShared } from '../utils/test-helpers';
Expand Down Expand Up @@ -43,9 +47,58 @@ type TestnetKey = {
describe('BNS integration tests', () => {
let db: PgWriteStore;
let api: ApiServer;
let bnsContractAbi: ClarityAbi | undefined;

const standByForTx = (expectedTxId: string) => standByForTxShared(expectedTxId, api);

async function getBnsContractAbi(): Promise<ClarityAbi> {
if (bnsContractAbi) return bnsContractAbi;
const contractId = `${deployedTo}.${deployedName}`;
const contractResp = await supertest(api.server).get(`/extended/v1/contract/${contractId}`);
if (contractResp.status === 200 && contractResp.body?.abi) {
const apiAbi =
typeof contractResp.body.abi === 'string'
? JSON.parse(contractResp.body.abi)
: contractResp.body.abi;
if (apiAbi?.functions) {
bnsContractAbi = apiAbi as ClarityAbi;
return bnsContractAbi;
}
}

const abiUrl = network.getAbiApiUrl(deployedTo, deployedName);
const response = await fetch(abiUrl);
if (!response.ok) {
throw new Error(
`Failed to fetch BNS ABI from API and ${abiUrl}: ${response.status} ${response.statusText}`
);
}
const payload = JSON.parse(await response.text());
const abiCandidate = payload?.functions ?? payload?.abi ?? payload?.contract_interface;
if (!abiCandidate?.functions) {
const debugShape = JSON.stringify(Object.keys(payload ?? {}));
throw new Error(
`Unexpected ABI response shape from ${abiUrl}. Top-level keys: ${debugShape}`
);
}
bnsContractAbi = abiCandidate as ClarityAbi;
return bnsContractAbi;
}

async function makeBnsContractCall(
txOptions: SignedContractCallOptions
): Promise<StacksTransaction> {
const abi = await getBnsContractAbi();
const senderAddress = getAddressFromPrivateKey(txOptions.senderKey, TransactionVersion.Testnet);
const nonces = await db.getAddressNonces({ stxAddress: senderAddress });
const options = {
...txOptions,
validateWithAbi: abi,
nonce: txOptions.nonce ?? BigInt(nonces.possibleNextNonce),
};
return await makeContractCall(options);
}

async function standbyBnsName(expectedTxId: string): Promise<string> {
const broadcastTx = new Promise<string>(resolve => {
const listener: (txId: string) => void = txId => {
Expand All @@ -61,7 +114,7 @@ describe('BNS integration tests', () => {
return txid;
}
async function getContractTransaction(txOptions: SignedContractCallOptions, zonefile?: string) {
const transaction = await makeContractCall(txOptions);
const transaction = await makeBnsContractCall(txOptions);
const body: { tx: string; attachment?: string } = {
tx: Buffer.from(transaction.serialize()).toString('hex'),
};
Expand Down Expand Up @@ -95,7 +148,7 @@ describe('BNS integration tests', () => {
fee: 100000,
};

const transaction = await makeContractCall(txOptions);
const transaction = await makeBnsContractCall(txOptions);
await broadcastTransaction(transaction, network);
const preorder = await standByForTx('0x' + transaction.txid());
if (preorder.status != 1) logger.error('Namespace preorder error');
Expand Down Expand Up @@ -144,7 +197,7 @@ describe('BNS integration tests', () => {
anchorMode: AnchorMode.Any,
fee: 100000,
};
const revealTransaction = await makeContractCall(revealTxOptions);
const revealTransaction = await makeBnsContractCall(revealTxOptions);
await broadcastTransaction(revealTransaction, network);
const reveal = await standByForTx('0x' + revealTransaction.txid());
if (reveal.status != 1) logger.error('Namespace Reveal Error');
Expand Down Expand Up @@ -172,7 +225,7 @@ describe('BNS integration tests', () => {
anchorMode: AnchorMode.Any,
fee: 100000,
};
const transaction = await makeContractCall(txOptions);
const transaction = await makeBnsContractCall(txOptions);
await broadcastTransaction(transaction, network);
const readyResult = await standByForTx('0x' + transaction.txid());
if (readyResult.status != 1) logger.error('namespace-ready error');
Expand Down Expand Up @@ -245,7 +298,7 @@ describe('BNS integration tests', () => {
fee: 100000,
};

const preOrderTransaction = await makeContractCall(preOrderTxOptions);
const preOrderTransaction = await makeBnsContractCall(preOrderTxOptions);
await broadcastTransaction(preOrderTransaction, network);
const preorderResult = await standByForTx('0x' + preOrderTransaction.txid());
return preOrderTransaction;
Expand Down
Loading