Разработка системы антифрода крипто-казино

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Разработка системы антифрода крипто-казино
Сложный
~1-2 недели
Часто задаваемые вопросы

Направления блокчейн-разработки

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1306
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1218
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    920
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1147
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    610
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    885

Разработка системы антифрода крипто-казино

Крипто-казино сталкивается с уникальной комбинацией угроз: bonus abuse через Sybil атаки, манипуляция с on-chain randomness, collusion между игроками, отмывание денег и fraud через compromised accounts. Анонимность криптовалют упрощает атаку, но pseudonymous природа блокчейна — paradoxically — упрощает отслеживание паттернов. Эффективная система антифрода работает на нескольких уровнях одновременно.

Ландшафт угроз

Bonus hunting и мульти-аккаунты

Welcome bonus в крипто-казино часто составляет 100-200% депозита. Злоумышленник создаёт десятки кошельков, получает бонус на каждый и выводит с минимальным wagering. Проблема усугубляется тем, что нет привязки к email или телефону — только адрес кошелька.

On-chain randomness manipulation

Контракты, использующие block.timestamp или block.prevrandao в качестве источника случайности, уязвимы: валидатор может сдвинуть timestamp или выбрать выгодный блок. Даже block.prevrandao (RANDAO) не является криптографически безопасным источником случайности для gambling — его можно частично предсказать.

Flash loan + game state manipulation

Некоторые игры имеют on-chain state. Flash loan позволяет:

  1. Взять кредит большой суммы
  2. Изменить game state (купить максимум токенов/ставок)
  3. Сыграть с изменённым house edge
  4. Вернуть flash loan в той же транзакции

Collusion в poker/multiplayer играх

Координированная игра нескольких аккаунтов против других игроков. В покере — chip dumping или sharing hole cards.

Архитектура антифрод системы

On-chain layer:           VRF, commit-reveal, pause guardian
Transaction monitoring:   Real-time flow analysis, address clustering
Behavioral analytics:     Session patterns, bet sizing anomalies
Identity layer:           Passport score, device fingerprint (off-chain)
Risk scoring engine:      ML модель, real-time scoring

Безопасная случайность: Chainlink VRF V2+

Замена block.hash на Chainlink VRF — базовое требование для любого gambling dApp:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2Plus.sol";
import "@chainlink/contracts/src/v0.8/vrf/interfaces/IVRFCoordinatorV2Plus.sol";

contract CasinoGame is VRFConsumerBaseV2Plus {
    IVRFCoordinatorV2Plus private immutable coordinator;

    // Chainlink VRF параметры (Ethereum mainnet)
    bytes32 private constant KEY_HASH =
        0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae;
    uint256 private immutable subscriptionId;
    uint16 private constant REQUEST_CONFIRMATIONS = 3;
    uint32 private constant NUM_WORDS = 1;
    uint32 private constant CALLBACK_GAS_LIMIT = 200000;

    struct BetRequest {
        address player;
        uint256 betAmount;
        uint8 betType;     // тип ставки (число, цвет и т.д.)
        bool fulfilled;
    }

    mapping(uint256 => BetRequest) public betRequests; // requestId -> BetRequest

    event BetPlaced(uint256 indexed requestId, address indexed player, uint256 amount);
    event BetSettled(uint256 indexed requestId, bool won, uint256 payout);

    constructor(
        address _coordinator,
        uint256 _subscriptionId
    ) VRFConsumerBaseV2Plus(_coordinator) {
        coordinator = IVRFCoordinatorV2Plus(_coordinator);
        subscriptionId = _subscriptionId;
    }

    function placeBet(uint8 betType) external payable returns (uint256 requestId) {
        require(msg.value >= MIN_BET && msg.value <= MAX_BET, "Invalid bet amount");

        // Запрос случайного числа — результат придёт в fulfillRandomWords
        requestId = coordinator.requestRandomWords(
            VRFV2PlusClient.RandomWordsRequest({
                keyHash: KEY_HASH,
                subId: subscriptionId,
                requestConfirmations: REQUEST_CONFIRMATIONS,
                callbackGasLimit: CALLBACK_GAS_LIMIT,
                numWords: NUM_WORDS,
                extraArgs: VRFV2PlusClient._argsToBytes(
                    VRFV2PlusClient.ExtraArgsV1({ nativePayment: false })
                )
            })
        );

        betRequests[requestId] = BetRequest({
            player: msg.sender,
            betAmount: msg.value,
            betType: betType,
            fulfilled: false
        });

        emit BetPlaced(requestId, msg.sender, msg.value);
    }

    function fulfillRandomWords(
        uint256 requestId,
        uint256[] calldata randomWords
    ) internal override {
        BetRequest storage bet = betRequests[requestId];
        require(!bet.fulfilled, "Already fulfilled");

        bet.fulfilled = true;

        // Используем случайное число для определения результата
        uint256 result = randomWords[0] % 37; // рулетка 0-36

        bool won = checkWin(bet.betType, result);
        uint256 payout = won ? calculatePayout(bet.betAmount, bet.betType) : 0;

        if (payout > 0) {
            payable(bet.player).transfer(payout);
        }

        emit BetSettled(requestId, won, payout);
    }
}

