이전 글에서는 비트코인이 해결한 문제들에 대해서 정리해 보았다. 이번 글에서는 이제 브트코인 시스템에서 트랜잭션이 어떻게 이루어지는지, 트랜잭션은 어떻게 구성되어 있는지 정리해 보려고 한다.
1. 비트코인 시스템 내부
그전에, 비트코인 내부에서 BTC가 어떻게 지불되고 사용되는지 간단히 정리해 보려고 한다. 다음과 같은 상황을 가정해보자.
- Alice가 Bob에게 5BTC를 지불하려고 한다.
이런 상황에서는 어떤 순서로 지불 절차가 일어나게 될까? 먼저, 지불을 하기 위해서는, Alice는 Wallet을 통해 private key와 public key(해시과정을 거쳐 address로 사용될 예정)를 만들어야 한다. 그 후, Alice는 다음과 같은 절차로 BTC를 지불한다.
- Alice는 transaction request message(Bob에게 5BTC를 지불하겠다는)에 private key를 이용해 서명을 한다.
- 이때, 서명은 네트워크 참여자들이 Alice가 지불하려는 코인이 진짜 Alice 소유였는지 검증하는 데 사용된다.
- 그 후, Alice는 Bitcoin network에 해당 트랜잭션을 broadcast한다.
- 이 트랜잭션을 블록체인 채굴자가 보고 블록에 포함시켜야 하는데, 이때 검증 과정이 필요하다.
- 이 검증은 채굴자가 진행하는데, 증명해야 될 것은 다음과 같다.
- Alice가 지불하려는 BTC가 실제 존재하는지(이전에 쓰이지 않은 UTXO)인지 Alice의 서명을 Alice의 public key에 의해 검증되는지 확인한다.
- 만약, 일치한다면, 해당 트랜잭션을 블록에 포함시키고 블록에 포함시킨다.
2. UTXO와 STXO
위에서, UTXO라는 단어가 나왔다. 이 UTXO라는 것은 무엇일까?
UTXO는, Unspent Transaction Output의 줄임말로, 말 그대로, 아직 사용되지 않은 트랜잭션의 Output이다. 현재 비트코인 시스템에서는 이러한 모든 미사용 출력들을 모은 UTXO set이 존재하고, 이 집합이 바로 비트코인 전체의 현재 잔액 상태를 나타낸다. 반대로 S이미 한 번 사용된 트랜잭션 아웃풋은 STXO로, Spent Transaction Output이다.
비트코인 시스템에서, Wallet이라는 Software가 블록체인 상에서 해당 지갑의 개인키로 소비할 수 있는 UTXO를 발견했을 때, bitcoin을 받았다고 인식한다.
예를 들어, Alice가 Bob에게 5BTC을 보내고 그 트랜잭션이 블록체인에 기록되면, 5 BTC가 Bob의 주소로 가는 새로운 Output이 생겨난다. 그리고 Bob의 Wallet이 블록체인을 스캔하면서, 이 UTXO가 자신이 가진 key로 해제 가능한 것임을 확인하면, 그 준산 Bob이 5BTC를 받았다고 판단하고 잔액에 반영한다.
트랜잭션 Output은 정수 단위로 표현되고, 가장 단위는 1 satoshi이고, 1BTC = 10^8 satoshi이다.
UTXO는 새로운 트랜잭션의 Input으로 사용될 때 전체 금액이 한 번에 소비된다. 즉, UTXO를 부분적으로 쪼개서 일부만 사용하는 것은 불가능 하다.
예를들어, UTXO가 5BTC이고, 지불하려는 금액이 0.1 BTC이고 해당 UTXO를 쓰고 싶을 때, 0.1BTC로 쪼개서 쓸 수 없다는 것이다. 이때, 그럼 나머지는 어떻게 해야 되냐면, 0.1BTC를 지불하려는 트랜잭션에 나머지 4.9BTC을 본인의 새로운 address로 보내는 Output을 같이 쓰면 된다.
+) 웬만하면 transaction fee를 남기는게 좋아서 4.5BTC를 새롭게 만든 address로 보내고 0.4BTC를 transaction fee로 지불하는 게 좋다. Transaction fee가 없다면, 채굴자들이 해당 트랜잭션을 블록에 포함시키지 않게되고, 그럼 트랜잭션이 포함되기 전까지 유효하지 않으며, 지불이 일어나지 않아 물건을 사지 못할 수도 있기 때문이다.
실제 트랜잭션의 모습을 살펴보자. 실제 트랜잭션 모습은 다음과 같다.

