Skip to content

IdentityBlockstore.get should be async generator, not sync generator #361

@NiKrause

Description

@NiKrause

Bug Description

IdentityBlockstore.get() is defined as a synchronous generator (* get) but uses yield* to delegate to child blockstores that return async generators (async * get). This causes a TypeError: yield* (intermediate value) is not iterable error.

Affected Version

  • Package: blockstore-core
  • Version: 6.1.1 (and likely 6.1.0)
  • File: packages/blockstore-core/src/identity.ts:48

Error Message

TypeError: yield* (intermediate value) is not iterable
    at IdentityBlockstore.get (blockstore-core/dist/src/identity.js:44:27)

Root Cause

In packages/blockstore-core/src/identity.ts, line 48:

* get (key: CID, options?: AbortOptions): AwaitGenerator<Uint8Array> {
  // ...
  yield * this.child.get(key, options);  // Line 62 - ERROR HERE
}

The method is a synchronous generator (* get), but this.child.get() (e.g., from LevelBlockstore) returns an async generator (async * get). You cannot use yield* to delegate from a sync generator to an async generator.

Steps to Reproduce

import { IdentityBlockstore } from 'blockstore-core/identity'
import { LevelBlockstore } from 'blockstore-level'
import { CID } from 'multiformats/cid'
import * as raw from 'multiformats/codecs/raw'
import { sha256 } from 'multiformats/hashes/sha2'
import toBuffer from 'it-to-buffer'

const levelBlockstore = new LevelBlockstore('./test-blocks')
await levelBlockstore.open()

const identityBlockstore = new IdentityBlockstore(levelBlockstore)

const input = Uint8Array.from([0, 1, 2, 3, 4])
const digest = await sha256.digest(input)
const cid = CID.createV1(raw.code, digest)

await identityBlockstore.put(cid, input)

// This will throw: TypeError: yield* (intermediate value) is not iterable
const result = await toBuffer(identityBlockstore.get(cid))

Expected Behavior

IdentityBlockstore.get() should successfully retrieve blocks from child blockstores.

Actual Behavior

Throws TypeError: yield* (intermediate value) is not iterable when trying to get a block.

Proposed Fix

Change line 48 from:

* get (key: CID, options?: AbortOptions): AwaitGenerator<Uint8Array> {

To:

async * get (key: CID, options?: AbortOptions): AwaitGenerator<Uint8Array> {

This makes it an async generator, which can properly delegate to async generators using yield*.

Impact

This bug affects any code using IdentityBlockstore with blockstores that return async generators (like LevelBlockstore, FSBlockstore, etc.). This includes:

  • Helia's NetworkedStorage which wraps blockstores with IdentityBlockstore
  • Any OrbitDB usage with Helia/IPFS
  • Any code using IdentityBlockstore with persistent blockstores

Related

  • Introduced in commit 4dbb136 ("feat!: streaming blockstores feat!: streaming blockstores #358")
  • Affects blockstore-core@6.1.0 and 6.1.1
  • blockstore-core@6.0.0 works correctly (doesn't have this bug)

Test Case

A minimal reproduction test:

import { IdentityBlockstore } from 'blockstore-core/identity'
import { LevelBlockstore } from 'blockstore-level'
import { CID } from 'multiformats/cid'
import * as raw from 'multiformats/codecs/raw'
import { sha256 } from 'multiformats/hashes/sha2'
import toBuffer from 'it-to-buffer'
import { join } from 'path'
import { tmpdir } from 'os'
import { rmSync } from 'fs'

async function testIdentityBlockstoreBug() {
  const testDir = join(tmpdir(), `blockstore-test-${Date.now()}`)
  const levelBlockstore = new LevelBlockstore(testDir)
  
  await levelBlockstore.open()
  const identityBlockstore = new IdentityBlockstore(levelBlockstore)
  
  const input = Uint8Array.from([0, 1, 2, 3, 4])
  const digest = await sha256.digest(input)
  const cid = CID.createV1(raw.code, digest)
  
  await identityBlockstore.put(cid, input)
  
  // This throws: TypeError: yield* (intermediate value) is not iterable
  const result = await toBuffer(identityBlockstore.get(cid))
  
  await levelBlockstore.close()
  rmSync(testDir, { recursive: true, force: true })
}

testIdentityBlockstoreBug().catch(console.error)

The test confirms the bug and shows it's fixed when IdentityBlockstore.get is changed to async * get.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions