콘텐츠로 이동

v0.0

체인 추상화 레이어 인터페이스 정의서

1. Executive Summary

본 문서는 D'CENT 엔터프라이즈 콜드월렛 커스터디 솔루션의 체인 추상화 레이어(Chain Abstraction Layer)를 설계한다. 다양한 블록체인의 차이점을 추상화하여, 상위 애플리케이션이 체인에 독립적인 공통 인터페이스를 통해 트랜잭션을 생성/서명/검증/전파할 수 있게 한다.

설계 원칙: - 의존성 역전: 애플리케이션은 추상 인터페이스에만 의존, 구체 어댑터를 런타임에 주입 - 체인 독립적 워크플로우: TX 생성 → 서명 → 검증 → 전파 흐름이 체인 무관하게 동일 - 확장 가능: 새 체인 추가 시 어댑터 구현만으로 전체 시스템 통합 - Phase 4 WYSIWYS 연계: 각 어댑터는 ChainParser 인터페이스를 구현하여 SE 파싱 지원


2. 추상화 레이어 아키텍처

2.1. 계층 구조

┌─────────────────────────────────────────────────────────────┐
│                    Application Layer                         │
│                                                              │
│  Transaction Manager  │  Policy Engine  │  Wallet Manager    │
│                                                              │
├──────────────────────────────────────────────────────────────┤
│                  Chain Abstraction Layer                      │
│                                                              │
│  ┌──────────────┐  ┌──────────────────┐  ┌───────────────┐ │
│  │ ChainAdapter │  │ TransactionBuilder│  │ Signature     │ │
│  │ (Interface)  │  │ (Interface)       │  │ Verifier      │ │
│  └──────┬───────┘  └────────┬─────────┘  │ (Interface)   │ │
│         │                   │             └───────┬───────┘ │
│  ┌──────┴───────┐  ┌───────┴────────┐           │          │
│  │ Address      │  │ FeeEstimator   │           │          │
│  │ Manager      │  │ (Interface)    │           │          │
│  │ (Interface)  │  └────────────────┘           │          │
│  └──────────────┘                               │          │
│                                                  │          │
├──────────────────────────────────────────────────┤          │
│              Chain-Specific Adapters              │          │
│                                                   │          │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐ │          │
│  │ Bitcoin    │  │ EVM        │  │ Solana     │ │          │
│  │ Adapter    │  │ Adapter    │  │ Adapter    │ │          │
│  │            │  │            │  │ (Phase 3)  │ │          │
│  │ UTXO Model │  │ Account    │  │ Account    │ │          │
│  │ PSBT       │  │ Model      │  │ Model      │ │          │
│  │ MuSig2     │  │ EIP-712    │  │ Borsh/IDL  │ │          │
│  │ Schnorr    │  │ Safe       │  │ Ed25519    │ │          │
│  └────────────┘  └────────────┘  └────────────┘ │          │
│                                                   │          │
└───────────────────────────────────────────────────┘          │
                                                               │
                                                               │

2.2. 의존성 규칙

Application Layer ──depends on──▶ Abstraction Interfaces (추상)
                                          ▲
                                          │ implements
Chain-Specific Adapters ─────────────────┘
  • Application Layer는 ChainAdapter, TransactionBuilder 등 인터페이스에만 의존
  • 구체 어댑터(BitcoinAdapter, EVMAdapter)는 런타임에 주입
  • 어댑터 교체/추가 시 Application Layer 코드 변경 불필요

3. ChainAdapter 공통 인터페이스