그림에 대해 설명하면,
- 트랜잭션 Id가 hash가 반영된 채로 존재한다.
- Input은 이전 트랜잭션 Id:이전 트랜잭션 Ouput Index와 UTXO 양을 적는다.
- Output은 어디에 지불할지와 얼마나 지불할지를 적는다.
이런 과정을 거치면 새로운 UTXO가 생겨나고, 이 UTXO는 또 다른 트랜잭션의 Output으로 사용되며, 최종적으로 트랜잭션도 chain구조를 가지게 된다.
+) 트랜잭션의 Input과 Output은 각각 여러 개 일 수 있다.
2. 트랜잭션 broadcast 과정
그럼 이 트랜잭션이 어떻게 전파되고, 블록에 포함되는지 한 번 알아보자. 다음 상황을 한 번 가정해보자.
- Alice가 Bob에게 비트코인을 지불하려고 한다.
비트코인 시스템에서 지불은 결국 네트워크의 모든 full node들이 이제 이 코인이 Bob의 소유다라고 동의하게 만드는 것이다. Alice는 full node를 납득시키기 위해 Transaction이라는 데이터를 생성하고, 이를 통해 사실 증명을 한다.
Alice는 full nodes를 납득시키기 위해서 먼저, UTXO가 본인 소유라는 것을 증명해야 된다. 이를 위해 Input 필드에 다음 정보를 포함시킨다.
- outpoint: Alice가 이전에 받은 트랜잭션 output을 참조하는 정보
- txid : output index 형태로 표현된다.
- signature: Alice의 private key로 서명한 데이터
- 이 서명은 해당 output을 소비할 권한이 Alice에게 있음을 증명한다.
즉, input은 이전 트랜잭션에서 내가 받은 UTXO를 끌어다 쓰는 방식이다.
트랜잭션이 만들어지면, Alice는 이를 비트코인 네트워크에 broadcast한다. 이때, 트랜잭션은 여러 노드로 퍼져 나가며, full node는 이 트랜잭션을 검증하고, 문제가 없으면 임시 저장해 블록에 넣을 계획을 한다.
+) 먼저 full node에 대해 잠깐 설명하면, Gensis Block ~ 현재 블록까지 다 가지고 있으면서 트랜잭션이 유효한지 검증할 수 있는 노드라고 할 수 있다. 모든 채굴자가 full node를 가지고 있어야 하는 것은 아니다. 채굴공장을 운영한다고 했을 때, full node 역할을 하는 컴퓨터는 한 개, 그리고 이 full node가 해당 트랜잭션이 유효하다고 판단하고 다른 컴퓨터들에 해시값 계산 작업을 분배하는 방식으로 운영할 수 있다.
그럼, 이 Full node가 검증해야 하는 것들을 살펴보자.
- 먼저, 이 Full node는 모든 UTXO를 알고 있어야 한다. 그래서, Alice가 사용하려는 UTXO가 UTXO set에 포함되는지 어떤 알고리즘을 통해 검증해야 한다.
- 또, Input이 Output보다 큰지 확인해야 한다. 즉, 지불하려는 금액이 끌어온 금액보다 작은지 검증해야 한다.
- 다음으로 Script가 유효한지 검사해야 한다.
- 그리고 마지막으로 timelock이라는 정보를 검증해야 하는데, 이는 차후 더 자세히 정리할 예정이다.
3. 트랜잭션 구조
다음은 트랜잭션이 어떤 format으로 되어 있는지 살펴보자. 트랜잭션은 크게 Input부분, Output 부분, Witness Structure 부분으로 나눌 수 있는데, 차례차례 살펴보자.
- Input
먼저, Input 부분이다. Input 부분은 다음과 같이 구성된다.

