Cardano 개발자를 위한 과제


https://cexplorer.io/article/challenges-for-cardano-developers
개발자는 병렬화를 고려해야 하기 때문에 UTxO 모델을 최대한 활용하는 것이 어려울 수 있습니다. Cardano는 스마트 계약의 온체인 부분에서 단일 글로벌 애플리케이션 상태를 유지하는 것을 허용하지 않습니다. 각 UTxO는 애플리케이션 상태의 일부를 나타낼 수 있으며 독립적으로 병렬로 처리될 수 있습니다. 이는 이론적으로 높은 처리량과 확장성을 허용하지만 애플리케이션은 동시 트랜잭션 관리와 관련된 복잡성을 처리해야 합니다. Cardano에서 개발자는 어떤 과제에 직면하고 있나요?

병렬화가 쉬운 경우
eUTxO(Extended Unspent Transaction Output) 모델에서는 각 UTxO를 독립적으로 병렬로 처리할 수 있습니다. UTxO의 지출은 전 세계 Cardano 상태에 의존하지 않습니다. 지출 조건이 충족되면 입력 UTxO(또는 그 이상의 입력 UTxO)의 트랜잭션을 통해 하나 이상의 출력 UTxO가 생성됩니다. 입력 UTxO를 완전히 소비해야 합니다. 출력 UTxO는 입력 UTxO와 동일한(또는 더 작은) 값을 가져야 합니다.

Alice가 Bob에게 100 ADA를 보내는 경우 UTxO 지출은 Alice의 증인에만 의존합니다. 100명의 다른 사용자가 유사한 트랜잭션을 보내는 경우 100개의 다른 고유 입력 UTxO가 사용됩니다. 각 트랜잭션 발신자는 입력 UTxO의 소유자입니다. 트랜잭션과 입력 UTxO 간에는 종속성이 없습니다.

모든 발신자가 서로 독립적이기 때문에 트랜잭션과 입력 UTxO 간에는 종속성이 없습니다. 그들은 100 ADA를 보내기로 독립적인 결정을 내렸습니다. 100개의 거래는 모두 동일한 블록에 삽입될 수 있으며 유효한 것으로 평가됩니다.

트랜잭션은 하나 이상의 UTxO를 입력으로 사용하고 하나 이상의 새로운 UTxO를 출력으로 생성합니다. 이 간단한 규칙은 나중에 보게 되겠지만 Alice와 Bob 사이의 가치 이전은 물론 애플리케이션의 경우에도 적용됩니다.

그림에는 3개의 동일한 거래가 표시됩니다. 100개의 거래로 동일하게 보일 것입니다. 보낸 사람은 항상 Alice이고 받는 사람은 Bob이라는 사실에 혼동하지 마세요. 매번 다른 앨리스와 다른 밥입니다. 이 그림은 트랜잭션 발신자 간에 동기화가 필요하지 않다는 사실을 보여주기 위한 것입니다. 거래가 유효하면 실패할 수 없으며 모두 블록체인에 들어갑니다.

나중에 DEX의 작동 방식을 설명한 후에 그림의 의미를 깨닫게 될 것입니다.
图片
Cardano 네트워크는 거래가 서로 독립적이기 때문에 어떤 순서로든 거래를 검증할 수 있습니다. 이는 합의가 순차 처리에 의존하지 않기 때문에 네트워크 처리량 측면에서 장점이 됩니다.

병렬 애플리케이션을 만드는 방법은 무엇입니까?
이상적으로 애플리케이션 개발자는 100명의 발신자(앨리스)가 트랜잭션을 보낼 때와 유사하게 병렬화 측면에서 동작하는 스마트 계약 논리를 생성해야 합니다. 그러나 이것은 거의 불가능합니다.

유동성 풀을 사용하는 DEX의 예를 통해 이를 명확히 하겠습니다.

유동성 풀은 출력 UTxO를 생성하는 여러 트랜잭션으로 채워집니다. 이러한 UTxO는 유동성 풀의 토큰을 나타냅니다. 원장에 블록이 새로 추가될 때마다 유동성 풀의 UTxO 구성이 변경될 수 있습니다.

그림에는 토큰 X와 Y 쌍이 있는 유동성 풀이 있습니다. 유동성 풀에는 토큰 X와 Y가 있는 여러 UTxO가 있습니다. 다음 블록에서는 Alice, Bob, Carol이 토큰과 함께 새 UTxO를 넣습니다. X는 유동성 풀에 들어가고 Dave, Eve 및 Frank는 토큰 Y와 함께 일부 UTxO를 넣었습니다. 스왑이 발생하지 않았으므로 풀에서 UTxO가 제거되지 않았습니다.


