Class: Bitcoin::Wallet::MasterKey

Inherits:
Object
  • Object
show all
Extended by:
Util
Includes:
Util
Defined in:
lib/bitcoin/wallet/master_key.rb

Overview

HD Wallet master seed

Constant Summary

Constants included from Util

Util::DIGEST_NAME_SHA256

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util

byte_to_bit, calc_checksum, decode_base58_address, double_sha256, encode_base58_address, hash160, hmac_sha256, pack_boolean, pack_var_int, pack_var_string, sha256, unpack_boolean, unpack_var_int, unpack_var_int_from_io, unpack_var_string

Constructor Details

#initialize(seed, salt: '', encrypted: false, mnemonic: nil) ⇒ MasterKey

Returns a new instance of MasterKey.



14
15
16
17
18
19
# File 'lib/bitcoin/wallet/master_key.rb', line 14

def initialize(seed, salt: '', encrypted: false, mnemonic: nil)
  @mnemonic = mnemonic
  @seed = seed
  @encrypted = encrypted
  @salt = salt
end

Instance Attribute Details

#encryptedObject

Returns the value of attribute encrypted.



11
12
13
# File 'lib/bitcoin/wallet/master_key.rb', line 11

def encrypted
  @encrypted
end

#mnemonicObject

ephemeral data existing only at initialization



12
13
14
# File 'lib/bitcoin/wallet/master_key.rb', line 12

def mnemonic
  @mnemonic
end

#saltObject

Returns the value of attribute salt.



10
11
12
# File 'lib/bitcoin/wallet/master_key.rb', line 10

def salt
  @salt
end

#seedObject (readonly)

Returns the value of attribute seed.



9
10
11
# File 'lib/bitcoin/wallet/master_key.rb', line 9

def seed
  @seed
end

Class Method Details

.generateObject

generate new master key.

Returns:

  • Bitcoin::Wallet::MasterKey



23
24
25
26
27
# File 'lib/bitcoin/wallet/master_key.rb', line 23

def self.generate
  entropy = SecureRandom.hex(32)
  mnemonic = Bitcoin::Mnemonic.new('english')
  self.recover_from_words(mnemonic.to_mnemonic(entropy))
end

.parse_from_payload(payload) ⇒ Bitcoin::Wallet::MasterKey

parse master key raw data

Parameters:

  • payload (String)

    raw data

Returns:



41
42
43
44
45
46
47
48
# File 'lib/bitcoin/wallet/master_key.rb', line 41

def self.parse_from_payload(payload)
  flag, payload = unpack_var_int(payload)
  raise 'encrypted flag is invalid.' unless [0, 1].include?(flag)
  salt, payload = unpack_var_string(payload)
  salt = '' unless salt
  seed, payload = unpack_var_string(payload)
  self.new(seed.bth, salt: salt.bth, encrypted: flag == 1)
end

.recover_from_words(words) ⇒ Object

recover master key from mnemonic word list.

Parameters:

  • words (Array)

    the mnemonic word list.

Returns:

  • Bitcoin::Wallet::MasterKey



32
33
34
35
36
# File 'lib/bitcoin/wallet/master_key.rb', line 32

def self.recover_from_words(words)
  mnemonic = Bitcoin::Mnemonic.new('english')
  seed = mnemonic.to_seed(words)
  self.new(seed, mnemonic: words)
end

Instance Method Details

#decrypt(passphrase) ⇒ Object

decrypt seed



95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/bitcoin/wallet/master_key.rb', line 95

def decrypt(passphrase)
  raise 'The wallet is not encrypted.' unless encrypted
  dec = OpenSSL::Cipher.new('AES-256-CBC')
  dec.decrypt
  dec.key, dec.iv = key_iv(dec, passphrase)
  decrypted_data = ''
  decrypted_data << dec.update(seed)
  decrypted_data << dec.final
  @seed = decrypted_data
  @encrypted = false
  @salt = ''
end

#derive(path) ⇒ Bitcoin::ExtKey

derive child key using derivation path.

Returns:



66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/bitcoin/wallet/master_key.rb', line 66

def derive(path)
  derived_key = key
  path.split('/').each_with_index do|p, index|
    if index == 0
      raise ArgumentError.new("#{path} is invalid format.") unless p == 'm'
      next
    end
    raise ArgumentError.new("#{path} is invalid format.") unless p.delete("'") =~ /^[0-9]+$/
    num = (p[-1] == "'" ? p.delete("'").to_i + 2**31 : p.to_i)
    derived_key = derived_key.derive(num)
  end
  derived_key
end

#encrypt(passphrase) ⇒ Object

encrypt seed



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/bitcoin/wallet/master_key.rb', line 81

def encrypt(passphrase)
  raise 'The wallet is already encrypted.' if encrypted
  @salt = SecureRandom.hex(16)
  enc = OpenSSL::Cipher.new('AES-256-CBC')
  enc.encrypt
  enc.key, enc.iv = key_iv(enc, passphrase)
  encrypted_data = ''
  encrypted_data << enc.update(seed)
  encrypted_data << enc.final
  @seed = encrypted_data
  @encrypted = true
end

#keyBitcoin::ExtKey

get master key

Returns:



59
60
61
62
# File 'lib/bitcoin/wallet/master_key.rb', line 59

def key
  raise 'seed is encrypted. please decrypt the seed.' if encrypted
  Bitcoin::ExtKey.generate_master(seed)
end

#to_payloadObject

generate payload with following format

encrypted(false:0, true:1)][salt(var str)][seed(var str)


52
53
54
55
# File 'lib/bitcoin/wallet/master_key.rb', line 52

def to_payload
  flg = encrypted ? 1 : 0
  pack_var_int(flg) << [salt, seed].map{|v|pack_var_string(v.htb)}.join
end