Interface ChainAdapter {

    // 체인 메타데이터 조회
    getChainInfo(): ChainInfo

    // 미서명 트랜잭션 구성
    buildTransaction(params: TransactionParams): UnsignedTransaction

    // 트랜잭션 파싱 (WYSIWYS용)
    parseTransaction(raw: bytes): ParsedTransaction

    // 서명 검증
    verifySignature(tx: UnsignedTransaction, signature: bytes, pubkey: bytes): boolean

    // 블록체인 전파
    broadcastTransaction(signedTx: bytes): TxHash

    // 잔액 조회
    getBalance(address: string, asset?: string): Balance

    // 수수료 추정
    estimateFee(tx: UnsignedTransaction): FeeEstimate

    // 주소 포맷
    formatAddress(rawPubkey: bytes, addressType?: string): string

    // 주소 유효성 검증
    validateAddress(address: string): AddressValidation

    // 트랜잭션 상태 조회
    getTransactionStatus(txHash: string): TransactionStatus
}

3.1. 공통 데이터 타입

ChainInfo {
    chainId: uint               // SLIP-0044 coin type 또는 EIP-155 chain ID
    name: string                // 체인명 (e.g., "Bitcoin", "Ethereum")
    symbol: string              // 네이티브 통화 심볼 (e.g., "BTC", "ETH")
    coinType: uint              // SLIP-0044 coin type
    decimals: uint              // 네이티브 통화 소수점 (BTC=8, ETH=18)
    model: "UTXO" | "Account"  // 체인 모델
    signatureScheme: "ECDSA" | "Schnorr" | "Ed25519"
    supportedAddressTypes: [string]  // 지원 주소 유형
}

TransactionParams {
    recipients: [{
        address: string
        amount: BigInt           // 최소 단위 (satoshis, wei)
        asset?: string           // 토큰 식별자 (ERC-20 등)
    }]
    memo?: string
    feeLevel?: "low" | "medium" | "high" | "custom"
    customFee?: BigInt
    // 체인별 확장 파라미터는 chainSpecific 필드로 전달
    chainSpecific?: map
}

UnsignedTransaction {
    chainId: uint
    raw: bytes                   // 직렬화된 미서명 TX (PSBT, RLP 등)
    displayData: ParsedTransaction  // WYSIWYS 표시용 파싱 데이터
    signingInputs: [{
        derivationPath: string
        signerIndex?: uint       // MuSig2 참여자 인덱스
        dataToSign: bytes        // 서명할 해시/데이터
    }]
    estimatedFee: FeeEstimate
}

ParsedTransaction {
    recipients: [{
        address: string
        amount: BigInt
        asset: string            // 자산 식별자
        displayAmount: string    // 사용자 표시용 (e.g., "1.5 BTC")
    }]
    fee: {
        amount: BigInt
        displayAmount: string    // e.g., "0.0001 BTC", "0.003 ETH"
        rate?: string            // e.g., "10 sat/vB", "30 Gwei"
    }
    metadata: {
        chain: string
        nonce?: uint
        memo?: string
        locktime?: uint
        contractCall?: {
            functionName: string
            functionSelector: bytes(4)
            decodedParams: map?
        }
    }
    warningLevel: "NONE" | "INFO" | "CAUTION" | "CRITICAL"
    warnings: [string]
}

Balance {
    confirmed: BigInt
    unconfirmed: BigInt
    total: BigInt
    displayTotal: string
    asset: string
}

FeeEstimate {
    low: BigInt
    medium: BigInt
    high: BigInt
    unit: string                 // "sat/vB", "Gwei", "lamports"
    estimatedTime: {
        low: uint                // 예상 확인 시간 (분)
        medium: uint
        high: uint
    }
}

TransactionStatus {
    hash: string
    status: "pending" | "confirmed" | "failed"
    confirmations: uint
    blockHeight?: uint
    blockHash?: string
    timestamp?: uint
}

4. 체인 유형별 어댑터 상세

4.1. UTXO 어댑터 (Bitcoin)

Class BitcoinAdapter implements ChainAdapter {

    // UTXO 관련 추가 메서드
    selectUTXOs(amount: BigInt, utxoSet: [UTXO], strategy: UTXOStrategy): [UTXO]
    buildPSBT(params: BTCTransactionParams): PSBT
    parsePSBT(psbt: bytes): ParsedTransaction
    addPartialSignature(psbt: PSBT, partialSig: bytes, signerIndex: uint): PSBT
    aggregateSignatures(psbt: PSBT): bytes  // MuSig2 최종 서명 집계
    estimateTxSize(inputCount: uint, outputCount: uint, scriptType: string): uint
}

