issue-166-open-timestamps #220
@ -6,7 +6,7 @@ module.exports = {
|
|||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/recommended',
|
||||||
'plugin:react-hooks/recommended'
|
'plugin:react-hooks/recommended'
|
||||||
],
|
],
|
||||||
ignorePatterns: ['dist', '.eslintrc.cjs', 'licenseChecker.cjs'],
|
ignorePatterns: ['dist', '.eslintrc.cjs', 'licenseChecker.cjs', "*.min.js"],
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
plugins: ['react-refresh'],
|
plugins: ['react-refresh'],
|
||||||
rules: {
|
rules: {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
<script src="/opentimestamps.min.js"></script>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
2
public/opentimestamps.min.js
vendored
Normal file
2
public/opentimestamps.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
28
src/types/opentimestamps.d.ts
vendored
Normal file
28
src/types/opentimestamps.d.ts
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
interface OpenTimestamps {
|
||||||
|
// Create a detached timestamp file from a buffer or file hash
|
||||||
|
DetachedTimestampFile: {
|
||||||
|
fromHash(op: any, hash: Uint8Array): any
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stamp the provided timestamp file and return a Promise
|
||||||
|
stamp(file: any): Promise<void>
|
||||||
|
|
||||||
|
// Verify the provided timestamp proof file
|
||||||
|
verify(file: any): Promise<void>
|
||||||
|
|
||||||
|
// Other utilities or operations (like OpSHA256, serialization)
|
||||||
|
Ops: {
|
||||||
|
OpSHA256: any
|
||||||
|
OpSHA1?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
Context: {
|
||||||
|
StreamSerialization: any
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load a timestamp file from a buffer
|
||||||
|
deserialize(bytes: Uint8Array): any
|
||||||
|
|
||||||
|
// Other potential methods based on repo functions
|
||||||
|
upgrade(file: any): Promise<void>
|
||||||
|
}
|
1
src/types/system/index.d.ts
vendored
1
src/types/system/index.d.ts
vendored
@ -3,5 +3,6 @@ import type { WindowNostr } from 'nostr-tools/nip07'
|
|||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
nostr?: WindowNostr
|
nostr?: WindowNostr
|
||||||
|
OpenTimestamps: OpenTimestamps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
src/utils/opentimestamps.ts
Normal file
39
src/utils/opentimestamps.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Timestamp } from '../types'
|
||||||
|
import { isPromiseFulfilled } from './utils.ts'
|
||||||
|
import { retryAll } from './retry.ts'
|
||||||
|
import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a timestamp for the provided document hashes.
|
||||||
|
* @returns Timestamp in a hex string format
|
||||||
|
*/
|
||||||
|
export const generateTimestamps = async (
|
||||||
|
fileHashes: string[]
|
||||||
|
): Promise<Timestamp[] | undefined> => {
|
||||||
|
const promises = fileHashes.map((fileHash) => () => timestamp(fileHash))
|
||||||
|
const resolvedPromises = await retryAll(promises)
|
||||||
|
if (resolvedPromises.every(isPromiseFulfilled)) {
|
||||||
|
return resolvedPromises.map(({ value }, index) => {
|
||||||
|
return {
|
||||||
|
fileHash: fileHashes[index],
|
||||||
|
timestamp: value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timestamp = async (hash: string) => {
|
||||||
|
const detachedTimestamp =
|
||||||
|
window.OpenTimestamps.DetachedTimestampFile.fromHash(
|
||||||
|
new window.OpenTimestamps.Ops.OpSHA256(),
|
||||||
|
hexToBytes(hash)
|
||||||
|
)
|
||||||
|
|
||||||
|
await window.OpenTimestamps.stamp(detachedTimestamp)
|
||||||
|
const ctx = new window.OpenTimestamps.Context.StreamSerialization()
|
||||||
|
detachedTimestamp.serialize(ctx)
|
||||||
|
const timestampBytes = ctx.getOutput()
|
||||||
|
const hex = bytesToHex(timestampBytes)
|
||||||
|
|
||||||
|
return hex
|
||||||
|
}
|
25
src/utils/retry.ts
Normal file
25
src/utils/retry.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export const retryAll = async <T>(
|
||||||
|
promises: (() => Promise<T>)[],
|
||||||
|
retries: number = 3,
|
||||||
|
delay: number = 1000
|
||||||
|
) => {
|
||||||
|
const wrappedPromises = promises.map((fn) => retry(fn, retries, delay))
|
||||||
|
return Promise.allSettled(wrappedPromises)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const retry = async <T>(
|
||||||
|
fn: () => Promise<T>,
|
||||||
|
retries: number = 3,
|
||||||
|
delay: number = 1000
|
||||||
|
): Promise<T> => {
|
||||||
|
try {
|
||||||
|
return await fn()
|
||||||
|
} catch (err) {
|
||||||
|
if (retries === 0) {
|
||||||
|
return Promise.reject(err)
|
||||||
|
}
|
||||||
|
return new Promise((resolve) =>
|
||||||
|
setTimeout(() => resolve(retry(fn, retries - 1)), delay)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user