Segwit과 P2WPKH

Segwit

Segwit 기초

Segregated Witness (SegWit)은 2015년 BIP-141 "Segregated Witness (Consensus layer)"에 의해 제안되었으며, 2017년 실제로 비트코인 네트워크에 도입되었다. SegWit은 Transaction malleability 문제를 해결하고, Block Size를 증가시켜 한 블럭 안에 더 많은 트랜잭션을 넣기 위해 구현되었다. SegWit은 또한 기존의 비트코인 주소와의 차별성을 위해 Bech32 인코딩 방식을 도입하였다.

Transaction Malleability 문제

Transaction Malleability란 하나의 트랜잭션이 여러 개의 Transaction Hash (txid)를 가질 수 있는 것을 말한다. (Malleability란 외부의 힘에 의해 변형되거나 영향을 받을 수 있는 성질을 일컫는다.)

비트코인에서 Transaction ID (txid)는 트랜잭션의 데이타를 Hashing하여 얻게 되는데, 트랜잭션 데이타의 모든 값들이 고정되어 있다면 해시값은 항상 일정할 것이다. 만약 해커가 트랜잭션이 Confirm 되기 전에, 트랜잭션 데이타의 일부를 변경해서 (서명 검증은 참이 되면서) Transaction ID를 변경하게 할 수 있다면, 이는 중복된 Unconfirmed Transaction이 될 것이고, 만약 변조된 트랜잭션이 Confirm 된다면, 해킹의 목적으로 사용될 수 있을 것이다. Transaction Malleability는 이러한 해킹의 방식으로 사용될 수 있다. Transaction Malleability의 특징은 Confirm 되기 이전에 Transaction ID를 변경한다는 것이고, 또한 그 트랜잭션 자체가 Peer에게 Accept 되기 위해서 서명 검증이 참이어야 한다는 것이다. 특히 짧은 시간 내에 이 모든 것을 해야한다는 점에서 성공 확률을 매우 낮지만, 낮은 가능성으로도 가능한 일이다.

Segwit은 Transaction Input에 있는 Unlocking Script(ScriptSig)를 별도의 witness 영역으로 옮기는 것을 일컫는다. Unlocking Script는 흔히 signature(서명), public key 등으로 구성되어 있는데, Transaction Input의 ScriptSig에서 이를 제거하고 별도의 공간인 witness로 옮긴 것이다. 여기서 witness란 트랜잭션의 타당성을 검증하기 위한 signature(서명), public key 정보를 가리키는 것으로 기존의 ScriptSig와 동일한 내용이지만 다른 영역이므로 별도의 용어로 지칭한다.

트랜잭션 데이타 중 서명(signature)는 매번 서명할 때마다 다른 값을 가질 수 있다. 즉, 동일한 내용을 서명하더라도 매번 다른 서명값을 가질 수 있으며, 다른 값이라도 서명은 참으로 검증(verify)될 수 있다. Segwit 이전의 Non-Segwit 트랜잭션의 경우, 트랜잭션의 데이타 중 특히 이 ScriptSig 부분에서 다른 값을 갖게 된다면 Transaction Malleability 문제가 발생할 가능성이 있었다. SegWit 트랜잭션의 경우에는 ScriptSig가 항상 empty로 고정된 값을 갖기 때문에, SegWit은 Transaction Malleability 이슈를 사전에 막는 역활을 하게 되었다.

Block Size 증가

비트코인을 사용하기 위해서는 locking된 코인을 private key로 서명하는 트랜잭션을 비트코인 네트워크에 전파하여야 한다. 즉, Locking Script (scriptPubKey)를 해제할 수 있는 Unlocking Script(scriptSig)를 만들어 코인을 소비(spend)하게 된다.

비트코인은 블럭크기가 1MB(정확히는 1MiB = 1,000,000 바이트)로 고정되어 있는데, 비트코인이 계속 확장되면서 더 많은 트랜잭션들이 생겨났고, 10분마다 1MB 만을 처리하는 Spec 때문에 처리되지 못하고 대기하는 트랜잭션들이 많이 생겨나기 시작했다. 1MB 블럭크기로는 약 1650개의 트랜잭션을 포함할 수 있었다.

SegWit은 기존의 바이트 단위가 아닌 "weight"라는 새로운 단위를 도입하였다. 이전의 비트코인의 블럭은 최대 1,000,000 바이트만큼을 가지고 있었지만, SegWit에서는 최대 4,000,000 Weight 만큼의 공간을 가지게 되었다. Weight는 기본적으로 witness 데이타인가 아닌가에 따라 다른 값을 갖는다. 즉, 트랜잭션의 데이타 중 witness는 한 바이트당 1 weight가 되고, witness가 아닌 데이타는 한 바이트당 4 weight가 된다.

만약 한 블럭내의 모든 트랜잭션들이 Non-Segwit 트랜잭션이라면, 각 바이트당 4 weight가 되어 4M Weight가 되어, 블럭의 크기 1MB = 4M Weight Unit이 될 것이다.