UTXO 선택 알고리즘:

알고리즘 설명 적합 시나리오
Branch-and-Bound 수수료 포함 정확한 금액 매칭 시도 잔돈 출력 없는 최적 TX
Knapsack 목표 금액에 가까운 UTXO 조합 선택 다양한 UTXO 분포
Largest-First 큰 UTXO부터 선택 UTXO 통합, 단순 전송
Privacy-Optimized UTXO 연결 최소화 프라이버시 중시

PSBT 지원:

PSBT {
    global: {
        unsigned_tx: bytes          // 전역 미서명 TX
        xpub: [{                    // 확장 공개키 (BIP-44/84/86)
            xpub: bytes
            fingerprint: bytes(4)
            derivation: string
        }]
        version: uint               // 0 (BIP-174) 또는 2 (BIP-370)
    }
    inputs: [{
        witness_utxo: {
            amount: uint64          // satoshis
            scriptPubKey: bytes
        }
        bip32_derivation: [{
            pubkey: bytes(33)
            fingerprint: bytes(4)
            path: string
        }]
        musig2_participant_pubkeys: [bytes(33)]?   // BIP-373
        musig2_public_nonce: bytes(66)?              // BIP-373
        musig2_partial_sig: bytes(32)?               // BIP-373
    }]
    outputs: [{
        bip32_derivation: [{...}]   // Change 출력 경로
    }]
}

MuSig2 (BIP-327) 지원:

MuSig2Manager {
    // Round 1: Nonce 생성
    generateNonce(sessionId: bytes, signerIndex: uint): PublicNonce

    // Round 1: Nonce 집계
    aggregateNonces(nonces: [PublicNonce]): AggregatedNonce

    // Round 2: 부분 서명 생성 (SE에서 수행)
    // → SE_SignRequest로 전달

    // Round 2: 서명 집계
    aggregatePartialSigs(
        partialSigs: [bytes(32)],
        aggregatedNonce: AggregatedNonce,
        message: bytes(32)
    ): bytes(64)  // 최종 Schnorr 서명
}

주소 유형:

유형 BIP 접두사 용도
P2WPKH BIP-84 bc1q... Native SegWit (기본)
P2TR BIP-86 bc1p... Taproot (MuSig2 필수)
P2SH-P2WPKH BIP-49 3... 레거시 호환 (선택)

4.2. EVM 어댑터 (Ethereum, Polygon, Arbitrum 등)

Class EVMAdapter implements ChainAdapter {

    // EVM 관련 추가 메서드
    buildEIP1559Transaction(params: EVMTransactionParams): RLPEncodedTx
    buildEIP712TypedData(domain: EIP712Domain, message: map): TypedData
    decodeCalldata(data: bytes, abi?: ABI): DecodedCall
    buildSafeTransaction(params: SafeTransactionParams): SafeTx
    estimateGas(tx: EVMTransaction): GasEstimate
    getNonce(address: string): uint
    getTokenBalance(address: string, tokenAddress: string): Balance
}

EIP-1559 트랜잭션 구성:

EVMTransaction {
    type: 0x02                      // EIP-1559
    chainId: uint
    nonce: uint
    maxPriorityFeePerGas: uint      // 팁 (Gwei → Wei)
    maxFeePerGas: uint              // 최대 수수료
    gasLimit: uint
    to: bytes(20)
    value: uint256                  // Wei
    data: bytes                     // calldata
    accessList: [{address, storageKeys}]?
}

EIP-712 구조화 데이터 서명:

EIP712Domain {
    name: string                    // e.g., "GnosisSafe"
    version: string                 // e.g., "1.3.0"
    chainId: uint
    verifyingContract: bytes(20)    // Safe 컨트랙트 주소
}

// 서명 대상 해시:
// keccak256("\x19\x01" || domainSeparator || hashStruct(message))