각 필드에 대해서 설명하면,
- previous output txid: 이전 트랜잭션 id를 적는 부분이다.
- previous output index: 해당 id의 몇 번째 Output인지 index번호로 나타내는 부분이다.
- Length: 전체길이를 나타내는 부분이다.
- Sequence 필드
이제, 조금 더 자세히 알아볼 필드에 대해서 정리해 볼 예정이다. 바로 Sequence 필드인데, 원래 사토시 나카모토가 의도한 sequence 필드는 같은 트랜잭션의 여러 버전을 구분하기 위한 번호로 설계되었다. 즉, 하나의 트랜잭션을 여러 번 수정하거나 갱신할 수 있도록 하는 수단이었다. 다음 예시를 통해 살펴보자.
- Alice가 카페를 운영 중인 Bob에게 커피를 사먹으려고 하는데, Alice는 하루에 두 번, 커피를 사먹는다.
- Bob의 카페는 비트코인으로만 pay를 받는다.
이런 상황에서는, Alice는 커피를 사먹을 때마다 트랜잭션을 발생시켜야 되고, transaction fee를 지불해야 한다. 이런 경우, 커피값보다 트랜잭션 fee가 더 나가는 경우가 있는데, 이렇게 되면, 커피를 비트코인으로 팔 이유도, 살 이유도 없어진다.
따라서, 이 경우에는 둘 다 사용할 수 있는 Output에 서명한다. 즉, 한 마디로 공동 계좌를 만드는 것이다
- 예를 들어, Alice가 10만원, Bob이 10만원을 끌어와서 하나의 Output으로 모아두고, Alice와 Bob 둘 다 서명을 한다.
- 이것이 의미하는 바는 나중에 이 Output을 사용할 때, 두 명의 서명이 있어야 한다는 것이다.
이제, 이 Output을 이용해 여러 임시 트랜잭션을 만들 수 있다.
- seq # = 0이면 refund transaction으로 환불용이다.
- seq # = 1은 첫 번째 커피 결제용 트랜잭션, seq # = 2는 두 번째 커피 결제용 트랜잭션,.. 등으로 점차 sequence 번호가 증가한다.
이런 식으로 동일한 Input으로 사용하고 서로 다른 sequence 번호를 부여해 여러 버전의 트랜잭션을 만들어 둘 수 있다. 이때, sequence 번호가 더 큰 트랜잭션이 더 최신 버전이라고 간주되어 이전 버전을 대체할 수 있도록 설계할 수 있다.
+) 만약, Alice가 seq # = 1인 트랜잭션을 broadcast해도 이미 더 높은 seq #가 존재한다면, 이전 버전은 인정되지 않는다.
이러한 방법은 악용될 가능성이 많다. 따라서 이런 원래의 버전 seq #는 사용되지 않는다. 그 대신, Sequence 필드를 Relative Timelock 방식으로 사용한다.
Relative Timelock 방법에 대해 정리하면 해당 output이 언제 블록체인에 포함되고 얼마나 시간이 지나서 새로운 Input으로 사용될 수 있을지 timelock을 거는 것과 비슷하다. sequence 필드는 다음과 같이 sequence가 encoding 되는데, 0~31 즉, 총 32bit로 인코딩 된다.

정리하자면,
- Disable Flag: 0이면 relative time lock을 사용하겠다, 1이면 사용하지 않겠다는 것이다.
- Type flag: Time lock을 사용할 경우, 1일때, 하위 0~15비트는 512 초의 배수이고, 0이라면, 0~15비트는 블록 수를 나타낸다.
즉, 정리하면, 만약 이 sequence encoding이 2^31보다 크거나 같다는 것은 relative timelock을 사용하지 않겠다는 뜻이고, 2^31보다 작다는 것은 relative timelock을 사용하겠다는 뜻이다.
+) 보통은 relative timelock을 사용하지 않는다.
- Output
이제 다음으로 살펴볼 부분은 Output이다. Output은 다음과 같이 구성된다.

