Class: CashAddr::Address

Inherits:
Object
  • Object
show all
Defined in:
lib/cash_addr/address.rb

Constant Summary collapse

VERSION_MAP =
{
  'legacy' => [
    ['P2SH', 5],
    ['P2PKH', 0],
    ['P2SHTestnet', 196],
    ['P2PKHTestnet', 111]
  ],
  'cash' => [
    ['P2SH', 8],
    ['P2PKH', 0],
    ['P2SHTestnet', 8],
    ['P2PKHTestnet', 0]
  ]
}.freeze
DEFAULT_PREFIX =
'bitcoincash'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(version, payload, prefix = nil, digest = nil) ⇒ Address

Returns a new instance of Address.



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/cash_addr/address.rb', line 26

def initialize(version, payload, prefix = nil, digest = nil)
  @version = version
  @payload = payload
  @digest = digest

  @prefix = prefix ? prefix : DEFAULT_PREFIX

  @version = 'P2SHTestnet' if prefix == 'bchtest' && version == 'P2SH'
  @version = 'P2PKHTestnet' if prefix == 'bchtest' && version == 'P2PKH'
  @prefix = 'bchtest' if %w[P2SHTestnet P2PKHTestnet].include?(@version)
end

Instance Attribute Details

#digestObject

Returns the value of attribute digest.



8
9
10
# File 'lib/cash_addr/address.rb', line 8

def digest
  @digest
end

#payloadObject

Returns the value of attribute payload.



8
9
10
# File 'lib/cash_addr/address.rb', line 8

def payload
  @payload
end

#prefixObject

Returns the value of attribute prefix.



8
9
10
# File 'lib/cash_addr/address.rb', line 8

def prefix
  @prefix
end

#versionObject

Returns the value of attribute version.



8
9
10
# File 'lib/cash_addr/address.rb', line 8

def version
  @version
end

Class Method Details

.address_type(address_type, version) ⇒ Object

Raises:



57
58
59
60
61
62
63
# File 'lib/cash_addr/address.rb', line 57

def self.address_type(address_type, version)
  VERSION_MAP[address_type].each do |mapping|
    return mapping if mapping.include?(version)
  end

  raise(CashAddr::InvalidAddress.new, 'Could not determine address version')
end

.cash_string(address_string) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/cash_addr/address.rb', line 85

def self.cash_string(address_string)
  if address_string.upcase != address_string && address_string.downcase != address_string
    raise(CashAddr::InvalidAddress.new, 'Cash address contains uppercase and lowercase characters')
  end

  address_string = address_string.downcase
  if !address_string.include?(':')
    address_string = DEFAULT_PREFIX + ':' + address_string
  end

  pre, base32string = address_string.split(':')
  decoded = CashAddr::Crypto.b32decode(base32string)

  if !CashAddr::Crypto.verify_checksum(pre, decoded)
    raise(CashAddr::InvalidAddress.new, 'Bad cash address checksum')
  end

  converted = CashAddr::Crypto.convertbits(decoded, 5, 8)
  ver = CashAddr::Address.address_type('cash', converted[0].to_i)[0]
  p = converted[1..-7]

  new(ver, p, pre, nil)
end

.code_list_to_string(code_list) ⇒ Object



53
54
55
# File 'lib/cash_addr/address.rb', line 53

def self.code_list_to_string(code_list)
  code_list.map { |i| Array(i).pack('C*') }.flatten.join
end

.from_string(address_string) ⇒ Object



65
66
67
68
69
# File 'lib/cash_addr/address.rb', line 65

def self.from_string(address_string)
  raise(CashAddr::InvalidAddress, 'Expected string as input') unless address_string.is_a?(String)
  return legacy_string(address_string) unless address_string.include?(':')
  cash_string(address_string)
end

.legacy_string(address_string) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/cash_addr/address.rb', line 71

def self.legacy_string(address_string)
  decoded = nil
  begin
    decoded = Base58.base58_to_binary(address_string, :bitcoin).bytes
  rescue StandardError
    raise(CashAddr::InvalidAddress.new, 'Could not decode legacy address')
  end
  ver = address_type('legacy', decoded[0].to_i)[0]
  payload = decoded[1..-5]
  digest = decoded[-4..-1]

  new(ver, payload, nil, digest)
end

Instance Method Details

#cash_addressObject



45
46
47
48
49
50
51
# File 'lib/cash_addr/address.rb', line 45

def cash_address
  version_int = self.class.address_type('cash', version)[1]
  p = [version_int] + payload
  p = CashAddr::Crypto.convertbits(p, 8, 5)
  checksum = CashAddr::Crypto.calculate_checksum(prefix, p)
  prefix + ':' + CashAddr::Crypto.b32encode(p + checksum)
end

#legacy_addressObject



38
39
40
41
42
43
# File 'lib/cash_addr/address.rb', line 38

def legacy_address
  version_int = self.class.address_type('legacy', version)[1]
  input = self.class.code_list_to_string([version_int] + payload + Array(digest))
  input += Digest::SHA256.digest(Digest::SHA256.digest(input))[0..3] unless digest
  Base58.binary_to_base58(input, :bitcoin)
end