Safe Multisig 통합:

SafeTransactionParams {
    safe: bytes(20)                 // Safe 프록시 주소
    to: bytes(20)                   // 실행 대상
    value: uint256
    data: bytes                     // 실행 calldata
    operation: 0 | 1               // 0=Call, 1=DelegateCall
    safeTxGas: uint
    baseGas: uint
    gasPrice: uint
    gasToken: bytes(20)            // 0x0 = ETH
    refundReceiver: bytes(20)
    nonce: uint                     // Safe nonce
}

// Safe execTransaction ABI:
// execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)

ABI 디코딩:

ABIDecoder {
    // 함수 셀렉터 매칭
    matchSelector(selector: bytes(4)): FunctionSignature?

    // 파라미터 디코딩
    decodeParams(data: bytes, abi: FunctionSignature): DecodedParams

    // 사전 등록 셀렉터:
    // 0xa9059cbb → transfer(address,uint256)
    // 0x095ea7b3 → approve(address,uint256)
    // 0x23b872dd → transferFrom(address,address,uint256)
    // 0x6a761202 → execTransaction(...)  [Safe]
    // 0x0d582f13 → addOwnerWithThreshold(address,uint256) [Safe]
    // 0xf8dc5dd9 → removeOwner(address,address,uint256) [Safe]
    // 0x694e80c3 → changeThreshold(uint256) [Safe]
}

ERC-20 토큰 전송 구성:

buildERC20Transfer(tokenAddress, recipient, amount, decimals):
    to: tokenAddress
    value: 0
    data: 0xa9059cbb                          // transfer selector
        + leftPad(recipient, 32)              // address padded
        + leftPad(amount * 10^decimals, 32)   // uint256 padded

4.3. EdDSA 어댑터 (Solana — Phase 3 출시 대상)

Class SolanaAdapter implements ChainAdapter {

    // Solana 관련 추가 메서드
    buildSolanaTransaction(params: SolanaTransactionParams): SolanaTransaction
    decodeInstruction(data: bytes, programId: string): DecodedInstruction
    getRecentBlockhash(): string
    getMinimumBalanceForRentExemption(dataSize: uint): uint
}

인터페이스 정의 (구현은 Phase 3 출시 대상):

SolanaTransaction {
    recentBlockhash: string         // 최근 블록해시 (TX 유효성)
    feePayer: string                // 수수료 지불 주소
    instructions: [{
        programId: string           // 프로그램 ID
        accounts: [{
            pubkey: string
            isSigner: boolean
            isWritable: boolean
        }]
        data: bytes                 // Borsh 인코딩 instruction data
    }]
}

서명 체계: Ed25519 (Edwards-curve Digital Signature Algorithm) - 키 크기: 32 bytes (private), 32 bytes (public) - 서명 크기: 64 bytes - 주소: Base58 인코딩 공개키

파싱 복잡도: Solana는 프로그램 ID 기반이므로, 알려진 프로그램(System Program, Token Program, Associated Token Account)에 대해서만 WYSIWYS 파싱 가능. 알 수 없는 프로그램은 CAUTION 경고.


5. TransactionBuilder 인터페이스

Interface TransactionBuilder {

    // 체인 독립적 TX 생성
    create(adapter: ChainAdapter, params: TransactionParams): TransactionBuilder

    // 체인별 특수 설정
    withUTXOSelection(strategy: UTXOStrategy): TransactionBuilder     // BTC
    withGasSettings(settings: GasSettings): TransactionBuilder         // EVM
    withMemo(memo: string): TransactionBuilder
    withSafeMultisig(safeParams: SafeParams): TransactionBuilder       // EVM Safe
    withMuSig2Session(session: MuSig2Session): TransactionBuilder      // BTC MuSig2

    // 빌드
    build(): UnsignedTransaction

    // 검증
    validate(): ValidationResult
}

빌더 패턴 사용 예시:

