21. AML · Travel Rule

Step 5b/5c — AML provider 통합 · incoming reject 처리

AML Provider 통합 (★ Stage 9 명시)

transaction-lifecycle.md, p.4 diagram 에서 정식 명시된 AML provider:

  • Chainalysis
  • Elliptic
  • Notabene

14-step Flow 내 위치

  • Step 5b: Transaction Manager → Screening Service → AML Provider
  • Step 5c: Transaction Manager → Screening Service → Travel Rule Provider
  • 두 검증 모두 통과 후에야 Policy Engine (Step 6) 진입

AML Policy 의 평면 분리

Fireblocks Policy 와 별개 평면 (security-checklist.md):

  • Fireblocks Policies (TAP): 일반 tx 단위 rule
  • AML Transaction Screening Policy: AML provider 통합 — 별도 종류
  • DCCP: confirmation 정책 — 또 다른 종류

Workspace-level AML Default (★ Stage 9)

vault-structure-best-practices.md, p.5-6: Multi-workspace 가 필요한 이유 중 하나로 다른 AML default 명시:

  • fail-on-unknown: 모르는 destination 은 차단
  • pass-on-unknown: 모르는 destination 은 통과

Incoming Reject 의 Lock 상태 (★)

primary-transaction-statuses.md, p.8:

Outgoing vs Incoming Rejected 차이
  • Outgoing rejected: 자산 즉시 사용 가능 (re-tx 가능)
  • Incoming rejected: 자산이 Admin 가 unfreeze 할 때까지 사용 불가

변경 거버넌스

user-roles.md, p.7-8 권한표:

  • Add or modify AML connections and policies: Owner / Admin / NSA ✓
  • Add or modify Travel Rule connections and policies: Owner / Admin / NSA ✓

DB Schema

CREATE TABLE aml_providers (
  id                BINARY(16) PRIMARY KEY,
  workspace_id      BINARY(16) NOT NULL,
  provider_name     ENUM('chainalysis', 'elliptic', 'notabene') NOT NULL,
  endpoint_url      VARCHAR(255) NOT NULL,
  api_credential_hsm_keyref VARCHAR(128) NOT NULL,    -- ★ HSM-wrapped, never plaintext
  default_policy    ENUM('fail-on-unknown', 'pass-on-unknown') NOT NULL,
  enabled_at        DATETIME(6) NOT NULL,
  disabled_at       DATETIME(6),
  UNIQUE KEY (workspace_id, provider_name)
);

-- ★ append-only
CREATE TABLE aml_screening_results (
  id                BINARY(16) PRIMARY KEY,
  transaction_id    BINARY(16) NOT NULL,
  provider_id       BINARY(16) NOT NULL,
  direction         ENUM('incoming', 'outgoing') NOT NULL,
  risk_score        DECIMAL(5, 2),
  decision          ENUM('allow', 'block', 'manual-review') NOT NULL,
  raw_response_cbor BLOB NOT NULL,                    -- vendor response 보존
  screened_at       DATETIME(6) NOT NULL,
  KEY (transaction_id),
  KEY (provider_id, screened_at)
);

-- ★ append-only — Travel Rule message 보존 (counterparty exchange)
CREATE TABLE travel_rule_records (
  id                BINARY(16) PRIMARY KEY,
  transaction_id    BINARY(16) NOT NULL,
  provider_id       BINARY(16) NOT NULL,
  direction         ENUM('outbound', 'inbound') NOT NULL,
  originator_info_cbor BLOB NOT NULL,                 -- VASP info
  beneficiary_info_cbor BLOB NOT NULL,
  vasp_message_id   VARCHAR(128),
  status            ENUM('pending', 'sent', 'acked', 'rejected') NOT NULL,
  exchanged_at      DATETIME(6) NOT NULL,
  KEY (transaction_id)
);

-- Incoming reject 의 asset lock 추적
CREATE TABLE rejected_incoming_assets (
  id                BINARY(16) PRIMARY KEY,
  transaction_id    BINARY(16) NOT NULL UNIQUE,
  asset_wallet_id   BINARY(16) NOT NULL,
  amount_decimal    DECIMAL(38, 18) NOT NULL,
  rejected_at       DATETIME(6) NOT NULL,
  unfreeze_attempts INT NOT NULL DEFAULT 0,
  unfrozen_at       DATETIME(6),                       -- ★ set-once
  unfrozen_by_user_id BINARY(16),                      -- Admin
  KEY (asset_wallet_id, unfrozen_at)
);

FSPM (Fireblocks Security Posture Management)

Security Auditor / Security Admin 의 권한 영역. Security Center 화면 + findings 관리. 정확한 entity-grade 명세는 Fireblocks 자료 부족 — Q-S07 Open Question.