Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(deps): CDB-2128 Address nonce errors #1004

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
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
363 changes: 195 additions & 168 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@
"@types/lodash.camelcase": "^4.3.7",
"@types/lodash.snakecase": "^4.1.7",
"await-semaphore": "^0.1.3",
"body-parser": "^1.20.2",
"cartonne": "^2.0.1",
"conditional-type-checks": "^1.0.6",
"cors": "^2.8.5",
"dag-jose": "^4.0.0",
"dotenv": "^16.0.3",
"ethers": "~5.7.2",
"ethers": "^6.2.3",
"express": "^4.18.1",
"fp-ts": "^2.13.1",
"http-status-codes": "^2.2.0",
Expand All @@ -75,9 +76,11 @@
"lodash.clonedeep": "^4.5.0",
"lodash.snakecase": "^4.1.1",
"lru-cache": "^7.17.0",
"merge-options": "^3.0.4",
"morgan": "^1.10.0",
"multiformats": "^11.0.1",
"os-utils": "^0.0.14",
"prom-client": "^14.2.0",
"pg": "^8.8.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.5.2",
Expand Down
6 changes: 5 additions & 1 deletion src/__tests__/ceramic_integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ process.env.NODE_ENV = 'test'
const randomNumber = Math.floor(Math.random() * 10000)
const TOPIC = `/ceramic/local/${randomNumber}`

// Workaround from https://stackoverflow.com/a/72416352/599991
import dns from 'node:dns'
dns.setDefaultResultOrder('ipv4first')

