충분히 분산된 소셜 네트워크, Farcaster

Kimdokin
CURG
Published in
18 min readJul 8, 2024

서민균 | Researcher of CURG | kimdokin0.0@gmail.com

크립토 씬에서 활동하는 사람이라면, Degen 토큰을 통해 더욱더 핫해진 파캐스터(Farcaster)에 대해서 모두들 한 번쯤은 들어봤을 것이다. 파캐스터는 옵티미즘을 기반으로 하는 “충분히” 분산된 소셜 네트워크 프로토콜로 현재 약 37만명의 유저를 거느리고 있다. 본 아티클에서는 이렇게 큰 이슈가 되고 있는 파캐스터가 어떻게 동작하고 왜 “충분히” 탈중앙화 되어있다고 표현하고 있는지에 대해서 간단히 살펴보도록 하겠다.

1. 파캐스터의 등장

파캐스터의 빌더 중 한 명인 Varun Srinivasan은 2022년 자신의 블로그에 “Sufficient Decentralization for Social Networks”라는 주제로 글을 올렸다. Sufficient Decentralized Social Networks, 바로 파캐스터를 정의하는 문장이다. 이 글을 통해 Varun은 중앙집중화된 소셜 네트워크가 매년 사용자와 개발자에게 더 많은 제한을 가하며, 그들은 선택을 제한하는 것이 건강한 네트워크로 가는 길이라고 믿지만, 실제로는 그렇지 않을 가능성이 큼을 지적한다.

사용자에게는 자신의 팔로워들과 직접적인 관계를 맺을 수 있는 탈중앙화 소셜 네트워크가, 개발자에게는 자유롭게 앱을 개발할 수 있는 탈중앙화 소셜 네트워크가 필요하다. 이러한 배경 속에서 탄생한 파캐스터는 네트워크 접근을 탈중앙화하여 기업이 독점적이거나 사용자를 무시할 수 없도록 한다. 이는 최고의 아이디어가 동등한 조건에서 경쟁할 수 있는 환경을 조성하여 사용자와 개발자 모두에게 이익을 제공한다.

2. 파캐스터의 구성 요소

파캐스터는 블록체인을 활용한 소셜 네트워크로, 여러 코어 컨셉을 가지고 있다. 구성 요소로는 Accounts, Usernames, Messages, Frames, Channels, Apps가 있는데, 이 중 핵심적이라고 생각되는 Accounts, Messages, Frame, Apps를 하나씩 훑어보도록 하자.

Accounts 즉, 계정은 일반적인 소셜 앱들에서와 마찬가지로 유저에게 연동된 그 “계정”에 해당한다. 계정 생성은 일반적인 블록체인의 Account를 기반으로 만들어지는데, EVM 기반의 Address에 파캐스터의 ID, 즉 FID가 할당되는 구조이다. 이 FID 할당은 온체인 상에서 이루어지며 IdGateway 컨트랙트를 통해 진행된다. 파캐스터는 하나의 어플리케이션 레벨이 아니라 프로토콜 개념에 가까운데, 파캐스터 측에서 만든 클라이언트인 워프캐스트(Warpcast)를 사용하면, 파캐스터 용 지갑과 EVM 기반 주소, 계정을 손쉽게 생성할 수 있다.

다음으로 메시지(Messages)이다. 먼저 파캐스터 네트워크에서 게시할 수 있는 메시지가 무엇이 있는지 부터 알아보자.

https://docs.farcaster.xyz/learn/what-is-farcaster/messages

쉽게 생각하면 소셜 어플리케이션에서 할 수 있는 액션들은 모두 메시지에 해당한다고 할 수 있다. 파캐스터는 하나의 “네트워크”이기 때문에 파캐스터 네트워크 위에서 구동할 수 있는 앱이나 클라이언트들은 여러 종류가 있는데, 유저들은 이 앱을 통해 위와 같은 액션을 취하고, 메시지를 전파할 수 있는 것이다.

https://docs.farcaster.xyz/learn/what-is-farcaster/frames

