Class: IcAgent::Agent

Inherits:
Object
  • Object
show all
Defined in:
lib/ic_agent/agent.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(identity, client, nonce_factory = nil, ingress_expiry = 300, root_key = IcAgent::IC_ROOT_KEY) ⇒ Agent

Returns a new instance of Agent.



29
30
31
32
33
34
35
# File 'lib/ic_agent/agent.rb', line 29

def initialize(identity, client, nonce_factory = nil, ingress_expiry = 300, root_key = IcAgent::IC_ROOT_KEY)
  @identity = identity
  @client = client
  @ingress_expiry = ingress_expiry
  @root_key = root_key
  @nonce_factory = nonce_factory
end

Instance Attribute Details

#clientObject

Returns the value of attribute client.



27
28
29
# File 'lib/ic_agent/agent.rb', line 27

def client
  @client
end

#identityObject

Returns the value of attribute identity.



27
28
29
# File 'lib/ic_agent/agent.rb', line 27

def identity
  @identity
end

#ingress_expiryObject

Returns the value of attribute ingress_expiry.



27
28
29
# File 'lib/ic_agent/agent.rb', line 27

def ingress_expiry
  @ingress_expiry
end

#nonce_factoryObject

Returns the value of attribute nonce_factory.



27
28
29
# File 'lib/ic_agent/agent.rb', line 27

def nonce_factory
  @nonce_factory
end

#root_keyObject

Returns the value of attribute root_key.



27
28
29
# File 'lib/ic_agent/agent.rb', line 27

def root_key
  @root_key
end

Instance Method Details

#call_endpoint(canister_id, request_id, data) ⇒ Object



57
58
59
60
# File 'lib/ic_agent/agent.rb', line 57

def call_endpoint(canister_id, request_id, data)
  @client.call(canister_id, request_id, data)
  request_id
end

#get_expiry_dateObject



41
42
43
# File 'lib/ic_agent/agent.rb', line 41

def get_expiry_date
  ((Time.now.to_i + @ingress_expiry) * 10**9).to_i
end

#get_principalObject



37
38
39
# File 'lib/ic_agent/agent.rb', line 37

def get_principal
  @identity.sender
end

#poll(canister_id, req_id, delay = 1, timeout = IcAgent::DEFAULT_POLL_TIMEOUT_SECS) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/ic_agent/agent.rb', line 153

def poll(canister_id, req_id, delay = 1, timeout = IcAgent::DEFAULT_POLL_TIMEOUT_SECS)
  status = nil
  cert = nil
  (timeout / delay).to_i.times do
    status, cert = request_status_raw(canister_id, req_id)
    break if %w[replied done rejected].include?(status)

    sleep(delay)
  end

  if status == 'replied'
    path = ['request_status', req_id, 'reply']
    res = IcAgent::Certificate.lookup(path, cert)
    [status, res]
  elsif status == 'rejected'
    path = ['request_status', req_id, 'reject_message']
    msg = IcAgent::Certificate.lookup(path, cert)
    [status, msg]
  else
    [status, _]
  end
end

#query_endpoint(canister_id, data) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/ic_agent/agent.rb', line 45

def query_endpoint(canister_id, data)
  ret = @client.query(canister_id, data)
  decode_ret = nil
  begin
    decode_ret = CBOR.decode(ret)
  rescue CBOR::MalformedFormatError
    decode_ret = ret
    # print logger
  end
  decode_ret
end

#query_raw(canister_id, method_name, arg, return_type = nil, effective_canister_id = nil) ⇒ Object

Raises:

  • (Exception)


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
# File 'lib/ic_agent/agent.rb', line 67

def query_raw(canister_id, method_name, arg, return_type = nil, effective_canister_id = nil)
  req_canister_id = canister_id.is_a?(String) ? Principal.from_str(canister_id).bytes : canister_id.bytes
  req = {
    'request_type' => 'query',
    'sender' => @identity.sender.bytes,
    'canister_id' => req_canister_id,
    'method_name' => method_name,
    'arg' => arg.hex2str,
    'ingress_expiry' => get_expiry_date
  }

  _, data = Request.sign_request(req, @identity)
  query_canister_id = effective_canister_id.nil? ? canister_id : effective_canister_id
  result = query_endpoint(query_canister_id, data)
  raise Exception, "Malformed result: #{result}" unless result.is_a?(CBOR::Tagged) && result.value.key?('status')

  if result.value['status'] == 'replied'
    arg = result.value['reply']['arg']
    if arg[0..3] == 'DIDL'
      IcAgent::Candid.decode(arg.to_hex, return_type)
    else
      arg
    end
  elsif result.value['status'] == 'rejected'
    raise Exception, "Canister reject the call: #{result['reject_message']}"
  end
end

#read_state_endpoint(canister_id, data) ⇒ Object



62
63
64
65
# File 'lib/ic_agent/agent.rb', line 62

def read_state_endpoint(canister_id, data)
  result = @client.read_state(canister_id, data)
  result
end

#read_state_raw(canister_id, paths) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/ic_agent/agent.rb', line 123

def read_state_raw(canister_id, paths)
  req = {
    'request_type' => 'read_state',
    'sender' => @identity.sender.bytes,
    'paths' => paths,
    'ingress_expiry' => get_expiry_date
  }
  _, data = Request.sign_request(req, @identity)
  ret = read_state_endpoint(canister_id, data)
  if ret == 'Invalid path requested.'
    raise ValueError, 'Invalid path requested!'
  elsif ret == 'Could not parse body as read request: invalid type: byte array, expected a sequence'
    raise ValueError, 'Could not parse body as read request: invalid type: byte array, expected a sequence'
  end

  begin
    d = CBOR.decode(ret)
  rescue StandardError
    raise ValueError, "Unable to decode cbor value: #{ret}"
  end
  CBOR.decode(d.value['certificate'])
end

#request_status_raw(canister_id, req_id) ⇒ Object



146
147
148
149
150
151
# File 'lib/ic_agent/agent.rb', line 146

def request_status_raw(canister_id, req_id)
  paths = [['request_status', req_id]]
  cert = read_state_raw(canister_id, paths)
  status = IcAgent::Certificate.lookup(['request_status', req_id, 'status'], cert)
  [status, cert]
end

#update_raw(canister_id, method_name, arg, return_type = nil, effective_canister_id = nil, **kwargs) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/ic_agent/agent.rb', line 95

def update_raw(canister_id, method_name, arg, return_type = nil, effective_canister_id = nil, **kwargs)
  req_canister_id = canister_id.is_a?(String) ? Principal.from_str(canister_id).bytes : canister_id.bytes
  req = {
    'request_type' => 'call',
    'sender' => @identity.sender.bytes,
    'canister_id' => req_canister_id,
    'method_name' => method_name,
    'arg' => arg.hex2str,
    'ingress_expiry' => get_expiry_date
  }
  req_id, data = Request.sign_request(req, @identity)
  eid = effective_canister_id.nil? ? canister_id : effective_canister_id
  _ = call_endpoint(eid, req_id, data)
  status, result = poll(eid, req_id, **kwargs)
  if status == 'rejected'
    raise Exception, "Rejected: #{result.to_s}"
  elsif status == 'replied'
    if result[0..3] == 'DIDL'
      IcAgent::Candid.decode(result.to_hex, return_type)
    else
      # Some canisters don't use DIDL (e.g. they might encode using json instead)
      result
    end
  else
    raise Exception, "Timeout to poll result, current status: #{status.to_s}"
  end
end