Важно: между placeBet и fulfillRandomWords нет атомарности. Игрок не знает результат до исполнения callback — это и есть правильная модель.

Обнаружение мульти-аккаунтов и Sybil атак

On-chain кластеризация кошельков

import networkx as nx
from collections import defaultdict
from typing import List, Dict, Set

class SybilDetector:
    def __init__(self, provider_url: str):
        self.w3 = Web3(Web3.HTTPProvider(provider_url))

    def get_funding_source(self, address: str, depth: int = 3) -> str:
        """
        Трассируем цепочку финансирования кошелька до первоначального источника.
        Если несколько кошельков имеют один funding source — вероятно один владелец.
        """
        current = address
        for _ in range(depth):
            funding_txs = self._get_first_incoming_tx(current)
            if not funding_txs:
                break
            # Первая транзакция пополнения — вероятный источник
            first_tx = funding_txs[0]
            sender = first_tx['from']
            # Известные exchange адреса — не считаем источником
            if sender in KNOWN_EXCHANGE_ADDRESSES:
                return sender  # остановились на бирже
            current = sender
        return current

    def cluster_by_funding(
        self,
        addresses: List[str]
    ) -> Dict[str, List[str]]:
        """Группируем адреса по общему источнику финансирования."""
        funding_map = {}
        for addr in addresses:
            source = self.get_funding_source(addr)
            funding_map[addr] = source

        clusters = defaultdict(list)
        for addr, source in funding_map.items():
            clusters[source].append(addr)

        # Возвращаем только кластеры с >1 адресом
        return {k: v for k, v in clusters.items() if len(v) > 1}

    def detect_temporal_correlation(
        self,
        addresses: List[str],
        window_seconds: int = 60
    ) -> List[Set[str]]:
        """
        Адреса, которые регулярно делают ставки в одно и то же время —
        вероятно управляются одним скриптом.
        """
        activity_times = {}
        for addr in addresses:
            bets = self._get_bet_timestamps(addr)
            activity_times[addr] = set(b // window_seconds for b in bets)

        correlated = []
        checked = set()

        for i, addr1 in enumerate(addresses):
            group = {addr1}
            for addr2 in addresses[i+1:]:
                if addr2 in checked:
                    continue
                times1 = activity_times[addr1]
                times2 = activity_times[addr2]
                overlap = len(times1 & times2)
                union = len(times1 | times2)
                jaccard = overlap / union if union > 0 else 0
                if jaccard > 0.7:  # 70% временного совпадения
                    group.add(addr2)
            if len(group) > 1:
                correlated.append(group)
            checked.add(addr1)

        return correlated

Behavioral fingerprinting

@dataclass
class PlayerProfile:
    address: str
    avg_bet_size: float
    bet_size_variance: float
    preferred_games: List[str]
    session_duration_avg: float  # минуты
    sessions_per_day: float
    withdrawal_to_deposit_ratio: float
    bonus_exploitation_score: float  # 0-1

def compute_bonus_exploitation_score(
    address: str,
    bets: List[Dict],
    deposits: List[Dict],
    withdrawals: List[Dict]
) -> float:
    """
    Высокий score = признаки bonus hunting:
    - минимальный wagering перед выводом
    - смена паттернов поведения после получения бонуса
    - высокий bet size относительно баланса (для быстрого wagering)
    """
    bonus_received = sum(d['amount'] for d in deposits if d.get('is_bonus'))
    if bonus_received == 0:
        return 0.0

    # Анализ ставок ПОСЛЕ получения бонуса
    bonus_deposit_time = min(d['timestamp'] for d in deposits if d.get('is_bonus'))
    post_bonus_bets = [b for b in bets if b['timestamp'] > bonus_deposit_time]

    if not post_bonus_bets:
        return 0.5  # нет данных — умеренный риск

    total_wagered_post_bonus = sum(b['amount'] for b in post_bonus_bets)
    wagering_ratio = total_wagered_post_bonus / bonus_received

    # Нормальный wagering requirement = 30-40x
    # Если вывод после 1-2x wagering — bonus hunting
    if wagering_ratio < 2:
        return 0.95
    elif wagering_ratio < 5:
        return 0.8
    elif wagering_ratio < 15:
        return 0.5
    else:
        return 0.1

Real-time scoring pipeline

from dataclasses import dataclass
from enum import Enum

class RiskLevel(Enum):
    ALLOW = "allow"
    MONITOR = "monitor"
    SOFT_BLOCK = "soft_block"  # повышенный KYC
    BLOCK = "block"

@dataclass
class TransactionRisk:
    address: str
    risk_level: RiskLevel
    risk_score: float
    triggered_rules: List[str]
    recommended_action: str

class RealTimeAntifraud:
    def __init__(self, model, sybil_detector: SybilDetector):
        self.model = model
        self.sybil_detector = sybil_detector
        self.rule_engine = RuleEngine()

    def assess_transaction(
        self,
        address: str,
        bet_amount: float,
        game_type: str
    ) -> TransactionRisk:
        triggered_rules = []
        base_score = 0.0

        # Rule-based checks (быстро, до ML)
        profile = self.get_profile(address)

        # Правило 1: аномально крупная ставка
        if bet_amount > profile.avg_bet_size * 10:
            triggered_rules.append("ANOMALOUS_BET_SIZE")
            base_score += 0.3

        # Правило 2: новый кошелёк с крупной ставкой
        account_age_days = self.get_account_age(address)
        if account_age_days < 7 and bet_amount > 1000:
            triggered_rules.append("NEW_ACCOUNT_HIGH_VALUE")
            base_score += 0.4

        # Правило 3: высокий bonus exploitation score
        if profile.bonus_exploitation_score > 0.8:
            triggered_rules.append("BONUS_HUNTING")
            base_score += 0.5

        # ML scoring
        features = self.extract_features(address, bet_amount)
        ml_score = self.model.predict_proba([features])[0][1]

        final_score = min(1.0, base_score + ml_score * 0.5)

        if final_score < 0.3:
            risk_level = RiskLevel.ALLOW
        elif final_score < 0.6:
            risk_level = RiskLevel.MONITOR
        elif final_score < 0.85:
            risk_level = RiskLevel.SOFT_BLOCK
        else:
            risk_level = RiskLevel.BLOCK

        return TransactionRisk(
            address=address,
            risk_level=risk_level,
            risk_score=final_score,
            triggered_rules=triggered_rules,
            recommended_action=self.get_action(risk_level)
        )

AML и transaction monitoring

Крипто-казино подпадает под регуляторные требования в большинстве юрисдикций. Transaction monitoring:

Паттерн Описание Порог
Smurfing Множество мелких депозитов вместо одного крупного > 10 транзакций/день по схожим суммам
Round-trip Депозит → минимальные ставки → вывод Wagering < 5% депозита
Layering Сложные цепочки переводов до депозита > 3 hop от source
Structuring Суммы чуть ниже reporting threshold Суммы 9000-9999 USDC системно

Интеграция с Chainalysis KYT или Elliptic для автоматической проверки адресов на связь с санкционными списками и известными exploit адресами — обязательна для лицензированных операторов.

On-chain pause механизм

При обнаружении аномалий система должна мочь заморозить контракт:

// Emergency pause при детекции аномального паттерна
contract CasinoGuardian {
    address public immutable casino;
    address public immutable securitySystem; // off-chain антифрод система

    uint256 public dailyPayoutLimit;
    uint256 public dailyPayoutSoFar;
    uint256 public lastResetDay;

    function emergencyPause() external {
        require(msg.sender == securitySystem, "Not authorized");
        ICasino(casino).pause();
        emit EmergencyPause(block.timestamp, msg.sender);
    }

    function checkDailyLimit(uint256 payoutAmount) external returns (bool) {
        uint256 today = block.timestamp / 1 days;
        if (today > lastResetDay) {
            dailyPayoutSoFar = 0;
            lastResetDay = today;
        }
        dailyPayoutSoFar += payoutAmount;
        if (dailyPayoutSoFar > dailyPayoutLimit) {
            ICasino(casino).pause();
            return false;
        }
        return true;
    }
}

Антифрод система — живой организм. Атакующие адаптируются к детекции, система должна регулярно обновляться с учётом новых паттернов.