프레임(Frame)은 위 메시지 중 캐스트(Casts)를 상호 작용이 가능한 앱(interactive app) 형태로 변환할 수 있는 표준이다. 텍스트 기반으로 캐스트를 올리는 것 대신에, 이미지와 버튼이 포함된 프레임을 게시함으로써 다른 유저들과 더 다양한 상호작용을 기대할 수 있다. 프레임을 어떻게 구현하느냐에 따라 온체인 활동 역시 할 수 있기에 현재 NFT 민팅과 같은 기능으로도 활용되고 있다. 파캐스터는 프레임 출시 이후 수많은 유저들을 확보하며 폭발적인 상승세를 보여주었다.

https://www.farcaster.xyz/

Apps은 말 그대로 “앱”, “애플리케이션”으로 파캐스터 네트워크 위에서 작동하는 유저들이 사용할 수 있는 서비스를 칭한다. 앱은 지갑 앱(Wallet App)연결된 앱(Connected App)으로 나뉘어지는데, 지갑 앱은 온체인 상에서의 계좌를 생성하고 관리하는 기능을 포함하며, 연결된 앱은 지갑 앱과는 다르게 계정 생성 혹은 계정 관리 행동과 관련되지 않은 캐스트 작성, 계정 팔로우 등의 기능만을 수행할 수 있다.

가장 유명한 앱은 파캐스터 개발진들이 만든 파캐스터 클라이언트인 워프캐스트(Warpcast)로, 유저들은 일종의 트위터처럼 캐스트를 작성하고 서로 팔로우, 좋아요, 댓글 등을 달면서 상호 작용할 수 있다. 또한 위에서 언급한 프레임을 만들어 캐스트와 같이 하나의 게시글로 올릴 수도 있다.

3. 파캐스터의 아키텍처

위에서 살펴본 내용 중에 지갑 앱과 연결된 앱이 분리되는 이유가 궁금할 것이다. 파캐스터는 완전히 분산된 네트워크가 아니라 “충분히” 분산된 네트워크로, 온체인 상에서 처리되는 지갑 관련 데이터들과 오프체인 상에서만 처리되는 데이터들이 분리되어 있다. 즉, 온체인과 오프체인을 겸하는 하이브리드 아키텍처를 지니고 있는데 간단한 구조를 그림으로 살펴보면 다음과 같다.

https://docs.farcaster.xyz/learn/architecture/overview

오프체인 상에서 수행 및 저장되는 내용들은 대부분 소셜 네트워크 활동 즉, 메시지와 관련된 것으로, 다음과 같이 정리해 볼 수 있다.

새로운 공개 메시지 게시
다른 사용자 팔로우
게시물에 반응(좋아요, 댓글)하기
프로필 사진 업데이트

데이터 저장에 모두 비용이 들어가는 블록체인의 특성상 메시지가 활발히 교류되는 소셜 네트워크 활동들은 데이터 저장에 큰 비용이 들어갈 수밖에 없다. 또한 모든 정보를 블록체인에 저장하게 되면 필연적으로 데이터를 불러오는 과정에서도 블록체인과 소통할 수밖에 없는데, 이 과정에서도 시간이 많이 소모된다. 파캐스터는 이러한 활동들을 모두 오프체인 상에 저장함으로써 큰 비용 절감 효과와 성능 향상에 대한 이점을 가져간다.

이와 반대로 온체인 상에 저장되는 내용은 조금 더 보안적인 측면이 강조되는 “계정” 관련 내용들이다.

계정 생성
데이터 저장을 위한 임대료 지불
연결된 앱을 위한 계정 키 추가

파캐스터는 이런 온체인 정보들을 옵티미즘 메인넷(이하 OP 메인넷)에 배포된 컨트랙트를 통해 처리하고 있으며, 소수의 활동들만 온체인과의 상호작용을 통해 진행한다. 여기서 “데이터 저장을 위한 임대료 지불” 부분에 의문을 가질 수 있다. 파캐스터에서 메시지라고 정의한 유저 액션들을 기억해 보자. 캐스트를 올리는 것, 좋아요를 누르는 것, 팔로우를 하는 것 등 모두 하나의 “데이터”로 오프체인 상에 저장되게 된다. 이때, 파캐스터에서는 계정 하나마다 이 “데이터 저장”에 대한 조건으로 돈을 지불해야 한다. 점차 많은 돈을 낼수록 더 많은 데이터를 저장할 수 있게 되며, 만약 데이터 저장 크기가 한도에 다다르게 되면 제일 오래된 데이터부터 지워지면서 새로운 메시지로 대체된다.

