BitcoinとCounterpartyの送信トランザクションの比較

はじめに

上記CounterPartyのトランザクションについての疑問ツイートを見つけました。回答になるかわかりませんがビットコインの送信トランザクションと、Counterpartyの送信トランザクションの違いをメモします。

CounterpartyのJavascript実装「TokenlyPocket」

Counterpartyの本家WebWallet「Counterwallet.io」は、Counterpartyトークンを送信するためにサーバプログラム「counterpartyd」を起動しています。ただし必ずしも「counterpartyd」サーバが必要ではなく、仕様に従った送信トランザクションを作成できるならクライアントサイドだけでCounterpartyプロトコルに準拠したWalletアプリを作成することができます。

その1つの例が「TokenlyPocket」です。Google Chrome Extensionとして利用できます。JavaScriptで実装されており、処理内容が分かりやすいため今回は「TokenlyPocket」を参考にして送信トランザクションを見ていきます。

TokenlyPocketの画面

スクリーンショット 2016-01-10 12.50.30.png

ビットコイン送信

まずはBTC送信の処理です。画面から入力した送金情報をTransactionのToプロパティに設定して、ブロックエクスプローからutxoを取得、Transactionのfromプロパテイに設定。署名。ブロードキャストという流れです。

(1)BTC送信メソッド「sendBTC」を呼び出す。( (2)以降はsendBTCのメソッド内の処理 )

sendBTC(add_from, add_to, sendtotal, transfee)

(2)ブロックエクスプローラー設定

ブロックエクスプローラーとしてinsightを利用します。

var source_html = "https://"+INSIGHT_SERVER+"/api/addr/"+add_from+"/utxo";

(3)total_utxo初期化。送信BTC量の設定

sendtotal_satoshisは整数にします。

var total_utxo = new Array();
var sendtotal_satoshis = parseFloat(sendtotal).toFixed(8) * 100000000;
sendtotal_satoshis = Math.round(sendtotal_satoshis);

(4)mnemonicから秘密鍵を取得

var mnemonic = $("#newpassphrase").html();
var privkey = getprivkey(add_from, mnemonic);

(5)ブロックエクスプローラーからutxoを取得

