Class: Chain::HSMSigner

Inherits:
Object
  • Object
show all
Defined in:
lib/chain/hsm_signer.rb

Instance Method Summary collapse

Constructor Details

#initializeHSMSigner

Returns a new instance of HSMSigner.



9
10
11
# File 'lib/chain/hsm_signer.rb', line 9

def initialize
  @xpubs_by_signer = {}
end

Instance Method Details

#add_key(xpub_or_key, signer_conn) ⇒ Object

Add a new key/signer pair to the HSM signer.

Parameters:

  • xpub_or_key (MockHsm::Key || String)

    An object with an xpub key, or an xpub as a string.

  • signer_conn (Connection)

    Authenticated connection to a specific HSM instance.



16
17
18
19
20
21
# File 'lib/chain/hsm_signer.rb', line 16

def add_key(xpub_or_key, signer_conn)
  xpub = xpub_or_key.is_a?(MockHSM::Key) ? xpub_or_key.xpub : xpub_or_key
  @xpubs_by_signer[signer_conn] ||= []
  @xpubs_by_signer[signer_conn] << xpub
  @xpubs_by_signer[signer_conn].uniq!
end

#sign(tx_template) ⇒ Object

Sign a single transaction

Parameters:

  • tx_template (Hash)

    A single transaction template.



25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/chain/hsm_signer.rb', line 25

def sign(tx_template)
  return tx_template if @xpubs_by_signer.empty?

  @xpubs_by_signer.each do |signer_conn, xpubs|
    tx_template = signer_conn.singleton_batch_request(
      '/sign-transaction',
      transactions: [tx_template],
      xpubs: xpubs,
    ) { |item| Transaction::Template.new(item) }
  end

  tx_template
end

#sign_batch(tx_templates) ⇒ Object

Sign a batch of transactions

Parameters:

  • tx_templates (Array<Hash>)

    Array of transaction templates.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/chain/hsm_signer.rb', line 41

def sign_batch(tx_templates)
  if @xpubs_by_signer.empty?
    # Treat all templates as if signed successfully.
    successes = tx_templates.each_with_index.reduce({}) do |memo, (t, i)|
      memo[i] = t
      memo
    end
    BatchResponse.new(successes: successes)
  end

  # We need to work towards a single, final BatchResponse that uses the
  # original indexes. For the next cycle, we should retain only those
  # templates for which the most recent sign response was successful, and
  # maintain a mapping of each template's index in the upcoming request
  # to its original index.

  orig_index = (0...tx_templates.size).to_a
  errors = {}

  @xpubs_by_signer.each do |signer_conn, xpubs|
    next_tx_templates = []
    next_orig_index = []

    batch = signer_conn.batch_request(
      '/sign-transaction',
      transactions: tx_templates,
      xpubs: xpubs,
    ) { |item| Transaction::Template.new(item) }

    batch.successes.each do |i, template|
      next_tx_templates << template
      next_orig_index << orig_index[i]
    end

    batch.errors.each do |i, err|
      errors[orig_index[i]] = err
    end

    tx_templates = next_tx_templates
    orig_index = next_orig_index

    # Early-exit if all templates have encountered an error.
    break if tx_templates.empty?
  end

  successes = tx_templates.each_with_index.reduce({}) do |memo, (t, i)|
    memo[orig_index[i]] = t
    memo
  end

  BatchResponse.new(
    successes: successes,
    errors: errors,
  )
end