Upgrade to Pro — share decks privately, control downloads, hide ads and more …

두 개의 서로 다른 블록체인은 어떻게 자산을 주고 받을까?

kakao
December 09, 2022

두 개의 서로 다른 블록체인은 어떻게 자산을 주고 받을까?

#Bridge #Multichain

하나의 블록체인은 하나의 네트워크라고 볼 수 있습니다. 블록체인 내의 한 계정에서 다른 계정으로 자산을 이동하는 것은 어려운 일이 아닙니다. 허나 다른 블록체인으로 자산을 보내는 것은 일반적인 방법으로는 어렵습니다. 상태(state)를 공유하고 있지 않고, 계정 및 프로토콜 등 서로 다른 체계를 사용하기 때문입니다.

본 세션에서는 이종 블록체인 간 자산전송을 가능하게 하는 '브릿지'라는 개념을 소개합니다. 클레이튼의 경우 메인체인과 서비스체인 간의 자산이동이 어떻게 가능한지 기술적으로 설명합니다. 또한 다른 체인의 경우는 이를 어떤 식으로 구현하고 있는지 간략히 짚어봅니다.

발표자 : pepper.jang
크러스트유니버스에서 클레이튼 코어 데브팀에서 개발을 하고 있는 페퍼입니다. 다양한 블록체인 플랫폼의 기술을 존중하고, 이를 클레이튼 환경과 접목시키는 연결성 기술들에 관심이 많습니다.

kakao

December 09, 2022
Tweet

More Decks by kakao

Other Decks in Programming