스왑을 수행하려면 DEX는 각 토큰 유형에 대한 입력으로 유동성 풀에서 UTxO(또는 더 많은 UTxO)를 소비해야 합니다. 이러한 UTxO는 완전히 소비되어야 합니다. 스왑에 UTxO의 모든 토큰이 필요하지 않은 경우 나머지 토큰은 새 UTxO로 유동성 풀에 반환되어야 합니다.

DEX의 경우 많은 작업이 동시에 진행됩니다. 사용자는 유동성 풀에 토큰(유동성 공급자)을 보냅니다. 공급자는 발신자이고 DEX는 수신자입니다. 동시에 다른 사용자도 스왑 요청을 보낼 수 있습니다. 그들은 X 토큰을 DEX에 주고 Y 토큰을 받고 싶어하는 발신자입니다. 혹은 그 반대로도.

여러 참가자가 짧은 시간 내에 교환 요청을 제출할 수 있습니다. 이러한 스왑은 단일 유동성 풀과 관련될 수 있습니다. 따라서 참가자들 사이에는 상호의존성이 존재합니다.

DEX가 정확히 무엇인지 명확히 하는 것도 좋은 생각입니다. DEX와 같은 Cardano의 복잡한 스마트 계약은 블록체인에서 실행되는 온체인 로직과 서버(또는 로컬 지갑)에서 실행되는 오프체인 로직이라는 두 부분으로 구성됩니다.

사진에서는 온체인 로직과 오프체인 로직으로 구성된 DEX를 볼 수 있습니다.
图片
온체인 로직의 실행은 Cardano 네트워크에서 발생하므로 자연스럽게 분산화되는 반면, DEX의 오프체인 로직의 분산화는 팀이 담당합니다. DEX의 오프체인 부분은 단 하나의 에이전트로 구성되는 것이 아니라 여러 에이전트로 구성됩니다.

DEX의 경우 이러한 에이전트를 배처(Batcher)라고 합니다. 그들은 스왑 실행을 담당합니다. Batcher는 유동성 풀에서 UTxO를 지출하는 조건을 충족하는 트랜잭션을 생성하고 두 스왑 참가자가 요청한 비율로 자산을 전송합니다.

Cardano는 스마트 계약의 온체인 부분에서 균일한 글로벌 애플리케이션 상태를 유지하는 것을 허용하지 않습니다. 하지만 기술적으로는 가능합니다.

개발자가 DApp의 전체 상태를 단일 UTXO에 저장한다면 본질적으로 이더리움의 계정 기반 모델에 존재하는 것과 유사한 전역 상태를 생성하게 될 것입니다. 이는 DApp의 동시성과 처리량을 제한할 수 있습니다. 이 접근법은 EUTxO 모델의 이점을 완전히 활용하지 못합니다.

DEX의 온체인 부분에서 UTxO 및 관련 데이텀은 애플리케이션 상태를 나타냅니다. 따라서 상태는 UTxO에 분산됩니다. DEX가 균일한 글로벌 애플리케이션 상태를 가지려면 여러 배처에서 오프체인으로 유지되어야 합니다.

그림에서 토큰 X, Y와 3개의 배처가 있는 유동성 풀을 볼 수 있습니다. 전역 애플리케이션 상태와 Batcher 간 상태 동기화는 파란색으로 표시됩니다. 애플리케이션 상태는 UTxO와 관련된 데이텀인 온체인 데이터와 배처(에이전트)가 유지 관리하는 오프체인 애플리케이션 상태로 구성됩니다. Batcher는 서로 통신하여 균일한 전역 애플리케이션 상태를 동기화합니다.


이는 배처(에이전트)가 동일한 리소스에 액세스하고 이것이 유동성 풀이기 때문에 필요합니다. 유동성 풀의 입력 UTxO를 사용해야 하며(스왑을 실행하기 위해) 두 개 이상의 에이전트가 동일한 UTxO를 사용하려는 경우가 발생할 수 있습니다. 경합 문제가 발생할 수 있습니다.

이제 기사의 첫 번째 이미지를 기억할 차례입니다. 100명의 발신자가 트랜잭션을 제출하면 각 발신자가 자신의 UTxO를 사용하므로 동일한 UTxO에 대해 서로 경쟁할 수 없습니다. 유동성 풀의 UTxO는 공유 리소스, 즉 여러 에이전트가 액세스하는 리소스입니다.

일반적으로 경합은 여러 스레드 또는 프로세스(이 경우 에이전트)가 동일한 리소스에 액세스하려고 시도하여 그 중 적어도 하나가 다른 프로세스가 실행되지 않을 때보다 더 느리게 실행되는 시나리오를 나타냅니다.