4. 조금 더 심도 있게 살펴보기

5–1. 파캐스터의 오프체인 아키텍처: Hub

https://docs.farcaster.xyz/learn/architecture/hubs

허브(Hub)는 파캐스터의 데이터를 검증 및 저장하는 분산 네트워크로, 파캐스터 상에 앱을 구축하려면 허브와 상호작용해야만 한다. 각 허브는 API를 통해 접근할 수 있는 모든 파캐스터의 데이터들을 저장하며, 옵티미즘 컨트랙트를 통해 모든 사용자들의 온체인 데이터 역시 저장하고 있다.

어떤 유저가 파캐스터 상의 앱에서 새로운 메시지를 쓰면, Alice는 자신의 App Key로 이 메시지에 서명하게 된다. 이후 이 메시지는 파캐스터 허브 노드 한 개로 전달되는데, 허브에서는 이 메시지를 받아 검증(Validate), 저장(Store), 복제(Replica) 과정을 거쳐 다른 피어 허브에게 메시지를 전파하게 된다.

[검증(Validate) 과정]

  • 정말 그 유저의 App Key를 사용해 서명 한 것이 맞는지 검증
  • 받은 메시지가 프로토콜의 요구 사항에 맞는지 검증
https://github.com/farcasterxyz/protocol/blob/main/docs/OVERVIEW.md

파캐스터가 정의하는 오프체인 단의 허브는 블록체인보다는 P2P 네트워크에 가깝지만, 특이하게도 사용자 인증(Authentication)을 위해 메시지마다 서명을 수행한다. 이때, 유저가 앱으로 메시지를 작성하면 해당 유저의 Key로 메시지 서명을 할 수도 있지만, 파캐스터에서는 앱에서 메시지 서명에 대한 권한을 위임 받은 App Key를 통해 대신 메시지에 서명을 할 수도 있다. 이 App Key는 온체인 상에 저장되어 있으며, 유저 ID - 앱 쌍마다 App Key가 부여되기에 유저 스스로 자신의 키를 이용해 서명을 진행하지 않아도, 서명에 사용된 App Key를 통해 간접적으로 특정 유저가 해당 메시지를 작성한 것이 맞는지 인증할 수 있다.

[저장(Storage) 과정]

  • 유저의 데이터 저장 크기가 한도에 다다랐는지 확인
  • 만약, 한도를 넘어섰다면 저장 공간 확보를 위해 제일 오래된 메시지부터 삭제
  • 이미 허브에 받은 이 메시지가 있는지 확인
  • 이미 허브에 이 메시지가 저장 되었다가, 삭제 요청으로 삭제되었는지 확인
https://github.com/farcasterxyz/protocol/blob/main/docs/OVERVIEW.md

앞서 검증이 끝나면 허브에 유저의 메시지가 저장되게 되는데, 이때 이 메시지에는 텍스트, 메타데이터 등이 포함되어 있으며 이 컨텐츠들의 해시값을 이용해 식별된다. 아무래도 P2P 네트워크다 보니까 메시지가 저장될 때 메시지 충돌(Conflict) 역시 검사하게 되는데 이를 위해 파캐스터에서는 리소스 ID(Resource ID)를 포함한다. 예를 들어, 사용자 123의 이름을 보여주는 메시지는 “identifier123.display_name”이라는 식별자를 포함하게 된다. 만약, 많은 메시지들이 같은 식별자를 갖고 있다면 파캐스터에는 시간 순으로 가장 최신의 메시지만을 유지하게 된다.

[복제(Replicate) 과정]

  • Gossip: 메시지 저장과 동시에 즉시 피어 허브에게 해당 메시지 전파
  • Diff Sync: 주기적으로 임의의 피어를 선택해 저장된 데이터 비교
