배경
TON (오픈 네트워크)은 텔레그램 팀이 처음에 설계하고 개발한 탈중앙화 블록체인 플랫폼입니다. TON은 대규모 탈중앙화 애플리케이션(DApp)과 스마트 컨트랙트를 지원하기 위한 고성능의 확장 가능한 블록체인 플랫폼을 제공하는 것을 목표로 하고 있습니다.
TON은 사용하기 쉽고, 텔레그램과 긴밀하게 통합되어 있어 일반인들도 쉽게 토큰을 사용할 수 있다는 점에서 독특합니다. 동시에, 다른 블록체인과 차별화된 아키텍처로 복잡하며, FunC라는 비주류 스마트 컨트랙트 언어를 사용합니다.
오늘은 계정, 토큰, 트랜잭션의 관점에서 TON의 특징과 사용자 자산의 보안에 대해 알아보겠습니다.
TON의 특징
계정 생성
TON에서 계정 주소를 생성하는 방식은 대부분의 블록체인과 다른 스마트 컨트랙트 주소입니다. 첫째, 모든 것은 개인 키로 시작됩니다. TON은 주로 Ed25519 알고리즘을 사용하여 공개 키를 생성하며, 그 과정은 다음과 같습니다:
공개 키에는 두 가지 형태가 있습니다. 하나는 개인 키에서 계산된 원시 공개 키로, 다음과 같습니다:
E39ECDA0A7B0C60A7107EC43967829DBE8BC356A49B9DFC6186B3EAC74B5477D
다른 하나는 일부 정보와 체크 비트를 포함하는 “미화된” 공개 키로, 다음과 같습니다:
Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2
이더리움에서처럼 공개키만 있으면 계정 주소를 얻을 수 있다고 생각하신다면 착각입니다. 사용자의 공개 키만으로는 사용자의 계정 주소를 계산하는 데 충분하지 않습니다.
앞서 언급했듯이 사용자의 계정 주소는 스마트 컨트랙트 주소인데, 계정이 없는 상태에서 어떻게 스마트 컨트랙트를 배포할 수 있을까요?
올바른 순서는 먼저 주소를 계산하고 초기 토큰을 받은 다음 컨트랙트를 배포하는 것입니다. 계정 주소를 계산하는 과정은 다음 다이어그램에 나와 있습니다:
사용자 주소는 여러 가지 형태로 제공됩니다. 먼저 다음과 같은 원시 형태가 있습니다:
0:b4c1b2ede12aa76f4a44353944258bcc8f99e9c7c474711a152c78b43218e296
그런 다음 다음과 같은 사용자 친화적인 양식이 있습니다:
메인넷:
- 바운스 가능:
EQC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0MhjilkPX
- 반송 불가:
UQC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0Mhjilh4S
테스트넷:
- 바운스 가능:
kQC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0Mhjilvhd
- 반송 불가:
0QC0wbLt4Sqnb0pENTlEJYvMj5npx8R0cRoVLHi0MhjilqWY
이 주소를 자세히 관찰하면 처음과 마지막 몇 글자만 다르며 가운데 account_id
은 동일하다는 것을 알 수 있습니다.
그러나 여전히 공개 키와 계정 주소 간의 관계를 확인할 수 없습니다.
비밀은 시작 부분의 initial data
에 있으며, 여기에는 사용자의 공개 키가 포함되어 있어 사용자가 지갑 컨트랙트를 제어할 수 있습니다. workchainId
는 이해하기 쉽습니다. TON은 단일 체인이 아니라 여러 개의 샤드로 구성되어 있습니다.
각 샤드는 전체 네트워크의 일부로, 특정 계정과 트랜잭션을 처리합니다. 스마트 컨트랙트를 찾고 관리하려면 스마트 컨트랙트가 어느 샤드에 있는지 지정해야 합니다. Bounceable
과 Non-bounceable
의 차이점은 무엇인가요?
이는 스마트 계약이 작동하는 방식과 관련이 있습니다. 새 프리미어 가이드”>스마트 계약의 작동 방식과 관련이 있지만, 좀 더 자세히 살펴보도록 하겠습니다.
월렛 계약
아래는 사용자 지갑 컨트랙트의 소스 코드 스니펫입니다. 사용자로부터 메시지를 수신할 때 stored_seqno
, stored_subwallet
, public_key
, plugins
의 네 가지 파라미터를 읽는 것을 확인할 수 있습니다:
wallet-v4-code.fc
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
var cs = in_msg;
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
throw_if(36, valid_until <= now());
var ds = get_data().begin_parse();
var (stored_seqno, stored_subwallet, public_key, plugins) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256), ds~load_dict()); ;;##The Initial Data
ds.end_parse();
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, subwallet_id == stored_subwallet);
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
//...
}
실제로 이 사용자 지갑 컨트랙트를 배포할 때 256비트 public_key
을 포함한 몇 가지 초기 파라미터가 필요합니다. 이렇게 하면 동일한 컨트랙트 코드를 사용하더라도 모든 사용자가 고유한 컨트랙트 주소를 갖게 됩니다.
사용자가 시작한 모든 트랜잭션은 in_msg
로 서명한 다음 지갑 컨트랙트(check_signature)로 확인해야 하며, 그 후 컨트랙트가 블록체인에서 작업을 수행합니다. 이를 통해 사용자의 공개 키가 수많은 지갑 주소에 대응할 수 있다는 것을 추론할 수 있습니다.
다른 지갑 컨트랙트를 배포하거나 다른 초기화 데이터를 사용하면 완전히 다른 컨트랙트 주소가 생성됩니다.
제톤 토큰
토큰은 블록체인의 자산을 나타내므로 우리가 이해해야 할 기본 요소입니다. 제톤은 TON 토큰의 표준 형태이며 두 가지 컨트랙트로 구성됩니다: 제톤 채굴자와 제톤 지갑입니다.
토큰이 발행되면 제톤 마이너 컨트랙트가 생성됩니다. 이 컨트랙트 초기화에는 토큰의 총 공급량, 관리자, 지갑 코드 및 기타 세부 정보와 같은 정보가 기록됩니다.
토큰이 사용자에게 배포되면 민터 컨트랙트는 각 사용자에 대한 지갑 컨트랙트를 배포합니다. 컨트랙트를 초기화하는 동안 사용자의 잔액, 소유권, 토큰 민터 컨트랙트 주소, 사용자 지갑 코드 및 기타 관련 정보를 기록합니다. 각 사용자에게는 개별적으로 배포된 컨트랙트가 있습니다.
여기서 생성된 컨트랙트는 특정 제톤 토큰을 관리하기 위한 지갑 컨트랙트로, 사용자의 계정 지갑 컨트랙트와는 다르다는 점에 유의하시기 바랍니다. 여기에 기록된 owner_address
은 사용자 계정 지갑의 주소입니다.
사용자 앨리스가 사용자 밥에게 토큰을 전송할 때 호출 관계는 다음과 같습니다:
앨리스는 오프체인 앱을 통해 트랜잭션에 서명하고 지갑 컨트랙트를 통해 작업 명령을 보냅니다. 이 명령은 앨리스의 토큰 지갑을 호출하여 전송을 실행합니다. 밥의 토큰 지갑이 토큰을 받으면 밥의 지갑 컨트랙트(밥의 제튼 지갑의 소유자 주소)에 이를 알립니다.
거래 중에 남은 가스가 있는 경우 해당 주소(일반적으로 앨리스의 계정 계약)로 반환됩니다.
아래는 톤뷰어 브라우저에서 파싱한 Jetton 토큰 전송의 예시입니다:
ERC20 전송에는 단 한 번의 컨트랙트 호출만 필요하지만, 제튼 토큰 전송에는 최소 네 번의 컨트랙트 호출이 필요합니다. 이 방식은 온체인 동시성을 활성화하여 트랜잭션 효율성을 개선하도록 설계되었습니다.
거래
TON 계정에서 특정 이벤트가 발생하면 트랜잭션이 트리거됩니다. 가장 일반적인 이벤트는 “메시지 수신”입니다. 트랜잭션에는 다음 요소가 포함됩니다:
- 처음에 컨트랙트를 트리거하는 수신 메시지(특수 트리거 메서드 사용 가능)입니다.
- 수신 메시지로 인해 발생하는 컨트랙트 작업(예: 컨트랙트 저장소 업데이트(선택 사항)).
- 다른 참가자에게 보내는 발신 메시지(선택 사항).
거래와 관련하여 알아야 할 몇 가지 주요 기능이 있습니다:
- 비동기식: TON 트랜잭션은 한 번의 호출로 완료되지 않으며, 일련의 호출을 실행하기 위해 여러 개의 다른 스마트 컨트랙트에 메시지를 전달해야 할 수 있습니다. 샤딩된 체인의 라우팅이 다르기 때문에 TON은 여러 스마트 컨트랙트 간의 메시지 전달 순서를 보장할 수 없습니다.
- 수수료: 트랜잭션의 비동기적 특성으로 인해 소비된 수수료를 추정하는 데 어려움이 있습니다. 따라서 지갑은 트랜잭션을 시작할 때 수수료로 약간의 추가 토큰을 보내는 경우가 많습니다. 호출된 컨트랙트의 수수료 처리 메커니즘이 우수하다면, 남은 수수료는 사용자의 지갑으로 반환됩니다. 사용자는 지갑 토큰이 갑자기 감소했다가 몇 분 후에 다시 증가하는 것을 볼 수 있는데, 이는 이러한 메커니즘 때문입니다.
- Bounce: 바운스는 컨트랙트의 오류 처리 메커니즘입니다. 호출한 컨트랙트가 존재하지 않거나 오류가 발생하여 트랜잭션이 바운스 가능으로 설정된 경우, 호출한 컨트랙트에 바운스 메시지가 반환됩니다. 예를 들어, 사용자가 송금을 시작했는데 그 과정에서 오류가 발생하면 사용자의 지갑 컨트랙트가 잔액을 복구할 수 있도록 반송 메시지가 필요합니다. 스마트 컨트랙트 간에 전송되는 거의 모든 내부 메시지는 바운스할 수 있어야 하며, 이는 “바운스” 비트가 설정되어 있어야 함을 의미합니다.
자산 보안
TON에는 보안 문제를 일으킬 수 있는 많은 기능이 있으므로 사용자는 몇 가지 일반적인 함정을 알고 있어야 합니다.
수수료 원천 징수 공격
앞서 말씀드린 것처럼 지갑은 거래 실패를 방지하기 위해 약간의 추가 수수료를 보내야 하는 경우가 종종 있으며, 이는 공격자에게 기회를 제공합니다. TON 지갑 사용자라면 지갑에 다양한 NFT나 토큰이 자주 수신되는 상황을 경험해 보셨을 것입니다.
처음에는 쓰레기 토큰 에어드랍처럼 보일 수 있지만, 거래 내역을 확인해보면 상당한 금액에 판매할 수 있다는 것을 알 수 있습니다. 그러나 거래를 시작하려고 하면 필요한 수수료가 매우 높다는 것을 알 수 있습니다(1톤). 이 시점에서는 수수료 사기일 수 있으므로 주의해야 합니다.
공격자는 지갑이 과도하게 높은 송금 수수료를 예상하게 하는 정교한 토큰 계약을 만들지만, 실제로는 수수료만 보류되고 송금 메시지는 전송되지 않습니다.
앞자리 및 뒷자리 피싱
앞자리와 뒷자리 피싱은 TON에만 국한된 것이 아니라 많은 주요 블록체인에 존재하는 피싱 공격입니다. 공격자는 네트워크의 모든 사용자 주소에 대해 앞자리와 뒷자리가 동일한 위조 계정을 생성합니다.
사용자가 송금을 시작하면 공격자는 사용자의 거래에 이어 소액 송금을 전송하여 사용자의 거래 내역에 기록을 남깁니다. 수취인 사용자가 토큰을 다시 보내려고 할 때 거래 기록에서 공격자의 주소일 수 있는 주소를 복사하여 잘못된 주소로 송금할 수 있습니다. 공격자는 사용자의 행동을 정확하게 악용한 것입니다.
댓글 피싱
TON에서 토큰을 전송할 때 사용자는 트랜잭션에 주석을 달기 위해 코멘트를 추가할 수 있습니다. 이 기능은 거래소에 입금할 때 자주 사용되는데, 거래소는 보통 입금 댓글에 사용자 ID를 포함하도록 요구합니다. 그러나 이 기능은 사용자의 자산을 훔치기 위해 댓글에 사기성 정보를 작성하는 악의적인 행위자들에 의해 자주 악용되고 있습니다. 예를 들어
사용자는 “익명 텔레그램 번호” NFT에 대해 특히 주의를 기울여야 합니다. 사용자가 익명 텔레그램 번호로 텔레그램 계정을 활성화했지만 2단계 인증을 활성화하지 않았을 경우, 이 NFT가 피싱될 경우 해커는 직접 대상 텔레그램 계정에 로그인하여 자산 탈취 및 사기 행위를 진행할 수 있습니다.
스마트 컨트랙트 취약점
스마트 컨트랙트의 보안 취약점으로 인해 컨트랙트에 저장된 자금이 손실될 수 있습니다. 사용자는 철저한 감사를 거친 프로젝트를 선택해야 합니다. TON의 스마트 컨트랙트는 주로 FunC 언어를 사용해 프로그래밍되지만, 일부는 더 고급인 Tact 또는 더 낮은 수준의 Fift를 사용하기도 합니다.
이 모든 언어는 매우 독창적입니다. 새로운 프로그래밍 언어는 특히 개발자에게 새로운 보안 위험을 초래하며, 개발자는 프로덕션 환경에 배포하기 전에 안전한 코딩 습관을 연습하고 모범 보안 사례를 숙지하며 엄격한 보안 감사를 거쳐야 합니다. 지면 제약으로 인해 이 글에서는 계약 보안에 대해 자세히 설명하지 않습니다.
위조 입금 공격
지갑이나 거래소 사용자는 보통 두 가지 형태로 나타나는 가짜 입금 공격에 주의해야 합니다:
- 가짜 토큰: 공격자가 대상 토큰과 동일한 메타데이터를 가진 토큰을 발행합니다. 자동 입금 프로그램이 토큰이 올바른 마이너 컨트랙트에서 발행된 것인지 확인하지 않으면 잘못된 입금이 발생할 수 있습니다.
- Bounce: TON의 송금 프로세스에는 두 사용자의 지갑 컨트랙트 간 호출 관계가 필요합니다. 수취인의 지갑 계약이 존재하지 않고 트랜잭션이 반송 가능으로 설정되어 있으면 메시지는 반송되며 수수료를 제외한 원래 자금이 송금인에게 반환됩니다. 자세한 내용이 궁금하신 분들은 이전 글인 위조 입금에 관한 글을 참고하시기 바랍니다.
결론
이 글에서는 키와 지갑 생성, 토큰 형태, 트랜잭션 특성의 관점에서 TON의 기본적인 기술 원칙을 소개했습니다. 또한 TON을 사용할 때 발생할 수 있는 잠재적인 보안 문제를 살펴보며 학습 여정에 도움이 되기를 바랐습니다.