우리의 경우 두 에이전트가 유동성 풀에서 동일한 입력 UTxO를 사용하는 트랜잭션을 구성할 위험이 있습니다. 이 경우 Cardano에서는 단 한 번의 거래만 허용됩니다. 두 번째는 실패합니다.

그림에서는 Batcher 1과 Batcher 3이 토큰 X와 동일한 UTxO를 사용하려고 하는 것을 볼 수 있습니다. 경합이 발생했습니다. Batcher는 분명히 동기화가 제대로 이루어지지 않았으며 이 특정 UTxO를 사용하려는 서로의 의도를 알지 못합니다. 2개의 스왑 트랜잭션이 생성되면 하나는 성공하고 다른 하나는 실패합니다.

DEX의 목표는 스왑이 동시에 실행되도록 하는 것입니다. 즉, 개별 에이전트가 동시에 트랜잭션을 구성할 수 있고 UTxO를 선택할 때 경합이 발생하지 않도록 하는 것입니다.

트랜잭션 실패를 방지하려면 에이전트 간 오프체인 통신이나 다른 형태의 동기화가 있어야 합니다. 즉, 에이전트는 DEX의 일관된 전역 상태를 유지해야 합니다.

개별 에이전트는 다른 에이전트가 동일한 UTxO를 사용하지 않도록 어떻게든 풀에서 UTxO를 예약해야 합니다. 또는 각 다음 블록(20초) 내에서 모든 트랜잭션이 단일(무작위로 선택된) 에이전트에 의해 구성되는 방식으로 작동할 수 있습니다. 이 접근 방식은 분산되어 있지만 동시성이 적습니다.

그림에서 볼 수 있듯이 Batcher 1과 Batcher 3은 스왑을 위해 독점적인 방식으로 토큰 X와 Y가 있는 UTxO를 선택했기 때문에 경합이 없었습니다. 스왑 1과 2가 동시에 실행됩니다. 모든 Batcher가 전역 상태를 서로 동기화했기 때문에 경합이 없었습니다.


토큰 X는 토큰 Y의 시장 가치의 정확히 2배를 가지며 우연히도 페어링을 위해 유동성 풀에 적합한 UTxO가 있었습니다. 스왑 1은 X 토큰 100개와 Y 토큰 50개를 소비합니다. 스왑 2는 200 X 토큰과 100 Y 토큰을 소비합니다. 풀에 100개의 X 토큰이 있는 UTxO가 없는 경우 두 번째로 가장 적합한 것은 114개의 X 토큰이 있는 UTxO입니다. 이는 14개의 X 토큰이 새로운 UTxO로 유동성 풀에 반환되어야 함을 의미합니다.

이 기사에서 더 이상 논의되지 않는 다른 과제 중 하나는 스왑을 위한 UTxO를 적절하게 선택하는 것입니다. Ethereum에서는 기본적으로 계정 잔액만 업데이트되므로 이는 문제가 되지 않습니다.

유동성 풀을 이용하는 것과는 전혀 다른 방식으로 문제를 생각해 볼 수도 있습니다. UTxO를 하나의 풀에 넣는 대신 개별 스왑 후보를 연결하는 것이 가능합니다. 그러나 단순화를 위해 이 기사에서는 유동성 풀을 고수하겠습니다.

분산화를 유지하면서 UTxO를 병렬로 처리할 수 있는 Cardano에서 DEX를 설계하려면 동시성 문제를 해결해야 합니다. 이는 개발자의 과제 중 하나입니다.

이 기사에서 우리는 가능한 솔루션 중 하나, 즉 오프체인 및 온체인 구성 요소의 사용을 보여주었습니다. 오프체인 구성요소는 온체인 코드와 상호 작용하는 트랜잭션을 올바르게 구성하는 데 사용될 수 있습니다. 애플리케이션의 전역 상태를 동기화할 수 있는 오프체인 통신을 통해 정확성이 보장됩니다.

다른 가능한 접근 방식 중 하나는 사용자에게 원하는 작업을 제출할 수 있는 독점적인 액세스 권한을 부여하는 알고리즘을 만드는 것입니다. 이후 알고리즘은 타이밍과 공정성을 고려하여 모든 작업을 병합할 수 있습니다.

