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
29 changes: 29 additions & 0 deletions .github/workflows/dependabot-auto-approve-minor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Dependabot auto-approve minor updates
on: pull_request

permissions:
pull-requests: write

jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
strategy:
matrix:
dependencyStartsWith:
- '@checkernetwork/prettier-config'
- neostandard
- prettier
- typescript
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
- name: Approve a PR
if: ${{startsWith(steps.metadata.outputs.dependency-names, matrix.dependencyStartsWith) && (steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor')}}
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
18 changes: 18 additions & 0 deletions .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Dependabot auto-merge
on: pull_request

permissions:
contents: write
pull-requests: write

jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Authenticate cli with a PAT
run: echo "${{ secrets.DEPENDABOT_TOKEN }}" | gh auth login --with-token
- name: Enable auto-merge for Dependabot PRs
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
5 changes: 0 additions & 5 deletions .prettierrc.yaml

This file was deleted.

4 changes: 3 additions & 1 deletion lib/activity-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ export class ActivityState {
#healthy = null

onOutdatedClient() {
this.onError('SPARK is outdated. Please upgrade Filecoin Station to the latest version.')
this.onError(
'SPARK is outdated. Please upgrade Filecoin Station to the latest version.',
)
}

onError(msg) {
Expand Down
3 changes: 2 additions & 1 deletion lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export const APPROX_ROUND_LENGTH_IN_MS = 20 * 60_000 // 20 minutes
export const MAX_JITTER_BETWEEN_TASKS_IN_MS = 10_000 // 10 seconds
export const RPC_URL = 'https://api.node.glif.io/'
export const RPC_AUTH = 'KZLIUb9ejreYOm-mZFM3UNADE0ux6CrHjxnS2D2Qgb8='
export const MINER_TO_PEERID_CONTRACT_ADDRESS = '0x14183aD016Ddc83D638425D6328009aa390339Ce' // Contract address on the Filecoin EVM
export const MINER_TO_PEERID_CONTRACT_ADDRESS =
'0x14183aD016Ddc83D638425D6328009aa390339Ce' // Contract address on the Filecoin EVM
export const MAX_REQUEST_DURATION_MS = 90_000
16 changes: 10 additions & 6 deletions lib/drand-client.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { fetchBeaconByTime, HttpChainClient, HttpCachingChain } from '../vendor/deno-deps.js'
import {
fetchBeaconByTime,
HttpChainClient,
HttpCachingChain,
} from '../vendor/deno-deps.js'

// See https://docs.filecoin.io/networks/mainnet#genesis
const FIL_MAINNET_GENESIS_TS = new Date('2020-08-24T22:00:00Z').getTime()
Expand All @@ -13,7 +17,8 @@ const DRAND_OPTIONS = {
noCache: false,
chainVerificationParams: {
// quicknet
chainHash: '52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971',
chainHash:
'52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971',
publicKey:
'83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a',
},
Expand All @@ -23,11 +28,10 @@ const DRAND_URL = `https://api2.drand.sh/${DRAND_OPTIONS.chainVerificationParams
const chain = new HttpCachingChain(DRAND_URL, DRAND_OPTIONS)
const client = new HttpChainClient(chain, DRAND_OPTIONS)

/**
* @param {number} roundStartEpoch
*/
/** @param {number} roundStartEpoch */
export async function getRandomnessForSparkRound(roundStartEpoch) {
const roundStartedAt = roundStartEpoch * FIL_MAINNET_BLOCK_TIME + FIL_MAINNET_GENESIS_TS
const roundStartedAt =
roundStartEpoch * FIL_MAINNET_BLOCK_TIME + FIL_MAINNET_GENESIS_TS
const beacon = await fetchBeaconByTime(client, roundStartedAt)
return beacon.randomness
}
21 changes: 15 additions & 6 deletions lib/ipni-client.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { decodeBase64, decodeVarint, pRetry, assertOkResponse } from '../vendor/deno-deps.js'
import {
decodeBase64,
decodeVarint,
pRetry,
assertOkResponse,
} from '../vendor/deno-deps.js'

/**
*
* @param {string} cid
* @param {string} providerId
* @returns {Promise<{
* indexerResult: string;
* provider?: { address: string; protocol: string };
* indexerResult: string
* provider?: { address: string; protocol: string }
* }>}
*/
export async function queryTheIndex(cid, providerId) {
Expand All @@ -26,7 +30,10 @@ export async function queryTheIndex(cid, providerId) {
} catch (err) {
console.error('IPNI query failed.', err)
return {
indexerResult: typeof err.statusCode === 'number' ? `ERROR_${err.statusCode}` : 'ERROR_FETCH',
indexerResult:
typeof err.statusCode === 'number'
? `ERROR_${err.statusCode}`
: 'ERROR_FETCH',
}
}

Expand Down Expand Up @@ -69,7 +76,9 @@ export async function queryTheIndex(cid, providerId) {
}
}

console.log('All advertisements are from other miners or for unsupported protocols.')
console.log(
'All advertisements are from other miners or for unsupported protocols.',
)
return { indexerResult: 'NO_VALID_ADVERTISEMENT' }
}

Expand Down
8 changes: 6 additions & 2 deletions lib/miner-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ const smartContractClient = new ethers.Contract(
/**
* @param {string} minerId - The ID of the miner.
* @param {object} options - Options for the function.
* @param {number} options.maxAttempts - The maximum number of attempts to fetch the peer ID.
* @param {number} options.maxAttempts - The maximum number of attempts to fetch
* the peer ID.
* @returns {Promise<string>} The peer ID of the miner.
*/
export async function getIndexProviderPeerId(minerId, { maxAttempts = 5 } = {}) {
export async function getIndexProviderPeerId(
minerId,
{ maxAttempts = 5 } = {},
) {
try {
const { peerId, source } = await getPeerId(minerId, smartContractClient, {
rpcUrl: RPC_URL,
Expand Down
26 changes: 18 additions & 8 deletions lib/multiaddr.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export function multiaddrToHttpUrl(addr) {
path = decodeURIComponent(httpPathMultiAddr.substring(1))
} catch (err) {
throw Object.assign(
new Error(`Cannot parse "${addr}": unsupported http path`, { cause: err }),
new Error(`Cannot parse "${addr}": unsupported http path`, {
cause: err,
}),
{ code: 'INVALID_HTTP_PATH' },
)
} // Handle HTTP/HTTPs addresses using the default port
Expand All @@ -26,16 +28,21 @@ export function multiaddrToHttpUrl(addr) {

if (ipProtocol !== 'tcp') {
throw Object.assign(
new Error(`Cannot parse "${addr}": unsupported protocol "${ipProtocol}"`),
new Error(
`Cannot parse "${addr}": unsupported protocol "${ipProtocol}"`,
),
{ code: 'UNSUPPORTED_MULTIADDR_PROTO' },
)
}
}

if (scheme !== 'http' && scheme !== 'https') {
throw Object.assign(new Error(`Cannot parse "${addr}": unsupported scheme "${scheme}"`), {
code: 'UNSUPPORTED_MULTIADDR_SCHEME',
})
throw Object.assign(
new Error(`Cannot parse "${addr}": unsupported scheme "${scheme}"`),
{
code: 'UNSUPPORTED_MULTIADDR_SCHEME',
},
)
}

if (rest.length) {
Expand Down Expand Up @@ -63,9 +70,12 @@ function getUriHost(hostType, hostValue) {
return `[${hostValue}]`
}

throw Object.assign(new Error(`Unsupported multiaddr host type "${hostType}"`), {
code: 'UNSUPPORTED_MULTIADDR_HOST_TYPE',
})
throw Object.assign(
new Error(`Unsupported multiaddr host type "${hostType}"`),
{
code: 'UNSUPPORTED_MULTIADDR_HOST_TYPE',
},
)
}

function getUriPort(scheme, port) {
Expand Down
47 changes: 35 additions & 12 deletions lib/spark.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ export default class Spark {
#activity = new ActivityState()
#tasker

constructor({ fetch = globalThis.fetch, getIndexProviderPeerId = defaultGetIndexProvider } = {}) {
constructor({
fetch = globalThis.fetch,
getIndexProviderPeerId = defaultGetIndexProvider,
} = {}) {
this.#fetch = fetch
this.#getIndexProviderPeerId = getIndexProviderPeerId
this.#tasker = new Tasker({
Expand All @@ -48,7 +51,9 @@ export default class Spark {
}

async executeRetrievalCheck(retrieval, stats) {
console.log(`Calling Filecoin JSON-RPC to get PeerId of miner ${retrieval.minerId}`)
console.log(
`Calling Filecoin JSON-RPC to get PeerId of miner ${retrieval.minerId}`,
)
try {
const peerId = await this.#getIndexProviderPeerId(retrieval.minerId)
console.log(`Found peer id: ${peerId}`)
Expand All @@ -63,24 +68,37 @@ export default class Spark {
// The third case should not happen unless we made a mistake, so we want to learn about it
if (err.name === 'FilecoinRpcError') {
// TODO: report the error to Sentry
console.error('The error printed below was not expected, please report it on GitHub:')
console.error(
'The error printed below was not expected, please report it on GitHub:',
)
console.error('https://github.com/filecoin-station/spark/issues/new')
}
// Abort the check, no measurement should be recorded
throw err
}

console.log(`Querying IPNI to find retrieval providers for ${retrieval.cid}`)
const { indexerResult, provider } = await queryTheIndex(retrieval.cid, stats.providerId)
console.log(
`Querying IPNI to find retrieval providers for ${retrieval.cid}`,
)
const { indexerResult, provider } = await queryTheIndex(
retrieval.cid,
stats.providerId,
)
stats.indexerResult = indexerResult

const providerFound = indexerResult === 'OK' || indexerResult === 'HTTP_NOT_ADVERTISED'
const providerFound =
indexerResult === 'OK' || indexerResult === 'HTTP_NOT_ADVERTISED'
if (!providerFound) return

stats.protocol = provider.protocol
stats.providerAddress = provider.address

await this.fetchCAR(provider.protocol, provider.address, retrieval.cid, stats)
await this.fetchCAR(
provider.protocol,
provider.address,
retrieval.cid,
stats,
)
if (stats.protocol === 'http') {
await this.testHeadRequest(provider.address, retrieval.cid, stats)
}
Expand Down Expand Up @@ -230,7 +248,9 @@ export default class Spark {
async nextRetrieval() {
const retrieval = await this.getRetrieval()
if (!retrieval) {
console.log('Completed all tasks for the current round. Waiting for the next round to start.')
console.log(
'Completed all tasks for the current round. Waiting for the next round to start.',
)
return
}

Expand Down Expand Up @@ -284,7 +304,7 @@ export default class Spark {
/**
* @param {object} args
* @param {number} args.roundLengthInMs
* @param {number} [args.maxJitterInMs=0]
* @param {number} [args.maxJitterInMs=0] Default is `0`
* @param {number} args.maxTasksPerRound
* @param {number} args.lastTaskDurationInMs
*/
Expand Down Expand Up @@ -348,9 +368,12 @@ async function verifyContent(cid, carBytes) {

for await (const block of reader) {
if (block.cid.toString() !== cid.toString()) {
throw Object.assign(new Error(`Unexpected block CID ${block.cid}. Expected: ${cid}`), {
code: 'UNEXPECTED_CAR_BLOCK',
})
throw Object.assign(
new Error(`Unexpected block CID ${block.cid}. Expected: ${cid}`),
{
code: 'UNEXPECTED_CAR_BLOCK',
},
)
}

await validateBlock(block)
Expand Down
Loading
Loading