Transcript

  1. : Klaytn 및 타 체인 사례 공유 Copyright 2022. Kakao

    Corp. All rights reserved. Redistribution or public display is not permitted without written permission from Kakao. 두 개의 서로 다른 블록체인은 어떻게 자산을 주고 받을까? 장형규 pepper.jang 크러스트유니버스 if(kakao)2022
  2. 발표자 소개 - HyungKyu (pepper) Jang - 그라운드X (2021) -

    크러스트 유니버스 (2022~) - 블록체인 코어 프로토콜 개발 - klaytn 서비스체인 관련 개발
  3. 서로 다른 블록체인 간의 자산교환, 가능할까? 클레이튼 서비스체인 소개 클레이튼과

    서비스체인 간 자산이동 클레이튼과 타 체인 간 자산이동
  4. 서비스 체인이란? - 메인체인과 별도로 운용되는 독립된 블록체인 - 클레이튼

    자체 Layer 2 solution - Klaytn main Chain (cypress) 와 interaction 할 수 있는 구조 - Data anchoring - Value Transfer Klaytn Public Main chain Klaytn ServiceChain Klaytn ServiceChain
  5. - Cost : 메인넷의 gas 비용이 부담될 때 - Security

    : 네트워크 접근을 제한하고 싶을 때 - Performance : 특정 서비스만을 위한 high TPS의 블록체인이 필요할 때 - Customization : - Network Topology (SCN/SPN/SEN) - Gas Price / Block Gas Limit / HardFork version - Consensus / Reward. - Test : 메인넷 별도의 체인에서 테스트를 해보고 싶을 때 Why Service Chain?
  6. Call function RequestKLAYTransfer of Bridge Contract Parameter : Bob address,

    amount of KLAY Input : Alice address, amount of KLAY Alice Bridge contract
  7. function _requestKLAYTransfer(address _to, uint256 _feeLimit, bytes memory _extraData) internal unlockedKLAY

    { require(isRunning, "stopped bridge"); require(msg.value > _feeLimit, "insuf fi cient amount"); uint256 fee = _payKLAYFeeAndRefundChange(_feeLimit); emit RequestValueTransfer( TokenType.KLAY, msg.sender, _to, address(0), msg.value.sub(_feeLimit), requestNonce, fee, _extraData ); requestNonce++; } Bridge Contract Main Bridge
  8. function _requestKLAYTransfer(address _to, uint256 _feeLimit, bytes memory _extraData) internal unlockedKLAY

    { require(isRunning, "stopped bridge"); require(msg.value > _feeLimit, "insuf fi cient amount"); uint256 fee = _payKLAYFeeAndRefundChange(_feeLimit); emit RequestValueTransfer( TokenType.KLAY, msg.sender, _to, address(0), msg.value.sub(_feeLimit), requestNonce, fee, _extraData ); requestNonce++; } Bridge Contract Main Bridge Event
  9. func (bm *BridgeManager) subscribeEvent(addr common.Address, bridge *bridgecontract.Bridge) error { tokenReceivedCh

    := make(chan *bridgecontract.BridgeRequestValueTransfer, TokenEventChanSize) tokenWithdrawCh := make(chan *bridgecontract.BridgeHandleValueTransfer, TokenEventChanSize) receivedSub, err := bridge.WatchRequestValueTransfer(nil, tokenReceivedCh, nil, nil, nil) bm.receivedEvents[addr] = receivedSub withdrawnSub, err := bridge.WatchHandleValueTransfer(nil, tokenWithdrawCh, nil, nil, nil) bm.withdrawEvents[addr] = withdrawnSub bridgeInfo, ok := bm.GetBridgeInfo(addr) bridgeInfo.subscribed = true go bm.loop(addr, tokenReceivedCh, tokenWithdrawCh, receivedSub, withdrawnSub) return nil } Bridge Contract Main Bridge Event subscribeEvent()
  10. func (bm *BridgeManager) subscribeEvent(addr common.Address, bridge *bridgecontract.Bridge) error { tokenReceivedCh

    := make(chan *bridgecontract.BridgeRequestValueTransfer, TokenEventChanSize) tokenWithdrawCh := make(chan *bridgecontract.BridgeHandleValueTransfer, TokenEventChanSize) receivedSub, err := bridge.WatchRequestValueTransfer(nil, tokenReceivedCh, nil, nil, nil) bm.receivedEvents[addr] = receivedSub withdrawnSub, err := bridge.WatchHandleValueTransfer(nil, tokenWithdrawCh, nil, nil, nil) bm.withdrawEvents[addr] = withdrawnSub bridgeInfo, ok := bm.GetBridgeInfo(addr) bridgeInfo.subscribed = true go bm.loop(addr, tokenReceivedCh, tokenWithdrawCh, receivedSub, withdrawnSub) return nil } Bridge Contract Main Bridge Event goroutine subscribeEvent()
  11. for { select { case <- bi.closed: return case ev

    := <- requestEventCh: bm.requestEventFeeder.Send(&RequestValueTransferEvent{ev}) case ev := <- handleEventCh: bm.handleEventFeeder.Send(&HandleValueTransferEvent{ev}) case err := <- requestEventSub.Err(): return case err := <- handleEventSub.Err(): return } } Bridge Contract Main Bridge Event goroutine Event subscribeEvent()
  12. Main Bridge Sub Bridge case ev := < sb.requestEventCh: sb.eventhandler.ProcessRequestEvent(ev);

    case ev := < sb.handleEventCh: sb.eventhandler.ProcessHandleEvent(ev); func (bi *BridgeInfo) AddRequestValueTransferEvents(evs []*RequestValueTransferEvent) { for _, ev := range evs { / / do some condition check } bi.SetRequestNonceFromCounterpart(ev.RequestNonce + 1) bi.pendingRequestEvent.Put(ev) } goroutine Event process Put into pool
  13. Main Bridge Sub Bridge func (bi *BridgeInfo) processingPendingRequestEvents() error {

    ReadyEvent := bi.GetReadyRequestValueTransferEvents() (Condition check..) if err := bi.handleRequestValueTransferEvent(ev); err ! = nil { bi.AddRequestValueTransferEvents(ReadyEvent[idx:]) return err } } return nil }
  14. Main Bridge Sub Bridge func (bi *BridgeInfo) handleRequestValueTransferEvent(ev *RequestValueTransferEvent) error

    { ..중략 switch tokenType { case KLAY: handleTx, err = bi.bridge.HandleKLAYTransfer(parameters..) case ERC20: handleTx, err = bi.bridge.HandleERC20Transfer(parameters..) case ERC721: handleTx, err = bi.bridge.HandleERC721Transfer(parameters..) }
  15. function handleKLAYTransfer(parameters..) public onlyOperators { / /update nonce logic emit

    HandleValueTransfer(parameters..); _to.transfer(_value); } Bridge contract Sub Bridge
  16. case ev := < sb.requestEventCh: sb.eventhandler.ProcessRequestEvent(ev); case ev := <

    sb.handleEventCh: sb.eventhandler.ProcessHandleEvent(ev); func (cce *ChildChainEventHandler) ProcessHandleEvent(ev *HandleValueTransferEvent) error { handleBridgeInfo, ok := cce.subbridge.bridgeManager.GetBridgeInfo(ev.Raw.Address) if !ok { return errors.New("there is no bridge") } handleBridgeInfo.MarkHandledNonce(ev.HandleNonce) handleBridgeInfo.UpdateLowerHandleNonce(ev.LowerHandleNonce) return nil } Bridge contract Sub Bridge
  17. function handleKLAYTransfer(parameters..) public onlyOperators { / /update nonce logic emit

    HandleValueTransfer(parameters..); _to.transfer(_value); } Bridge contract Sub Bridge Transfer to Bob
  18. 거의 비슷한 로직 - Native token인 KLAY의 경우 채굴에 의해

    수량이 조절 - FT, NFT의 경우 컨트랙트에 의해 mint,burn(발행 및 소각)을 함으로써 수량이 조절되게 할 수도 있고, 단순히 Bridge contract가 해당 체인에 보유하고 있는 토큰을 transfer 하는 로직을 수행할 수도 있 음 발행 및 소각 로직 Transfer 로직
  19. 대표적인 Value Transfer Failure Case - request에 대한 Receipt가 상대

    체인에 도착조차 안한 경우 - Receipt 가 도착했지만 실패로 처리된 경우
  20. Case 1 - request에 대한 Receipt가 상대 체인에 도착조차 안한

    경우 - 네트워크 Latency, peer connection issue 등 다양한 이유가 원인이 될 수 있음 - 이는 보내고자 하는 chain에다가 pending 되어있 는 트랜잭션을 전부 p2p로 재전송 함으로써 해결 func (sbh *SubBridgeHandler) broadcastServiceChainTx() { parentChainID := sbh.parentChainID txs := sbh.subbridge.GetBridgeTxPool().PendingTxsByAddress (&sbh.subbridge.bridgeAccounts.pAccount.address) peers := sbh.subbridge.BridgePeerSet().peers for _, peer := range peers { if peer.GetChainID().Cmp(parentChainID) != 0 { continue } peer.SendServiceChainTxs(txs) } }
  21. Case 2 func (vtr *valueTransferRecovery) Recover() error { err :=

    vtr.updateRecoveryHint() if err != nil { return err } err = vtr.retrievePendingEvents() if err != nil { return err } err = vtr.recoverPendingEvents() if err != nil { return err } - request에 대한 Receipt가 상대 체인에 도착했지 만 처리가 안되고 있는 경우 - Txpool , nonce 처리 과정에서 에러가 발생했을 가 능성이 있음 - Value Transfer Recovery logic이 실행됨 - 이 경우는 pending events를 전부 resend 함으 로써 해결할 수 있다. (Tx resend와는 다름)
  22. Klaytn node Service Chain Asset Asset Sub Bridge Main Bridge

    클레이튼 과 서비스체인 간.. P2P Event Listening
  23. Klaytn node Other Blockchain Other token KLAY Bridge Bridge 하지만

    Native하게 토큰을 옮기진 못한다.
  24. 따라서 타 체인간의 전송은.. - 이 미들웨어 서비스를 어떻게 구성하느냐에

    따라 솔루 션은 여러가지가 될 수 있다. - 서비스 철학에 따라 아키텍쳐는 여러가지로 달라질 수 있다. - 중요한 것은 체인간 전송 시 어떻게 ‘합의’를 볼 것이냐
  25. IBC (Inter Blockchain Protocol) - 각 체인은 상대 체인에 대한

    light node를 가지고 있 음 - 상대 체인형식에 맞는 데이터를 보낼 수 있음을 의미 Chain A Chain B Relayers Module A Module B IBC packets Channel
  26. IBC (Inter Blockchain Protocol) - transport layer - 하지만 상대

    체인에 직접 메세지 패킷을 보내지 않음 - 데이터 패킷을 상대 체인의 state에 맞춰서 commit Chain A Chain B Relayers Module A Module B IBC packets Channel - Relayer는 off - chain process이고 해당 데이터 에 대해서 지속적으로 관찰, 메세지를 선택하고 전달하 는 역할 - Relayer는 그 대가로 Incentive를 받을 수 있음
  27. IBC (Inter Blockchain Protocol) - application layer - 이러한 패킷

    형태로 보내게 되면 패킷을 받는 쪽에서는 패킷을 해석하는 부분만 신경쓰면 됨 - 패킷 내용은 토큰전송/ oracle / interchain query 등 다양할 수 있음. - 편지를 받고 내용 확인 후 판단 및 다음 action은 수신 자(application) 만이 알고, 우체국 및 배달원은 전송 (transport)만 신경쓰는 편지 배달 시스템과 유사 Chain A Chain B Relayers Module A Module B IBC packets Channel
  28. Orbit chain, Orbit Bridge - 합의의 핵심 - Multi -

    sig based transaction consensus(vault, minter) - 자체 체인에서 governance 동작
  29. Orbit chain, Orbit Bridge - Validator들이 각자 데이터를 operator를 통해

    orbit chain 으로 보냄 - Consensus 및 validation process는 orbit chain의 노드 운영 주체들이 담당 - Orbit Chain은 각 조각데이터를 이용해 체인에서 실행되는 트랜잭션을 구성하며, 합의가 완료되면 destination chain으로 mint, burn을 하는 요청 을 날림
  30. Wormhole Bridge Klaytn Guardians Ethereum Etc.. Bridge Contracts Emit VAA

    Pool of VAAS Publish Through Relayer (client) Other chain Other chain - Guardian은 각각의 체인의 full node를 띄워 bridge contract의 event log 를 watch - VAA는 Guardian의 signature가 포함된 Message payload - 2/3 이상의 signature가 valid해야 해당 approval이 valid하다고 간주 - 토큰 정보 등록, 전송 및 체인 정보와 같은 액션은 전 부 VAA를 통해 이루어짐
  31. 정리 - 서로 다른 블록체인 간의 자산교환을 위해서는 프로 토콜

    단에서의 지원 혹은 미들웨어 역할의 별도의 프 로그램이 필요하다. - 또한 브릿지 아키텍쳐는 체인 혹은 브릿지 프로그램 의 철학에 따라 달라질 수 있다.