$.getJSON( source_html, function( data ) {

(6)受け取ったutxoをAmountの大きい順にソート

    var amountremaining = (parseFloat(sendtotal) + parseFloat(transfee));
    data.sort(function(a, b) {
        return b.amount - a.amount;
    });

(7)送信Amount(送信BTC+手数料)に達するまでutxoを配列に追加

    $.each(data, function(i, item) {
         var txid = data[i].txid;
         var vout = data[i].vout;
         var script = data[i].scriptPubKey;
         var amount = parseFloat(data[i].amount);

         amountremaining = amountremaining - amount;
         amountremaining.toFixed(8);

         var obj = {
            "txid": txid,
            "address": add_from,
            "vout": vout,
            "scriptPubKey": script,
            "amount": amount
         };

         total_utxo.push(obj);

         if (amountremaining == 0 || amountremaining < -0.00005460) {
             return false;
         }
    });

(8)おつりの金額を設定

    if (amountremaining < 0) {
        var satoshi_change = -(amountremaining.toFixed(8) * 100000000).toFixed(0);
    } else {
        var satoshi_change = 0;
    }

(9)トランザクション初期化

bitcoreを利用します。

 var transaction = new bitcore.Transaction();

(10)(7)で作成したutxoのリスト分をtransaction.fromに設定

    for (i = 0; i < total_utxo.length; i++) {
        transaction.from(total_utxo[i]);
    }

(11)送金額・宛先、おつり額・宛先を設定

    transaction.to(add_to, sendtotal_satoshis);

    if (satoshi_change > 5459) {
        transaction.to(add_from, satoshi_change);
    }

(12)署名

 transaction.sign(privkey);

(13)ブロードキャスト

 var final_trans = transaction.serialize();
    sendBTCpush(final_trans);

Counterpartyトークン送信

Counterpartyの場合をみていきます。

(1)Counterpartyトークン送信処理 ( (2)以降はsendXCP_opreturnのメソッド内の処理 )

Counterpartyの送信情報はビットコイントランザクションに含まれるため、BTCも送信する必要があります。btc_total = 0.0000547 はその数量です。(Dustと呼ばれます。5460satoshiがBTC送信の最小サイズになります。正しくは546satoshiかも)

var btc_total = 0.0000547;
var transfee = 0.0001;

sendXCP_opreturn(add_from, add_to, asset, asset_total, btc_total, transfee, mnemonic)

(2)ブロックエクスプローラー設定

ブロックエクスプローラーとしてinsightを利用します。

var source_html = "https://"+INSIGHT_SERVER+"/api/addr/"+add_from+"/utxo";

(3)total_utxo初期化

var total_utxo = new Array();

(4)mnemonicから秘密鍵を取得

var privkey = getprivkey(add_from, mnemonic);

(5)ブロックエクスプローラーからutxoを取得

$.getJSON( source_html, function( data ) {

(6)受け取ったutxoをAmountの大きい順にソート

    var amountremaining = ((parseFloat(btc_total) * 100000000) + (parseFloat(transfee)*100000000))/100000000;
    data.sort(function(a, b) {
        return b.amount - a.amount;
    });

(7)送信Amount(送信BTC+手数料)に達するまでutxoを配列に追加

    $.each(data, function(i, item) {

         var txid = data[i].txid;
         var vout = data[i].vout;
         var script = data[i].scriptPubKey;
         var amount = parseFloat(data[i].amount);

         amountremaining = amountremaining - amount;
         amountremaining.toFixed(8);

         var obj = {
            "txid": txid,
            "address": add_from,
            "vout": vout,
            "scriptPubKey": script,
            "amount": amount
         };

         total_utxo.push(obj);

         if (amountremaining == 0 || amountremaining < -0.00005460) {
             return false;
         }

    });

(8)おつりの金額を設定

    if (amountremaining < 0) {
        var satoshi_change = -(amountremaining.toFixed(8) * 100000000).toFixed(0);
    } else {
        var satoshi_change = 0;
    }

(9)トークン名と数量から、OP_RETURNに設定する値を取得

 var datachunk_unencoded = create_xcp_send_data_opreturn(asset, asset_total);
function create_xcp_send_data_opreturn(asset_name, amount) {
    var prefix = "434e54525052545900000000"; // hex to ascii = CNTRPRTY
    var asset_id = assetid(asset_name); // assetidメソッドの詳細は省略。トークンに対する一意のIDを返します。例:d806c1d5
    var asset_id_hex = padprefix(asset_id.toString(16), 16); // 例:00000000d806c1d5
    var amount_round = parseInt((amount*100000000).toFixed(0)); // 例:100000000 
    var amount_hex = padprefix((amount_round).toString(16), 16); // 例:0000000005f5e100
    var data = prefix + asset_id_hex + amount_hex; // 例:434e5452505254590000000000000000d806c1d50000000005f5e100
    return data;
}

(10)データ整合性チェック

    var check_data = "1c"+datachunk_unencoded;
    var correct = isdatacorrect(check_data, asset, asset_total);

(11)1件目のutxoをキーにしてrc4暗号化

 var utxo_key = total_utxo[0].txid;
    var datachunk_encoded = xcp_rc4(utxo_key, datachunk_unencoded); // 例:92f78658f58ba3109c81e3a401a7e38214914d4838ef7270bf41fed0

(12)トランザクション初期化

 var transaction = new bitcore.Transaction();

(13)(7)で作成したutxoのリスト分をtransaction.fromに設定

    for (i = 0; i < total_utxo.length; i++) {
        transaction.from(total_utxo[i]);
    }

(14)送金額・宛先、おつり額・宛先を設定

    var btc_total_satoshis = parseFloat((btc_total * 100000000).toFixed(0));
    transaction.to(add_to, btc_total_satoshis);
    
    if (satoshi_change > 5459) {
        transaction.change(add_from);
    }

(15)OP_RETURNをトランザクションに設定

    var scriptstring = "OP_RETURN 28 0x"+datachunk_encoded; // 例:OP_RETURN 28 0x92f78658f58ba3109c81e3a401a7e38214914d4838ef7270bf41fed0
    var data_script = new bitcore.Script(scriptstring);
    var xcpdata_opreturn = new bitcore.Transaction.Output({script: data_script, satoshis: 0});
    transaction.addOutput(xcpdata_opreturn);

(16)署名

 transaction.sign(privkey);

(17)ブロードキャスト

 var final_trans = transaction.uncheckedSerialize();
    sendBTCpush(final_trans);

まとめ

BTC送信との違いは以下2点です。

次回はトランザクションの受信と情報の保存部分の実装を見てみます。

参考

Dust limit is listed as 546 satoshis

RC4

ビットコインのブロックチェーンにデータを記録する(OP_RETURNの利用)

TipMe

TipMe with IndieSquare