Class: CoinOp::Bit::MultiWallet

Inherits:
Object
  • Object
show all
Includes:
Encodings
Defined in:
lib/coin-op/bit/multi_wallet.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Encodings

#base58, #decode_base58, #decode_hex, #hex, #int_to_byte_array

Constructor Details

#initialize(options) ⇒ MultiWallet

Returns a new instance of MultiWallet.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/coin-op/bit/multi_wallet.rb', line 17

def initialize(options)
  @private_trees = {}
  @public_trees = {}
  @trees = {}

  # FIXME: we should allow this.
  # if !private_trees
  #   raise "Must supply :private"
  # end

  if private_trees = options[:private]
    private_trees.each do |name, arg|
      name = name.to_sym
      @private_trees[name] = @trees[name] = self.get_node(arg)
    end
  end

  if public_trees = options[:public]
    public_trees.each do |name, arg|
      name = name.to_sym
      @public_trees[name] = @trees[name] = self.get_node(arg)
    end
  end
end

Instance Attribute Details

#treesObject (readonly)

Returns the value of attribute trees.



15
16
17
# File 'lib/coin-op/bit/multi_wallet.rb', line 15

def trees
  @trees
end

Class Method Details

.generate(names) ⇒ Object



6
7
8
9
10
11
12
13
# File 'lib/coin-op/bit/multi_wallet.rb', line 6

def self.generate(names)
  masters = {}
  names.each do |name|
    name = name.to_sym
    masters[name] = MoneyTree::Master.new
  end
  self.new(private: masters)
end

Instance Method Details

#address(path) ⇒ Object



141
142
143
# File 'lib/coin-op/bit/multi_wallet.rb', line 141

def address(path)
  path(path).address
end

#authorize(transaction, *signers) ⇒ Object

Takes a Transaction and any number of Arrays of signature dictionaries. Each sig_dict in an Array corresponds to the Input with the same index.

Uses the combined signatures from all the signers to generate and set the script_sig for each Input.

Returns the transaction.



181
182
183
184
185
186
187
188
# File 'lib/coin-op/bit/multi_wallet.rb', line 181

def authorize(transaction, *signers)
  transaction.set_script_sigs *signers do |input, *sig_dicts|
    node = self.path(input.output.[:wallet_path])
    signatures = combine_signatures(*sig_dicts)
    node.script_sig(signatures)
  end
  transaction
end

#combine_signatures(*sig_dicts) ⇒ Object

Takes any number of “signature dictionaries”, which are Hashes where the keys are tree names, and the values are base58-encoded signatures for a single input.

Returns an Array of the signatures in binary, sorted by their tree names.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/coin-op/bit/multi_wallet.rb', line 195

def combine_signatures(*sig_dicts)
  combined = {}
  sig_dicts.each do |sig_dict|
    sig_dict.each do |tree, signature|
      decoded_sig = decode_base58(signature)
      low_s_der_sig = Bitcoin::Script.is_low_der_signature?(decoded_sig) ?
        decoded_sig : Bitcoin::OpenSSL_EC.signature_to_low_s(decoded_sig)
      combined[tree] = Bitcoin::OpenSSL_EC.repack_der_signature(low_s_der_sig)
    end
  end

  # Order of signatures is important for validation, so we always
  # sort public keys and signatures by the name of the tree
  # they belong to.
  combined.sort_by { |tree, value| tree }.map { |tree, sig| sig }
end

#drop(*names) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/coin-op/bit/multi_wallet.rb', line 53

def drop(*names)
  names = names.map(&:to_sym)
  options = {:private => {}, :public => {}}
  @private_trees.each do |name, node|
    unless names.include?(name.to_sym)
      options[:private][name] = node
    end
  end
  @public_trees.each do |name, node|
    unless names.include?(name.to_sym)
      options[:private][name] = node
    end
  end
  self.class.new options
end

#drop_private(*names) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/coin-op/bit/multi_wallet.rb', line 69

