Skip to content

Commit

Permalink
Merge pull request #177 from blockfrost/feat/mithril-proxy
Browse files Browse the repository at this point in the history
feat: mithril proxy
  • Loading branch information
vladimirvolek authored Jul 1, 2024
2 parents d36525c + 5d86b89 commit 4d42279
Show file tree
Hide file tree
Showing 101 changed files with 1,172 additions and 373 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = {
'unicorn/no-process-exit': 0,
'unicorn/prefer-ternary': 0,
'unicorn/no-null': 'off',
'no-nested-ternary': 'off',
'unicorn/prevent-abbreviations': [
'error',
{
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"libc",
"milkomeda",
"mirs",
"mithril",
"mydomain",
"nutcoin",
"nutlink",
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed .yarn/cache/ret-npm-0.2.2-f5d3022812-774964bb41.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Proxy for Mithril Aggregator API
- config options `mithril.enabled`, `mithril.aggregator`, `mithril.snapshotCDN` (optional) and `mithril.allowedEndpoints` (optional)
- ENV var options `BLOCKFROST_MITHRIL_ENABLED`, `BLOCKFROST_MITHRIL_AGGREGATOR` and `BLOCKFROST_MITHRIL_SNAPSHOT_CDN`

### Changed
- Upgraded Fastify dependencies
- Upgraded Typescript

## [2.0.3] - 2024-05-23

### Fixed
Expand Down
78 changes: 54 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,34 @@ If you are using an authenticated db connection that requires a password, you'd

#### Schema

```ts
{
```yaml

// Blockfrost backend settings
server: {
// Server listen address, you need to set this to 0.0.0.0 if running within docker
listenAddress: 'localhost',
// Server port
port: 3000,
// Whether to enable verbose logging, when disabled only ERRORs are printed to a console
debug: true,
// Whether to expose /prometheus endpoint
prometheusMetrics: false,
},
// Cardano DB Sync SQL connection
dbSync: {
host: 'cdbsync-dev.mydomain.com',
user: 'username',
database: 'dbname',
// Optionally define a password
server:
# Server listen address, you need to set this to 0.0.0.0 if running within docker
listenAddress: 'localhost'
# Server port
port: 3000
# Whether to enable verbose logging, when disabled only ERRORs are printed to a console
debug: true
# Whether to expose /prometheus endpoint
prometheusMetrics: false
# Cardano DB Sync SQL connection
dbSync:
host: 'cdbsync-dev.mydomain.com'
user: 'username'
database: 'dbname'
# Optionally define a password
password: 'randomstringthatissolongandpowerfulthatnoonecanguess'
},
// Cardano network - mainnet, testnet, preview, preprod
network: 'mainnet',
// Path to token registry directory (see next section for more details)
tokenRegistryUrl: 'https://tokens.cardano.org',
}
# Cardano network - mainnet, testnet, preview, preprod
network: 'mainnet'
# Path to token registry directory (see next section for more details)
tokenRegistryUrl: 'https://tokens.cardano.org'
# Experimental Mithril proxy
mithril:
enabled: true # ENV var BLOCKFROST_MITHRIL_ENABLED=true
aggregator: "https://aggregator.pre-release-preview.api.mithril.network/aggregator" # ENV var BLOCKFROST_MITHRIL_AGGREGATOR
snapshotCDN: "https://example.com/" # ENV var BLOCKFROST_MITHRIL_SNAPSHOT_CDN
```
<details>
Expand Down Expand Up @@ -104,6 +106,34 @@ CREATE INDEX IF NOT EXISTS bf_idx_instant_reward_addr_id ON instant_reward USING
CREATE INDEX IF NOT EXISTS bf_idx_instant_reward_spendable_epoch ON instant_reward USING btree (spendable_epoch);
```

### Experimental features

### Mithril

Blockfrost Backend optionally provides a proxy for the Mithril aggregator API. This feature allows users to interact with Mithril's endpoints through Blockfrost, with additional enhancements and customizations specific to Blockfrost.

> This is an experimental feature. Mithril is currently a work in progress and its API may change.

All Mithril-related endpoints are available under the `/mithril` path.
For list of available endpoints please visit https://mithril.network/doc/aggregator-api/.

To enable this experimental feature add following lines to your config:
```yaml
mithril:
enabled: true # ENV var BLOCKFROST_MITHRIL_ENABLED=true
aggregator: "https://aggregator.pre-release-preview.api.mithril.network/aggregator" # ENV var BLOCKFROST_MITHRIL_AGGREGATOR
snapshotCDN: "https://example.com/" # Optional, ENV var BLOCKFROST_MITHRIL_SNAPSHOT_CDN
```

Then you can simply query Mithril API using Blockfrost Backend:

```
curl localhost:3000/mithril/artifact/snapshots
```

If you set `mithril.snapshotCDN` option, then the response of `/artifact/snapshots` and `/artifact/snapshot/{digest}` endpoints is enhanced with additional link to the list of snapshot locations.


### Docker

We are hosting latest release of this software on Dockerhub. To run it using Docker:
Expand Down
3 changes: 3 additions & 0 deletions config/development.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ dbSync:
maxConnections: 10
network: "mainnet"
tokenRegistryUrl: "https://tokens.cardano.org"
mithril:
enabled: true
aggregator: "https://aggregator.release-mainnet.api.mithril.network/aggregator"
4 changes: 4 additions & 0 deletions config/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ dbSync:
ssl: true
network: "mainnet"
tokenRegistryUrl: "http://localhost:3100"
mithril:
enabled: true
aggregator: "https://aggregator.release-mainnet.api.mithril.network/aggregator"
snapshotCDN: "https://dummy-mithril-snapshot-cdn.com"
17 changes: 10 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"start": "yarn build && node --max-http-header-size=32768 dist/server.js",
"start-mainnet": "cross-env-shell NODE_ENV=mainnet \"yarn start\"",
"start-testnet": "cross-env-shell NODE_ENV=testnet \"yarn start\"",
"test": "cross-env NODE_OPTIONS='--max-http-header-size 32768' NODE_ENV=test vitest",
"test": "cross-env NODE_ENV=test NODE_OPTIONS='--max-http-header-size 32768' vitest",
"test-badges": "make-coverage-badge --output-path ./docs/images/badge.svg",
"type-check": "tsc --project tsconfig.json"
},
Expand All @@ -25,17 +25,20 @@
"@blockfrost/blockfrost-utils": "2.8.0",
"@blockfrost/openapi": "0.1.63",
"@emurgo/cardano-serialization-lib-nodejs": "11.5.0",
"@fastify/cors": "^8.3.0",
"@fastify/postgres": "^5.2.0",
"@fastify/cors": "^9.0.1",
"@fastify/http-proxy": "^9.5.0",
"@fastify/postgres": "^5.2.2",
"@sentry/node": "^7.69.0",
"JSONStream": "^1.3.5",
"ajv": "^8.12.0",
"axios": "^1.5.0",
"config": "3.3.9",
"crc": "^4.3.2",
"cross-env": "^7.0.3",
"fastify": "4.23.2",
"fastify": "4.27.0",
"fastify-plugin": "^4.5.1",
"nock": "^13.5.4",
"path-to-regexp": "^6.2.2",
"pg": "^8.11.3",
"pg-format": "^1.0.4",
"pino-pretty": "10.2.0",
Expand All @@ -51,8 +54,8 @@
"@types/pg-format": "^1.0.2",
"@types/sinon": "^10.0.16",
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/parser": "6.7.2",
"@typescript-eslint/eslint-plugin": "^7.10.0",
"@typescript-eslint/parser": "7.10.0",
"@vitest/coverage-v8": "^1.2.2",
"blake2b": "^2.1.4",
"eslint": "8.49.0",
Expand All @@ -67,7 +70,7 @@
"sinon": "^16.0.0",
"supertest": "^6.3.3",
"ts-node": "^10.9.1",
"typescript": "^5.2.2",
"typescript": "^5.4.5",
"vitest": "^1.2.2"
},
"packageManager": "yarn@3.6.3",
Expand Down
4 changes: 4 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getConfig } from './config.js';
import { registerRoute } from './utils/common.js';
import { errorHandler, notFoundHandler } from './utils/error-handler.js';
import { createRequire } from 'module';
import { registerMithrilProxy } from './proxies/mithril.js';

