Class: MoneyTree::PrivateKey

Inherits:
Key
  • Object
show all
Defined in:
lib/money-tree/key.rb

Constant Summary

Constants inherited from Key

Key::GROUP_NAME, Key::ORDER

Constants included from Support

Support::BASE58_CHARS, Support::INT32_MAX, Support::INT64_MAX

Instance Attribute Summary

Attributes inherited from Key

#ec_key, #key, #options, #raw_key

Instance Method Summary collapse

Methods inherited from Key

#to_bytes, #to_i, #valid?

Methods included from Support

#base58_to_int, #bytes_to_hex, #bytes_to_int, #convert_p2wpkh_p2sh, #custom_hash_160, #decode_base58, #decode_base64, #digestify, #encode_base58, #encode_base64, #encode_p2wpkh_p2sh, #from_serialized_base58, #hex_to_bytes, #hex_to_int, #hmac_sha512, #hmac_sha512_hex, #int_to_base58, #int_to_bytes, #int_to_hex, #ripemd160, #sha256, #to_serialized_base58, #to_serialized_bech32

Constructor Details

#initialize(opts = {}) ⇒ PrivateKey

Returns a new instance of PrivateKey.



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/money-tree/key.rb', line 40

def initialize(opts = {})
  @options = opts
  generate
  if @options[:key]
    @raw_key = @options[:key]
    @key = parse_raw_key
    import
  else
    @key = to_hex
  end
end

Instance Method Details

#base64_format?(base64_key = raw_key) ⇒ Boolean

Returns:

  • (Boolean)


123
124
125
# File 'lib/money-tree/key.rb', line 123

def base64_format?(base64_key = raw_key)
  base64_key.length == 44 && base64_key =~ /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
end

#calculate_public_key(opts = {}) ⇒ Object



69
70
71
72
73
74
# File 'lib/money-tree/key.rb', line 69

def calculate_public_key(opts = {})
  opts[:compressed] = true unless opts[:compressed] == false
  group = ec_key.group
  group.point_conversion_form = opts[:compressed] ? :compressed : :uncompressed
  point = group.generator.mul OpenSSL::BN.new(key, 16)
end

#compressed_wif_format?Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/money-tree/key.rb', line 109

def compressed_wif_format?
  wif_format?(:compressed)
end

#data_sequenceObject



60
61
62
63
64
65
66
67
# File 'lib/money-tree/key.rb', line 60

def data_sequence
  OpenSSL::ASN1::Sequence([
    OpenSSL::ASN1::Integer(1),
    OpenSSL::ASN1::OctetString(OpenSSL::BN.new(key, 16).to_s(2)),
    OpenSSL::ASN1::ObjectId(GROUP_NAME, 0, :EXPLICIT),
    OpenSSL::ASN1::BitString(calculate_public_key.to_octet_string(:uncompressed), 1, :EXPLICIT),
  ])
end

#from_base64(base64_key = raw_key) ⇒ Object



104
105
106
107
# File 'lib/money-tree/key.rb', line 104

def from_base64(base64_key = raw_key)
  raise InvalidBase64Format unless base64_format?(base64_key)
  decode_base64(base64_key)
end

#from_hex(hex = raw_key) ⇒ Object



92
93
94
# File 'lib/money-tree/key.rb', line 92

def from_hex(hex = raw_key)
  hex
end

#from_integer(bignum = raw_key) ⇒ Object



87
88
89
90
# File 'lib/money-tree/key.rb', line 87

def from_integer(bignum = raw_key)
  # TODO: does this need a byte size specification?
  int_to_hex(bignum)
end

#from_wif(wif = raw_key) ⇒ Object



96
97
98
99
100
101
102
# File 'lib/money-tree/key.rb', line 96

def from_wif(wif = raw_key)
  compressed = wif.length == 52
  validate_wif(wif)
  hex = decode_base58(wif)
  last_char = compressed ? -11 : -9
  hex.slice(2..last_char)
end

#generateObject



52
53
54
# File 'lib/money-tree/key.rb', line 52

def generate
  @ec_key = PKey::EC.generate GROUP_NAME
end

#hex_format?Boolean

Returns:

  • (Boolean)


127
128
129
# File 'lib/money-tree/key.rb', line 127

def hex_format?
  raw_key.length == 64 && !raw_key[/\H/]
end

#importObject



56
57
58
# File 'lib/money-tree/key.rb', line 56

def import
  @ec_key = OpenSSL::PKey::EC.new(data_sequence.to_der)
end

#parse_raw_keyObject



80
81
82
83
84
85
# File 'lib/money-tree/key.rb', line 80

def parse_raw_key
  result = if raw_key.is_a?(Integer) then from_integer elsif hex_format? then from_hex elsif base64_format? then from_base64 elsif compressed_wif_format? then from_wif elsif uncompressed_wif_format? then from_wif else
      raise KeyFormatNotFound
    end
  result.downcase
end

#set_public_key(opts = {}) ⇒ Object



76
77
78
# File 'lib/money-tree/key.rb', line 76

def set_public_key(opts = {})
  ec_key.public_key = calculate_public_key(opts)
end

#to_base64Object



159
160
161
# File 'lib/money-tree/key.rb', line 159

def to_base64
  encode_base64(to_hex)
end

#to_hexObject



131
132
133
# File 'lib/money-tree/key.rb', line 131

def to_hex
  int_to_hex @ec_key.private_key, 64
end

#to_s(network: :bitcoin) ⇒ Object



163
164
165
# File 'lib/money-tree/key.rb', line 163

def to_s(network: :bitcoin)
  to_wif(network: network)
end

#to_wif(compressed: true, network: :bitcoin) ⇒ Object



135
136
137
138
139
140
141
142
143
# File 'lib/money-tree/key.rb', line 135

def to_wif(compressed: true, network: :bitcoin)
  source = NETWORKS[network][:privkey_version] + to_hex
  source += NETWORKS[network][:privkey_compression_flag] if compressed
  hash = sha256(source)
  hash = sha256(hash)
  checksum = hash.slice(0..7)
  source_with_checksum = source + checksum
  encode_base58(source_with_checksum)
end

#uncompressed_wif_format?Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/money-tree/key.rb', line 113

def uncompressed_wif_format?
  wif_format?(:uncompressed)
end

#validate_wif(wif) ⇒ Object

Raises:



155
156
157
# File 'lib/money-tree/key.rb', line 155

def validate_wif(wif)
  raise InvalidWIFFormat unless wif_valid?(wif)
end

#wif_format?(compression) ⇒ Boolean

Returns:

  • (Boolean)


117
118
119
120
121
# File 'lib/money-tree/key.rb', line 117

def wif_format?(compression)
  length = compression == :compressed ? 52 : 51
  wif_prefixes = MoneyTree::NETWORKS.map { |k, v| v["#{compression}_wif_chars".to_sym] }.flatten
  raw_key.length == length && wif_prefixes.include?(raw_key.slice(0))
end

#wif_valid?(wif) ⇒ Boolean

Returns:

  • (Boolean)


145
146
147
148
149
150
151
152
153
# File 'lib/money-tree/key.rb', line 145

def wif_valid?(wif)
  hex = decode_base58(wif)
  checksum = hex.chars.to_a.pop(8).join
  source = hex.slice(0..-9)
  hash = sha256(source)
  hash = sha256(hash)
  hash_checksum = hash.slice(0..7)
  checksum == hash_checksum
end