개발자는 애플리케이션의 온체인 부분에서 단일 상태를 유지하거나 여러 UTxO에 걸쳐 분할할 수 있습니다. 단일 온체인 상태를 갖는 것은 일관성을 유지하는 것이 더 쉽기 때문에 쉽습니다. 애플리케이션의 모든 부분은 동일한 데이터로 작동합니다. 여러 UTxoS에 걸쳐 온체인 상태를 분할하면 동시성을 높일 수 있지만 몇 가지 과제가 있습니다. 여러 UTxO를 관리하면 스마트 계약 논리가 복잡해집니다. 정확성(경합 방지)을 보장하는 일부 형태의 동기화를 보장해야 합니다.

문제의 핵심은 분산된 환경에서 병렬화를 달성하는 데 있습니다.

애플리케이션 로직은 항상 UTxO에 연결됩니다. 각 UTxO는 병렬로 처리될 수 있는 독립적인 상태 부분을 나타냅니다. 기사에서 설명했듯이 이는 신뢰할 수 있는 동기화 형식이 구현된 경우에만 가능합니다.

단일 배치 처리기 또는 오프체인 에이전트만 있는 경우 동시성 문제에 대해 걱정할 필요 없이 DEX 상태를 관리하고 트랜잭션을 준비할 수 있습니다. 이는 잠재적으로 더 빠른 트랜잭션 처리와 더 높은 처리량으로 이어질 수 있습니다.

그러나 이러한 접근 방식은 본질적으로 DEX의 오프체인 부분을 중앙 집중화하게 되며 이는 분산화 원칙에 어긋납니다. 따라서 과제는 높은 성능을 유지하고 동시성 문제를 피하면서 오프체인 분산화를 달성하는 것입니다.

이더리움 개발자들도 어려움에 직면해 있습니다
Ethereum은 계정 기반 모델을 사용하며 스마트 계약은 거래에 의해 업데이트되는 전역 상태를 갖습니다. 글로벌 상태는 이더리움 블록체인의 특정 주소에 있는 코드(함수)와 데이터(상태)입니다.

DEX의 글로벌 상태는 모든 공개 매수 및 매도 주문을 포함하여 주문장의 현재 상태를 나타낼 수 있습니다. 새로운 스왑 트랜잭션이 제출되면 이는 이 전역 상태에 대한 잠재적인 변경을 나타냅니다. 그러나 이 변경 사항은 거래가 블록에 포함되고 네트워크에서 검증된 후에만 허용됩니다.

이더리움의 거래는 한 번에 하나씩 순차적으로 처리됩니다. 이는 이더리움 세계에는 동시성이 없으므로 동시성 문제가 없음을 의미합니다. 이러한 순차 처리를 사용하면 개발자가 동시 트랜잭션 관리의 복잡성을 처리할 필요가 없기 때문에 동시성 및 병렬성과 관련하여 Ethereum에서 DEX를 설계하는 것이 더 간단해집니다.

그러나 이 순차적 유효성 검사는 동시성을 활용하지 못합니다. 트랜잭션을 병렬로 실행하는 것은 계약 간에 종속성이 있을 수 있으므로 안전하지 않습니다. 하나의 계약이 다른 계약의 결과에 의존하는 경우 해당 계약은 모든 검증자가 동일한 순서로 실행해야 합니다.

블록 내 거래 순서는 검증인에 의해 결정됩니다. GAS 가격, 임시값, 최초 확인 시간 등의 요소를 기준으로 거래를 정렬하도록 선택할 수 있습니다. 따라서 DEX는 많은 스왑 대기열을 생성할 수 있지만 이러한 스왑이 어떤 순서로 실행될지는 확신할 수 없습니다.

이러한 불확실성은 서로 다른 거래가 동일한 유동성을 소비하기 위해 경쟁하는 경쟁 조건으로 인해 거래가 실패하는 상황으로 이어질 수 있습니다. 이를 처리하기 위해 일부 DEX는 미끄러짐 허용 오차 및 트랜잭션 마감일과 같은 메커니즘을 구현하여 트랜잭션이 성공적으로 실행될 가능성을 높입니다.

여러 사용자가 동시에 토큰을 교환하려고 하면 경쟁 조건이 발생할 수 있습니다. 예를 들어 두 명의 사용자가 모두 ETH를 USDT로 교환하기를 원하고 유동성 풀에 스왑 중 하나를 수행할 만큼 충분한 USDT만 있다고 가정해 보겠습니다. 두 사용자 모두 거의 동시에 스왑 거래를 제출합니다. 이더리움 검증자는 이러한 거래가 블록에 포함되는 순서를 결정합니다.

사용자 A의 거래가 먼저 포함되면 스왑이 진행되고 풀에 사용자 B의 스왑을 위한 충분한 USDT가 남지 않게 됩니다. 이더리움 네트워크가 사용자 B의 거래를 처리하려고 시도하면 스왑을 이행할 수 없기 때문에 실패합니다.