https://github.com/farcasterxyz/protocol/blob/main/docs/OVERVIEW.md

파캐스터 허브들은 이 복제 과정을 통해 메시지를 전파하고, 싱크를 맞추면서 하나의 일관된 상태를 유지하게 된다. 이때, 메시지의 일관성을 유지하기 위해 간단한 규칙을 통한 CRDT(Conflict-Free Replicated Data Type) 기반의 메시지 그래프를 사용한다. 사용자가 다양한 앱을 통해 여러 허브에 메시지 업데이트 요청을 보내도, 결국엔 모든 메시지가 전파되고 규칙에 맞춰 자신들의 메시지 저장 상태를 업데이트하므로 결국 모든 허브가 하나의 일관된 상태로 다다르게 된다.

5–2. 파캐스터의 온체인 아키텍처: Contract

파캐스터는 오프체인과 온체인을 결합한 하이브리드형 아키텍처로, 현재 온체인 부분은 OP 메인넷을 사용하고 있다. 크게 세 가지의 핵심 스마트 컨트랙트가 배포되어 있다.

https://docs.farcaster.xyz/learn/architecture/contracts

Id Registry 새로운 계정 생성
Storage Registry 계정당 저장 공간 관리 및 저장 공간 임대료 수취
Key Registry 계정마다 연동된 App Key 관리

유저가 파캐스터에 계정을 등록하게 되면, IdRegistry의 register 함수를 통해 계정당 FID 하나씩 부여된다. 또한 특이하게도 계정마다 자신의 계정을 복구할 수 있는 다른 복구 주소 역시 설정할 수 있다.


function register(address to, address recovery) external whenNotPaused returns (uint256 fid) {
if (msg.sender != idGateway) revert Unauthorized();

/* Revert if the target(to) has an fid */
if (idOf[to] != 0) revert HasId();

/* Safety: idCounter won't realistically overflow. */
unchecked {
/* Incrementing before assignment ensures that no one gets the 0 fid. */
fid = ++idCounter;
}

_unsafeRegister(fid, to, recovery);
}

function _unsafeRegister(uint256 id, address to, address recovery) internal {
idOf[to] = id;
custodyOf[id] = to;
recoveryOf[id] = recovery;

emit Register(to, id, recovery);
}

컨트랙트 구현 내용을 살펴보면, 굉장히 간단하게 쓰여진 것을 알 수 있다. 등록하고자 하는 주소와 복구 주소를 함께 매개 변수로 전달하면, 내부적으로 fid가 1씩 증가하면서 부여되고 내부 매핑 구조를 통해 주소와 fid, 복구 주소 등이 서로 연결된다.

유저가 파캐스터에 자신의 주소를 등록한 뒤, 실제 메시지를 작성하고 허브 내에 이를 저장하기 위해서는 저장 공간을 임대해야 한다. 이를 위해 StorageRegistry 컨트랙트의 rent 함수를 사용한다.


function rent(
uint256 fid,
uint256 units
) external payable whenNotDeprecated whenNotPaused returns (uint256 overpayment) {
// Checks
if (units == 0) revert InvalidAmount();
if (rentedUnits + units > maxUnits) revert ExceedsCapacity();
uint256 totalPrice = _price(units);
if (msg.value < totalPrice) revert InvalidPayment();

// Effects
rentedUnits += units;
emit Rent(msg.sender, fid, units);

// Interactions
// Safety: overpayment is guaranteed to be >=0 because of checks
overpayment = msg.value - totalPrice;
if (overpayment > 0) {
msg.sender.sendNative(overpayment);
}
}

먼저 앞서 부여된 고유한 fid와 대여할 유닛 개수를 입력하면 이에 맞춰 유저가 지불해야 하는 totalPrice가 계산된다. 만약 유저가 이 함수를 호출하면서 적절한 만큼의 ETH(=Native Token)을 지불했다면 해당 유닛 개수만큼 유저가 사용할 수 있는 저장 공간의 크기가 늘어난다.

