본문으로 바로가기

비트코인의 거래를 검증하기 위해서 잠금 스크립트와 해제 스크립트가 필요합니다.

잠금 스크립트는 출력값에 위치한 예상지출로, 향후 출력값을 소비하기 위해 충족되어야 하는 요건을 명시하고 있으며, 보틍 공개키 또는 비트코인 주소가 담겨있기 떄문에 스크립트펍키(ScriptPubKey) 라고 합니다. 

해제 스크립트는 잠금 스크립트가 출력값에 놓아둔 조건을 해결하거나 충족시켜서 출력값이 소비될수 있도록 하는 스크립트입니다.

해제 스크립트는 보통 디지털 서명이 담겨있기 떄문에 스크립트시그(ScriptSig)라고 불려왔습니다.

모든 비트코인 클라이언트는 잠금 스크립트와 해제 스크립트를 함께 실행해서 거래를 유효화시킵니다.


다음과 같이 거래의 입력값과 출력값이 포함되어 있는 해제 스크립트와 잠금 스크립트를 스택에 넣어 연산을 실행하고, 그 결과가 TRUE가 됨을 검증하여 유효성을 체크합니다. 스택구조를 사용하여 연산이 수행되기 떄문에 하기 스크립트는 왼쪽에서 오른쪽으로 처리되면서 스크립트가 실행됩니다.



본 예제는 하기의 Tutorial을 참고로 사용하였습니다.

http://aaronjaramillo.org/intoduction-to-multisig-addresses


그리고 하기의 작성코드는 다음의 github에서 다운로드 받을수 있습니다.

https://github.com/ihpark92/Libbitcoin_Tutorial/


multisig 스크립트 지원을 위해 이전에 작성한 HD_Wallet에 인자가 없는 생성자를 먼저 추가하도록 하겠습니다.


// Constructor
HD_Wallet()
{
entropy = data_chunk(16);
pseudo_random_fill(entropy);
mnemonic = wallet::create_mnemonic(entropy);
seed = to_chunk(wallet::decode_mnemonic(mnemonic));
privateKey = wallet::hd_private(seed);
publicKey = privateKey.to_public();
}


그리고 main 함수를 작성할 코드로 ScriptingTest.cpp로 저장을 하고 다음과 같이 시작하도록 하겠습니다.


#include <bitcoin/bitcoin.hpp>
#include "HD_Wallet.cpp"
using namespace bc;
using namespace bc::wallet;
using namespace bc::machine;
using namespace bc::chain;


main 함수에서는 다음과 같이 먼저 3개의 공개키를 생성합니다.


HD_Wallet wallet1 = HD_Wallet();
HD_Wallet wallet2 = HD_Wallet();
HD_Wallet wallet3 = HD_Wallet();
data_chunk pubkey1 = to_chunk(wallet1.childPublicKey(1).point());
data_chunk pubkey2 = to_chunk(wallet2.childPublicKey(1).point());
data_chunk pubkey3 = to_chunk(wallet3.childPublicKey(1).point());


그리고 다음 코드를 통해서 redeem 스크립트를 생성합니다.

비트코인의 스크립트 연산자에 대해서는 Wiki 페이지를 참고하기기 바랍니다.

82 opcode는 숫자 2를 스택에 추가하는 연산을 의미하고 이어서 3개의 공개키를 스택에 추가하고, 83 opcode로 숫자 3을 추가하고 마지막으로 CHECKMULTISIGVERIFY 연산을 수행합니다.

즉 다음과 같은 순서로 스택에 넣어지게 됩니다.


2 [PUBKEY1] [PUBKEY2] [PUBKEY3] 3 CHECKMULTISIGVERIFY


operation::list opList {operation(opcode(82)), operation(opcode(33)), operation(pubkey1), operation(opcode(33)), operation(pubkey2), operation(opcode(33)), operation(pubkey3), operation(opcode(83)), operation(opcode(175))};
script multisigScript(opList);


다음 코드를 통해서 스크립트가 유효한지 확인후 화면상에 출력해줍니다.


if(multisigScript.is_valid())
{
std::cout << "Script is Valid!\n" << std::endl;
}else{
std::cout << "Script Invalid! \n" << std::endl;
}
std::cout << "Redeeem Script: \n" << std::endl;
std::cout << multisigScript.to_string(0) << "\n" << std::endl;


다음으로 redeem 스크립트를 사용하여 잠금 스크립트를 생성합니다.

생성된 잠금 스크립트는 다음과 같은 순서로 스택에 넣어지게 됩니다.


HASH160 [20-byte script hash] EQUAL


short_hash scriptHash = ripemd160_hash(multisigScript.to_data(0));
std::cout << encode_base16(multisigScript.to_data(0)) << "\n" <<std::endl;
multisigScript = script(multisigScript.to_pay_script_hash_pattern(scriptHash));


생성된 잠금 스크립트와 화면상에 출력합니다.

그리고, 잠금 스크립트를 사용하여 P2SH(Pay-To-Scripp-Hash)용 주소를 생성하고 화면상에 출력합니다.


std::cout << "Locking Script: " << std::endl;
std::cout << multisigScript.to_string(0) << "\n" << std::endl;
payment_address multsigAddress(multisigScript);
std::cout << "Payment Address: " << std::endl;
std::cout << multsigAddress.encoded() << "\n" << std::endl;


마지막으로 3개의 객체에 대해 각각의 연상기호 단어를 화면상에 출력해줍니다.


std::cout << "Private Key Mnemonics: \n" << std::endl;
std::cout << "Key One: " << wallet1.displayMnemonic() << std::endl;
std::cout << "Key Two: " << wallet2.displayMnemonic() << std::endl;
std::cout << "Key Three: " << wallet3.displayMnemonic() << "\n" <<std::endl;


이와같이 거래에 필요한 P2SH용 잠금 스크립트를 생성할수 있으며, 이것을 이용하여 이후 거래생성에 사용하도록 하겠습니다.


다음은 실행결과를 보여주고 있습니다.


ihpark92@ubuntu:~/Libbitcoin_Tutorial/Multisig$ ./ScriptingTest
Script is Valid!
Redeeem Script:
2 push_33 [022e28faaac6347f7b303717e9a71e291303ee3c134722f1dcbca557cb28200b48] push_33 [03995fac6a35aa13cbd62b372efdb8b14bdbe34b813b3bf03e0d458ade6d912489] push_33 [02829196344963c78dc33830ffc917ade0e217dbb18216b43155db3aa97f46f264] 3 checkmultisigverify
522121022e28faaac6347f7b303717e9a71e291303ee3c134722f1dcbca557cb28200b48212103995fac6a35aa13cbd62b372efdb8b14bdbe34b813b3bf03e0d458ade6d912489212102829196344963c78dc33830ffc917ade0e217dbb18216b43155db3aa97f46f26453af
Locking Script:
hash160 [badf42e8d43a65baadfc41eb4a8e9758217c78f4] equal
Payment Address:
3QRtgANV1cFYoqfPYrP2TbPPM971BDZXnV
Private Key Mnemonics:
Key One: upon quit old negative clean name phone audit undo erode stereo group
Key Two: easily void wild impose manual inquiry auction document twist original suit board
Key Three: marble word echo scrap lady fine garlic swap make battle when blanket
ihpark92@ubuntu:~/Libbitcoin_Tutorial/Multisig$