16. Signing State Machine
Co-Signer Engine routing · 3-share ceremony · zero-trust handoffTwo-Tier Co-Signer Model (★ Stage 8)
mpc-cmp.md, p.5-6 직접 인용: "None of the parties (neither Fireblocks nor the customer) can sign a transaction alone."
| Tier | 정체 | Key share | 정책 |
|---|---|---|---|
| Customer Co-Signer | Mobile device (Keychain/TEE) 또는 SGX server | 1/3 | Customer 측 |
| Fireblocks Co-Signers (2) | Azure SGX server | 2/3 | "Safeguards in case keys owned by customers are compromised" — tx amount threshold, destination address integrity |
Co-Signer Broker + Aggregator Architecture
- Co-Signer Broker: MPC 메시지 + end cert 를 broadcast (chain of trust 검증)
- Aggregator: partial signature 들을 full signature 로 결합 → Blockchain
- "data is not passed between them during signing" — 각 co-signer 는 독립 partial sig 생성 (zero-trust)
Signing State Machine
stateDiagram-v2
[*] --> QUEUED : Secure Vault → Co-Signer Engine (Step 9)
QUEUED --> ROUTING : UUID + Policy rules cached
ROUTING --> AWAITING_CUSTOMER : route to Co-Signer 3 (mobile/SGX)
ROUTING --> AWAITING_FB_1 : route to Co-Signer 1 (FB Azure SGX)
ROUTING --> AWAITING_FB_2 : route to Co-Signer 2 (FB Azure SGX)
AWAITING_CUSTOMER --> PARTIAL_SIG_3 : signed (PIN+biometric or API)
AWAITING_CUSTOMER --> REJECTED : customer co-signer reject
AWAITING_FB_1 --> PARTIAL_SIG_1 : FB safeguards pass
AWAITING_FB_1 --> REJECTED : safeguard violation (amount/destination)
AWAITING_FB_2 --> PARTIAL_SIG_2 : FB safeguards pass
PARTIAL_SIG_1 --> AGGREGATING : all 3 collected
PARTIAL_SIG_2 --> AGGREGATING
PARTIAL_SIG_3 --> AGGREGATING
AGGREGATING --> VERIFYING : Auth Engine hash 검증 (Step 11)
VERIFYING --> SIGNED : Secure Vault tx 조립 (Step 12)
VERIFYING --> FAILED : hash mismatch
SIGNED --> READY_TO_BROADCAST
REJECTED --> [*]
Figure 8. Signing state machine — 3-share ceremony, parallel collection. Either party reject = REJECTED. Auth Engine 가 partial sig hash 검증.
Special Branches
- Hosted MPC: 3 share 모두 customer 환경 — Fireblocks 0 share.
signing_events.co_signer_location가 모두 'customer' - Solana Sign-Only: SIGNED 상태에서 broadcast 안 함, payload client 에 반환.
NOT BROADCAST BY FIREBLOCKS태그 - Cached signature (Raw Signing): 동일 raw data 재서명 시 전체 ceremony 우회 — SIGNED 직접 진입
- Co-signer rejection at PENDING_SIGNATURE → tx 전체가 CANCELLED (transaction state machine 참조)
DB Schema
CREATE TABLE signing_requests (
id BINARY(16) PRIMARY KEY,
transaction_id BINARY(16) NOT NULL,
workspace_id BINARY(16) NOT NULL,
policy_rule_id BINARY(16), -- triggering rule
co_signer_set_id BINARY(16) NOT NULL, -- 3-share set 식별자
state ENUM('QUEUED', 'ROUTING', 'AGGREGATING', 'VERIFYING',
'SIGNED', 'REJECTED', 'FAILED') NOT NULL,
queued_at DATETIME(6) NOT NULL,
signed_at DATETIME(6), -- ★ set-once
KEY (transaction_id),
KEY (workspace_id, state)
);
-- append-only: BEFORE UPDATE / DELETE 트리거로 강제
CREATE TABLE signing_events (
id BINARY(16) PRIMARY KEY,
signing_request_id BINARY(16) NOT NULL,
co_signer_id BINARY(16) NOT NULL,
co_signer_role ENUM('customer-mobile', 'customer-sgx', 'fb-azure-sgx') NOT NULL,
share_index TINYINT NOT NULL, -- 1, 2, or 3
event_type ENUM('partial-sig', 'reject') NOT NULL,
partial_sig_hash BINARY(32), -- Auth Engine 검증용
mrenclave_at_signing BINARY(32), -- SGX MRENCLAVE (★ set-once)
occurred_at DATETIME(6) NOT NULL,
KEY (signing_request_id)
);
CREATE TABLE callback_invocations (
-- API Co-Signer 의 Callback Handler 호출 (optional)
id BINARY(16) PRIMARY KEY,
signing_request_id BINARY(16) NOT NULL,
callback_endpoint_id BINARY(16) NOT NULL,
request_url VARCHAR(255) NOT NULL, -- /v2/tx_sign_request or /tx_sign_request
request_jwt_or_json ENUM('jwt', 'json') NOT NULL,
response_decision ENUM('approve', 'reject', 'ignore', 'retry') NOT NULL,
latency_ms INT,
invoked_at DATETIME(6) NOT NULL
);
SGX MRENCLAVE 의 의미
Fireblocks 의 Co-Signer 가 SGX enclave 안에서 실행되므로, 각 signing 시점의 enclave measurement (MRENCLAVE) 가 audit trail 에 기록됨. 외부 감사자는 attested MRENCLAVE 와 known-good 값을 비교해 enclave integrity 검증.