def drop_private(*names)
  names.each do |name|
    name = name.to_sym
    tree = @private_trees.delete(name)
    serialized_priv = tree.to_bip32
    @public_trees[name] = MoneyTree::Master.from_bip32(serialized_priv)
  end
end

#get_node(arg) ⇒ Object



42
43
44
45
46
47
48
49
50
51
# File 'lib/coin-op/bit/multi_wallet.rb', line 42

def get_node(arg)
  case arg
  when MoneyTree::Node
    arg
  when String
    MoneyTree::Node.from_bip32(arg)
  else
    raise "Unusable type: #{node.class}"
  end
end

#import(addresses) ⇒ Object



78
79
80
81
82
83
84
85
86
87
# File 'lib/coin-op/bit/multi_wallet.rb', line 78

def import(addresses)
  addresses.each do |name, address|
    node = MoneyTree::Master.from_bip32(address)
    if node.private_key
      @private_trees[name] = node
    else
      @public_trees[name] = node
    end
  end
end

#path(path) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/coin-op/bit/multi_wallet.rb', line 125

def path(path)
  options = {
    :path => path,
    :private => {},
    :public => {},
  }
  @private_trees.each do |name, node|
    options[:private][name] = node.node_for_path(path)
  end
  @public_trees.each do |name, node|
    options[:public][name] = node.node_for_path(path)
  end

  MultiNode.new(options)
end

#private_seed(name, network:) ⇒ Object Also known as: private_address



89
90
91
92
# File 'lib/coin-op/bit/multi_wallet.rb', line 89

def private_seed(name, network:)
  raise "No such node: '#{name}'" unless (node = @private_trees[name.to_sym])
  node.to_bip32(:private, network: network)
end

#private_seeds(network:) ⇒ Object Also known as: private_addresses



105
106
107
108
109
110
111
# File 'lib/coin-op/bit/multi_wallet.rb', line 105

def private_seeds(network:)
  out = {}
  @private_trees.each do |name, tree|
    out[name] = self.private_address(name, network: network)
  end
  out
end

#public_seed(name, network:) ⇒ Object Also known as: public_address



96
97
98
99
100
101
102
103
# File 'lib/coin-op/bit/multi_wallet.rb', line 96

def public_seed(name, network:)
  name = name.to_sym
  if node = (@public_trees[name] || @private_trees[name])
    node.to_bip32(network: network)
  else
    raise "No such node: '#{name}'"
  end
end

#public_seeds(network:) ⇒ Object Also known as: public_addresses



113
114
115
116
117
118
119
# File 'lib/coin-op/bit/multi_wallet.rb', line 113

def public_seeds(network:)
  out = {}
  @private_trees.each do |name, node|
    out[name] = node.to_bip32(network: network)
  end
  out
end

#set_sig_hashes(transaction) ⇒ Object



166
167
168
169
170
171
172
# File 'lib/coin-op/bit/multi_wallet.rb', line 166

def set_sig_hashes(transaction)
  transaction.inputs.each do |input|
    path = input.output.[:wallet_path]
    node = self.path(path)
    input.binary_sig_hash = transaction.sig_hash(input, node.script)
  end
end

#signatures(transaction, names: [:primary]) ⇒ Object

Takes a Transaction ready to be signed.

Returns an Array of signature dictionaries.



157
158
159
160
161
162
163
164
# File 'lib/coin-op/bit/multi_wallet.rb', line 157

def signatures(transaction, names: [:primary])
  transaction.inputs.map do |input|
    path = input.output.[:wallet_path]
    node = self.path(path)
    sig_hash = transaction.sig_hash(input, node.script)
    node.signatures(sig_hash, names: names)
  end
end

#valid_output?(output) ⇒ Boolean

Returns:

  • (Boolean)


145
146
147
148
149
150
151
152
# File 'lib/coin-op/bit/multi_wallet.rb', line 145

def valid_output?(output)
  if path = output..wallet_path
    node = self.path(path)
    node.p2sh_script.to_s == output.script.p2sh_script.to_s
  else
    true
  end
end