정리하면,
- Amount: 지불할 총액을 적는다.
- length: 총 길이를 적는다.
- Output Script: 어디에 지불할 지 코딩한 부분이다.
보통 위의 구조가 반복되어 여러 Output을 한 트랜잭션 포맷에 포함될 수 있게 한다. 즉, 파란색 부분 Amount ~ Output script까지 Output index #1, 그 다음에 흰색 부분 Amount ~ Output script까지가 Output index #2 로 사용된다.
- Witness Structure
다음으로 알아볼 부분은 Witness Sturcture이다. 기존 버전의 트랜잭션(legacy transaction)의 포맷에 따르면, signature와 다른 데이터들이 input format에 들어가게 된다. 그런데, 이 필드들이 input에 들어가게 된다면, 다음과 같은 문제가 생긴다.
- Circular Dependencies
- Third-party transaction malleability(=타인의 트랜잭션 ID를 변경할 수 있는 문제)
- Second-party transaction malleability(=당사자가 서명을 바꿔 txid를 바꿀 수 있는 문제)
각 문제점에 대해서 정리해보려고 한다.
- Circular Dependencies
먼저, Circular Dependecies이다. 이 문제상황을 설명하기 위해 위의 Alice, Bob 예시를 그대로 사용하겠다. Alice와 Bob은 공동 서명을 통해 커피를 사고 돈을 받으려고 하는데, 다음과 같은 상황이 발생한다. (다만, Seq #는 기존 버전을 사용하지 않는다.)
- T_x0(트랜잭션 0번)
- Alice와 Bob이 각자 10만원 씩 끌어와 공동으로 사용할 output을 만들기 위해 서명한다. 이 Output을 사용하기 위해서는 Alice와 Bob 둘의 서명이 다 필요하다.
- T_x1(트랜잭션 1번)
- Alice와 Bob이 서로가 10만원을 넣고 사용 중, 한 측이 도망가 돈을 환불받기 위해 공동 서명을 하는 트랜잭션이다.
- 그래서 Alice와 Bob은 미리 T_x1에 서명을 해둔다.
- 그러나 T_x1이 유효하려면, T_x1이 참조하는 outpoint(T_x0의 txid와 output index)가 있어야 한다. 즉, T_x1은 T_x0의 txid를 알아야만 유효할 수 있다.
그런데 이때, T_x1을 미리 만드려면 T_x0의 txid가 필요하다. 그런데 legacy transaction format에서는 트랜잭션 txid 계산에 input 의 서명이 포함된다. 따라서, T_x0이 먼저 확정되어야 한다. 따라서 서로가 서로를 기다려야 하는 Circular Dependencies 문제가 생긴다.
- Third-party transaction malleability
다음은 third-party transaction malleability 문제이다. Output으로 지정했다고 가정해보자.
- 2 OP_ADD 4 OP_EQUL
이 Output을 만족하는 Input을 제시하면, 이에 해당하는 트랜잭션의 비트코인을 사용할 수 있게 된다. 위의 Output script를 만족하는 Input script 예시는 다음과 같다.
- OP_2
- OP_PUSH1 0x02
이때, 위의 두 Input은 서로 txid가 다르다. 둘은 다른 문자열이기 때문에, 해시값도 다르고, 서로 다른 Input이 된다. Input script와 Output script는 스택 베이스로 계산이 이루어지기 때문에 예를 들어 OP_2를 제시했다면 다음과 같이 진행된다.

즉 마지막, 4와 4가 EQUL하기 때문에 true로 반환하고, 스택은 비게 되고, 이 output을 풀 수 있는 답을 input으로 제시하면 아무나 output을 새로운 트랜잭션의 input으로 쓸 수 있다는 것이다.
예를들어, Alice가 위의 Output script와 OP_2를 이용해 Bob에게 pay를 했고, Bob이 즉시 그 output을 사용해 Carol에게 지불했다고 가정하자. 근데, 누군가가 OP_PUSH1 0x02를 이용해서 해당 ouput을 끌어와 새로운 transaction의 input으로 사용했고, 해당 트랜잭션이 블록에 포함되었다고 가정해보자. 그럼 Alice의 pay는 인정되지 않고, Bob이 지불한 내용도 무효가 되는 문제가 발생한다는 것이다.
- Second-party transaction malleability
다음으로는 second-party transaction malleability이다. 위 예시와 똑같이 Alice와 Bob이 공동 서명을 했다고 해보자.
T_x0은 Alice의 10BTC와 Bob의 10BTC를 Input으로 하며, Output은 Alice가 3BTC을 Bob의 address로 보내는 Output 0, 17BTC(공동 계좌)의 Output 1이 존재한다.
이때, 17BTC에 대해서 다시 Alice와 Bob이 공동서명을 진행했다. 그 후, refund를 보장받기 위해 T_x1에 대해 Alice와 Bob이 공동 서명을 진행했고, Alice는 사인이 완료된 이후이기 때문에 안심하고 T_x0를 먼저 비트코인 네트워크에 broadcast했다.
그런데, ECDSA의 특성을 잘 아는 Bob은 똑같은 private key로 T_x1에 했던 서명과 또 다른 서명을 만든 후, 이를 T_x0에 다시 포함시켜 새로운 T_x0*을 비트코인 네트워크에 전달한다.
그 후, T_x0*이 T_x0보다 먼저 블록에 포함되어 유효한 트랜잭션이 된다면, T_x1은 T_x0의 txid를 참조하는 트랜잭션이기 때문에, 원래의 T_x0이 블록체인에 포함되지 않았으므로 무효한 트랜잭션이 된다.
이렇게 되면, Alice가 안전장치로 만들어 두었던 T_x1(refund transaction)이 의미가 없어지며, 이는 심각한 문제로 이어진다. 즉, 서명 데이터가 트랜잭션 ID(tx_id)에 포함되어 있다면, 위와 같은 문제가 발생할 수 있다는 것이다.
4. Segregated Witness
위에서 정리한 3가지 문제들을 해결하기 위해 등장한 개념이 바로 Segregated Witness이다. 이 Segregated Witness는 트랜잭션의 txid를 만드는 계산에 input script가 포함되는 것을 피하기 위해 고안된 방법이다.
기존에는 새로운 트랜잭션을 만들 때, 이전 트랜잭션의 UTXO인 Output을 끌어와 이를 Input으로 명시하고, 그 안에 서명을 함께 포함시켰다. 이 경우, 서명이 포함된 input script 내용이 txid 계산에 영향을 미치기 때문에 서명이 바뀌면 txid도 달라지게 된다. 그 결과, 앞서 설명한 세 가지 문제(Circular Dependencies, Third-party transaction malleability, Second-party transaction malleability)가 발생하게 된다.
이를 해결하기 위해 witness라는 새로운 필드를 도입했다. 즉, 기존의 input script에 들어 있던 서명 데이터를 별도의 witness 필드로 분리하여, txid 계산 시에는 이 데이터를 완전히 제외하도록 설계한 것이다. 이로써 서명 데이터가 달라져도 트랜잭션의 ID는 변하지 않게 된다.
이 Segwit이라는 것이 비트코인 시스템에 새로 도입될 때, 기존 규칙과의 호환성(backward-compatible)을 유지하기 위해 Soft Fork 방식으로 적용되었다. 이전에 Segwit이 존재하지 않는 트랜잭션도 기존 방식 그대로 유효하게 업데이트 되었다.
- Segwit을 인식하지 못하는 노드(old nodes)도 Segwit 트랜잭션을 형식상 유효한 트랜잭션으로 본다.
- 다만, old nodes는 witness 데이터를 보지 못하기 때문에 이러한 트랜잭션을 사실 Anyone-Can-Spend 스크립트처럼 해석하지만, 실제로는 블록 내부의 witenss 영역에서 검증이 이루어지므로 안전하다.
- 반면 Segwit 노드는 witness 데이터를 검증하며, 새로운 규칙을 적용한다.
- 시간이 지나면서 대부분의 노드와 채굴자들이 SegWit을 지원하게 되고 Segwit 트랜잭션이 표준 형태로 자리 잡는다.
Segwit을 포함한 구조는 다음과 같다.
- 옛 구조: pubkey로 비트코인이 전달되고 서명을 통해 사용되었다.
- 바뀐 구조: output scripts로 비트코인이 전달되고 input scripts로 사용된다. 즉, witness 프로그램으로 비트코인이 전달되고 witness structure을 통해 사용된다.
Segwit의 soft fork 관점에서 정리하면, Segwit 활성화 후, Segwit 노드들은 old transaction 자체는 검증할 수 있지만, 합의 프로토콜에서 Segwit 규칙을 따르지 않는 블록들을 canonical chain 후보로 선택하지 않는다. 따라서 Segwit이 대세가 되면 non-Segwit 노드가 만든 블록들은 네트워크 canonical chain에서 밀려나게 되고, 네트워크에서 생존하려면 Segwit 규칙을 지원하도록 업데이트를 해야 한다.
5. Coinbase Transactions
Coinbase Transaction은 기존에 없던 비트코인을 새로 만들어 채굴자에게 보상으로 지급하는 특별한 트랜잭션이다. 따라서 일반적인 트랜잭션과 달리 이전 UTXO를 참조할 필요가 없기 때문에 input이 사실상 필요 없다.
Coinbase Transaction 특징에 대해서 정리하면 다음과 같다.
- 단 한개의 input이 존재한다.
- 이 input은 이전 트랜잭션을 참조하지 않기 때문에 txid = null, output index = 0xffffffff로 설정된다.
- 이는 coinbase 트랜잭션이 기존 트랜잭션의 output을 참조하는 일을 원천적으로 막기 위한 규칙이다.
- input script 필드를 coinbase라고 부르며 최소 2bytes ~ 최대 100bytes 범위의 임의 데이터가 들어갈 수 있다.
- output은 여러 개가 존재할 수 있다.
- output의 총합은 block reward와 해당 블록의 모든 트랜잭션 수수료이다.
- Coinbase 트랜잭션의 output은 100 confirmations 전까지 사용할 수 없다.
- 즉, 100 confirmations 이전의 coinbase output은 immature(미성숙) 상태이며, 다른 트랜잭션의 input으로 사용 할 수 없다.
6. Weight and Bytes
앞서, 비트코인 각 블록은 1MB로 제한되어 있다고 정리했다. 그렇지만, 트랜잭션 증가로 인해 기존 1MB 제한만으로는 충분한 처리량을 확보하기 어려워졌고, 이를 해결하기 위해 블록 크기를 확장하자는 다양한 의견이 등장했다. 이 과정에서 두 가지 방향이 생겼다.
- 1MB 제한을 완전히 제거하자는 주장
- 1MB 제한을 없애고, 블록 크기를 여러 MB로 확장하자는 의견이 나왔다.
- 이는 기존 노드와의 호환이 불가능해 hard fork로 이어졌으며, 그 결과 Bitcoin cash라는 새로운 체인이 탄생했다.
- 기존 규칙(1MB 제한)을 유지하면서 동시에 처리량을 늘리자는 의견
- 기존 비트코인은 하드 포크를 거부하고 대신 Segwit으로 witness 데이터를 분리해 블록 구조 공간 효율을 높였고,
- 기존 byte 기반 블록 크기 제한을 버리고 weight 기반 제한으로 변경했다.
즉, 기존 비트코인에 대해서 더 정리하면, Segwit 이후 비트코인은 1MB bytes 제한 대신 4,000,000 weight 제한이라는 새로운 합의 규칙을 사용한다.
Witness structure에 포함되는 Segwit 구조에서는 트랜잭션은 다음과 같은 두 부분으로 구성된다.
- Base data(Non-witness Data, witness data 제외한 모든 트랜잭션 데이터)
- Witness data(서명 등 증명 데이터)
Segwit에서는 각 영역에 Base data 1byte = 4 weight, witness data 1byte = 1 weight 와 같은 서로 다른 weight를 부여한다.
Weight 방식 덕분에 블록의 실제 byte 크기는 1MB를 넘을 수 있게 되었으며, witenss 영역까지 포함하면 최대 약 4MB에 해당하는 데이터까지 담을 수 있는 효과를 낸다.
즉, 기존의 1MB 블록 제한은 더 이상 consensus rule에서 적용되지 않으며, 현재 비트코인은 4M weight 이하인지만을 기준으로 블록 유효성을 판단한다.
'학교공부 > [블록체인]' 카테고리의 다른 글
| [블록체인] - 비트코인_Consensus (0) | 2025.12.11 |
|---|---|
| [블록체인] - 비트코인_채굴 (1) | 2025.12.11 |
| [블록체인] - 비트코인 (0) | 2025.12.11 |
| [블록체인] - Cryptography for Blockchains (0) | 2025.12.11 |
| [블록체인] - 비트코인의 기술적 특징 (0) | 2025.10.12 |