multisig script を scriptPubKey とした Re-issuable Token の発行

scriptPubKeyを任意に設定できるかやってみる

作ったToken本体を2-of-3にするのはまた今度 → ♠️Arrow icon of a page linkRe-issuable Token を 2-of-3 multisig にする

require 'tapyrus' include Tapyrus::Opcodes Tapyrus.chain_params = :dev TAPYRUS_DEV_AGGREGATE_PRIV_KEY = 'cUJN5RVzYWFoeY8rUztd47jzXCu1p57Ay8V7pqCzsBD3PEXN7Dd4' TAPYRUS_RPC_CONFIG = { schema: 'http', host: 'localhost', port: 12381, user: 'rpcuser', password: 'rpcpassword' } TAPYRUS_RPC_CLIENT = Tapyrus::RPC::TapyrusCoreClient.new(TAPYRUS_RPC_CONFIG) FEE = 0.00003 # ================================= # 発行 # keys brand_key = Tapyrus::Key.generate issuer_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 ## color_script = token の script_pubkey ## ロック条件はなし color_script = Tapyrus::Script.new << color_identifier.to_payload << OP_COLOR << 3 ## 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 # => { ... "COLOR_ID" => 1 } # ================================= # reissue してみる # 必要な scriptPubKey は変わらず multisig_script = Tapyrus::Script.new << 2 << [brand_key.pubkey, issuer_key.pubkey] << 2 << OP_CHECKMULTISIG # 再発行の元になるTXの元になる資源 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 color_script = Tapyrus::Script.new << color_identifier.to_payload << OP_COLOR << 3 ## 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 # => { ... "COLOR_ID" => 2 }

でぎだ

color_scriptがクソ雑なのでlistunspentに出なくてわろた

試しに自分に移転してみる

# 続き 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: Tapyrus::Script.parse_from_addr(TAPYRUS_RPC_CLIENT.getnewaddress("", color_identifier.to_hex))) 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 raise StandardError.new('Verify failed') unless tx.verify_input_sig(0, fund_tx_script_pubkey) txid = TAPYRUS_RPC_CLIENT.sendrawtransaction(tx.to_payload.bth) TAPYRUS_RPC_CLIENT.generatetoaddress(1, TAPYRUS_RPC_CLIENT.getnewaddress, TAPYRUS_DEV_AGGREGATE_PRIV_KEY) TAPYRUS_RPC_CLIENT.listunspent.select { _1['token'] != 'TPC' }

OK!