Add fuzzing for amounts
* +- 0 to 1%, or +- 1 sat for tiny amounts * Ensure balance cannot go negative
This commit is contained in:
parent
874c984c6d
commit
c16db7bbb2
@ -17,7 +17,7 @@ for (const key of ["DB_USER", "DB_PASSWORD", "DB_NAME", "ANONYMIZATION_KEY"]) {
|
||||
// Adjust based on database capacity
|
||||
const BATCH_SIZE = parseInt(env.BATCH_SIZE) || 10;
|
||||
// Maximum number of invoices to process per user
|
||||
// Skip user if exceeded (likely the case for streaming payments e.g.)
|
||||
// Skip user if exceeded (likely the case for streaming payments)
|
||||
const MAX_INVOICES = parseInt(env.MAX_INVOICES) || 21000;
|
||||
|
||||
// Database connection configuration from .env or environment variables
|
||||
@ -72,7 +72,7 @@ const csvHeaders = [
|
||||
// Write headers to CSV (create file or overwrite if it exists)
|
||||
await Deno.writeTextFile(csvFilePath, csvHeaders.join(",") + "\n");
|
||||
|
||||
// Function to compute HMAC-SHA256 hash of user_id, truncated to 48 bits (12 hex chars)
|
||||
// Compute HMAC-SHA256 hash of user_id, truncated to 48 bits (12 hex chars)
|
||||
async function anonymizeUserId(userId) {
|
||||
const keyData = new TextEncoder().encode(env.ANONYMIZATION_KEY);
|
||||
const data = new TextEncoder().encode(userId.toString());
|
||||
@ -88,7 +88,19 @@ async function anonymizeUserId(userId) {
|
||||
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
|
||||
// Function to process a single user and return CSV rows
|
||||
// Generate a random fuzzing factor
|
||||
function getFuzzFactor(amount) {
|
||||
const bytes = new Uint8Array(4);
|
||||
crypto.getRandomValues(bytes);
|
||||
const randomValue = new Uint32Array(bytes.buffer)[0];
|
||||
const normalized = randomValue / 0xFFFFFFFF;
|
||||
if (amount < 100) {
|
||||
return normalized < 0.5 ? -1 : 1; // ±1 satoshi for small amounts
|
||||
}
|
||||
return normalized * 0.02 - 0.01; // -1% to +1% for larger amounts
|
||||
}
|
||||
|
||||
// Process a single user and return CSV rows
|
||||
async function processUser(userId, client) {
|
||||
// Check the number of settled invoices for the user
|
||||
const countResult = await client.queryObject(
|
||||
@ -123,16 +135,34 @@ async function processUser(userId, client) {
|
||||
|
||||
for (const invoice of invoices) {
|
||||
const day = invoice.settled_at.toISOString().split("T")[0]; // YYYY-MM-DD
|
||||
// Convert amounts to BigInt, handling NULL fees
|
||||
const amount = BigInt(invoice.amount);
|
||||
const serviceFee = invoice.service_fee ? BigInt(invoice.service_fee) : BigInt(0);
|
||||
const routingFee = invoice.routing_fee ? BigInt(invoice.routing_fee) : BigInt(0);
|
||||
|
||||
// Calculate effective amount: include fees for outgoing, not for incoming
|
||||
const effectiveAmount = invoice.type === "incoming"
|
||||
? amount
|
||||
: amount + serviceFee + routingFee; // Add fees for outgoing
|
||||
const signedAmount = invoice.type === "incoming" ? effectiveAmount : -effectiveAmount;
|
||||
// Get fuzzing factor for this invoice
|
||||
const fuzzFactor = getFuzzFactor(Number(invoice.amount));
|
||||
|
||||
// Convert amounts to BigInt
|
||||
const amount = BigInt(invoice.amount);
|
||||
const serviceFee = BigInt(invoice.service_fee);
|
||||
const routingFee = BigInt(invoice.routing_fee);
|
||||
|
||||
// Apply fuzzing: value * (1 + fuzzFactor) or ±1 sat, rounded to nearest integer
|
||||
let fuzzedAmount = amount < 100
|
||||
? amount + BigInt(fuzzFactor)
|
||||
: BigInt(Math.round(Number(amount) * (1 + fuzzFactor)));
|
||||
|
||||
// Calculate effective amount: include fees for outgoing
|
||||
let effectiveAmount = invoice.type === "incoming"
|
||||
? fuzzedAmount
|
||||
: fuzzedAmount + serviceFee + routingFee; // Add fees for outgoing
|
||||
let signedAmount = invoice.type === "incoming" ? effectiveAmount : -effectiveAmount;
|
||||
|
||||
// Prevent negative running balance for outgoing invoices
|
||||
if (invoice.type === "outgoing" && runningBalance + signedAmount < 0) {
|
||||
// Adjust fuzzed amount to spend only up to available balance
|
||||
const maxSpend = runningBalance;
|
||||
fuzzedAmount = maxSpend - serviceFee - routingFee;
|
||||
effectiveAmount = fuzzedAmount + serviceFee + routingFee;
|
||||
signedAmount = -effectiveAmount;
|
||||
}
|
||||
|
||||
if (!dailyData[day]) {
|
||||
dailyData[day] = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user