const esmRequire = createRequire(import.meta.url);
const packageJson = esmRequire('../package.json');
Expand Down Expand Up @@ -58,6 +59,9 @@ const start = (options = {}): FastifyInstance => {
ssl: config.dbSync.ssl,
});

// proxies
registerMithrilProxy(app);

// addresses
registerRoute(app, import('./routes/addresses/address/extended.js'));
registerRoute(app, import('./routes/addresses/address/index.js'));
Expand Down
59 changes: 57 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import config from 'config';

import { CARDANO_NETWORKS, Network } from './types/common.js';

const MITHRIL_ENDPOINT_ALLOWLIST_DEFAULT = [
'/',
'/epoch-settings',
'/certificate-pending',
'/certificates',
'/certificate/:certificate_hash',
'/artifact/snapshots',
'/artifact/snapshot/:digest',
'/artifact/snapshot/:digest/download',
'/artifact/mithril-stake-distributions',
'/artifact/cardano-transactions',
'/artifact/cardano-transaction/:hash',
'/proof/cardano-transaction',
'/signers/registered/:epoch',
'/signers/tickers',
];

export const loadConfig = () => {
// server
const listenAddress =
Expand Down Expand Up @@ -41,12 +57,44 @@ export const loadConfig = () => {
const network = process.env.BLOCKFROST_CONFIG_NETWORK ?? config.get('network');

if (!network || !CARDANO_NETWORKS.includes(network)) {
throw new Error('Invalid network in the config.');
throw new Error('Invalid network configuration.');
}
// token registry
const tokenRegistryUrl =
process.env.BLOCKFROST_CONFIG_TOKEN_REGISTRY_URL ?? config.get('tokenRegistryUrl');

// Mithril
let mithrilEnabled = config.has('mithril.enabled')
? config.get<boolean>('mithril.enabled')
: false;

let mithrilAggregator =
process.env.BLOCKFROST_MITHRIL_AGGREGATOR ?? config.has('mithril.aggregator')
? config.get<string>('mithril.aggregator')
: undefined;

const mithrilSnapshotCDN =
process.env.BLOCKFROST_MITHRIL_SNAPSHOT_CDN ?? config.has('mithril.snapshotCDN')
? config.get<string>('mithril.snapshotCDN')
: undefined;

const mithrilAllowedEndpoints = config.has('mithril.mithrilAllowedEndpoints')
? config.get<string[]>('mithril.mithrilAllowedEndpoints')
: MITHRIL_ENDPOINT_ALLOWLIST_DEFAULT;

// ENV vars override config
if (process.env.BLOCKFROST_MITHRIL_ENABLED) {
mithrilEnabled = process.env.BLOCKFROST_MITHRIL_ENABLED === 'true';
}

if (process.env.BLOCKFROST_MITHRIL_AGGREGATOR) {
mithrilAggregator = process.env.BLOCKFROST_MITHRIL_AGGREGATOR;
}

if (mithrilEnabled && !mithrilAggregator) {
throw new Error('Invalid Mithril Aggregator configuration');
}

return {
server: {
listenAddress,
Expand All @@ -65,11 +113,18 @@ export const loadConfig = () => {
},
network: network as Network,
tokenRegistryUrl,
mithril: {
enabled: mithrilEnabled,
aggregator: mithrilAggregator as string,
snapshotCDN: mithrilSnapshotCDN,
allowedEndpoints: mithrilAllowedEndpoints,
},
};
};

export const mainConfig = loadConfig();

// Use this function to load config to allow easier mocking in unit tests
export const getConfig = () => {
return mainConfig;
};
Loading

0 comments on commit 4d42279

Please sign in to comment.