/**
* Create an IPFS instance
*/
Expand Down Expand Up @@ -90,7 +94,7 @@ async function makeCeramicCore(
anchorServiceUrl,
// TODO CDB-2317 Remove `indexing` config when Ceramic Core allows that
indexing: {
db: "TODO",
db: 'TODO',
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prettier

allowQueriesBeforeHistoricalSync: false,
disableComposedb: true,
enableHistoricalSync: false,
Expand Down
73 changes: 38 additions & 35 deletions src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,53 @@ function buildExpressMiddleware() {
* Notice that the absense of a did header or body bypasses any checks below
* this app will still work if the logice above is not in place.
*/
return function(req: Request, _res: Response, next: NextFunction) {
if (req.headers) {
if (req.headers['did'] && req.body) {
if (Object.keys(req.body).length > 0) {
const digest = buildBodyDigest(req.headers['content-type'], req.body)
if (req.headers['digest'] == digest) {
return next()
} else {
throw Error('Body digest verification failed')
}
}
}
return function (req: Request, _res: Response, next: NextFunction) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prettier

if (req.headers) {
if (req.headers['did'] && req.body) {
if (Object.keys(req.body).length > 0) {
const digest = buildBodyDigest(req.headers['content-type'], req.body)
if (req.headers['digest'] == digest) {
return next()
} else {
throw Error('Body digest verification failed')
}
}
return next()
}
}
return next()
}
}

function buildBodyDigest(contentType: string | undefined, body: any): string | undefined {
if (!body) return
if (!body) return

let hash: Uint8Array | undefined
let hash: Uint8Array | undefined

if (contentType) {
if (contentType.includes('application/vnd.ipld.car')) {
const carFactory = new CARFactory()
carFactory.codecs.add(DAG_JOSE)
console.log('Will build a car file from req.body', body)
try {
console.log('Will build a car file from req.body (as utf8 string)', u8a.toString(body, 'base64'))
} catch(e) {
console.log('Couldn\'t convert req.body to string: ', e)
}
const car = carFactory.fromBytes(body)
if (!car.roots[0]) throw Error('Missing CAR root')
return car.roots[0].toString()
} else if (contentType.includes('application/json')) {
hash = sha256.hash(u8a.fromString(JSON.stringify(body)))
if (contentType) {
if (contentType.includes('application/vnd.ipld.car')) {
const carFactory = new CARFactory()
carFactory.codecs.add(DAG_JOSE)
console.log('Will build a car file from req.body', body)
try {
console.log(
'Will build a car file from req.body (as utf8 string)',
u8a.toString(body, 'base64')
)
} catch (e) {
console.log("Couldn't convert req.body to string: ", e)
}
}

if (!hash) {
// Default to hashing stringified body
const car = carFactory.fromBytes(body)
if (!car.roots[0]) throw Error('Missing CAR root')
return car.roots[0].toString()
} else if (contentType.includes('application/json')) {
hash = sha256.hash(u8a.fromString(JSON.stringify(body)))
}
}

return `0x${u8a.toString(hash, 'base16')}`
if (!hash) {
// Default to hashing stringified body
hash = sha256.hash(u8a.fromString(JSON.stringify(body)))
}

return `0x${u8a.toString(hash, 'base16')}`
}
6 changes: 3 additions & 3 deletions src/models/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ export class Transaction {
chain: string
txHash: string
blockNumber: number
blockTimestamp: number
blockTimestamp: Date

constructor(chain: string, txHash: string, blockNumber: number, blockTimestamp: number) {
constructor(chain: string, txHash: string, blockNumber: number, blockDate: Date) {
this.chain = chain
this.txHash = txHash
this.blockNumber = blockNumber
this.blockTimestamp = blockTimestamp
this.blockTimestamp = blockDate
}
}
6 changes: 4 additions & 2 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ export class CeramicAnchorServer extends Server {
super(true)

this.app.set('trust proxy', true)
this.app.use(bodyParser.raw({inflate: true, type: 'application/vnd.ipld.car'}))
this.app.use(bodyParser.raw({ inflate: true, type: 'application/vnd.ipld.car' }))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prettier

this.app.use(bodyParser.json({ type: 'application/json' }))
this.app.use(bodyParser.urlencoded({ extended: true, type: 'application/x-www-form-urlencoded' }))
this.app.use(
bodyParser.urlencoded({ extended: true, type: 'application/x-www-form-urlencoded' })
)
this.app.use(expressLoggers)
if (config.requireAuth == true) {
this.app.use(auth)
Expand Down
151 changes: 149 additions & 2 deletions src/services/__tests__/anchor-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { Utils } from '../../utils.js'
import { validate as validateUUID } from 'uuid'
import { TransactionRepository } from '../../repositories/transaction-repository.js'
import type { BlockchainService } from '../blockchain/blockchain-service'
import type { Transaction } from '../../models/transaction.js'
import { Transaction } from '../../models/transaction.js'
import { createInjector, Injector } from 'typed-inject'
import { MetadataRepository } from '../../repositories/metadata-repository.js'
import { randomString } from '@stablelib/random'
Expand All @@ -43,8 +43,10 @@ export class MockEventProducerService implements EventProducerService {
}
}

const CHAIN_ID = 'impossible'

class FakeEthereumBlockchainService implements BlockchainService {
chainId = 'impossible'
chainId = CHAIN_ID

connect(): Promise<void> {
throw new Error(`Failed to connect`)
Expand Down Expand Up @@ -799,3 +801,148 @@ describe('anchor service', () => {
})
})
})

const FAKE_TRANSACTION = new Transaction(
CHAIN_ID,
'impossible',
1,
new Date(Date.UTC(2000, 1, 1, 1, 1, 1, 0))
)

class FakeTransactionEthereumBlockchainService implements BlockchainService {
chainId = CHAIN_ID

connect(): Promise<void> {
return Promise.resolve()
}

sendTransaction(): Promise<Transaction> {
return Promise.resolve(FAKE_TRANSACTION)
}
}

describe('anchor service with fake transaction', () => {
jest.setTimeout(10000)
let ipfsService: IIpfsService
let metadataService: IMetadataService
let connection: Knex
let injector: Injector<Context>
let requestRepository: RequestRepository
let anchorService: AnchorService

beforeAll(async () => {
connection = await createDbConnection()
injector = createInjector()
.provideValue('dbConnection', connection)
.provideValue(
'config',
Object.assign({}, config, {
merkleDepthLimit: MERKLE_DEPTH_LIMIT,
minStreamCount: MIN_STREAM_COUNT,
readyRetryIntervalMS: READY_RETRY_INTERVAL_MS,
})
)
.provideClass('anchorRepository', AnchorRepository)
.provideClass('metadataRepository', MetadataRepository)
.provideFactory('requestRepository', RequestRepository.make)
.provideClass('transactionRepository', TransactionRepository)
.provideClass('blockchainService', FakeTransactionEthereumBlockchainService)
.provideClass('ipfsService', MockIpfsService)
.provideClass('eventProducerService', MockEventProducerService)
.provideClass('metadataService', MetadataService)
.provideClass('anchorService', AnchorService)

ipfsService = injector.resolve('ipfsService')
await ipfsService.init()
requestRepository = injector.resolve('requestRepository')
anchorService = injector.resolve('anchorService')
metadataService = injector.resolve('metadataService')
})

beforeEach(async () => {
await clearTables(connection)
jest.restoreAllMocks()
await requestRepository.table.delete()
})

afterAll(async () => {
await connection.destroy()
})

test('create anchor records', async () => {
// Create pending requests
const requests: Request[] = []
const numRequests = 4
for (let i = 0; i < numRequests; i++) {
const genesisCID = await ipfsService.storeRecord({
header: {
controllers: [`did:method:${randomString(32)}`],
},
})
const streamId = new StreamID(1, genesisCID)
await metadataService.fillFromIpfs(streamId)
const request = await createRequest(streamId.toString(), ipfsService, requestRepository)
requests.push(request)
}
requests.sort(function (a, b) {
return a.streamId.localeCompare(b.streamId)
})

await requestRepository.findAndMarkReady(0)

const storeSpy = jest.spyOn(ipfsService, 'storeRecord')

await anchorService.anchorRequests()

expect(storeSpy).toBeCalledTimes(9)
expect(storeSpy).nthCalledWith(
5,
expect.objectContaining({
chainId: FAKE_TRANSACTION.chain,
blockNumber: FAKE_TRANSACTION.blockNumber,
blockTimestamp: 949366861000,
})
)

const publishSpy = jest.spyOn(ipfsService, 'publishAnchorCommit')
const [candidates] = await anchorService._findCandidates(requests, 0)
const merkleTree = await anchorService._buildMerkleTree(candidates)
const ipfsProofCid = await ipfsService.storeRecord({})

const anchors = await anchorService._createAnchorCommits(ipfsProofCid, merkleTree)

expect(candidates.length).toEqual(requests.length)
expect(anchors.length).toEqual(candidates.length)

expect(publishSpy).toBeCalledTimes(anchors.length)

// All requests are anchored, in a different order because of IpfsLeafCompare
expect(anchors.map((a) => a.requestId).sort()).toEqual(requests.map((r) => r.id).sort())
for (const i in anchors) {
const anchor = anchors[i]
expectPresent(anchor)
expect(anchor.proofCid).toEqual(ipfsProofCid.toString())
const request = requests.find((r) => r.id === anchor.requestId)
expectPresent(request)
expect(anchor.requestId).toEqual(request.id)

const anchorRecord = await ipfsService.retrieveRecord(anchor.cid)
expect(anchorRecord.prev.toString()).toEqual(request.cid)
expect(anchorRecord.proof).toEqual(ipfsProofCid)
expect(anchorRecord.path).toEqual(anchor.path)
expect(publishSpy.mock.calls[i]).toEqual([
anchorRecord,
StreamID.fromString(request.streamId),
])
}

expectPresent(anchors[0])
expect(anchors[0].path).toEqual('0/0')
expectPresent(anchors[1])
expect(anchors[1].path).toEqual('0/1')
expectPresent(anchors[2])
expect(anchors[2].path).toEqual('1/0')
expectPresent(anchors[3])
expect(anchors[3].path).toEqual('1/1')
})
})
2 changes: 1 addition & 1 deletion src/services/anchor-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ export class AnchorService {
if (this.includeBlockInfoInAnchorProof) {
ipfsAnchorProof = {
blockNumber: tx.blockNumber,
blockTimestamp: tx.blockTimestamp,
blockTimestamp: tx.blockTimestamp.getTime(),
...ipfsAnchorProof,
}
}
Expand Down
Loading