이후 메시지를 허브에게 보내기 위해서는 먼저 메시지에 서명을 진행해야 한다. 위에서 유저 스스로 서명을 하기보다는 유저마다 할당된 App Key를 이용해 서명하는 경우가 더 많을 것이라고 언급했었다. 이때 앱마다 부여하는 App Key를 저장하는 컨트랙트가 바로 KeyRegistry이다.

    function add(
address fidOwner,
uint32 keyType,
bytes calldata key,
uint8 metadataType,
bytes calldata metadata
) external whenNotPaused {
if (msg.sender != keyGateway) revert Unauthorized();
_add(_fidOf(fidOwner), keyType, key, metadataType, metadata);
}

function _add(
uint256 fid,
uint32 keyType,
bytes calldata key,
uint8 metadataType,
bytes calldata metadata,
bool validate
) internal {
KeyData storage keyData = keys[fid][key];
if (keyData.state != KeyState.NULL) revert InvalidState();
if (totalKeys(fid, KeyState.ADDED) >= maxKeysPerFid) revert ExceedsMaximum();

IMetadataValidator validator = validators[keyType][metadataType];
if (validator == IMetadataValidator(address(0))) {
revert ValidatorNotFound(keyType, metadataType);
}

_addToKeySet(fid, key);
keyData.state = KeyState.ADDED;
keyData.keyType = keyType;
emit Add(fid, keyType, key, key, metadataType, metadata);

if (validate) {
bool isValid = validator.validate(fid, key, metadata);
if (!isValid) revert InvalidMetadata();
}
}

간단하게 매개 변수를 살펴보자. keyType과 key는 추가할 키에 대한 내용을 의미한다. key는 추가할 서명 키 중 공개 키에 해당하고, keyType은 해당 키가 어떤 방식으로 만들어졌는지를 의미한다. 현재는 ECDSA 방식의 한 가지 타입만 제공하고 있다.

이 함수의 호출 결과로 유저의 fid에 하나의 App Key가 추가되며, 해당 App key와 연관된 앱은 사용자 데이터에 접근할 수 있는 권한을 얻게 된다.

파캐스터는 이렇게 온체인과 오프체인을 결합한 하이브리드 아키텍처로 구현되어 있다. 오프체인 단에서는 중앙화된 서버 대신 P2P 네트워크를 활용하고 민감한 계정 관련 데이터는 온체인 단에서는 옵티미즘에 배포한 컨트랙트를 이용해 저장한다. 이로 인해 파캐스터 사용자들은 어떤 허브든지 접근하여 데이터를 가져올 수 있으며, 앱들은 다른 데이터에서 쓰여진 데이터라도 허브를 통해 모든 데이터를 연동하여 한 번에 확인할 수 있다. 또한 파캐스터 네트워크 상에서 앱을 개발하고자 하는 개발자들은 검열 없이 자유롭게 개발을 진행하고 원하는 앱을 배포할 수 있게 되었다.

마치며

파캐스터는 완전히 탈중앙화된 블록체인 네트워크는 아니지만, P2P 네트워크와 온체인 컨트랙트를 함께 사용함으로써 사용자 입장에서 충분히 분산화를 이뤄낸 것으로 보인다. 이를 통해 사용자들은 더 많은 자유와 유연성을 누릴 수 있으며, 개발자들은 검열의 제약 없이 혁신적인 앱을 개발할 수 있는 환경이 마련되었다.

많은 사람들이 $DEGEN으로 파캐스터를 알게 되었겠지만, 현재 파캐스터는 다양한 빌더들과 함께 생태계를 점차 확장 시키고 있다. 소셜 네트워킹을 내세우는 앱인 Supercast, Yup 부터 파캐스터 프레임을 위한 프레임워크 Flog까지 다양한 분야로 빌더들이 활동하고 있다. 현재도 계속해서 성장 중이기 때문에 파캐스터 생태계는 점차 풍부해질 것으로 기대된다.

참고자료

https://docs.farcaster.xyz/

https://github.com/farcasterxyz/protocol/blob/main/docs/OVERVIEW.md

https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md

https://www.varunsrinivasan.com/2022/01/11/sufficient-decentralization-for-social-networks

--

--