// BTC MuSig2 트랜잭션
tx = TransactionBuilder
    .create(bitcoinAdapter, {
        recipients: [{address: "bc1p...", amount: 100000000, asset: "BTC"}]
    })
    .withUTXOSelection("branch-and-bound")
    .withMuSig2Session(musig2Session)
    .build()

// EVM Safe Multisig ERC-20 전송
tx = TransactionBuilder
    .create(evmAdapter, {
        recipients: [{address: "0x742d...", amount: 1000000, asset: "USDT"}]
    })
    .withGasSettings({maxFeePerGas: 30e9, maxPriorityFeePerGas: 2e9})
    .withSafeMultisig({safeAddress: "0xSafe...", threshold: 3})
    .build()

6. SignatureVerifier 인터페이스

Interface SignatureVerifier {

    // 서명 검증
    verify(
        message: bytes,           // 서명된 메시지/해시
        signature: bytes,         // 서명값
        publicKey: bytes,         // 공개키
        scheme: SignatureScheme   // 서명 알고리즘
    ): boolean

    // 공개키에서 주소 파생
    deriveAddress(publicKey: bytes, chain: ChainInfo, addressType?: string): string

    // 서명 직렬화 변환
    serializeSignature(signature: bytes, format: SignatureFormat): bytes

    // 서명에서 공개키 복구 (ECDSA만)
    recoverPublicKey(message: bytes, signature: bytes): bytes?
}

SignatureScheme = "ECDSA_secp256k1" | "Schnorr_BIP340" | "Ed25519"

SignatureFormat = "DER" | "compact" | "raw" | "EIP2098"

알고리즘별 검증:

알고리즘 체인 서명 크기 공개키 크기 해시 함수
ECDSA secp256k1 BTC (legacy), EVM 64-72B (DER) 33B (compressed) SHA-256 (BTC), Keccak-256 (EVM)
Schnorr BIP-340 BTC (Taproot) 64B 32B (x-only) SHA-256 (tagged hash)
Ed25519 Solana 64B 32B SHA-512

7. AddressManager 인터페이스

Interface AddressManager {

    // HD 지갑 경로 생성
    getDerivationPath(
        coinType: uint,          // SLIP-0044
        account: uint,
        change: uint,
        index: uint,
        purpose?: uint           // 44, 84, 86
    ): string

    // 주소 유효성 검증
    validateAddress(address: string, chain: ChainInfo): AddressValidation

    // 주소 형식 정규화
    normalizeAddress(address: string, chain: ChainInfo): string

    // 체인별 체크섬 검증
    verifyChecksum(address: string, chain: ChainInfo): boolean

    // SLIP-0044 Coin Type 조회
    getCoinType(chainSymbol: string): uint
}

AddressValidation {
    valid: boolean
    addressType: string          // e.g., "P2TR", "EIP-55", "Base58"
    checksumValid: boolean
    normalizedAddress: string
}

HD 키 파생 경로:

체인 Purpose Coin Type 경로 패턴 주소 유형
Bitcoin (SegWit) 84 0 m/84'/0'/account'/change/index P2WPKH (bc1q...)
Bitcoin (Taproot) 86 0 m/86'/0'/account'/change/index P2TR (bc1p...)
Ethereum 44 60 m/44'/60'/account'/0/index EIP-55 (0x...)
Polygon 44 60 m/44'/60'/account'/0/index EIP-55 (동일)
Solana 44 501 m/44'/501'/account'/0' Base58

SLIP-0044 주요 Coin Type:

Coin Type 체인
0 Bitcoin
60 Ethereum (+ EVM 체인 공유)
501 Solana
118 Cosmos (ATOM)
195 Tron

8. 어댑터 등록 및 확장 메커니즘

8.1. 런타임 어댑터 등록 (Plugin 패턴)

Class ChainRegistry {

    // 어댑터 등록
    register(chainId: uint, adapter: ChainAdapter): void

    // 어댑터 조회
    getAdapter(chainId: uint): ChainAdapter?

    // 등록된 체인 목록
    listChains(): [ChainInfo]

    // 어댑터 존재 확인
    isSupported(chainId: uint): boolean
}

