OP_COLOR 複数付与検証

できたらトークンの表現の幅拡がるくねのノリでできるか検証

💡
正確にはステーブルコイン構想の中でステーブルコインID(トークンID)とは別にブランドIDを付与できたらいいなが発端

# keys brand_key = Tapyrus::Key.generate issuer_key = Tapyrus::Key.generate user_key = Tapyrus::Key.generate # 2-of-2 multisig script multisig_script = Tapyrus::Script.new << 2 << [brand_key.pubkey, issuer_key.pubkey] << 2 << OP_CHECKMULTISIG # トークンの元になるTXの元になる資源 TAPYRUS_RPC_CLIENT.generatetoaddress(1, TAPYRUS_RPC_CLIENT.getnewaddress, TAPYRUS_DEV_AGGREGATE_PRIV_KEY) fund_utxo = TAPYRUS_RPC_CLIENT.listunspent.select { _1['spendable'] == true and _1['token'] == 'TPC' }.sort_by { _1['amount'] }.reverse.first raise StandardError.new('Insufficient funds') if fund_utxo.nil? fund_tx = Tapyrus::Tx.parse_from_payload(TAPYRUS_RPC_CLIENT.getrawtransaction(fund_utxo['txid']).htb) fund_outpoint = Tapyrus::OutPoint.from_txid(fund_utxo['txid'], fund_utxo['vout']) # トークンの元になるTX token_source_tx = Tapyrus::Tx.new token_source_tx.in << Tapyrus::TxIn.new(out_point: fund_outpoint) input_tapyrus = (fund_utxo['amount'].to_f * (10**8)).to_i fee_tapyrus = (FEE * (10**8)).to_i amount_tapyrus = (0.0002 * (10**8)).to_i change_tapyrus = input_tapyrus - amount_tapyrus - fee_tapyrus token_source_tx.out << Tapyrus::TxOut.new(value: amount_tapyrus, script_pubkey: multisig_script) token_source_tx.out << Tapyrus::TxOut.new(value: change_tapyrus, script_pubkey: Tapyrus::Script.parse_from_addr(TAPYRUS_RPC_CLIENT.getnewaddress)) fund_tx_script_pubkey = fund_tx.outputs[fund_utxo['vout'].to_i].script_pubkey sig_hash = token_source_tx.sighash_for_input(0, fund_tx_script_pubkey) key = Tapyrus::Key.from_wif(TAPYRUS_RPC_CLIENT.dumpprivkey(fund_tx_script_pubkey.to_addr)) signature = key.sign(sig_hash) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C') token_source_tx.in[0].script_sig << signature token_source_tx.in[0].script_sig << key.pubkey.htb raise StandardError.new('Verify failed') unless token_source_tx.verify_input_sig(0, fund_tx_script_pubkey) token_source_txid = TAPYRUS_RPC_CLIENT.sendrawtransaction(token_source_tx.to_payload.bth) TAPYRUS_RPC_CLIENT.generatetoaddress(1, TAPYRUS_RPC_CLIENT.getnewaddress, TAPYRUS_DEV_AGGREGATE_PRIV_KEY) # トークンの元になるやつ token_source_tx = Tapyrus::Tx.parse_from_payload(TAPYRUS_RPC_CLIENT.getrawtransaction(token_source_txid).htb) token_source_outpoint = Tapyrus::OutPoint.from_txid(token_source_txid, 0) # トークンを作る ## トークンのカラーを作る color_identifier = Tapyrus::Color::ColorIdentifier.reissuable(multisig_script) color_id = color_identifier.to_payload.bth # => "c192ade0cf2cf7a634fdd9b68fe88c9d2b6c81a6bd986c80661f89e6645969cba5" hoge = Tapyrus::Script.new << 1 color_identifier_2 = Tapyrus::Color::ColorIdentifier.reissuable(hoge) color_id_2 = color_identifier_2.to_payload.bth # => "c14ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260" color_script = Tapyrus::Script.new << color_identifier.to_payload << OP_COLOR << color_identifier_2.to_payload << OP_COLOR << 2 << [brand_key.pubkey, issuer_key.pubkey, user_key.pubkey] << 3 << OP_CHECKMULTISIG ## TXを作る token_tx = Tapyrus::Tx.new token_tx.in << Tapyrus::TxIn.new(out_point: token_source_outpoint) input_tapyrus = (0.0002 * (10**8)).to_i fee_tapyrus = (FEE * (10**8)).to_i change_tapyrus = input_tapyrus - fee_tapyrus token_tx.out << Tapyrus::TxOut.new(value: 1, script_pubkey: color_script) token_tx.out << Tapyrus::TxOut.new(value: change_tapyrus, script_pubkey: Tapyrus::Script.parse_from_addr(TAPYRUS_RPC_CLIENT.getnewaddress)) sig_hash = token_tx.sighash_for_input(0, multisig_script) sig1 = brand_key.sign(sig_hash) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C') sig2 = issuer_key.sign(sig_hash) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C') token_tx.in[0].script_sig << OP_0 token_tx.in[0].script_sig << sig1 token_tx.in[0].script_sig << sig2 raise StandardError.new('Verify failed') unless token_tx.verify_input_sig(0, multisig_script) token_txid = TAPYRUS_RPC_CLIENT.sendrawtransaction(token_tx.to_payload.bth) TAPYRUS_RPC_CLIENT.generatetoaddress(1, TAPYRUS_RPC_CLIENT.getnewaddress, TAPYRUS_DEV_AGGREGATE_PRIV_KEY) # 出来上がったトークンはこちら token_tx = Tapyrus::Tx.parse_from_payload(TAPYRUS_RPC_CLIENT.getrawtransaction(token_txid).htb) token_outpoint = Tapyrus::OutPoint.from_txid(token_txid, 0) # トークン量の確認 TAPYRUS_RPC_CLIENT.gettxoutsetinfo # => ... "c192ade0cf2cf7a634fdd9b68fe88c9d2b6c81a6bd986c80661f89e6645969cba5"=>1

