Skip to content

Commit 2e04f7a

Browse files
authored
feat: feature api (#846)
* feat: feature api * chore: linter changes * feat: expose features API * tests(integration): features api tests * chore: linter changes * tests(integration): check return format * chore: linter changes
1 parent 660d283 commit 2e04f7a

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

__tests__/integration/api.test.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import featureApi, { getBlockFeaturesSchema, getFeaturesSchema } from '../../src/api/featuresApi';
2+
3+
describe('Feature api', () => {
4+
it('should be able to call the features api', async () => {
5+
await expect(featureApi.getFeatures()).resolves.not.toThrow();
6+
7+
// Validate response format
8+
const response = await featureApi.getFeatures();
9+
expect(() => getFeaturesSchema.parse(response)).not.toThrow();
10+
11+
expect(response.features).toEqual(
12+
expect.arrayContaining([
13+
expect.objectContaining({
14+
name: 'INCREASE_MAX_MERKLE_PATH_LENGTH',
15+
}),
16+
])
17+
);
18+
});
19+
20+
it('should be able to call the block features api', async () => {
21+
const response = await featureApi.getFeatures();
22+
await expect(featureApi.getBlockFeatures(response.block_hash)).resolves.not.toThrow();
23+
// Validate response format
24+
const blockResponse = await featureApi.getBlockFeatures(response.block_hash);
25+
expect(() => getBlockFeaturesSchema.parse(blockResponse)).not.toThrow();
26+
});
27+
});

src/api/featuresApi.ts

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* Copyright (c) Hathor Labs and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import { z } from 'zod';
9+
import { createRequestInstance } from './axiosInstance';
10+
import { transformJsonBigIntResponse } from '../utils/bigint';
11+
12+
export const featureActivationSchema = z
13+
.object({
14+
name: z.string(),
15+
state: z.string(),
16+
acceptance: z.number().nullish(),
17+
threshold: z.number(),
18+
start_height: z.number(),
19+
minimum_activation_height: z.number(),
20+
timeout_height: z.number(),
21+
lock_in_on_timeout: z.boolean(),
22+
version: z.string(),
23+
})
24+
.passthrough();
25+
26+
export const getFeaturesSchema = z
27+
.object({
28+
block_hash: z.string(),
29+
block_height: z.number().min(0),
30+
features: z.array(featureActivationSchema),
31+
})
32+
.passthrough();
33+
34+
export const errorSchema = z.object({
35+
success: z.literal(false),
36+
error: z.string(),
37+
});
38+
39+
export const getBlockFeatureSignalBitSchema = z
40+
.object({
41+
bit: z.number(),
42+
signal: z.number(),
43+
feature: z.string(),
44+
feature_state: z.string(),
45+
})
46+
.passthrough();
47+
48+
export const getBlockFeaturesSuccessSchema = z
49+
.object({
50+
signal_bits: z.array(getBlockFeatureSignalBitSchema),
51+
})
52+
.passthrough();
53+
54+
export const getBlockFeaturesSchema = z.union([getBlockFeaturesSuccessSchema, errorSchema]);
55+
56+
const featuresApi = {
57+
/**
58+
* Get feature activation information
59+
*/
60+
async getFeatures(): Promise<z.output<typeof getFeaturesSchema>> {
61+
return new Promise((resolve, reject) => {
62+
// @ts-expect-error XXX: createRequestInstance resolve argument is not typed correctly
63+
createRequestInstance(resolve)
64+
.get(`feature`, {
65+
transformResponse: res => transformJsonBigIntResponse(res, getFeaturesSchema),
66+
})
67+
.then(
68+
res => {
69+
resolve(res.data);
70+
},
71+
res => {
72+
reject(res);
73+
}
74+
);
75+
});
76+
},
77+
78+
/**
79+
* Get block features information
80+
* @param blockHash Block id encoded as hex
81+
*/
82+
async getBlockFeatures(blockHash: string): Promise<z.output<typeof getBlockFeaturesSchema>> {
83+
return new Promise((resolve, reject) => {
84+
// @ts-expect-error XXX: createRequestInstance resolve argument is not typed correctly
85+
createRequestInstance(resolve)
86+
.get(`feature`, {
87+
params: { block: blockHash },
88+
transformResponse: res => transformJsonBigIntResponse(res, getBlockFeaturesSchema),
89+
})
90+
.then(
91+
res => {
92+
resolve(res.data);
93+
},
94+
res => {
95+
reject(res);
96+
}
97+
);
98+
});
99+
},
100+
};
101+
102+
export default featuresApi;

src/lib.ts

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import healthApi from './api/health';
1010
import versionApi from './api/version';
1111
import * as axios from './api/axiosInstance';
1212
import metadataApi from './api/metadataApi';
13+
import featuresApi from './api/featuresApi';
1314
import { Storage } from './storage/storage';
1415
import { MemoryStore } from './storage/memory_store';
1516
import network from './network';
@@ -70,6 +71,7 @@ export {
7071
healthApi,
7172
versionApi,
7273
metadataApi,
74+
featuresApi,
7375
errors,
7476
ErrorMessages,
7577
constants,

0 commit comments

Comments
 (0)