OpenPay (1 crate, 2487 LOC, 33 tests)
op-fraud-graph Graph-based fraud detection. EntityKind (card/email/
device/IP/address/phone/BIN+last4/account) with
canonicalization + hashed EntityKey + billing↔shipping
mismatch detection. Adjacency-list FraudGraph with
idempotent upsert + edge-merge. Double-hashing Bloom
filter (capacity+FPR-sized). Union-find connected
components. PageRank with dangling-sink handling.
Louvain community detection. Shared-instrument hub
rings. Sliding ring-buffer velocity counters. Synthetic
identity heuristics (freshness + heavy-use + mail-drop).
Structuring / smurfing / mule-chain DFS. EntityRisk +
TransactionRisk + RiskBand combiner.
Smart Byte (1 crate, ~1844 LOC, 44 tests)
smart-byte-ilp Interledger Protocol v4. OER codec (length-determinant,
var-octet-string, BE u32/u64). Prepare/Fulfill/Reject
packet roundtrip with all 19 IL-RFC-27 reject codes.
SHA-256 Condition + Fulfillment with constant-time
verify. ILP address validator (7 schemes, 1-128 char
segments, 1023-byte cap, segment-aligned prefix match).
STREAM (15 frame types). BTP. SPSP payment-pointer
resolver. Open Payments API. Longest-prefix routing
Connector with fixed-point quote conversion + balance +
rate-limit checks.
EOC (1 crate, 1921 LOC, 36 tests)
eoc-quantize Quantization primitives. int8 (symmetric/asymmetric
with zero-inclusion fix), int4 (GPTQ + AWQ group quant),
int2 (BitNet), fp8 (E4M3/E5M2), nf4 (QLoRA). GGUF v3
header parse. safetensors read sketch. Calibration
datasets + per-channel scales. KV-cache quant. Energy
tradeoff curves (joules-per-token across schemes).
Co-Authored-By: Claude Opus 4.7 <[email protected]>
74 lines
2.8 KiB
Rust
74 lines
2.8 KiB
Rust
//! Edges in the fraud graph.
|
|
//!
|
|
//! An edge is *always* undirected from the algorithms' perspective, but we
|
|
//! attach a [`EdgeKind`] tag so heuristics can filter ("walk only along
|
|
//! `SharesInstrument` edges to find rings") and so weights can encode
|
|
//! domain meaning ("co-transaction within 60 seconds = strong link").
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::graph::VertexId;
|
|
|
|
/// The semantic kind of a connection between two entities.
|
|
///
|
|
/// Adding a kind is forward-compatible: heuristics either care about it
|
|
/// explicitly (and filter for it) or treat it the same as the rest.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum EdgeKind {
|
|
/// Two entities appeared together on the same payment attempt — the
|
|
/// generic "co-occurred" link. Almost everything starts here.
|
|
CoTransaction,
|
|
/// Two entities (typically two accounts) share the same payment
|
|
/// instrument (card, bank account). This is the primary ring signal.
|
|
SharesInstrument,
|
|
/// Two entities share a device fingerprint or originating IP.
|
|
/// Often catches velocity / multi-account abuse.
|
|
SharesDevice,
|
|
/// The "billing → shipping" lien — the billing address was
|
|
/// observed shipping to a different address.
|
|
BillingToShipping,
|
|
/// Money actually moved: source → destination. Asymmetric in
|
|
/// reality, but we still store it as an undirected weighted edge
|
|
/// for the algorithms. The weight encodes amount and the
|
|
/// `LaunderingDetector` walks the chain in time order.
|
|
Transfer,
|
|
/// Synthetic-identity link: two entities show signs of belonging
|
|
/// to the same fabricated identity (matching DOB, fragments).
|
|
SyntheticLink,
|
|
}
|
|
|
|
/// A typed, weighted, undirected edge between two [`VertexId`]s.
|
|
///
|
|
/// Edges are stored on both endpoints' adjacency lists by the graph
|
|
/// layer; callers do not need to insert twice.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
|
pub struct Edge {
|
|
/// Lower-id endpoint (canonicalised so equal undirected edges are
|
|
/// `==`-equal even after a swap).
|
|
pub a: VertexId,
|
|
/// Higher-id endpoint.
|
|
pub b: VertexId,
|
|
/// Semantic kind.
|
|
pub kind: EdgeKind,
|
|
/// Positive finite weight; meaningful to PageRank / Louvain.
|
|
pub weight: f32,
|
|
}
|
|
|
|
impl Edge {
|
|
/// Construct an edge with a canonical endpoint ordering.
|
|
pub fn new(u: VertexId, v: VertexId, kind: EdgeKind, weight: f32) -> Self {
|
|
let (a, b) = if u.0 <= v.0 { (u, v) } else { (v, u) };
|
|
Self { a, b, kind, weight }
|
|
}
|
|
|
|
/// Given one endpoint, return the other.
|
|
pub fn other(&self, v: VertexId) -> Option<VertexId> {
|
|
if v == self.a {
|
|
Some(self.b)
|
|
} else if v == self.b {
|
|
Some(self.a)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|