SegWit이 enable된 노드에서는 4M Weight 만큼의 블럭크기를 가진다. 만약 어느 트랜잭션이 SegWit 트랜잭션이라면, ScriptSig을 제외한 Non-witness 데이타들은 한 바이트당 원래대로 4 Weight로 계산되지만, ScriptSig 부분은 witness 로 빠지게 되고 이는 바이트당 1 Weight로 계산될 것이다. 따라서, 그 트랜잭션에 대한 Weight는 전체적으로 줄어들게 될 것이다. 만약 블럭 내의 모든 트랜잭션들이 Segwit이라면, ScriptSig 부분들의 바이트들이 1/4로 줄어들게 계산될 것이므로 그 블럭에는 더 많은 트랜잭션을 넣을 수 있게 된다.

P2WPKH : Segwit 스크립트

SegWit 트랜잭션은 기존의 Non-Segwit 노드들과 Backward Compatibility를 지원하면서 도입되었다. 다시 말하면, Segwit을 지원하지 않는 기존의 노드들도 기존의 비트코인 스크립트를 실행했을 때, 에러가 나지 않도록 구현되었다.

Native Segwit의 비트코인 스크립트 P2WPKH 은 다음과 형식을 갖는다.

p2wpkh

P2WPKH 스크립트에서 ScriptSig는 비어있으며, 대신 witness 영역(txinwitness)에 서명(signature)과 public key가 "스크립트 형식이 아닌" 데이타로 들어 있다.

SegWit 트랜잭션의 UTXO는 Transaction Output에 "OP_0 + {public-key-hash}" 포맷으로 데이타가 들어 있다. 여기서 OP_0 (0x00)은 Segwit 버전 넘버를 의미한다.

Non-Segwit 노드는 이러한 Segwit 트랜잭션을 검증할 때, ScriptSig + ScriptPubKey 형식으로 스크립트를 실행할 것이다. ScriptSig는 비어 있으므로 결국 실행할 스크립트는 "" + OP_0 + {public-key-hash} 이 되어 최종적으로 public-key-hash가 스택의 top에 남게 되어 트랜잭션이 성공한다.

하지만, Segwit 노드의 경우는 P2WPKH를 스크립트 방식으로 처리하지 않고, ScriptPubKey의 첫 바이트가 OP_0 이므로 이것이 Segwit임을 알고, txinwitness에 있는 public key값을 읽어 해싱한 후 이를 ScriptPubKey의 public key hash값과 비교하고 맞으면 txinwitness에 있는 signature와 public key를 가지고 서명이 맞는지 체크하게 된다. 즉, 비트코인 스크립트를 실행하여 처리하는 대신 Segwit을 특별한 케이스를 코드에서 직접 처리하는 것이다.

그런데, 위에서 Non-Segwit 노드의 경우 해당 트랜잭션은 스크립트 실행시 항상 참이 되는데, 이는 특별한 signature 없이 누구나 코인을 Spend할 수 있다는 것을 의미한다. Segwit은 95%의 Miner들이 Segwit으로 업그레이드 되었을 때 활성화 되었는데, 만약 누군가 Segwit 트랜잭션을 서명없이 Spend하였고 이것이 어쩌다 블럭에 들어갔다면, 95%의 Miner들이 이 블럭을 타당한 블럭으로 여기지 않을 것이고, 결국 이는 메인 블럭체인에서 멀어지는 Ophaned Chain으로 남게 될 것이다.

아래는 Segwit Raw Transaction을 디코드해 본 것으로, vin 에서 ScriptSig가 비어 있고 txinwitness 영역에 signature와 publickey 데이타가 순서대로 들어가 있을 것을 볼 수 있다. 또한, ScriptPubKey에는 OP_0 + publickeyhash 데이타만 들어 있음을 볼 수 있다.

$ bitcoin-cli decoderawtransaction 02000000000101a565ef83c8bae05edbf74130a03...
{
    "txid": "17762885b1b17781cacfb56fed0d3b18ead894609745c8456c686e3830aef939",
    "hash": "1fd537e93b3fe1e515e8aa30b153d9c965b0f44d108919dacee7a4bd6ca1aec2",
    "version": 2,
    "size": 191,
    "vsize": 110,
    "weight": 437,
    "locktime": 0,
    "vin": [
    {
        "txid": "cf6ddbe15d7ddfe808fa9005bc91ab0281b631a03041f7db5ee0bac883ef65a5",
        "vout": 0,
        "scriptSig": {   <== Empty ScriptSig
        "asm": "",
        "hex": ""
        },
        "txinwitness": [   <== signature와 publickey 데이타
        "304402205776ced2fefbd4b4701cc4e1cb6ca7559971795ec95c88669cdeb0a44b3c6b7a02200c547489006a2c460e4426d054cf46c03d097137bd45e86c491647c198ab948801",
        "035cc4d61ef7cfa0c3aa31ef42d1a887bf898b78d58993a5eab1bb2b0457dd1d99"
        ],
        "sequence": 4294967295
    }
    ],
    "vout": [
    {
        "value": 0.00048000,
        "n": 0,
        "scriptPubKey": {   <== OP_0 + publickeyhash
            "asm": "0 3bb01d97deb2bb5502dd23e5c56f8227b1da9d72",
            "hex": "00143bb01d97deb2bb5502dd23e5c56f8227b1da9d72",
        "reqSigs": 1,
        "type": "witness_v0_keyhash",
        "addresses": [
            "tb1q8wcpm977k2a42qkay0ju2muzy7ca48tjqkys3d"
        ]
        }
    }
    ]
}    
본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.