Skip to content

Instantly share code, notes, and snippets.

@kannapoix
Created December 24, 2019 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kannapoix/8207fd1c5b4d69b398bfe98ea5480bb4 to your computer and use it in GitHub Desktop.
Save kannapoix/8207fd1c5b4d69b398bfe98ea5480bb4 to your computer and use it in GitHub Desktop.
# bip47 Pyament Code @ https://github.com/bitcoin/bips/blob/master/bip-0047.mediawiki#version-1
# bitcoionrb @ https://github.com/chaintope/bitcoinrb
require 'bitcoin'
require 'ecdsa'
group = ECDSA::Group::Secp256k1
include Bitcoin
include Bitcoin::Util
Bitcoin.chain_params = :mainnet
class ExtKeyPaymentCode
attr_accessor :ext_key, :payment_code
attr_writer :byte3_34, :byte35_66
def initialize(seed=nil)
@bip32_master = Bitcoin::ExtKey.generate_master(seed) if seed
constants
genereate_payment_code if seed
end
def genereate_payment_code
# m/47'/0'/0'
@ext_key = @bip32_master.derive(47, harden=true).derive(0, harden=true).derive(0, harden=true)
chain_code = ext_key.chain_code
# first byte is odd prefix?
@byte3_34 = ext_key.pub.slice(2...ext_key.pub.length) # x of pubkey
@byte35_66 = chain_code.unpack('H*').first # chain code
@payment_code = Bitcoin::Base58.encode(row_payment_code + Bitcoin.calc_checksum(row_payment_code))
end
def masked_payment_code(outpoint, shared_secret)
blinding_factor_string = blinding_factor(outpoint, shared_secret).first
x_xor = (@byte3_34.to_i(16)^blinding_factor_string[0..63].to_i(16)).to_s(16).rjust(64, '0')
c_xor = (@byte35_66.to_i(16)^blinding_factor_string[64..127].to_i(16)).to_s(16).rjust(64, '0')
# @prefix + @byte0 + @byte1 + @byte2 + x_xor + c_xor + @byte67_79
@byte0 + @byte1 + @byte2 + x_xor + c_xor + @byte67_79
end
def decode_masked_payment_code(outpoint, shared_secret, masked_payment_code)
p masked_payment_code
p byte3_34 = masked_payment_code[6, 64]
p byte35_66 = masked_payment_code[70, 64]
blinding_factor_string = blinding_factor(outpoint, shared_secret).first
@byte3_34 = (@byte3_34.to_i(16)^blinding_factor_string[0..63].to_i(16)).to_s(16).rjust(64, '0')
@byte35_66 = (@byte35_66.to_i(16)^blinding_factor_string[64..127].to_i(16)).to_s(16).rjust(64, '0')
row_payment_code
end
def row_payment_code
@prefix + @byte0 + @byte1 + @byte2 + @byte3_34 + @byte35_66 + @byte67_79
end
def blinding_factor(outpoint, shared_secret)
Bitcoin.hmac_sha512([outpoint].pack("H*"), [shared_secret].pack("H*")).unpack("H*")
end
def shared_secret
alice_key = Bitcoin::Key.from_wif('Kx983SRhAZpAhj7Aac1wUXMJ6XZeyJKqCxJJ49dxEbYCT4a1ozRD')
alice_ec = OpenSSL::PKey::EC.new('secp256k1')
alice_ec.private_key = OpenSSL::BN.new(alice_key.priv_key, 16)
bob_payment_code = ExtKeyPaymentCode.new('87eaaac5a539ab028df44d9110defbef3797ddb805ca309f61a69ff96dbaa7ab5b24038cf029edec5235d933110f0aea8aeecf939ed14fc20730bba71e4b1110')
bob_payment_code_pub = bob_payment_code.ext_key.derive(0).pub
bob_pub_bn = OpenSSL::BN.new(bob_payment_code_pub, 16)
alice_secret_point = OpenSSL::PKey::EC::Point.new(alice_ec.group, bob_pub_bn)
# This is shared secret.
a_common_key = alice_ec.dh_compute_key(alice_secret_point).bth
end
def self.from_string hex_string
new_payment_code = new
new_payment_code.byte3_34 = hex_string[6, 64]
new_payment_code.byte35_66 = hex_string[70, 64]
new_payment_code
end
private
def constants
@prefix = '47'
@byte0 = '01'
@byte1 = '00'
@byte2 = '02'
@byte67_79 = '0' * 26
end
end
Alice = ExtKeyPaymentCode.new('64dca76abc9c6f0cf3d212d248c380c4622c8f93b2c425ec6a5567fd5db57e10d3e6f94a2f6af4ac2edb8998072aad92098db73558c323777abf5bd1082d970a')
Bob = ExtKeyPaymentCode.new('87eaaac5a539ab028df44d9110defbef3797ddb805ca309f61a69ff96dbaa7ab5b24038cf029edec5235d933110f0aea8aeecf939ed14fc20730bba71e4b1110')
# Notification TX
# m/47'/0'/0'/0
a = Alice.ext_key.derive(0).priv
A = Alice.ext_key.derive(0).pub
a_notification_address = Alice.ext_key.derive(0).key.to_p2pkh
p "alice notification private"
p a
p "alice notification pub"
p A
puts 'alice notification_address'
p a_notification_address
b = Bob.ext_key.derive(0).priv
B = Bob.ext_key.derive(0).pub
bob_notification = Bob.ext_key.derive(0).key
b_notification_address = bob_notification.to_p2pkh
p "bob notification private"
p b
p "bob notification pub"
p B
puts 'bob notification_address'
p b_notification_address
p
alice_key = Bitcoin::Key.from_wif('Kx983SRhAZpAhj7Aac1wUXMJ6XZeyJKqCxJJ49dxEbYCT4a1ozRD')
alice_ec = OpenSSL::PKey::EC.new('secp256k1')
alice_ec.private_key = OpenSSL::BN.new(alice_key.priv_key, 16)
alice_pub_bn = OpenSSL::BN.new(alice_key.pubkey, 16)
bob_ec = OpenSSL::PKey::EC.new('secp256k1')
bob_ec.private_key = OpenSSL::BN.new(b, 16)
bob_pub_bn = OpenSSL::BN.new(B, 16)
p alice_secret_point = OpenSSL::PKey::EC::Point.new(alice_ec.group, bob_pub_bn)
p bob_secret_point = OpenSSL::PKey::EC::Point.new(bob_ec.group, alice_pub_bn)
exit
# # This is shared secret.
a_common_key = alice_ec.dh_compute_key(alice_secret_point).bth
b_common_key = bob_ec.dh_compute_key(bob_secret_point).bth
p a_common_key == b_common_key
p a_common_key
p Alice.shared_secret
# # # o is outpoint
o = '86f411ab1c8e70ae8a0795ab7a6757aea6e4d5ae1826fc7b8f00c597d500609c01000000'
outpoint = Bitcoin::OutPoint.new('86f411ab1c8e70ae8a0795ab7a6757aea6e4d5ae1826fc7b8f00c597d500609c', 1)
# p "masked"
masked_payment_code = Alice.masked_payment_code(o, a_common_key)
notification_transaction = Bitcoin::Tx.new
input = Bitcoin::TxIn.new(out_point: outpoint)
notification_transaction.inputs[0] = input
script = Bitcoin::Script.new
script << Bitcoin::Opcodes::OP_RETURN << masked_payment_code
output = Bitcoin::TxOut.new script_pubkey: script
notification_transaction.outputs[0] = output
p new_tx
p new_tx.to_hex
# Make signature
a_pubkey = alice_key.pubkey
pubkey_hash = Bitcoin.hash160 a_pubkey
pubkey_script = Bitcoin::Script.to_p2pkh(pubkey_hash)
notification_transaction.in[0].script_sig = pubkey_script
p new_tx
signed_data = alice_key.sign(notification_transaction.to_hex)
notification_transaction.in[0].script_sig = Bitcoin::Script.from_string(signed_data)
notification_transaction.to_hex
p notification_transaction
masked_payment_code = notification_transaction.out[0].script_pubkey.op_return_data.bth
alice_payment_code_by_bob = ExtKeyPaymentCode.from_string(masked_payment_code).decode_masked_payment_code(o, a_common_key, masked_payment_code)
a_payment_code = Alice.row_payment_code
alice_payment_code_by_bob == a_payment_code
# Alice to Bob Payment
## Alice selects the 0th private key derived from her payment code:
## this a is correct!
a
## Alice selects the next unused public key derived from Bob's payment code, starting from zero:
## this bobpub is correct!
bobpub = Bob.ext_key.ext_pubkey.derive(0).pubkey
bob_pub_bn = OpenSSL::BN.new(bobpub, 16)
## Alice calculates a secret point:
# a*B
alice_ec = OpenSSL::PKey::EC.new('secp256k1')
alice_ec.private_key = OpenSSL::BN.new(a, 16)
alice_secret_point = OpenSSL::PKey::EC::Point.new(alice_ec.group, bob_pub_bn)
large_s = alice_ec.dh_compute_key(alice_secret_point)
## Alice calculates a scalar shared secret using the x value of S:
# p sx = Key.new(pubkey: large_s).to_point
# this s is correct!!!
s = Bitcoin::Util.sha256(large_s).bth
## B + sG
### sG ?
# sg = Key.new(priv_key: s, compressed: false)
p sg = Key.new(priv_key: s)
# p sg == '0283850c6835576554261fbab5845a099638859528b2725455e6a48e7566415c02'
p sg_point = sg.to_point
# sg = OpenSSL::PKey::EC::Point.new(bob_ec.group, OpenSSL::BN.new(s.bth, 16))
### B=bob_pub_point
# bob_pub_point = OpenSSL::PKey::EC::Point.new(bob_ec.group, bob_pub_bn)
p bob_pub_point = Bob.ext_key.derive(0).key.to_point
p bob_pub_point.x.to_s(16)
p b_prime = bob_pub_point + sg_point
p b_prime.x.to_s(16)
p b_prime.x.to_s(16) == '0344b4795e48df097bd87e6cf87a70e4f0c30b2d847b6e34cddde64af10296952d'
## 03 が必要だった....
b_prime = '03' + b_prime.x.to_s(16)
key = Key.new(pubkey: b_prime)
p key.to_p2pkh == '141fi7TY3h936vRUKh1qfUZr8rSBuYbVBK'
# 141fi7TY3h936vRUKh1qfUZr8rSBuYbVBK
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment