Class: Trezor::Device

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/trezor/device.rb

Constant Summary collapse

HOST =
'http://127.0.0.1:21325'
HEADERS =
{ origin: 'https://python.trezor.io' }
MUTEX =
Mutex.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(device_info) ⇒ Device

Returns a new instance of Device.



15
16
17
18
19
20
# File 'lib/trezor/device.rb', line 15

def initialize(device_info)
  @session_counter = 0
  @device_info = device_info
  @connection = HTTP.persistent(HOST)
  @connection.default_options.headers.merge!(HEADERS)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object



64
65
66
67
# File 'lib/trezor/device.rb', line 64

def method_missing(method_name, *args, &block)
  return super unless respond_to?(method_name)
  call(Protobuf[method_name].new(*args), &block)
end

Class Method Details

.enumerateObject



102
103
104
105
106
# File 'lib/trezor/device.rb', line 102

def self.enumerate
  JSON.parse(HTTP[HEADERS].post("#{HOST}/enumerate").to_s).map do |device_info|
    new(device_info)
  end
end

.prompter(&block) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
# File 'lib/trezor/device.rb', line 123

def self.prompter(&block)
  return @default_prompter = lambda(&block) if block_given?
  prompter do |prompt, echo|
    STDOUT.print(prompt)
    if echo
      STDIN.gets
    else
      STDIN.noecho(&:gets)
    end.chomp.tap { STDOUT.print("\n") }
  end
end

.with_sessionObject



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/trezor/device.rb', line 109

def self.with_session
  MUTEX.lock
  device = enumerate.first
  unless device
    # log debug message 'Trezor not found'
    return
  end
  device.start_session
  yield(device)
ensure
  device.end_session if device
  MUTEX.unlock
end

Instance Method Details

#acquire!Object



32
33
34
35
36
37
# File 'lib/trezor/device.rb', line 32

def acquire!
  response = @connection.post("/acquire/#{@device_info['path']}/null")
  body = response.to_s
  raise body unless response.status.ok?
  @session = JSON.parse(body)['session']
end

#call(message) ⇒ Object



47
48
49
50
51
52
53
54
55
# File 'lib/trezor/device.rb', line 47

def call(message)
  response = post(message)
  handler = "callback_#{response.class.name.demodulize}"
  unless respond_to?(handler)
    #puts "could not find handler for #{response.class.name}"
    return response
  end
  call(send(handler, response))
end

#callback_ButtonRequest(msg) ⇒ Object



73
74
75
# File 'lib/trezor/device.rb', line 73

def callback_ButtonRequest(msg)
  Protobuf::ButtonAck.new
end

#callback_PassphraseRequest(msg) ⇒ Object



77
78
79
80
81
# File 'lib/trezor/device.rb', line 77

def callback_PassphraseRequest(msg)
  return Protobuf::PassphraseAck.new if msg.on_device
  passphrase = ask('Enter passphrase:', false).unicode_normalize(:nfkd)
  Protobuf::PassphraseAck.new(passphrase: passphrase)
end

#callback_PassphraseStateRequest(msg) ⇒ Object



83
84
85
# File 'lib/trezor/device.rb', line 83

def callback_PassphraseStateRequest(msg)
  Protobuf::PassphraseStateAck.new
end

#callback_PinMatrixRequest(msg) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/trezor/device.rb', line 87

def callback_PinMatrixRequest(msg)
  prompt =
    case msg.type
    when Protobuf::PinMatrixRequestType::PinMatrixRequestType_Current
      'current PIN'
    when Protobuf::PinMatrixRequestType::PinMatrixRequestType_NewFirst
      'new PIN'
    when Protobuf::PinMatrixRequestType::PinMatrixRequestType_NewSecond
      'new PIN again'
    else
      'PIN'
    end
  Protobuf::PinMatrixAck.new(pin: ask("Enter #{prompt}:", false))
end

#end_sessionObject



27
28
29
30
# File 'lib/trezor/device.rb', line 27

def end_session
  @session_counter -= 1
  release! if @session_counter == 0
end

#post(message) ⇒ Object



57
58
59
60
61
62
# File 'lib/trezor/device.rb', line 57

def post(message)
  serialized = Protobuf.serialize(message)
  response = @connection.post("/call/#{@session}", body: serialized)
  raise response.to_s unless response.status.ok?
  Protobuf.decode(response.to_s)
end

#release!Object



39
40
41
42
43
44
45
# File 'lib/trezor/device.rb', line 39

def release!
  return unless @session
  response = @connection.post("/release/#{@session}")
  body = response.to_s
  raise body unless response.status.ok?
  @session = nil
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/trezor/device.rb', line 69

def respond_to_missing?(method_name, include_private = false)
  super || Protobuf[method_name].present?
end

#start_sessionObject



22
23
24
25
# File 'lib/trezor/device.rb', line 22

def start_session
  acquire! if @session_counter == 0
  @session_counter += 1
end