Skip to content

Instantly share code, notes, and snippets.

@sashazykov
Created January 9, 2013 09:04
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sashazykov/4491729 to your computer and use it in GitHub Desktop.
Save sashazykov/4491729 to your computer and use it in GitHub Desktop.
Rails 3 bitcoin address validator. Validator syntaxis: validates :address, :bitcoin_address => true, :presence => true
require 'digest'
class BitcoinAddressValidator < ActiveModel::EachValidator
def validate_each(record, field, value)
unless value.blank? || valid_bitcoin_address?(value)
record.errors[field] << "Bitcoin address is invalid"
end
end
private
B58Chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
B58Base = B58Chars.length
def valid_bitcoin_address?(address)
(address =~ /^[a-zA-Z1-9]{33,35}$/) and version(address)
end
def version(address)
decoded = b58_decode(address, 25)
version = decoded[0, 1]
checksum = decoded[-4, decoded.length]
vh160 = decoded[0, decoded.length - 4]
hashed = (Digest::SHA2.new << (Digest::SHA2.new << vh160).digest).digest
hashed[0, 4] == checksum ? version[0] : nil
end
def b58_decode(value, length)
long_value = 0
index = 0
result = ""
value.reverse.each_char do |c|
long_value += B58Chars.index(c) * (B58Base ** index)
index += 1
end
while long_value >= 256 do
div, mod = long_value.divmod 256
result = mod.chr + result
long_value = div
end
result = long_value.chr + result
if result.length < length
result = 0.chr * (length - result.length) + result
end
result
end
end
@chrhansen
Copy link

Seems the validator needs a check for first character is either 1 or 3. I can delete the first number (1 or 3) of a valid address and it will still pass the validator even though the address now begins with say, 4 or 8. Also, it's not allowing addresses less than 33 characters. A bitcoin address could be down to 27 characters.
https://en.bitcoin.it/wiki/Address
Let me know if I misunderstood something.

@christiangenco
Copy link

My simpler revision: https://gist.github.com/christiangenco/8348162

(doesn't calculate checksum)

@crabvk
Copy link

crabvk commented Aug 12, 2015

Validator syntaxis: validates :address, :bitcoin_address => true, presence => true

':presence => true' (and 'allow_blank: true' also) does not make sense here because of 'value.blank?' condition in your 'validate_each'. Anyway, blank string is not valid bitcoin address. Have to remove this.

Also "fake_address\n3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy\nwhat_the" is matched for the regex. Should use \A and \z instead of ^ and $.

There is a good bitcoin address validator in bitcoin-ruby gem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment