Proof of Payment

# server nonce = 1 + SecureRandom.random_number(ECDSA::Group::Secp256k1.order - 1) # send nonce
# client require 'bitcoin' require 'net/http' require 'json' include Bitcoin::Opcodes Bitcoin.chain_params = :signet client = Bitcoin::RPC::BitcoinCoreClient.new({ schema: 'http', host: 'localhost', port: 38332, user: 'hoge', password: 'hoge' }) # payment transaction payment_txid = "c2789578340200af3769b7d325986a5a72dfdf36e9ea14dfcbdc4b0b6222c8df" payment_tx = Bitcoin::Tx.parse_from_payload(client.getrawtransaction(payment_txid).htb) # pop transaction pop_tx = Bitcoin::Tx.new # nonce nonce = # receive nonce # input payment_tx.in.each do |input| pop_tx.in << Bitcoin::TxIn.new(out_point: input.out_point, sequence: 0) end # output output_script = Bitcoin::Script.new << OP_RETURN << "0x01 #{payment_txid} #{nonce}".bth pop_tx.out << Bitcoin::TxOut.new(value: 0, script_pubkey: output_script) # ロックタイム pop_tx.lock_time = 499999999 # 署名 pop_tx.in.each_with_index do |input, index| source_tx_output = Bitcoin::Tx.parse_from_payload(client.getrawtransaction(input.out_point.txid).htb).out[input.out_point.index] key = Bitcoin::Key.from_wif(client.dumpprivkey(source_tx_output.script_pubkey.to_addr)) sig_hash = pop_tx.sighash_for_input(index, source_tx_output.script_pubkey, sig_version: :witness_v0, amount: source_tx_output.value) signature = key.sign(sig_hash) + [Bitcoin::SIGHASH_TYPE[:all]].pack('C') input.script_witness.stack << signature input.script_witness.stack << key.pubkey.htb end # send tx # pop_tx.txid # => "adff2bacbb3772f0b3b5493ae6856b9143fe89ea48dbe7a3b776f61da01378c0"
# server # receive pop_tx_id pop_tx = Bitcoin::Tx.parse_from_payload(client.getrawtransaction(pop_tx_id).htb) # 1. Check the format of the PoP. # 送金済みであることを除いて検証が成功することを確認。 # 2. Check that lock_time is 499999999. raise StandardError if pop_tx.lock_time != 499999999 # 3. Check that there is exactly one output. raise StandardError if pop_tx.out.size != 1 # This output must have value 0 raise StandardError if pop_tx.out[0].value != 0 # and conform to the OP_RETURN output format outlined above. pop_op_return = pop_tx.outputs[0].script_pubkey.op_return_data.split raise StandardError if pop_op_return.size != 3 or !(['0x00', '0x01'].include?(pop_op_return[0])) # 4. Check that the nonce is the same as the one requested. pop_nonce = pop_op_return[2].to_i raise StandardError if nonce != pop_nonce # 5. Check that the inputs of the PoP are exactly the same as in transaction T, except that the sequence numbers must all be 0. payment_tx = Bitcoin::Tx.parse_from_payload(client.getrawtransaction(pop_payment_txid).htb) payment_tx.in.each_with_index do |t_input, index| raise StandardError if t_input.out_point.to_s != pop_tx.in[index].out_point.to_s raise StandardError if pop_tx.in[index].sequence != 0 end # 6. Run the scripts of all the inputs. All scripts must return true. pop_tx.in.each_with_index do |input, index| source_tx_output = Bitcoin::Tx.parse_from_payload(client.getrawtransaction(input.out_point.txid).htb).out[input.out_point.index] pop_tx.verify_input_sig(index, source_tx_output.script_pubkey, amount: source_tx_output.value) end # 7. Check that the txid in the PoP output is the transaction you actually want proof for. # 8. Return "valid". true