packages/api/src/routes/nfts-upload.js
/**
 * Gets CAR file information. Throws if the CAR does not conform to our idea of
 * a valid CAR i.e.
 *
 * - Missing root CIDs
 * - >1 root CID
 * - Any block bigger than MAX_BLOCK_SIZE (2MiB)
 * - 0 blocks
 * - Missing root block
 * - Missing non-root blocks (when root block has links)
 *
 * The DAG size will be returned ONLY IF the root node is dag-pb or raw.
 *
 * @typedef {import('multiformats').CID} CID
 * @typedef {{ size?: number, rootCid: CID, structure?: DagStructure }} CarStat
 * @param {Blob} carBlob
 * @param {Object} [options]
 * @param {DagStructure} [options.structure]
 * @returns {Promise<CarStat>}
 */
Gets CAR file information. Throws if the CAR does not conform to our idea of
a valid CAR i.e.



The DAG size will be returned ONLY IF the root node is dag-pb or raw.


Gets CAR file information . Throws if the CAR does not conform to our idea of a valid CAR i . e . The DAG size will be returned ONLY IF the root node is dag - pb or raw .
async function carStat(carBlob, { structure } = {}) {
  const carBytes = new Uint8Array(await carBlob.arrayBuffer())
  const blocksIterator = await CarBlockIterator.fromBytes(carBytes)
  const roots = await blocksIterator.getRoots()
  if (roots.length === 0) {
    throw new Error('missing roots')
  }
  if (roots.length > 1) {
    throw new Error('too many roots')
  }
  const rootCid = roots[0]
  let rawRootBlock
  const blocks = []
  for await (const block of blocksIterator) {
    const blockSize = block.bytes.byteLength
    if (blockSize > MAX_BLOCK_SIZE) {
      throw new Error(`block too big: ${blockSize} > ${MAX_BLOCK_SIZE}`)
    }
    if (block.cid.multihash.code === sha256.code) {
      const ourHash = await sha256.digest(block.bytes)
      if (!equals(ourHash.digest, block.cid.multihash.digest)) {
        throw new InvalidCarError(
          `block data does not match CID for ${block.cid.toString()}`
        )
      }
    }
    if (!rawRootBlock && block.cid.equals(rootCid)) {
      rawRootBlock = block
    }
    blocks.push(block)
  }
  if (!blocks.length) {
    throw new Error('empty CAR')
  }
  if (!rawRootBlock) {
    throw new Error('missing root block')
  }
  const decoder = decoders.find((d) => d.code === rootCid.code)
  let size
  if (decoder) {
    if (blocks.length === 1 && rootCid.code === raw.code) {
      size = rawRootBlock.bytes.byteLength
      structure = 'Complete'
    } else {
      const rootBlock = new Block({
        cid: rootCid,
        bytes: rawRootBlock.bytes,
        value: decoder.decode(rawRootBlock.bytes),
      })
      const hasLinks = !rootBlock.links()[Symbol.iterator]().next().done
      if (hasLinks && blocks.length < 2) {
        throw new Error('CAR must contain at least one non-root block')
      }
      if (rootBlock.cid.code === pb.code) {
        size = cumulativeSize(rootBlock.bytes, rootBlock.value)
      }
      if (!structure || structure === 'Unknown') {
        const isComplete = iterateDag(rawRootBlock, blocks)
        structure = isComplete ? 'Complete' : 'Partial'
      }
    }
  }
  return { rootCid, size, structure }
}
async function carStat ( carBlob , { structure } = { } ) { const carBytes = new Uint8Array ( await carBlob . arrayBuffer ( ) ) const blocksIterator = await CarBlockIterator . fromBytes ( carBytes ) const roots = await blocksIterator . getRoots ( ) if ( roots . length === 0 ) { throw new Error ( 'missing roots' ) } if ( roots . length > 1 ) { throw new Error ( 'too many roots' ) } const rootCid = roots [ 0 ] let rawRootBlock const blocks = [ ] for await ( const block of blocksIterator ) { const blockSize = block . bytes . byteLength if ( blockSize > MAX_BLOCK_SIZE ) { throw new Error ( ` ${ blockSize } ${ MAX_BLOCK_SIZE } ` ) } if ( block . cid . multihash . code === sha256 . code ) { const ourHash = await sha256 . digest ( block . bytes ) if ( ! equals ( ourHash . digest , block . cid . multihash . digest ) ) { throw new InvalidCarError ( ` ${ block . cid . toString ( ) } ` ) } } if ( ! rawRootBlock && block . cid . equals ( rootCid ) ) { rawRootBlock = block } blocks . push ( block ) } if ( ! blocks . length ) { throw new Error ( 'empty CAR' ) } if ( ! rawRootBlock ) { throw new Error ( 'missing root block' ) } const decoder = decoders . find ( ( d ) => d . code === rootCid . code ) let size if ( decoder ) { if ( blocks . length === 1 && rootCid . code === raw . code ) { size = rawRootBlock . bytes . byteLength structure = 'Complete' } else { const rootBlock = new Block ( { cid : rootCid , bytes : rawRootBlock . bytes , value : decoder . decode ( rawRootBlock . bytes ) , } ) const hasLinks = ! rootBlock . links ( ) [ Symbol . iterator ] ( ) . next ( ) . done if ( hasLinks && blocks . length < 2 ) { throw new Error ( 'CAR must contain at least one non-root block' ) } if ( rootBlock . cid . code === pb . code ) { size = cumulativeSize ( rootBlock . bytes , rootBlock . value ) } if ( ! structure || structure === 'Unknown' ) { const isComplete = iterateDag ( rawRootBlock , blocks ) structure = isComplete ? 'Complete' : 'Partial' } } } return { rootCid , size , structure } }
Gets CAR file information.
Gets CAR file information .
/** @type {import('@ipld/car/api').Block[]} */ // if there's only 1 block (the root block) and it's a raw node, we know the size. // if the root block has links, then we should have at least 2 blocks in the CAR // get the size of the full dag for this root, even if we only have a partial CAR. // get Structure if not already known