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) 형태이며, 특정 프로그래밍 언어에 종속되지 않는다.
관련 문서¶
- 에어갭 통신 프로토콜 설계서 -- 시스템 아키텍처
- 컴포넌트 간 데이터 흐름 및 인터페이스 명세 -- 시스템 아키텍처
- 온라인 대시보드 백엔드 아키텍처 설계서 -- 시스템 아키텍처
- D'CENT X 펌웨어 서명 인터페이스 설계서 -- 시스템 아키텍처
- 오프라인 서명 앱 아키텍처 설계서 -- 시스템 아키텍처
- STRIDE 기반 보안 위협 모델 -- 시스템 아키텍처
- 3-Zone 보안 아키텍처 설계서 -- 시스템 아키텍처