// 초기화 예시:
registry = ChainRegistry()
registry.register(0, BitcoinAdapter(config))      // BTC
registry.register(1, EVMAdapter(config, chainId=1)) // ETH
registry.register(137, EVMAdapter(config, chainId=137)) // Polygon
registry.register(42161, EVMAdapter(config, chainId=42161)) // Arbitrum

8.2. 새 체인 추가 시 구현 체크리스트

단계 항목 필수 설명
1 ChainAdapter 구현 필수 10개 공통 메서드 구현
2 TransactionBuilder 확장 필수 체인별 빌더 메서드
3 SignatureVerifier 지원 필수 서명 알고리즘 추가
4 AddressManager 확장 필수 주소 포맷/검증
5 WYSIWYS ChainParser 필수 SE 파싱 모듈 (Phase 4 인터페이스)
6 CBOR/UR 스키마 정의 필수 에어갭 메시지 포맷
7 NFC APDU 확장 선택 체인별 커맨드 추가 시
8 어댑터 테스트 필수 아래 테스트 항목 전체 통과

8.3. 어댑터 테스트 프레임워크

테스트 카테고리 테스트 항목 합격 기준
TX 생성 유효한 미서명 TX 생성 TX가 체인 노드에서 유효성 검증 통과
TX 파싱 Raw TX → ParsedTransaction 모든 필수 필드 정확 추출
서명 검증 서명 생성 → 검증 검증 성공
주소 생성 공개키 → 주소 알려진 테스트 벡터와 일치
수수료 추정 수수료 범위 실제 수수료의 50-200% 범위
잔액 조회 잔액 반환 블록 익스플로러와 일치
전파 TX 브로드캐스트 멤풀 진입 확인
WYSIWYS ParsedTransaction의 warningLevel 올바른 경고 레벨

9. Phase 3 연계: 체인 확장 로드맵 매핑

9.1. 출시 단계별 어댑터 구현 우선순위

Phase 체인 어댑터 ChainParser 서명 방식
1 (출시) Bitcoin BitcoinAdapter BTC PSBT Parser MuSig2 (Schnorr)
1 (출시) Ethereum EVMAdapter(1) EVM RLP+ABI Parser ECDSA secp256k1
1 (출시) USDT/USDC EVMAdapter(1) + 토큰 ERC-20 Parser ECDSA (동일)
2 (확장) Polygon EVMAdapter(137) 재사용 ECDSA (동일)
2 (확장) Arbitrum EVMAdapter(42161) 재사용 ECDSA (동일)
2 (확장) BSC EVMAdapter(56) 재사용 ECDSA (동일)
3 (다각화) Solana SolanaAdapter Solana Parser Ed25519
3 (다각화) Tron TronAdapter Tron Parser ECDSA secp256k1

9.2. WYSIWYS ChainParser 연계

Phase 4 wysiwys-design.md에서 정의한 ChainParser 인터페이스를 각 어댑터가 구현:

Interface ChainParser {
    chainId: uint
    parse(rawTx: bytes): ParsedTransaction
    validate(parsed: ParsedTransaction, expected: ExpectedFields): ValidationResult
    formatDisplay(parsed: ParsedTransaction): [DisplayPage]
}

어댑터의 parseTransaction() 메서드가 이 인터페이스를 내부적으로 호출하여 WYSIWYS 표시 데이터를 생성한다. SE 펌웨어에는 동일 인터페이스의 경량 구현체가 탑재되어 독립 파싱을 수행한다.


본 문서는 Phase 5 System Architecture Design의 일부로, Phase 3 multichain-support.md의 체인 확장 로드맵을 아키텍처 인터페이스로 구체화한다. Phase 4 wysiwys-design.md의 ChainParser 인터페이스와 연계하여 WYSIWYS 파싱 범위를 어댑터 수준에서 정의한다. 인터페이스는 의사코드(pseudocode) 형태이며, 특정 프로그래밍 언어에 종속되지 않는다.


관련 문서