/+ btc |% +$ bits [wid=@ dat=@ub] ++ params |% ++ p 19 ++ m 784.931 -- :: ++ siphash |= [k=byts m=byts] ^- byts |^ ?> =(wid.k 16) ?> (lte (met 3 dat.k) wid.k) ?> (lte (met 3 dat.m) wid.m) =. k (flim:sha k) =. m (flim:sha m) (flim:sha (fin (comp m (init dat.k)))) :: Initialise internal state :: ++ init |= k=@ ^- [@ @ @ @] =/ k0=@ (end [6 1] k) =/ k1=@ (cut 6 [1 1] k) :^ (mix k0 0x736f.6d65.7073.6575) (mix k1 0x646f.7261.6e64.6f6d) (mix k0 0x6c79.6765.6e65.7261) (mix k1 0x7465.6462.7974.6573) :: :: Compression rounds ++ comp |= [m=byts v=[v0=@ v1=@ v2=@ v3=@]] ^- [@ @ @ @] =/ len=@ud (div wid.m 8) =/ last=@ (lsh [3 7] (mod wid.m 256)) =| i=@ud =| w=@ |- =. w (cut 6 [i 1] dat.m) ?: =(i len) =. v3.v (mix v3.v (mix last w)) =. v (rnd (rnd v)) =. v0.v (mix v0.v (mix last w)) v %= $ v =. v3.v (mix v3.v w) =. v (rnd (rnd v)) =. v0.v (mix v0.v w) v i (add i 1) == :: :: Finalisation rounds ++ fin |= v=[v0=@ v1=@ v2=@ v3=@] ^- byts =. v2.v (mix v2.v 0xff) =. v (rnd (rnd (rnd (rnd v)))) :- 8 :(mix v0.v v1.v v2.v v3.v) :: :: Sipround ++ rnd |= [v0=@ v1=@ v2=@ v3=@] ^- [@ @ @ @] =. v0 (~(sum fe 6) v0 v1) =. v2 (~(sum fe 6) v2 v3) =. v1 (~(rol fe 6) 0 13 v1) =. v3 (~(rol fe 6) 0 16 v3) =. v1 (mix v1 v0) =. v3 (mix v3 v2) =. v0 (~(rol fe 6) 0 32 v0) =. v2 (~(sum fe 6) v2 v1) =. v0 (~(sum fe 6) v0 v3) =. v1 (~(rol fe 6) 0 17 v1) =. v3 (~(rol fe 6) 0 21 v3) =. v1 (mix v1 v2) =. v3 (mix v3 v0) =. v2 (~(rol fe 6) 0 32 v2) [v0 v1 v2 v3] -- :: +str: bit streams :: read is from the front :: write appends to the back :: ++ str |% ++ read-bit |= s=bits ^- [bit=@ub rest=bits] ?> (gth wid.s 0) :* ?:((gth wid.s (met 0 dat.s)) 0b0 0b1) [(dec wid.s) (end [0 (dec wid.s)] dat.s)] == :: ++ read-bits |= [n=@ s=bits] ^- [bits rest=bits] =| bs=bits |- ?: =(n 0) [bs s] =^ b s (read-bit s) $(n (dec n), bs (write-bits bs [1 b])) :: ++ write-bits |= [s1=bits s2=bits] ^- bits [(add wid.s1 wid.s2) (can 0 ~[s2 s1])] -- :: +gol: Golomb-Rice encoding/decoding :: ++ gol |% :: +en: encode x and append to end of s :: - s: bits stream :: - x: number to add to the stream :: - p: golomb-rice p param :: ++ en |= [s=bits x=@ p=@] ^- bits =+ q=(rsh [0 p] x) =+ unary=[+(q) (lsh [0 1] (dec (bex q)))] =+ r=[p (end [0 p] x)] %+ write-bits:str s (write-bits:str unary r) :: ++ de |= [s=bits p=@] ^- [delta=@ rest=bits] |^ ?> (gth wid.s 0) =^ q s (get-q s) =^ r s (read-bits:str p s) [(add dat.r (lsh [0 p] q)) s] :: ++ get-q |= s=bits =| q=@ =^ first-bit s (read-bit:str s) |- ?: =(0 first-bit) [q s] =^ b s (read-bit:str s) $(first-bit b, q +(q)) -- -- :: +hsh :: ++ hsh |% :: +to-range :: - item: scriptpubkey to hash :: - f: N*M :: - k: key for siphash (end of blockhash, reversed) :: ++ to-range |= [item=byts f=@ k=byts] ^- @ (rsh [0 64] (mul f (swp 3 dat:(siphash k item)))) :: +set-construct: return sorted hashes of scriptpubkeys :: ++ set-construct |= [items=(list byts) k=byts f=@] ^- (list @) %+ sort %+ turn items |= item=byts (to-range item f k) lth -- :: +get-n: get N from head of block filter, little endian :: ++ get-n |= filter=@ux ^- @ux =+ start=(dec (met 3 filter)) =/ n=@ux (cut 3 [start 1] filter) %+ swp 3 ?: =(n 0xfd) (cut 3 [(sub start 2) 2] filter) ?: =(n 0xfe) (cut 3 [(sub start 4) 4] filter) ?: =(n 0xff) (cut 3 [(sub start 8) 8] filter) n :: ++ parse-filter |= [filter=@ux] ^- [n=@ux gcs-set=bits] =+ n=(get-n filter) =+ gcs-size=(sub (met 3 filter) (met 3 n)) [n [(mul 8 gcs-size) `@ub`(end [3 gcs-size] filter)]] :: +to-key: blockhash (little endian) to key for siphash :: ++ to-key |= blockhash=tape ^- byts %+ take:byt:btc 16 %- flip:byt:btc [32 (to-hex:btc (crip blockhash))] :: +match: whether block filter matches *any* target scriptpubkeys :: - filter: full block filter, with leading N :: - k: key for siphash (end of blockhash, reversed) :: - targets: scriptpubkeys to match :: ++ match |= [filter=@ux k=byts targets=(list byts)] ^- ? =/ [p=@ m=@] [p:params m:params] =/ [n=@ux gcs-set=bits] (parse-filter filter) =+ target-hs=(set-construct:hsh targets k (mul n m)) =+ last-val=0 |- ?: (lth wid.gcs-set p) %.n ?~ target-hs %.n =^ delta gcs-set (de:gol gcs-set p) =. last-val (add delta last-val) ?: =(last-val i.target-hs) %.y ?: (lth last-val i.target-hs) $ $(target-hs t.target-hs) :: +all-match: returns all target byts that match :: - filter: full block filter, with leading N :: - k: key for siphash (end of blockhash, reversed) :: - targets: scriptpubkeys to match :: ++ all-match |= [filter=@ux k=byts targets=(list byts)] ^- (list byts) =/ [p=@ m=@] [p:params m:params] =/ [n=@ux gcs-set=bits] (parse-filter filter) =/ target-map=(map @ byts) %- ~(gas by *(map @ byts)) %+ turn targets |=(t=byts [(to-range:hsh t (mul n m) k) t]) =+ target-hs=~(tap in ~(key by target-map)) =+ last-val=0 =| matches=(list @) |- ?: (lth wid.gcs-set p) (murn matches ~(get by target-map)) ?~ target-hs (murn matches ~(get by target-map)) =^ delta gcs-set (de:gol gcs-set p) =. last-val (add delta last-val) ?: =(last-val i.target-hs) %= $ target-hs t.target-hs matches [last-val matches] == ?: (lth last-val i.target-hs) $ $(target-hs t.target-hs) --