All files / src/stamper stamper.ts

100% Statements 21/21
100% Branches 1/1
100% Functions 5/5
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 6433x   33x   33x                         2x 2x 2x 2x 2x       1x                 1x       3x 3x 3x   3x 1x   2x 2x 2x 2x   2x                   1x      
import { Binary, Chunk } from 'cafe-utility'
import { EnvelopeWithBatchId } from '../types'
import { BatchId, PrivateKey } from '../utils/typed-bytes'
 
export class Stamper {
  signer: PrivateKey
  batchId: BatchId
  buckets: Uint32Array
  depth: number
  maxSlot: number
 
  private constructor(
    signer: PrivateKey | Uint8Array | string,
    batchId: BatchId | Uint8Array | string,
    buckets: Uint32Array,
    depth: number,
  ) {
    this.signer = new PrivateKey(signer)
    this.batchId = new BatchId(batchId)
    this.buckets = buckets
    this.depth = depth
    this.maxSlot = 2 ** (this.depth - 16)
  }
 
  static fromBlank(signer: PrivateKey | Uint8Array | string, batchId: BatchId | Uint8Array | string, depth: number) {
    return new Stamper(signer, batchId, new Uint32Array(65536), depth)
  }
 
  static fromState(
    signer: PrivateKey | Uint8Array | string,
    batchId: BatchId | Uint8Array | string,
    buckets: Uint32Array,
    depth: number,
  ) {
    return new Stamper(signer, batchId, buckets, depth)
  }
 
  stamp(chunk: Chunk): EnvelopeWithBatchId {
    const address = chunk.hash()
    const bucket = Binary.uint16ToNumber(address, 'BE')
    const height = this.buckets[bucket]
 
    if (height >= this.maxSlot) {
      throw Error('Bucket is full')
    }
    this.buckets[bucket]++
    const index = Binary.concatBytes(Binary.numberToUint32(bucket, 'BE'), Binary.numberToUint32(height, 'BE'))
    const timestamp = Binary.numberToUint64(BigInt(Date.now()), 'BE')
    const signature = this.signer.sign(Binary.concatBytes(address, this.batchId.toUint8Array(), index, timestamp))
 
    return {
      batchId: this.batchId,
      index,
      issuer: this.signer.publicKey().address().toUint8Array(),
      signature: signature.toUint8Array(),
      timestamp,
    }
  }
 
  getState(): Uint32Array {
    return this.buckets
  }
}