결과는 둘 이상의 작업(스왑)의 상대적인 타이밍에 따라 달라집니다. Ethereum이 트랜잭션을 순차적으로 처리하더라도 여러 트랜잭션이 공유 리소스(예: DEX의 유동성 풀)에 의존하고 동시에 제출될 때 경쟁 조건이 발생할 수 있습니다.

유동성 풀이 하나의 DEX로만 관리되는 경우에도 경쟁 조건이 발생할 수 있습니다. 이는 경쟁 조건이 DEX 자체에 의한 것이 아니라 트랜잭션 처리 특성에 의해 발생하기 때문입니다.

즉, 이더리움의 애플리케이션은 트랜잭션이 처리되어야 하는 특정 순서를 선호할 수 있지만 이는 자체적으로 제어할 수는 없습니다. 카르다노의 경우 선착순으로 거래가 처리됩니다. 그러나 풀은 이 규칙을 따를 필요가 없으며 풀이 mem-pool에서 트랜잭션을 올바르게 선택하도록 강제하는 메커니즘이 없다는 점을 언급할 필요가 있습니다.

두 플랫폼 모두에서 개발자가 직면하는 과제를 간략하게 비교해 보겠습니다.

Cardano에서 DEX 또는 기타 분산형 애플리케이션을 구축할 때 과제 중 하나는 여러 UTXO 및 에이전트에서 상태를 관리하고 동기화하는 것입니다. 이를 통해 처리량과 확장성이 향상되지만 동시 트랜잭션 관리와 관련된 복잡성이 추가로 발생합니다.

애플리케이션은 오프체인 상태를 관리하고 업데이트한 다음 주기적으로 상태를 블록체인에 커밋할 수 있습니다. 이 접근 방식은 블록체인의 부하를 줄이고 거래 속도를 높이는 데 도움이 될 수 있습니다. 또한, 오프체인 동기화는 온체인 트랜잭션의 병렬 실행을 촉진할 수도 있습니다. DEX는 여러 트랜잭션을 병렬로 준비한 다음 실행을 위해 블록체인에 제출할 수 있습니다.

빠른 오프체인 동기화는 잠재적으로 온체인의 높은 확장성과 병렬성으로 이어질 수 있습니다. 개발자는 동기화를 통해 가능한 최대 병렬화를 달성하려고 노력합니다.

이는 전체 애플리케이션의 상태가 단일 전역 상태에 저장되는 이더리움의 계정 기반 모델과 다릅니다. 전역 상태는 DEX에 필요한 모든 데이터와 트랜잭션을 통한 변경 사항을 보유할 수 있습니다. 이더리움은 트랜잭션(호출)을 순차적으로 처리하기 때문에 개발자는 동시성에 대해 전혀 걱정할 필요가 없습니다.

그러나 문제는 이더리움의 DEX에서 동일한 유동성을 소비하려는 사용자의 행동으로 어떤 형태의 병렬성이 표현된다는 것입니다. 그들은 기본적으로 동일한 UTxO를 소비하려는 배처와 유사한 단일 리소스를 위해 싸우고 있습니다.

Ethereum DEX 개발자는 동일한 유동성을 소비하려는 여러 사용자와 관련된 경쟁 조건을 완전히 방지할 수 없습니다. 유동성 풀의 상태(즉, 사용 가능한 유동성의 양)는 블록체인에 포함된 거래에 따라 결정됩니다. 이더리움은 트랜잭션을 순차적으로 처리하고 순서는 검증인에 의해 결정되기 때문에 사용자는 자신의 트랜잭션이 성공할지 실시간으로 알 수 없습니다.

개발자는 사용자가 실패 가능성이 높은 트랜잭션을 제출하지 못하도록 일부 메커니즘을 만들려고 할 수 있습니다. 그러나 이것은 매우 어려운 작업입니다.

Cardano는 Ethereum과 달리 결정론적으로 동작합니다.

결론
UTxO 모델과 병렬화를 최대한 활용하려면 Ouroboros Proof-of-Stake를 개선해야 합니다. Cardano 네트워크는 한 번에 많은 수의 거래를 검증하고 사전 승인할 수 있어야 합니다. 20초에 한 번씩 수십 건의 트랜잭션이 블록에 삽입된다면 병렬 처리가 가능하더라도 문제가 되지 않습니다. 확장성은 여전히 상대적으로 낮습니다. 입력 승인자는 UTxO 모델이 훨씬 더 빛을 발할 PoS 합의에 필요한 개선을 가져올 것입니다.