안녕하세요. 남정수입니다.
자리를 마련해 주신 오리지날님과 관심가져주시는 모든분들 감사드립니다.
원문: https://medium.com/@namjungsoo/bitcoinsv-6-op-return-data-5fa38c32a7c6
OP_RETURN은 Bitcoin Script의 OP code중의 하나이며, transaction에 데이터를 추가하기 위해서 사용한다. BitcoinSV에서는 현재(Genesis upgrade 이전) 100KB 크기의 데이터를 OP_RETURN에 포함시킬 수 있다.
OP_RETURN의 위치는 transaction의 output이다. 기존의 output에는 잠금 스크립트(scriptPubKey)가 존재하여 spend가 가능하지만, OP_RETURN은 잠금 스크립트가 존재하지 않아서 spend가 불가능하다.
다음과 같은 코드를 통해 간단한 string 타입의 OP_RETURN 데이터를 포함한 transaction을 작성할 수 있다. addData() 함수가 바로 OP_RETURN 데이터를 추가 하는 함수이다. 여러번 호출할 수 있으며, to()와도 섞어서 사용할수 있으나, 호출하는 순서대로 output index가 정해진다.
const address = 'n1ZQ9ZkCy8MqEuxEG9FNgJtUQAqvZhWmmT'
const fee = 400
const txId = '5e415d789551ddc1b5f65db28c3200e7018584bd464cddf8edcf9d7107e851aa'
const outputIndex = 0
const satoshis = 5000// for input
const utxo = {
address,
txId,
outputIndex,
script: bsv.Script.buildPublicKeyHashOut(address).toString(),
satoshis
}const privateKey = '<n1ZQ9ZkCy8MqEuxEG9FNgJtUQAqvZhWmmT's private key>'const tx = new bsv.Transaction()
tx.from(utxo)tx.addData(['moneybutton.com', 'hello'])
tx.fee(fee)// add output for change
tx.change(address)// sign input utxo
tx.sign(privateKey)const str = tx.serialize(true)
console.log(str)
생성된 raw transaction은 아래와 같다. [0]번의 output에 OP_RETURN 데이터가, [1]번의 output에는 change의 P2PKH 송금이 포함되어 있다.
bitcoin-cli -testnet decoderawtransaction 0100000001aa51e807719dcfedf8dd4c46bd848501e700328cb25df6b5c1dd5195785d415e010000006b4830450221009a2e12bb1006f4698beb47574f835bf0c94180c374fad2170115cf0c114fb0a30220774ef7f4f5eb8fd7be771d6ee288e0b3e4b897cf0a94666ce14d5ee2484708cd412103cada4795099d48aaf781650809c076b79bc31e39a80410c893931c4c467bb1d1ffffffff020000000000000000176a0f6d6f6e6579627574746f6e2e636f6d0568656c6c6f415ebc02000000001976a914dbd8eddbc1af19d1bd697f0772b5b2cea76964ab88ac00000000
{
"txid": "89a9bfbd9c89b74ff4b5d1a386ea372549a289681b9be6de3f81268a2bbecff4",
"hash": "89a9bfbd9c89b74ff4b5d1a386ea372549a289681b9be6de3f81268a2bbecff4",
"size": 224,
"version": 1,
"locktime": 0,
"vin": [
{
"txid": "5e415d789551ddc1b5f65db28c3200e7018584bd464cddf8edcf9d7107e851aa",
"vout": 1,
"scriptSig": {
"asm": "30450221009a2e12bb1006f4698beb47574f835bf0c94180c374fad2170115cf0c114fb0a30220774ef7f4f5eb8fd7be771d6ee288e0b3e4b897cf0a94666ce14d5ee2484708cd[ALL|FORKID] 03cada4795099d48aaf781650809c076b79bc31e39a80410c893931c4c467bb1d1",
"hex": "4830450221009a2e12bb1006f4698beb47574f835bf0c94180c374fad2170115cf0c114fb0a30220774ef7f4f5eb8fd7be771d6ee288e0b3e4b897cf0a94666ce14d5ee2484708cd412103cada4795099d48aaf781650809c076b79bc31e39a80410c893931c4c467bb1d1"
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.00000000,
"n": 0,
"scriptPubKey": {
"asm": "OP_RETURN 6d6f6e6579627574746f6e2e636f6d 68656c6c6f",
"hex": "6a0f6d6f6e6579627574746f6e2e636f6d0568656c6c6f",
"type": "nulldata"
}
},
{
"value": 0.45899329,
"n": 1,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 dbd8eddbc1af19d1bd697f0772b5b2cea76964ab OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914dbd8eddbc1af19d1bd697f0772b5b2cea76964ab88ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"bchtest:qrda3mwmcxh3n5dad9lswu44kt82w6ty4vhweu275t"
]
}
}
]
}
Mainnet에서 OP_RETURN string 데이터의 예제는 다음과 같다.
Transaction에 입력한 addData()는 [‘buskon’, ‘hello’]이다. 그리고 ASCII의 결과가 jbuskonhello
로 j가 앞에 붙는 이유는 OP_RETURN의 OP code가 0x6a로 ASCII code ‘j’와 겹치기 때문이다.
실제 Script의 결과는 다음과 같이 Script 탭에서 확인 가능하다.
Hex도 마찬가지이다. Hex에서는 OP code와 각 데이터 사이에 따라오는 데이터의 bytes length를 hex code로 기록해 넣는다. OP_RETURN의 6a 다음에 나오는 06은 buskon
의 length이다. hello
의 hex code인 ‘68656c6c6f’ 앞에도 length인 05가 기록되어 있다.
Binary 데이터를 기록하는 방법을 알아보자. BSV의 생태계에서는 Bitcoin의 프로토콜을 이용한 3rd party의 개발도구 및 프로토콜들이 많이 있다. B protocol은 그중의 하나이며 OP_RETURN을 이용하여 저장한 바이너리 파일을 식별하기 위한 protocol이다.
프로토콜 스펙은 다음과 같다. Bitcom prefix는 OP_RETURN 사용시에 header와 같은 역할을 하게 되는데, 발송자가 서로 겹치지 않게 하는 우편번호의 역할을 한다. 현재로서는 강제성은 없다.
B://
Bitcoin Simple Storage Protocol
Bitcom Prefix: 19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut
Protocol Spec: https://github.como/unwriter/B
문법은 다음과 같다. 아래로 이어지는 구문들은 output index로 종으로 증가하는게 아니라 횡으로 공백(띄어쓰기)으로 구분이 된다. 즉, 하나의 transaction의 같은 output index에 포함이 된다는 말이다.
문법을 설명하면, 19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut
로 시작하면 B protocol이며, [DATA]
는 실제 파일의 데이터이고, 그 데이터의 타입은 [MEDIA TYPE]
에서 정의 한다. 나머지 파일명과 인코딩은 선택사항이다.
OP_RETURN
19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut
[DATA]
[MEDIA TYPE]
[ENCODING (optional if no filename)]
[FILENAME (optional)]
위의 스펙을 코드로 구현할 때는 하나의 addData()에 배열로 추가하되, 바이너리 포맷이면 Javascript의 Buffer로 포함시켜야 한다. 내부적으로는 자동으로 hex code로 변환이 된다.
이미지 발송 예제를 보자. 아래의 예제는 bico.media
에서 구할수 있다. (우리가 잘 아는 이미지이다.)
https://bico.media/a3907e5b910f798c8d0fb450d483a0aefa5ce40ac74064b377603e5ea51deccb
URL의 뒤에 있는 a3907e5b910f798c8d0fb450d483a0aefa5ce40ac74064b377603e5ea51deccb
가 txId이다. txId이므로 Block Explorer에서 찾을 수 있다.
Transaction을 확인해 보면 아래와 같은 결과를 볼수 있다. 1번째 컬럼에 B protocol의 prefix가 존재한다. (19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut
) 그러므로 이 OP_RETURN은 B protocol이다.
2번째 컬럼에 이어지는 데이터는 파일의 바이너리 데이터 [DATA]
이다.
이제 스크롤 해서 파일 끝으로 가보자. 3번째 컬럼에는 [MEDIA TYPE]
이 존재해야 하는데 확인해 보니 이 파일은 image/jpeg
포맷이다.
축하합니다! 행운의 5 HUB가 적립되었습니다 ^.^