Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a35dc508b0 | |||
| 0f34eb3e41 | |||
| 2ffa63df41 | |||
| e4430ae27c | |||
| 2071db93e5 | |||
| b6019a48e5 | |||
| 14ac869ed6 |
@@ -2,7 +2,8 @@ name: Publish to NPM Registry
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created, published]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
@@ -16,7 +17,6 @@ jobs:
|
|||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '20.x'
|
node-version: '20.x'
|
||||||
registry-url: 'https://git.dragonchain.com/api/packages/dragonchain/npm/'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -28,6 +28,9 @@ jobs:
|
|||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Publish to Gitea NPM registry
|
- name: Publish to Gitea NPM registry
|
||||||
run: npm publish
|
run: |
|
||||||
|
echo "@dragonchain-inc:registry=https://git.dragonchain.com/api/packages/dragonchain/npm/" > ~/.npmrc
|
||||||
|
echo "//git.dragonchain.com/api/packages/dragonchain/npm/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc
|
||||||
|
npm publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.SA_DC_BUILD_API_TOKEN }}
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@dragonchain-inc/prime-sdk",
|
"name": "@dragonchain-inc/prime-sdk",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@dragonchain-inc/prime-sdk",
|
"name": "@dragonchain-inc/prime-sdk",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-yaml": "^4.1.0"
|
"js-yaml": "^4.1.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@dragonchain-inc/prime-sdk",
|
"name": "@dragonchain-inc/prime-sdk",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"description": "Official Dragonchain Prime SDK for Node.js and TypeScript",
|
"description": "Official Dragonchain Prime SDK for Node.js and TypeScript",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.mjs",
|
"module": "./dist/index.mjs",
|
||||||
|
|||||||
10
src/block.ts
10
src/block.ts
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { DragonchainClient } from './client';
|
import { DragonchainClient } from './client';
|
||||||
import { Block } from './types';
|
import { Block, InterchainTrace } from './types';
|
||||||
|
|
||||||
export class BlockClient {
|
export class BlockClient {
|
||||||
private client: DragonchainClient;
|
private client: DragonchainClient;
|
||||||
@@ -18,4 +18,12 @@ export class BlockClient {
|
|||||||
async get(blockId: string): Promise<Block> {
|
async get(blockId: string): Promise<Block> {
|
||||||
return this.client.get<Block>(`/api/v1/block/${blockId}`);
|
return this.client.get<Block>(`/api/v1/block/${blockId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traces a block to the validator (verification) blocks that validated it and
|
||||||
|
* the public-chain interchain anchors those validator blocks were bundled into.
|
||||||
|
*/
|
||||||
|
async getInterchain(blockId: string): Promise<InterchainTrace> {
|
||||||
|
return this.client.get<InterchainTrace>(`/api/v1/block/${blockId}/interchain`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,15 @@ export interface ClientConfig {
|
|||||||
authKey: string;
|
authKey: string;
|
||||||
baseURL: string;
|
baseURL: string;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
/**
|
||||||
|
* Optional http(s).Agent used for every request. Inject an agent whose
|
||||||
|
* connection factory refuses to connect to internal IPs when baseURL is
|
||||||
|
* attacker-influenced (a tenant's prime_endpoint), to defend against SSRF
|
||||||
|
* (incl. DNS rebinding) at connect time. The SSRF policy belongs in the
|
||||||
|
* server that points this client at untrusted endpoints, not baked into
|
||||||
|
* the client library, so the default (no agent) is unguarded.
|
||||||
|
*/
|
||||||
|
agent?: http.Agent | https.Agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DragonchainClient {
|
export class DragonchainClient {
|
||||||
@@ -21,6 +30,7 @@ export class DragonchainClient {
|
|||||||
private readonly authKey: string;
|
private readonly authKey: string;
|
||||||
private readonly baseURL: string;
|
private readonly baseURL: string;
|
||||||
private readonly timeout: number;
|
private readonly timeout: number;
|
||||||
|
private readonly agent?: http.Agent | https.Agent;
|
||||||
|
|
||||||
constructor(config: ClientConfig) {
|
constructor(config: ClientConfig) {
|
||||||
this.publicId = config.publicId;
|
this.publicId = config.publicId;
|
||||||
@@ -28,6 +38,7 @@ export class DragonchainClient {
|
|||||||
this.authKey = config.authKey;
|
this.authKey = config.authKey;
|
||||||
this.baseURL = config.baseURL.replace(/\/$/, ''); // Remove trailing slash
|
this.baseURL = config.baseURL.replace(/\/$/, ''); // Remove trailing slash
|
||||||
this.timeout = config.timeout || 30000; // Default 30 seconds
|
this.timeout = config.timeout || 30000; // Default 30 seconds
|
||||||
|
this.agent = config.agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,6 +134,7 @@ export class DragonchainClient {
|
|||||||
'Content-Length': bodyBuffer.length,
|
'Content-Length': bodyBuffer.length,
|
||||||
},
|
},
|
||||||
timeout: this.timeout,
|
timeout: this.timeout,
|
||||||
|
...(this.agent && { agent: this.agent }),
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise<T>((resolve, reject) => {
|
return new Promise<T>((resolve, reject) => {
|
||||||
@@ -137,6 +149,19 @@ export class DragonchainClient {
|
|||||||
res.on('end', () => {
|
res.on('end', () => {
|
||||||
const responseBody = Buffer.concat(chunks);
|
const responseBody = Buffer.concat(chunks);
|
||||||
|
|
||||||
|
// Refuse redirects. node's http.request does not follow them, but
|
||||||
|
// treating a 3xx as success would mis-parse the (empty) body — and
|
||||||
|
// a followed redirect to an internal host would be an SSRF vector.
|
||||||
|
// Prime never legitimately redirects, so a 3xx is an error.
|
||||||
|
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400) {
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
`Unexpected redirect (status ${res.statusCode}) to ${res.headers.location ?? '?'}; refusing to follow`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for errors
|
// Check for errors
|
||||||
if (res.statusCode && res.statusCode >= 400) {
|
if (res.statusCode && res.statusCode >= 400) {
|
||||||
const errorMessage = responseBody.toString('utf8').trim();
|
const errorMessage = responseBody.toString('utf8').trim();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
TransactionBulkResponse,
|
TransactionBulkResponse,
|
||||||
Transaction,
|
Transaction,
|
||||||
ListTransactionsResponse,
|
ListTransactionsResponse,
|
||||||
|
InterchainTrace,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export class TransactionClient {
|
export class TransactionClient {
|
||||||
@@ -49,6 +50,16 @@ export class TransactionClient {
|
|||||||
return this.client.get<Transaction>(`/api/v1/transaction/${transactionId}`);
|
return this.client.get<Transaction>(`/api/v1/transaction/${transactionId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traces a transaction to the validator (verification) blocks that validated
|
||||||
|
* its prime block and the public-chain interchain anchors those validator
|
||||||
|
* blocks were bundled into. If the transaction is still pending (not yet in a
|
||||||
|
* block) the trace's arrays are empty.
|
||||||
|
*/
|
||||||
|
async getInterchain(transactionId: string): Promise<InterchainTrace> {
|
||||||
|
return this.client.get<InterchainTrace>(`/api/v1/transaction/${transactionId}/interchain`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists all transactions
|
* Lists all transactions
|
||||||
*/
|
*/
|
||||||
|
|||||||
56
src/types.ts
56
src/types.ts
@@ -122,21 +122,67 @@ export interface SmartContract {
|
|||||||
|
|
||||||
// Block Types
|
// Block Types
|
||||||
export interface BlockProof {
|
export interface BlockProof {
|
||||||
scheme: string;
|
|
||||||
proof: string;
|
proof: string;
|
||||||
|
scheme?: string; // absent on trust-scheme chains; present on PoW
|
||||||
nonce?: number;
|
nonce?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BlockHeader {
|
||||||
|
blockId: string;
|
||||||
|
dcId: string;
|
||||||
|
prevId: string;
|
||||||
|
prevProof: string;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Block {
|
export interface Block {
|
||||||
version: string;
|
version: string;
|
||||||
block_id: string;
|
header: BlockHeader;
|
||||||
timestamp: string;
|
|
||||||
prev_id: string;
|
|
||||||
prev_proof: string;
|
|
||||||
transactions: string[];
|
transactions: string[];
|
||||||
proof: BlockProof;
|
proof: BlockProof;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interchain Trace Types
|
||||||
|
|
||||||
|
/** A validator's verification of a prime block. */
|
||||||
|
export interface VerificationBlock {
|
||||||
|
version: string;
|
||||||
|
primeChainId: string;
|
||||||
|
primeBlockId: string;
|
||||||
|
timestamp: string;
|
||||||
|
verifierPublicKey: string;
|
||||||
|
verifierSignature: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An anchor broadcast to a public blockchain (e.g. ETH/BTC) bundling one or more
|
||||||
|
* validator blocks. `validatorBlocks` holds the covered prime block ids;
|
||||||
|
* `coveredPrimeChainIds` the prime chains they belong to.
|
||||||
|
*/
|
||||||
|
export interface InterchainTransaction {
|
||||||
|
id: number;
|
||||||
|
version: string;
|
||||||
|
timestamp: string;
|
||||||
|
chainId: string;
|
||||||
|
transHash: string;
|
||||||
|
blockId: string;
|
||||||
|
validatorBlocks: string[];
|
||||||
|
validatorBlockhash: string;
|
||||||
|
signature: string;
|
||||||
|
coveredPrimeChainIds: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links a prime block to the validator (verification) blocks that validated it
|
||||||
|
* and the public-chain interchain anchors those validator blocks were bundled
|
||||||
|
* into. Returned by `transaction.getInterchain` and `block.getInterchain`.
|
||||||
|
*/
|
||||||
|
export interface InterchainTrace {
|
||||||
|
blockId: string;
|
||||||
|
validatorBlocks: VerificationBlock[];
|
||||||
|
interchainTransactions: InterchainTransaction[];
|
||||||
|
}
|
||||||
|
|
||||||
// System Types
|
// System Types
|
||||||
export interface SystemStatus {
|
export interface SystemStatus {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ describe('DragonchainSDK', () => {
|
|||||||
expect(typeof sdk.transaction.create).toBe('function');
|
expect(typeof sdk.transaction.create).toBe('function');
|
||||||
expect(typeof sdk.transaction.createBulk).toBe('function');
|
expect(typeof sdk.transaction.createBulk).toBe('function');
|
||||||
expect(typeof sdk.transaction.get).toBe('function');
|
expect(typeof sdk.transaction.get).toBe('function');
|
||||||
|
expect(typeof sdk.transaction.getInterchain).toBe('function');
|
||||||
expect(typeof sdk.transaction.list).toBe('function');
|
expect(typeof sdk.transaction.list).toBe('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ describe('DragonchainSDK', () => {
|
|||||||
|
|
||||||
it('should have block module with correct methods', () => {
|
it('should have block module with correct methods', () => {
|
||||||
expect(typeof sdk.block.get).toBe('function');
|
expect(typeof sdk.block.get).toBe('function');
|
||||||
|
expect(typeof sdk.block.getInterchain).toBe('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have system module with correct methods', () => {
|
it('should have system module with correct methods', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user