Skip to content

Commit

Permalink
Add Bitcoin::SilentPayment::Addr
Browse files Browse the repository at this point in the history
  • Loading branch information
azuchi committed Jul 30, 2024
1 parent 1164a77 commit e34aa1f
Show file tree
Hide file tree
Showing 4 changed files with 2,839 additions and 0 deletions.
5 changes: 5 additions & 0 deletions lib/bitcoin/silent_payment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Bitcoin
module SilentPayment
autoload :Addr, 'bitcoin/sp/addr'
end
end
55 changes: 55 additions & 0 deletions lib/bitcoin/sp/addr.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module Bitcoin
module SilentPayment
class Addr

HRP_MAINNET = 'sp'
HRP_TESTNET = 'tsp'
MAX_CHARACTERS = 1023

attr_reader :version
attr_reader :scan_key
attr_reader :spend_key

# Constructor.
# @param [Bitcoin::Key] scan_key
# @param [Bitcoin::Key] spend_key
def initialize(version, scan_key:, spend_key:)
raise ArgumentError, "version must be integer." unless version.is_a?(Integer)
raise ArgumentError, "scan_key must be Bitcoin::Key." unless scan_key.is_a?(Bitcoin::Key)
raise ArgumentError, "spend_key must be Bitcoin::Key." unless spend_key.is_a?(Bitcoin::Key)
raise ArgumentError, "version '#{version}' is unsupported." unless version.zero?

@version = version
@scan_key = scan_key
@spend_key = spend_key
end

# Parse silent payment address.
# @param [String] A silent payment address.
# @return [Bitcoin::SilentPayment::Addr]
def self.from_string(addr)
raise ArgumentError, "addr must be string." unless addr.is_a?(String)
hrp, data, spec = Bech32.decode(addr, MAX_CHARACTERS)
unless hrp == Bitcoin.chain_params.mainnet? ? HRP_MAINNET : HRP_TESTNET
raise ArgumentError, "The specified hrp is different from the current network HRP."
end
raise ArgumentError, "spec must be bech32m." unless spec == Bech32::Encoding::BECH32M

ver = data[0]
payload = Bech32.convert_bits(data[1..-1], 5, 8, false).pack("C*")
scan_key = Bitcoin::Key.new(pubkey: payload[0...33].bth, key_type: Bitcoin::Key::TYPES[:compressed])
spend_key = Bitcoin::Key.new(pubkey: payload[33..-1].bth, key_type: Bitcoin::Key::TYPES[:compressed])
Addr.new(ver, scan_key: scan_key, spend_key: spend_key)
end

# Get silent payment address.
# @return [String]
def to_s
hrp = Bitcoin.chain_params.mainnet? ? HRP_MAINNET : HRP_TESTNET
payload = [scan_key.pubkey + spend_key.pubkey].pack("H*").unpack('C*')
Bech32.encode(hrp, [version] + Bech32.convert_bits(payload, 8, 5), Bech32::Encoding::BECH32M)
end

end
end
end
19 changes: 19 additions & 0 deletions spec/bitcoin/silent_payment_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'spec_helper'

RSpec.describe Bitcoin::SilentPayment, network: :mainnet do

describe 'BIP352 Test Vector' do
it do
vectors = fixture_file('bip352/send_and_receive_test_vectors.json')
vectors.each do |v|
v['sending'].each do |s|
d = s['given']
recipients = d['recipients'].map do |r|
Bitcoin::SilentPayment::Addr.from_string(r)
end
expect(recipients.map(&:to_s)).to eq(d['recipients'])
end
end
end
end
end
Loading

0 comments on commit e34aa1f

Please sign in to comment.