# beacon_chain
# Copyright (c) 2024 Status Research & Development GmbH
# Licensed and distributed under either of
#   * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
#   * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

{.push raises: [].}

import "."/spec/crypto

from stew/bitops2 import getBit, setBit
from "."/spec/datatypes/base import Validator, pubkey
from "."/spec/helpers import bytes_to_uint32

const
  # https://hur.st/bloomfilter/?n=4M&p=&m=8MiB&k=
  pubkeyBloomFilterScale = 23   # 21 too small, 22 borderline, 24 also ok

type
  PubkeyBloomFilter* = object
    data: array[1 shl pubkeyBloomFilterScale, byte]

iterator bloomFilterHashes(pubkey: ValidatorPubKey): auto =
  const pubkeyBloomFilterMask = (1 shl pubkeyBloomFilterScale) - 1
  for r in countup(0'u32, 20'u32, 4'u32):
    # ValidatorPubKeys have fairly uniform entropy; using enough hash
    # functions also reduces risk of low-entropy portions
    yield pubkey.blob.toOpenArray(r, r+3).bytes_to_uint32 and
      pubkeyBloomFilterMask

template incl*(bloomFilter: var PubkeyBloomFilter, pubkey: ValidatorPubKey) =
  for bloomFilterHash in bloomFilterHashes(pubkey):
    setBit(bloomFilter.data, bloomFilterHash)

func constructBloomFilter*(x: openArray[Validator]): auto =
  let res = new PubkeyBloomFilter
  for m in x:
    incl(res[], m.pubkey)
  res

func mightContain*(
    bloomFilter: PubkeyBloomFilter, pubkey: ValidatorPubKey): bool =
  # Might return false positive, but never false negative
  for bloomFilterHash in bloomFilterHashes(pubkey):
    if not getBit(bloomFilter.data, bloomFilterHash):
      return false

  true