先頭の カラーID がトークンのカラーIDとして認識され,二つ目のカラーIDは特に何もない

がしかし移転しようとすると

# 続き fund_utxo = TAPYRUS_RPC_CLIENT.listunspent.select { _1['spendable'] == true and _1['token'] == 'TPC' }.sort_by { _1['amount'] }.reverse.first raise StandardError.new('Insufficient funds') if fund_utxo.nil? fund_tx = Tapyrus::Tx.parse_from_payload(TAPYRUS_RPC_CLIENT.getrawtransaction(fund_utxo['txid']).htb) fund_outpoint = Tapyrus::OutPoint.from_txid(fund_utxo['txid'], fund_utxo['vout']) tx = Tapyrus::Tx.new tx.in << Tapyrus::TxIn.new(out_point: fund_outpoint) tx.in << Tapyrus::TxIn.new(out_point: token_outpoint) input_tapyrus = (fund_utxo['amount'].to_f * (10**8)).to_i fee_tapyrus = (FEE * (10**8)).to_i change_tapyrus = input_tapyrus - fee_tapyrus tx.out << Tapyrus::TxOut.new(value: 1, script_pubkey: color_script) tx.out << Tapyrus::TxOut.new(value: change_tapyrus, script_pubkey: Tapyrus::Script.parse_from_addr(TAPYRUS_RPC_CLIENT.getnewaddress)) fund_tx_script_pubkey = fund_tx.outputs.first.script_pubkey sig_hash = tx.sighash_for_input(0, fund_tx_script_pubkey) key = Tapyrus::Key.from_wif(TAPYRUS_RPC_CLIENT.dumpprivkey(fund_tx_script_pubkey.to_addr)) signature = key.sign(sig_hash) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C') tx.in[0].script_sig << signature tx.in[0].script_sig << key.pubkey.htb sig_hash = tx.sighash_for_input(1, color_script) sig1 = issuer_key.sign(sig_hash) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C') sig2 = user_key.sign(sig_hash) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C') tx.in[1].script_sig << OP_0 tx.in[1].script_sig << sig1 tx.in[1].script_sig << sig2 raise StandardError.new('Verify failed') unless tx.verify_input_sig(0, fund_tx_script_pubkey) txid = TAPYRUS_RPC_CLIENT.sendrawtransaction(tx.to_payload.bth) # => ... mandatory-script-verify-flag-failed (Multiple OP_COLOR found in script) (code 16)

ちゃんとダメっぽい