crypto_address_validator 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptoAddressValidator
4
+ class Ltc < Altcoin
5
+ ADDRESS_TYPES = { prod: %w[30 05 32], test: %w[6f c4 3a] }.freeze
6
+ end
7
+ Litecoin = Ltc
8
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptoAddressValidator
4
+ module Utils
5
+ module Bch
6
+ CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
7
+
8
+ module_function
9
+
10
+ def code_list_to_string(code_list)
11
+ code_list.map { |i| Array(i).pack('C*') }.flatten.join
12
+ end
13
+
14
+ def b32decode(inputs)
15
+ out = []
16
+ return out unless inputs
17
+
18
+ inputs.split('').each do |letter|
19
+ out.push(CHARSET.index(letter))
20
+ end
21
+ out
22
+ end
23
+
24
+ def polymod(values)
25
+ chk = 1
26
+ generator = [
27
+ [0x01, 0x98f2bc8e61],
28
+ [0x02, 0x79b76d99e2],
29
+ [0x04, 0xf33e5fb3c4],
30
+ [0x08, 0xae2eabe2a8],
31
+ [0x10, 0x1e4f43e470]
32
+ ]
33
+ values.each do |value|
34
+ top = chk >> 35
35
+ chk = ((chk & 0x07ffffffff) << 5) ^ value
36
+ generator.each do |i|
37
+ chk ^= i[1] if (top & i[0]) != 0
38
+ end
39
+ end
40
+ chk ^ 1
41
+ end
42
+
43
+ def expanded_prefix
44
+ val = if prefix
45
+ prefix.to_s.split('').map do |i|
46
+ i.ord & 0x1f
47
+ end
48
+ else
49
+ []
50
+ end
51
+
52
+ val + [0]
53
+ end
54
+
55
+ def calculate_cash_checksum(payload)
56
+ poly = polymod(expanded_prefix + payload + [0, 0, 0, 0, 0, 0, 0, 0])
57
+ out = []
58
+ 8.times do |i|
59
+ out.push((poly >> 5 * (7 - i)) & 0x1f)
60
+ end
61
+ out
62
+ end
63
+
64
+ def verify_cash_checksum(payload)
65
+ polymod(expanded_prefix + payload) == 0
66
+ rescue TypeError
67
+ raise CryptoAddressValidator::InvalidAddress
68
+ end
69
+
70
+ def b32encode(inputs)
71
+ out = ''
72
+ inputs.each do |char_code|
73
+ out += CHARSET[char_code].to_s
74
+ end
75
+ out
76
+ end
77
+
78
+ def convertbits(data, frombits, tobits, pad = true)
79
+ acc = 0
80
+ bits = 0
81
+ ret = []
82
+ maxv = (1 << tobits) - 1
83
+ max_acc = (1 << (frombits + tobits - 1)) - 1
84
+ data.each do |value|
85
+ return nil if value < 0 || ((value >> frombits) != 0)
86
+
87
+ acc = ((acc << frombits) | value) & max_acc
88
+ bits += frombits
89
+ while bits >= tobits
90
+ bits -= tobits
91
+ ret.push((acc >> bits) & maxv)
92
+ end
93
+ end
94
+ if pad
95
+ ret.push((acc << (tobits - bits)) & maxv) if bits != 0
96
+ elsif bits >= frombits || (((acc << (tobits - bits)) & maxv) != 0)
97
+ return nil
98
+ end
99
+ ret
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptoAddressValidator
4
+ # Ruby reference implementation: https://github.com/sipa/bech32/tree/master/ref/c
5
+ module Utils
6
+ module Bech32
7
+ CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'.unpack('C*')
8
+ CHARSET_REV = [
9
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
10
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
11
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
12
+ 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
13
+ -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
14
+ 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
15
+ -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
16
+ 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
17
+ ].freeze
18
+
19
+ class << self
20
+ def polymod_step(pre)
21
+ b = pre >> 25
22
+ ((pre & 0x1FFFFFF) << 5) ^ \
23
+ (-((b >> 0) & 1) & 0x3b6a57b2) ^ \
24
+ (-((b >> 1) & 1) & 0x26508e6d) ^ \
25
+ (-((b >> 2) & 1) & 0x1ea119fa) ^ \
26
+ (-((b >> 3) & 1) & 0x3d4233dd) ^ \
27
+ (-((b >> 4) & 1) & 0x2a1462b3)
28
+ end
29
+
30
+ # def encode(hrp, data)
31
+ # buf = []
32
+ # chk = 1
33
+
34
+ # hrp.unpack('C*').each do |ch|
35
+ # return nil if ch < 33 || ch > 126
36
+ # return nil if ch >= 'A'.ord && ch <= 'Z'.ord
37
+
38
+ # chk = polymod_step(chk) ^ (ch >> 5)
39
+ # end
40
+
41
+ # return nil if (hrp.bytesize + 7 + data.size) > 90
42
+
43
+ # chk = polymod_step(chk)
44
+ # hrp.unpack('C*').each do |ch|
45
+ # chk = polymod_step(chk) ^ (ch & 0x1f)
46
+ # buf << ch
47
+ # end
48
+
49
+ # buf << '1'.ord
50
+
51
+ # data.each do |i|
52
+ # return nil if (i >> 5) != 0
53
+
54
+ # chk = polymod_step(chk) ^ i
55
+ # buf << CHARSET[i]
56
+ # end
57
+
58
+ # 6.times do
59
+ # chk = polymod_step(chk)
60
+ # end
61
+
62
+ # chk ^= 1
63
+
64
+ # 6.times do |i|
65
+ # buf << CHARSET[(chk >> ((5 - i) * 5)) & 0x1f]
66
+ # end
67
+
68
+ # buf.pack('C*')
69
+ # end
70
+
71
+ # rubocop:disable CyclomaticComplexity,PerceivedComplexity
72
+ def decode(input)
73
+ chk = 1
74
+ input_len = input.bytesize
75
+ have_lower = false
76
+ have_upper = false
77
+
78
+ return nil if input_len < 8 || input_len > 90
79
+
80
+ data_len = 0
81
+ data_len += 1 while data_len < input_len && input[(input_len - 1) - data_len] != '1'
82
+
83
+ hrp_len = input_len - (1 + data_len)
84
+ return nil if hrp_len < 1 || data_len < 6
85
+
86
+ hrp = []
87
+ hrp_len.times do |i|
88
+ ch = input[i].ord
89
+ return nil if ch < 33 || ch > 126
90
+
91
+ if ch >= 'a'.ord && ch <= 'z'.ord
92
+ have_lower = true
93
+ elsif ch >= 'A'.ord && ch <= 'Z'.ord
94
+ have_upper = true
95
+ ch = (ch - 'A'.ord) + 'a'.ord
96
+ end
97
+
98
+ hrp << ch
99
+ chk = polymod_step(chk) ^ (ch >> 5)
100
+ end
101
+
102
+ chk = polymod_step(chk)
103
+
104
+ hrp_len.times do |i|
105
+ chk = polymod_step(chk) ^ (input[i].ord & 0x1f)
106
+ end
107
+
108
+ data = []
109
+ i = hrp_len + 1
110
+ while i < input_len
111
+ ch = input[i].ord
112
+ v = (ch & 0x80) != 0 ? -1 : CHARSET_REV[ch]
113
+
114
+ have_lower = true if ch >= 'a'.ord && ch <= 'z'.ord
115
+ have_upper = true if ch >= 'A'.ord && ch <= 'Z'.ord
116
+ return nil if v == -1
117
+
118
+ chk = polymod_step(chk) ^ v
119
+ data << v if (i + 6) < input_len
120
+ i += 1
121
+ end
122
+
123
+ return nil if have_lower && have_upper
124
+ return nil if chk != 1
125
+
126
+ [hrp.pack('C*'), data]
127
+ end
128
+ # rubocop:enable CyclomaticComplexity,PerceivedComplexity
129
+
130
+ # Utility for converting bytes of data between bases. These is used for
131
+ # BIP 173 address encoding/decoding to convert between sequences of bytes
132
+ # representing 8-bit values and groups of 5 bits. Conversions may be padded
133
+ # with trailing 0 bits to the nearest byte boundary. Returns nil if
134
+ # conversion requires padding and pad is false.
135
+ #
136
+ # For example:
137
+ #
138
+ # convert_bits("\xFF\xFF", from_bits: 8, to_bits: 5, pad: true)
139
+ # => "\x1F\x1F\x1F\10"
140
+ #
141
+ # See https://github.com/bitcoin/bitcoin/blob/595a7bab23bc21049526229054ea1fff1a29c0bf/src/utilstrencodings.h#L154
142
+ def convert_bits(chunks, from_bits:, to_bits:, pad:)
143
+ output_mask = (1 << to_bits) - 1
144
+ buffer_mask = (1 << (from_bits + to_bits - 1)) - 1
145
+
146
+ buffer = 0
147
+ bits = 0
148
+
149
+ output = []
150
+ chunks.each do |chunk|
151
+ buffer = ((buffer << from_bits) | chunk) & buffer_mask
152
+ bits += from_bits
153
+ while bits >= to_bits
154
+ bits -= to_bits
155
+ output << ((buffer >> bits) & output_mask)
156
+ end
157
+ end
158
+
159
+ output << ((buffer << (to_bits - bits)) & output_mask) if pad && bits > 0
160
+
161
+ return nil if !pad && (bits >= from_bits || ((buffer << (to_bits - bits)) & output_mask) != 0)
162
+
163
+ output
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptoAddressValidator
4
+ VERSION = '0.2'
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptoAddressValidator
4
+ class Xrp < Altcoin
5
+ ADDRESS_TYPES = { common: %w[00] }.freeze
6
+ ALPHABET_TYPE = :ripple
7
+ end
8
+ Ripple = Xrp
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptoAddressValidator
4
+ class Zec < Altcoin
5
+ EXPECTED_LENGTH = 52
6
+ ADDRESS_TYPES = { prod: %w[1cb8 1cbd], test: %w[1d25 1cba] }.freeze
7
+ end
8
+ Zcash = Zec
9
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'crypto_address_validator/utils/bech32'
4
+ require 'crypto_address_validator/utils/bch'
5
+
6
+ require 'crypto_address_validator/altcoin'
7
+ require 'crypto_address_validator/bch'
8
+ require 'crypto_address_validator/eth'
9
+ require 'crypto_address_validator/btc'
10
+ require 'crypto_address_validator/xrp'
11
+ require 'crypto_address_validator/dash'
12
+ require 'crypto_address_validator/zec'
13
+ require 'crypto_address_validator/ltc'
14
+
15
+ module CryptoAddressValidator
16
+ class UnknownCurrency < StandardError; end
17
+ module_function
18
+
19
+ def valid?(address, currency, type = nil)
20
+ address(address, currency).valid?(type)
21
+ end
22
+
23
+ def address(address, currency)
24
+ CryptoAddressValidator.const_get(currency.capitalize).new(address)
25
+ rescue NameError
26
+ raise UnknownCurrency, "Wrong currency #{currency}"
27
+ end
28
+
29
+ def address_type(address, currency)
30
+ CryptoAddressValidator.const_get(currency.capitalize).new(address).address_type
31
+ end
32
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe(CryptoAddressValidator::Bch) do
4
+ let(:legacy_p2sh) { '3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC' }
5
+ let(:legacy_p2pkh) { '155fzsEBHy9Ri2bMQ8uuuR3tv1YzcDywd4' }
6
+ let(:cashaddr_p2sh) { 'bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq' }
7
+ let(:cashaddr_p2pkh) { 'bitcoincash:qqkv9wr69ry2p9l53lxp635va4h86wv435995w8p2h' }
8
+ let(:cashaddr_p2pkh_testnet) { 'bchtest:qpqtmmfpw79thzq5z7s0spcd87uhn6d34uqqem83hf' }
9
+ let(:legacy_p2pkh_testnet) { 'mmRH4e9WW4ekZUP5HvBScfUyaSUjfQRyvD' }
10
+ let(:cashaddr_p2sh_testnet) { 'bchtest:pp8f7ww2g6y07ypp9r4yendrgyznysc9kqxh6acwu3' }
11
+ let(:legacy_p2sh_testnet) { '2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc' }
12
+ let(:mixed_case_cashadddr) { 'bitcoincash:qqkv9wr69ry2p9l53lxP635va4h86wv435995w8p2H' }
13
+
14
+ describe '#legacy_address' do
15
+ it 'converts legacy testnet p2pkh' do
16
+ expect(described_class.new(legacy_p2pkh_testnet).legacy_address).to eq(legacy_p2pkh_testnet)
17
+ expect(described_class.new(cashaddr_p2pkh_testnet).legacy_address).to eq(legacy_p2pkh_testnet)
18
+ end
19
+
20
+ it 'converts legacy testnet p2sh' do
21
+ expect(described_class.new(legacy_p2sh_testnet).legacy_address).to eq(legacy_p2sh_testnet)
22
+ expect(described_class.new(cashaddr_p2sh_testnet).legacy_address).to eq(legacy_p2sh_testnet)
23
+ end
24
+
25
+ it 'converts to legacy p2sh' do
26
+ expect(described_class.new(legacy_p2sh).legacy_address).to eq(legacy_p2sh)
27
+ expect(described_class.new(cashaddr_p2sh).legacy_address).to eq(legacy_p2sh)
28
+ end
29
+
30
+ it 'converts to legacy p2pkh' do
31
+ expect(described_class.new(legacy_p2pkh).legacy_address).to eq(legacy_p2pkh)
32
+ expect(described_class.new(cashaddr_p2pkh).legacy_address).to eq(legacy_p2pkh)
33
+ end
34
+
35
+ context 'uppercase cashaddr address' do
36
+ it 'converts to legacy p2sh' do
37
+ expect(described_class.new(cashaddr_p2sh.upcase).legacy_address).to eq(legacy_p2sh)
38
+ end
39
+
40
+ it 'converts to legacy p2pkh' do
41
+ expect(described_class.new(cashaddr_p2pkh.upcase).legacy_address).to eq(legacy_p2pkh)
42
+ end
43
+ end
44
+ end
45
+
46
+ context '#cash_address' do
47
+ it 'converts cash testnet p2pkh' do
48
+ expect(described_class.new(legacy_p2pkh_testnet).cash_address).to eq(cashaddr_p2pkh_testnet)
49
+ expect(described_class.new(cashaddr_p2pkh_testnet).cash_address).to eq(cashaddr_p2pkh_testnet)
50
+ end
51
+
52
+ it 'converts cash testnet p2sh' do
53
+ expect(described_class.new(legacy_p2sh_testnet).cash_address).to eq(cashaddr_p2sh_testnet)
54
+ expect(described_class.new(cashaddr_p2sh_testnet).cash_address).to eq(cashaddr_p2sh_testnet)
55
+ end
56
+
57
+ it 'converts to cash p2sh' do
58
+ expect(described_class.new(legacy_p2sh).cash_address).to eq(cashaddr_p2sh)
59
+ expect(described_class.new(cashaddr_p2sh).cash_address).to eq(cashaddr_p2sh)
60
+ end
61
+
62
+ it 'converts to cash p2pkh' do
63
+ expect(described_class.new(legacy_p2pkh).cash_address).to eq(cashaddr_p2pkh)
64
+ expect(described_class.new(cashaddr_p2pkh).cash_address).to eq(cashaddr_p2pkh)
65
+ end
66
+
67
+ context 'uppercase cashaddr address' do
68
+ it 'converts to cash p2sh' do
69
+ expect(described_class.new(cashaddr_p2sh.upcase).cash_address).to eq(cashaddr_p2sh)
70
+ end
71
+
72
+ it 'converts to cash p2pkh' do
73
+ expect(described_class.new(cashaddr_p2pkh.upcase).cash_address).to eq(cashaddr_p2pkh)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe(CryptoAddressValidator) do
4
+ describe '.valid?' do
5
+ context 'Bitcoin' do
6
+ it 'validates hash160 addresses' do
7
+ expect(described_class).to be_valid('12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP', 'bitcoin')
8
+ expect(described_class).to be_valid('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y', 'BTC')
9
+ expect(described_class).to be_valid('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y', 'Bitcoin')
10
+ expect(described_class).to be_valid('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y', 'btc')
11
+ expect(described_class).to be_valid('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y', 'btc', :hash160)
12
+
13
+ # testnet
14
+ expect(described_class).to be_valid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef', 'bitcoin', 'hash160test')
15
+ expect(described_class).to be_valid('mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB', :btc)
16
+ end
17
+
18
+ it 'validates p2sh addresses' do
19
+ expect(described_class).to be_valid('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt', 'BTC')
20
+ expect(described_class).to be_valid('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt', 'bitcoin', 'p2sh')
21
+
22
+ # testnet
23
+ expect(described_class).to be_valid('2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7', 'btc')
24
+ expect(described_class).to be_valid('2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7', 'bitcoin', 'p2shtest')
25
+ end
26
+
27
+ it 'validates segwit addresses' do
28
+ expect(described_class).to be_valid('BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4', 'BTC')
29
+ expect(described_class).to be_valid('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', 'bitcoin')
30
+ expect(described_class).to be_valid('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', 'bitcoin', :segwit_v0_keyhash)
31
+ expect(described_class).to be_valid('bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9', 'BTC')
32
+ expect(described_class).to be_valid('bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9', 'bitcoin', 'segwit_v0_scripthash')
33
+ end
34
+
35
+ it 'validates multiple types at once' do
36
+ expect(described_class).to be_valid('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y', 'btc', [:p2sh, :hash160])
37
+ end
38
+
39
+ it 'validates wrong addresses' do
40
+ expect(described_class).not_to be_valid('asdf', :bitcoin)
41
+ expect(described_class).not_to be_valid('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt', 'bitcoin', :segwit_v0_keyhash)
42
+ expect(described_class).not_to be_valid('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt', 'bitcoin', 'asdf')
43
+ expect(described_class).not_to be_valid('tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty', :bitcoin)
44
+ expect(described_class).not_to be_valid('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5', 'bitcoin')
45
+ expect(described_class).not_to be_valid('BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2', 'bitcoin')
46
+ expect(described_class).not_to be_valid('bc1rw5uspcuh', 'bitcoin')
47
+ expect(described_class).not_to be_valid('bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90', 'bitcoin')
48
+ expect(described_class).not_to be_valid('BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P', 'bitcoin')
49
+ expect(described_class).not_to be_valid('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7', 'BTC')
50
+ expect(described_class).not_to be_valid('bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du', 'bitcoin')
51
+ expect(described_class).not_to be_valid('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv', 'Bitcoin')
52
+ expect(described_class).not_to be_valid('bc1gmk9yu', 'bitcoin')
53
+ end
54
+ end
55
+
56
+ context 'Bitcoincash' do
57
+ it 'validates legacy addresses' do
58
+ expect(described_class).to be_valid('3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC', :bch, :p2sh)
59
+ expect(described_class).to be_valid('155fzsEBHy9Ri2bMQ8uuuR3tv1YzcDywd4', 'bitcoincash', :p2pkh)
60
+ expect(described_class).to be_valid('2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc', 'BCH', :p2shtest)
61
+ expect(described_class).to be_valid('mmRH4e9WW4ekZUP5HvBScfUyaSUjfQRyvD', :BCH, :p2pkhtest)
62
+ end
63
+
64
+ it 'validates cash addresses' do
65
+ expect(described_class).to be_valid('bitcoincash:qqkv9wr69ry2p9l53lxp635va4h86wv435995w8p2h', :bch, :p2pkh)
66
+ expect(described_class).to be_valid('bitcoincash:pqdg9uq52wzhf228hweext9j2jdjgdpj9qt7xxfngd', :bitcoincash, :p2sh)
67
+ expect(described_class).to be_valid('bchtest:qpqtmmfpw79thzq5z7s0spcd87uhn6d34uqqem83hf', :Bitcoincash, :p2pkhtest)
68
+ expect(described_class).to be_valid('bchtest:pp8f7ww2g6y07ypp9r4yendrgyznysc9kqxh6acwu3', :BCH, :p2shtest)
69
+ expect(described_class).to be_valid('bitcoincash:qrtj3rd8524cndt2eew3s6wljqggmne00sgh4kfypk', :bitcoincash)
70
+ end
71
+
72
+ it 'validates cash addresses without prefix addresses' do
73
+ expect(described_class).to be_valid('qrtj3rd8524cndt2eew3s6wljqggmne00sgh4kfypk', :bitcoincash)
74
+ expect(described_class).to be_valid('pqdg9uq52wzhf228hweext9j2jdjgdpj9qt7xxfngd', :bitcoincash, :p2sh)
75
+ end
76
+
77
+ it 'validates wrong addresses' do
78
+ expect(described_class).not_to be_valid('bitcoincash:qqkv9wr69ry2p9l53lxP635va4h86wv435995w8p2H', :bch)
79
+ expect(described_class).not_to be_valid('wrong', :bch)
80
+ expect(described_class).not_to be_valid('bitcoincash:wrong', :bch)
81
+ expect(described_class).not_to be_valid('bitcoincash:123', :bch)
82
+
83
+ expect(described_class).not_to be_valid('bitcoincash:qqkv9wr69ry2p9l53lxp635va4h86wv435995w8p2h', :bch, :p2pkhtest)
84
+ expect(described_class).not_to be_valid('bitcoincash:pqdg9uq52wzhf228hweext9j2jdjgdpj9qt7xxfngd', :bitcoincash, :p2pkhtest)
85
+ expect(described_class).not_to be_valid('bchtest:qpqtmmfpw79thzq5z7s0spcd87uhn6d34uqqem83hf', :Bitcoincash, :p2sh)
86
+ expect(described_class).not_to be_valid('bchtest:pp8f7ww2g6y07ypp9r4yendrgyznysc9kqxh6acwu3', :BCH, :p2sh)
87
+ end
88
+ end
89
+
90
+ context 'Ethereum' do
91
+ it 'validates addresses' do
92
+ expect(described_class).to be_valid('0xE37c0D48d68da5c5b14E5c1a9f1CFE802776D9FF', 'ethereum')
93
+ expect(described_class).to be_valid('0xa00354276d2fC74ee91e37D085d35748613f4748', :ethereum)
94
+ expect(described_class).to be_valid('0xAff4d6793F584a473348EbA058deb8caad77a288', :ETH)
95
+ expect(described_class).to be_valid('0xc6d9d2cd449a754c494264e1809c50e34d64562b', 'ETH')
96
+ expect(described_class).to be_valid('0x52908400098527886E0F7030069857D2E4169EE7', 'ETH')
97
+ expect(described_class).to be_valid('0x8617E340B3D01FA5F11F306F4090FD50E238070D', 'ETH')
98
+ expect(described_class).to be_valid('0xde709f2102306220921060314715629080e2fb77', 'ETH')
99
+ end
100
+
101
+ it 'validates without prefixes addresses' do
102
+ expect(described_class).to be_valid('27b1fdb04752bbc536007a920d24acb045561c26', 'ETH')
103
+ expect(described_class).to be_valid('5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', 'ETH')
104
+ expect(described_class).to be_valid('fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359', 'ETH')
105
+ expect(described_class).to be_valid('dbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB', 'ETH')
106
+ expect(described_class).to be_valid('D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb', 'ETH')
107
+ end
108
+
109
+ it 'validates wrong addresses' do
110
+ expect(described_class).not_to be_valid('wrong', :ETH)
111
+ expect(described_class).not_to be_valid('0xD1110A0cf47c7B9Be7A2E6BA89F429762e7b9aDb', 'ETH')
112
+ expect(described_class).not_to be_valid('a10354276d2fC74ee91e37D085d35748613f4748', :ethereum)
113
+ end
114
+ end
115
+
116
+ context 'Ripple' do
117
+ it 'validates addresses' do
118
+ expect(described_class).to be_valid('rPMLwSwyyULN2acf5JKB1nj8F8Eu8pVMV8', :ripple)
119
+ expect(described_class).to be_valid('rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', :ripple)
120
+ expect(described_class).to be_valid('rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', 'RIPPLE')
121
+ expect(described_class).to be_valid('r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV', 'XRP')
122
+ expect(described_class).to be_valid('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', :XRP)
123
+ expect(described_class).to be_valid('rDTXLQ7ZKZVKz33zJbHjgVShjsBnqMBhmN', 'XRP')
124
+ end
125
+
126
+ it 'validates wrong addresses' do
127
+ expect(described_class).not_to be_valid('wrong', :xrp)
128
+ expect(described_class).not_to be_valid('r3kmLJN5D28dHuH8vZNUZpMC43pEHpaoc1', :xrp)
129
+ expect(described_class).not_to be_valid('r1kmLJN5D28dHuH8vZNUZpMC43pEHpaocV', 'ripple')
130
+ end
131
+ end
132
+
133
+ context 'Dash' do
134
+ it 'validates addresses' do
135
+ expect(described_class).to be_valid('Xx4dYKgz3Zcv6kheaqog3fynaKWjbahb6b', :dash)
136
+ expect(described_class).to be_valid('XcY4WJ6Z2Q8w7vcYER1JypC8s2oa3SQ1b1', 'DASH')
137
+ expect(described_class).to be_valid('XqMkVUZnqe3w4xvgdZRtZoe7gMitDudGs4', 'DASH', :prod)
138
+ expect(described_class).to be_valid('yPv7h2i8v3dJjfSH4L3x91JSJszjdbsJJA', :DASH, 'test')
139
+ end
140
+
141
+ it 'validates wrong addresses' do
142
+ expect(described_class).not_to be_valid('wrong', :dash)
143
+ expect(described_class).not_to be_valid('yPv7h2i8v3dJ1fSH4L3x91JSJszjdbsJJA', :dash)
144
+ expect(described_class).not_to be_valid('XqMkVUZnqe3w4xvgdZRtZoe7gMitDudGs4', 'dash', :test)
145
+ expect(described_class).not_to be_valid('yPv7h2i8v3dJjfSH4L3x91JSJszjdbsJJA', :DASH, :prod)
146
+ end
147
+ end
148
+
149
+ context 'Zcash' do
150
+ it 'validates addresses' do
151
+ expect(described_class).to be_valid('t1U9yhDa5XEjgfnTgZoKddeSiEN1aoLkQxq', :zec)
152
+ expect(described_class).to be_valid('t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd', 'zcash', :prod)
153
+ expect(described_class).to be_valid('t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi', 'ZEC', :test)
154
+ end
155
+
156
+ it 'validates wrong addresses' do
157
+ expect(described_class).not_to be_valid('wrong', :zec)
158
+ expect(described_class).not_to be_valid('t1Y9yhDa5XEjgfnTgZoKddeSiEN1aoLkQxq', :zcash)
159
+ expect(described_class).not_to be_valid('t3Yz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd', :ZEC)
160
+ expect(described_class).not_to be_valid('t2YNzUUx8mWBCRYPRezvA363EYXyEpHokyi', :zcash, :test)
161
+ end
162
+ end
163
+
164
+ context 'Litecoin' do
165
+ it 'validates addresses' do
166
+ expect(described_class).to be_valid('LVg2kJoFNg45Nbpy53h7Fe1wKyeXVRhMH9', :ltc)
167
+ expect(described_class).to be_valid('LVg2kJoFNg45Nbpy53h7Fe1wKyeXVRhMH9', 'ltc', :prod)
168
+ expect(described_class).to be_valid('LTpYZG19YmfvY2bBDYtCKpunVRw7nVgRHW', 'LTC')
169
+ expect(described_class).to be_valid('Lb6wDP2kHGyWC7vrZuZAgV7V4ECyDdH7a6', 'Litecoin')
170
+ expect(described_class).to be_valid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef', 'Litecoin', :test)
171
+
172
+ expect(described_class).to be_valid('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt', 'LTC')
173
+ expect(described_class).to be_valid('2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7', 'LTC', :test)
174
+ expect(described_class).to be_valid('QW2SvwjaJU8LD6GSmtm1PHnBG2xPuxwZFy', 'LTC', :test)
175
+ expect(described_class).to be_valid('QjpzxpbLp5pCGsCczMbfh1uhC3P89QZavY', 'LTC', :test)
176
+ end
177
+
178
+ it 'validates wrong addresses' do
179
+ expect(described_class).not_to be_valid('wrong', :zec)
180
+ expect(described_class).not_to be_valid('t1Y9yhDa5XEjgfnTgZoKddeSiEN1aoLkQxq', :zcash)
181
+ expect(described_class).not_to be_valid('t3Yz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd', :ZEC)
182
+ expect(described_class).not_to be_valid('t2YNzUUx8mWBCRYPRezvA363EYXyEpHokyi', :zcash, :test)
183
+ end
184
+ end
185
+ end
186
+
187
+ describe '.address' do
188
+ it 'returns insance' do
189
+ expect(described_class.address('D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb', 'eth')).to be_kind_of(CryptoAddressValidator::Eth)
190
+ end
191
+
192
+ it 'raises UnknownCurrency with unknown currency' do
193
+ expect { described_class.address('addr', 'asdf') }.to raise_error(CryptoAddressValidator::UnknownCurrency, /asdf/)
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'coveralls'
4
+ Coveralls.wear!
5
+
6
+ $LOAD_PATH.unshift File.dirname(__FILE__)
7
+ require 'rspec'
8
+ require 'crypto_address_validator'
9
+
10
+ RSpec.configure do |config|
11
+ # Enable flags like --only-failures and --next-failure
12
+ config.example_status_persistence_file_path = '.rspec_status'
13
+
14
+ config.order = :random
15
+ config.filter_run :focus
16
+ config.run_all_when_everything_filtered = true
17
+ end