ic_agent 0.1.4 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +16 -10
- data/README.md +2 -0
- data/ic_agent.gemspec +2 -0
- data/lib/ic_agent/agent.rb +154 -4
- data/lib/ic_agent/ast/assembler.rb +17 -0
- data/lib/ic_agent/ast/nodes/named_nodes.rb +99 -1
- data/lib/ic_agent/ast/nodes/statement_nodes.rb +63 -1
- data/lib/ic_agent/ast/nodes/string_literal.rb +5 -0
- data/lib/ic_agent/ast/parser.rb +35 -1
- data/lib/ic_agent/ast/statement_parser.rb +15 -0
- data/lib/ic_agent/ast/writer.rb +15 -0
- data/lib/ic_agent/candid.rb +26 -5
- data/lib/ic_agent/canister.rb +7 -0
- data/lib/ic_agent/certificate.rb +98 -0
- data/lib/ic_agent/client.rb +32 -0
- data/lib/ic_agent/common/cycles_wallet.rb +5 -0
- data/lib/ic_agent/common/governance.rb +4 -0
- data/lib/ic_agent/common/ledger.rb +4 -0
- data/lib/ic_agent/common/management.rb +4 -0
- data/lib/ic_agent/identity.rb +52 -0
- data/lib/ic_agent/principal.rb +104 -6
- data/lib/ic_agent/system_state.rb +37 -0
- data/lib/ic_agent/utils.rb +30 -2
- data/lib/ic_agent/version.rb +1 -1
- data/lib/ic_agent.rb +7 -2
- metadata +17 -2
data/lib/ic_agent/certificate.rb
CHANGED
@@ -8,10 +8,56 @@ module IcAgent
|
|
8
8
|
end
|
9
9
|
|
10
10
|
class Certificate
|
11
|
+
# Performs a lookup operation in the certificate tree based on the given path.
|
12
|
+
#
|
13
|
+
# Parameters:
|
14
|
+
# - path: The path to lookup.
|
15
|
+
# - cert: The certificate object containing the tree.
|
16
|
+
#
|
17
|
+
# Returns: The value found at the specified path in the tree.
|
11
18
|
def self.lookup(path, cert)
|
12
19
|
lookup_path(path, cert.value['tree'])
|
13
20
|
end
|
14
21
|
|
22
|
+
# Retrieves the signature from a certificate.
|
23
|
+
#
|
24
|
+
# Parameters:
|
25
|
+
# - cert: The certificate object.
|
26
|
+
#
|
27
|
+
# Returns: The signature value.
|
28
|
+
def self.signature(cert)
|
29
|
+
cert.value['signature']
|
30
|
+
end
|
31
|
+
|
32
|
+
# Retrieves the delegation from a certificate.
|
33
|
+
#
|
34
|
+
# Parameters:
|
35
|
+
# - cert: The certificate object.
|
36
|
+
#
|
37
|
+
# Returns: The delegation value.
|
38
|
+
def self.delegation(cert)
|
39
|
+
cert.value['delegation']
|
40
|
+
end
|
41
|
+
|
42
|
+
# Retrieves the tree from a certificate.
|
43
|
+
#
|
44
|
+
# Parameters:
|
45
|
+
# - cert: The certificate object.
|
46
|
+
#
|
47
|
+
# Returns: The tree value.
|
48
|
+
def self.tree(cert)
|
49
|
+
cert.value['tree']
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Recursive helper method for performing the lookup operation.
|
55
|
+
#
|
56
|
+
# Parameters:
|
57
|
+
# - path: The remaining path to lookup.
|
58
|
+
# - tree: The current tree node to search in.
|
59
|
+
#
|
60
|
+
# Returns: The value found at the specified path in the tree.
|
15
61
|
def self.lookup_path(path, tree)
|
16
62
|
offset = 0
|
17
63
|
if path.length == 0
|
@@ -29,6 +75,12 @@ module IcAgent
|
|
29
75
|
end
|
30
76
|
end
|
31
77
|
|
78
|
+
# Flattens fork nodes in the tree into a single array.
|
79
|
+
#
|
80
|
+
# Parameters:
|
81
|
+
# - t: The tree node to flatten.
|
82
|
+
#
|
83
|
+
# Returns: The flattened array of tree nodes.
|
32
84
|
def self.flatten_forks(t)
|
33
85
|
if t[0] == NodeId::EMPTY
|
34
86
|
[]
|
@@ -42,6 +94,13 @@ module IcAgent
|
|
42
94
|
end
|
43
95
|
end
|
44
96
|
|
97
|
+
# Finds a labeled tree node with the specified label in the given array of trees.
|
98
|
+
#
|
99
|
+
# Parameters:
|
100
|
+
# - l: The label to search for.
|
101
|
+
# - trees: The array of trees to search in.
|
102
|
+
#
|
103
|
+
# Returns: The labeled tree node with the matching label, or nil if not found.
|
45
104
|
def self.find_label(l, trees)
|
46
105
|
trees.each do |t|
|
47
106
|
if t[0] == NodeId::LABELED
|
@@ -51,5 +110,44 @@ module IcAgent
|
|
51
110
|
end
|
52
111
|
nil
|
53
112
|
end
|
113
|
+
|
114
|
+
# Recursively reconstructs the hash value of a tree node.
|
115
|
+
#
|
116
|
+
# Parameters:
|
117
|
+
# - t: The tree node to reconstruct.
|
118
|
+
#
|
119
|
+
# Returns: The reconstructed hash value of the tree node.
|
120
|
+
def self.reconstruct(t)
|
121
|
+
case t[0]
|
122
|
+
when IcAgent::NodeId::EMPTY
|
123
|
+
domain_sep = domain_sep('ic-hashtree-empty')
|
124
|
+
Digest::SHA256.digest(domain_sep)
|
125
|
+
when IcAgent::NodeId::PRUNED
|
126
|
+
t[1]
|
127
|
+
when IcAgent::NodeId::LEAF
|
128
|
+
domain_sep = domain_sep('ic-hashtree-leaf')
|
129
|
+
Digest::SHA256.digest(domain_sep + t[1])
|
130
|
+
when IcAgent::NodeId::LABELED
|
131
|
+
domain_sep = domain_sep('ic-hashtree-labeled')
|
132
|
+
Digest::SHA256.digest(domain_sep + t[1] + reconstruct(t[2]))
|
133
|
+
when IcAgent::NodeId::FORK
|
134
|
+
domain_sep = domain_sep('ic-hashtree-fork')
|
135
|
+
Digest::SHA256.digest(domain_sep + reconstruct(t[1]) + reconstruct(t[2]))
|
136
|
+
else
|
137
|
+
raise 'unreachable'
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Generates the domain separation prefix for hash computations.
|
142
|
+
#
|
143
|
+
# Parameters:
|
144
|
+
# - s: The domain separation string.
|
145
|
+
#
|
146
|
+
# Returns: The domain separation prefix as a binary string.
|
147
|
+
def self.domain_sep(s)
|
148
|
+
len = [s.bytesize].pack('C')
|
149
|
+
str = s.encode(Encoding::UTF_8)
|
150
|
+
len + str
|
151
|
+
end
|
54
152
|
end
|
55
153
|
end
|
data/lib/ic_agent/client.rb
CHANGED
@@ -5,6 +5,10 @@ module IcAgent
|
|
5
5
|
DEFAULT_TIMEOUT = 120
|
6
6
|
DEFAULT_TIMEOUT_QUERY = 30
|
7
7
|
|
8
|
+
# Initializes a new instance of the Client class.
|
9
|
+
#
|
10
|
+
# Parameters:
|
11
|
+
# - url: The URL of the IC agent. Defaults to 'https://ic0.app'.
|
8
12
|
def initialize(url = 'https://ic0.app')
|
9
13
|
@url = url
|
10
14
|
@conn = Faraday.new(url: url) do |faraday|
|
@@ -16,6 +20,13 @@ module IcAgent
|
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
23
|
+
# Sends a query to a canister.
|
24
|
+
#
|
25
|
+
# Parameters:
|
26
|
+
# - canister_id: The ID of the canister to query.
|
27
|
+
# - data: The data to send with the query.
|
28
|
+
#
|
29
|
+
# Returns: The response from the canister as a UTF-8 encoded string.
|
19
30
|
def query(canister_id, data)
|
20
31
|
endpoint = "/api/v2/canister/#{canister_id}/query"
|
21
32
|
ret = @conn.post(endpoint, data)
|
@@ -23,6 +34,14 @@ module IcAgent
|
|
23
34
|
ret.body
|
24
35
|
end
|
25
36
|
|
37
|
+
# Calls a function on a canister.
|
38
|
+
#
|
39
|
+
# Parameters:
|
40
|
+
# - canister_id: The ID of the canister to call.
|
41
|
+
# - req_id: The request ID.
|
42
|
+
# - data: The data to send with the call.
|
43
|
+
#
|
44
|
+
# Returns: The request ID.
|
26
45
|
def call(canister_id, req_id, data)
|
27
46
|
endpoint = "/api/v2/canister/#{canister_id}/call"
|
28
47
|
ret = @conn.post(endpoint, data)
|
@@ -30,6 +49,13 @@ module IcAgent
|
|
30
49
|
req_id
|
31
50
|
end
|
32
51
|
|
52
|
+
# Reads the state of a canister.
|
53
|
+
#
|
54
|
+
# Parameters:
|
55
|
+
# - canister_id: The ID of the canister to read the state from.
|
56
|
+
# - data: The data to send with the read_state request.
|
57
|
+
#
|
58
|
+
# Returns: The response from the canister as a UTF-8 encoded string.
|
33
59
|
def read_state(canister_id, data)
|
34
60
|
endpoint = "/api/v2/canister/#{canister_id}/read_state"
|
35
61
|
ret = @conn.post(endpoint, data)
|
@@ -37,6 +63,12 @@ module IcAgent
|
|
37
63
|
ret.body
|
38
64
|
end
|
39
65
|
|
66
|
+
# Retrieves the status of the IC agent.
|
67
|
+
#
|
68
|
+
# Parameters:
|
69
|
+
# - timeout: The timeout for the status request. Defaults to DEFAULT_TIMEOUT_QUERY.
|
70
|
+
#
|
71
|
+
# Returns: The response from the status endpoint as a UTF-8 encoded string.
|
40
72
|
def status(timeout: DEFAULT_TIMEOUT_QUERY)
|
41
73
|
endpoint = '/api/v2/status'
|
42
74
|
ret = @conn.get(endpoint, timeout: timeout)
|
@@ -261,6 +261,11 @@ module IcAgent
|
|
261
261
|
|
262
262
|
attr_accessor :identity, :client, :agent, :canister
|
263
263
|
|
264
|
+
# Constructor for the CyclesWallet class.
|
265
|
+
#
|
266
|
+
# Parameters:
|
267
|
+
# - iden: (Optional) An instance of the Identity class.
|
268
|
+
# - wallet_id: The ID of the CyclesWallet.
|
264
269
|
def initialize(iden = nil, wallet_id)
|
265
270
|
@identity = iden.nil? ? IcAgent::Identity.new : iden
|
266
271
|
@client = IcAgent::Client.new
|
@@ -355,6 +355,10 @@ module IcAgent
|
|
355
355
|
|
356
356
|
attr_accessor :identity, :client, :agent, :canister
|
357
357
|
|
358
|
+
# Constructor for the Governance class.
|
359
|
+
#
|
360
|
+
# Parameters:
|
361
|
+
# - iden: (Optional) An instance of the Identity class.
|
358
362
|
def initialize(iden = nil)
|
359
363
|
@identity = iden.nil? ? IcAgent::Identity.new : iden
|
360
364
|
@client = IcAgent::Client.new
|
@@ -166,6 +166,10 @@ module IcAgent
|
|
166
166
|
|
167
167
|
attr_accessor :identity, :client, :agent, :canister
|
168
168
|
|
169
|
+
# Constructor for the Ledger class.
|
170
|
+
#
|
171
|
+
# Parameters:
|
172
|
+
# - iden: (Optional) An instance of the Identity class.
|
169
173
|
def initialize(iden = nil)
|
170
174
|
@identity = iden.nil? ? IcAgent::Identity.new : iden
|
171
175
|
@client = IcAgent::Client.new
|
@@ -182,6 +182,10 @@ module IcAgent
|
|
182
182
|
|
183
183
|
attr_accessor :identity, :client, :agent, :canister
|
184
184
|
|
185
|
+
# Constructor for the Management class.
|
186
|
+
#
|
187
|
+
# Parameters:
|
188
|
+
# - iden: (Optional) An instance of the Identity class.
|
185
189
|
def initialize(iden = nil)
|
186
190
|
@identity = iden.nil? ? IcAgent::Identity.new : iden
|
187
191
|
@client = IcAgent::Client.new
|
data/lib/ic_agent/identity.rb
CHANGED
@@ -11,6 +11,12 @@ module IcAgent
|
|
11
11
|
class Identity
|
12
12
|
attr_reader :privkey, :pubkey, :der_pubkey, :sk, :vk, :key_type
|
13
13
|
|
14
|
+
# Initializes a new instance of the Identity class.
|
15
|
+
#
|
16
|
+
# Parameters:
|
17
|
+
# - privkey: The private key of the identity in hexadecimal format. Defaults to an empty string.
|
18
|
+
# - type: The key type of the identity. Defaults to 'ed25519'.
|
19
|
+
# - anonymous: A flag indicating whether the identity is anonymous. Defaults to false.
|
14
20
|
def initialize(privkey = '', type = 'ed25519', anonymous = false)
|
15
21
|
privkey = [privkey].pack('H*')
|
16
22
|
@anonymous = anonymous
|
@@ -37,6 +43,12 @@ module IcAgent
|
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
46
|
+
# Creates a new Identity instance from a seed phrase (mnemonic).
|
47
|
+
#
|
48
|
+
# Parameters:
|
49
|
+
# - mnemonic: The seed phrase (mnemonic) used to generate the identity.
|
50
|
+
#
|
51
|
+
# Returns: The Identity instance.
|
40
52
|
def self.from_seed(mnemonic)
|
41
53
|
seed = Bitcoin::Trezor::Mnemonic.to_seed(mnemonic)
|
42
54
|
privkey = seed[0..63]
|
@@ -44,6 +56,9 @@ module IcAgent
|
|
44
56
|
Identity.new(privkey = privkey, type = key_type)
|
45
57
|
end
|
46
58
|
|
59
|
+
# Returns the sender Principal associated with the Identity.
|
60
|
+
#
|
61
|
+
# Returns: The sender Principal.
|
47
62
|
def sender
|
48
63
|
if @anonymous
|
49
64
|
IcAgent::Principal.anonymous
|
@@ -52,6 +67,12 @@ module IcAgent
|
|
52
67
|
end
|
53
68
|
end
|
54
69
|
|
70
|
+
# Signs a message using the Identity.
|
71
|
+
#
|
72
|
+
# Parameters:
|
73
|
+
# - msg: The message to sign.
|
74
|
+
#
|
75
|
+
# Returns: An array containing the DER-encoded public key and the signature.
|
55
76
|
def sign(msg)
|
56
77
|
if @anonymous
|
57
78
|
[nil, nil]
|
@@ -65,6 +86,13 @@ module IcAgent
|
|
65
86
|
end
|
66
87
|
end
|
67
88
|
|
89
|
+
# Verifies a message signature using the Identity.
|
90
|
+
#
|
91
|
+
# Parameters:
|
92
|
+
# - msg: The message to verify.
|
93
|
+
# - sig: The signature to verify.
|
94
|
+
#
|
95
|
+
# Returns: `true` if the signature is valid, otherwise `false`.
|
68
96
|
def verify(msg, sig)
|
69
97
|
if @anonymous
|
70
98
|
false
|
@@ -73,6 +101,9 @@ module IcAgent
|
|
73
101
|
end
|
74
102
|
end
|
75
103
|
|
104
|
+
# Returns the PEM-encoded private key of the Identity.
|
105
|
+
#
|
106
|
+
# Returns: The PEM-encoded private key.
|
76
107
|
def to_pem
|
77
108
|
der = @key_type == 'secp256k1' ? "#{IcAgent::IC_PUBKEY_SECP_DER_HERD}#{@sk.data.unpack1('H*')}".hex2str : "#{IcAgent::IC_PUBKEY_ED_DER_HEAD}#{@sk.to_bytes.unpack1('H*')}".hex2str
|
78
109
|
b64 = Base64.strict_encode64(der)
|
@@ -92,20 +123,41 @@ module IcAgent
|
|
92
123
|
class DelegateIdentity
|
93
124
|
attr_reader :identity, :delegations, :der_pubkey
|
94
125
|
|
126
|
+
# Initializes a new instance of the DelegateIdentity class.
|
127
|
+
#
|
128
|
+
# Parameters:
|
129
|
+
# - identity: The Identity associated with the DelegateIdentity.
|
130
|
+
# - delegation: The delegation JSON object containing the delegated keys.
|
95
131
|
def initialize(identity, delegation)
|
96
132
|
@identity = identity
|
97
133
|
@delegations = delegation['delegations'].map { |d| d }
|
98
134
|
@der_pubkey = [delegation['publicKey']].pack('H*')
|
99
135
|
end
|
100
136
|
|
137
|
+
# Signs a message using the DelegateIdentity.
|
138
|
+
#
|
139
|
+
# Parameters:
|
140
|
+
# - msg: The message to sign.
|
141
|
+
#
|
142
|
+
# Returns: An array containing the DER-encoded public key and the signature.
|
101
143
|
def sign(msg)
|
102
144
|
@identity.sign(msg)
|
103
145
|
end
|
104
146
|
|
147
|
+
# Returns the sender Principal associated with the DelegateIdentity.
|
148
|
+
#
|
149
|
+
# Returns: The sender Principal.
|
105
150
|
def sender
|
106
151
|
Principal.self_authenticating(@der_pubkey)
|
107
152
|
end
|
108
153
|
|
154
|
+
# Creates a new DelegateIdentity instance from JSON representations of the Identity and delegation.
|
155
|
+
#
|
156
|
+
# Parameters:
|
157
|
+
# - ic_identity: The JSON representation of the Identity.
|
158
|
+
# - ic_delegation: The JSON representation of the delegation.
|
159
|
+
#
|
160
|
+
# Returns: The DelegateIdentity instance.
|
109
161
|
def self.from_json(ic_identity, ic_delegation)
|
110
162
|
parsed_ic_identity = JSON.parse(ic_identity)
|
111
163
|
parsed_ic_delegation = JSON.parse(ic_delegation)
|
data/lib/ic_agent/principal.rb
CHANGED
@@ -7,16 +7,21 @@ module IcAgent
|
|
7
7
|
MAX_LENGTH_IN_BYTES = 29
|
8
8
|
|
9
9
|
class PrincipalSort
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
OPAQUE_ID = 1
|
11
|
+
SELF_AUTHENTICATING = 2
|
12
|
+
DERIVED_ID = 3
|
13
|
+
ANONYMOUS = 4
|
14
14
|
# Unassigned
|
15
15
|
end
|
16
16
|
|
17
|
+
# Base class for Principal.
|
17
18
|
class Principal
|
18
19
|
attr_reader :len, :bytes, :is_principal, :hex
|
19
20
|
|
21
|
+
# Initializes a new instance of the Principal class.
|
22
|
+
#
|
23
|
+
# Parameters:
|
24
|
+
# - bytes: The bytes representing the principal. Defaults to an empty string.
|
20
25
|
def initialize(bytes: ''.b)
|
21
26
|
@len = bytes.length
|
22
27
|
@bytes = bytes
|
@@ -24,23 +29,41 @@ module IcAgent
|
|
24
29
|
@is_principal = true
|
25
30
|
end
|
26
31
|
|
32
|
+
# Creates a new Principal instance representing the management canister.
|
33
|
+
#
|
34
|
+
# Returns: The Principal instance representing the management canister.
|
27
35
|
def self.management_canister
|
28
36
|
Principal.new
|
29
37
|
end
|
30
38
|
|
39
|
+
# Creates a new self-authenticating Principal.
|
40
|
+
#
|
41
|
+
# Parameters:
|
42
|
+
# - pubkey: The public key associated with the self-authenticating Principal.
|
43
|
+
#
|
44
|
+
# Returns: The self-authenticating Principal instance.
|
31
45
|
def self.self_authenticating(pubkey)
|
32
46
|
# check pubkey.size for is ed25519 or secp256k1
|
33
47
|
pubkey = [pubkey].pack('H*') if pubkey.size != 44 && pubkey.size != 88
|
34
48
|
|
35
49
|
hash_ = OpenSSL::Digest::SHA224.digest(pubkey)
|
36
|
-
hash_ += [PrincipalSort::
|
50
|
+
hash_ += [PrincipalSort::SELF_AUTHENTICATING].pack('C')
|
37
51
|
Principal.new(bytes: hash_)
|
38
52
|
end
|
39
53
|
|
54
|
+
# Creates a new anonymous Principal.
|
55
|
+
#
|
56
|
+
# Returns: The anonymous Principal instance.
|
40
57
|
def self.anonymous
|
41
58
|
Principal.new(bytes: "\x04".b)
|
42
59
|
end
|
43
60
|
|
61
|
+
# Creates a new Principal from a string representation.
|
62
|
+
#
|
63
|
+
# Parameters:
|
64
|
+
# - s: The string representation of the Principal.
|
65
|
+
#
|
66
|
+
# Returns: The Principal instance.
|
44
67
|
def self.from_str(s)
|
45
68
|
s1 = s.delete('-')
|
46
69
|
pad_len = ((s1.length / 8.0).ceil * 8) - s1.length
|
@@ -53,10 +76,19 @@ module IcAgent
|
|
53
76
|
p
|
54
77
|
end
|
55
78
|
|
79
|
+
# Creates a new Principal from a hexadecimal string representation.
|
80
|
+
#
|
81
|
+
# Parameters:
|
82
|
+
# - s: The hexadecimal string representation of the Principal.
|
83
|
+
#
|
84
|
+
# Returns: The Principal instance.
|
56
85
|
def self.from_hex(s)
|
57
86
|
Principal.new(bytes: [s].pack('H*'))
|
58
87
|
end
|
59
88
|
|
89
|
+
# Converts the Principal to a string representation.
|
90
|
+
#
|
91
|
+
# Returns: The string representation of the Principal.
|
60
92
|
def to_str
|
61
93
|
checksum = Zlib.crc32(@bytes) & 0xFFFFFFFF
|
62
94
|
b = ''
|
@@ -71,6 +103,12 @@ module IcAgent
|
|
71
103
|
ret + s
|
72
104
|
end
|
73
105
|
|
106
|
+
# Converts the Principal to an AccountIdentifier.
|
107
|
+
#
|
108
|
+
# Parameters:
|
109
|
+
# - sub_account: The sub-account identifier. Defaults to 0.
|
110
|
+
#
|
111
|
+
# Returns: The AccountIdentifier instance.
|
74
112
|
def to_account_id(sub_account = 0)
|
75
113
|
AccountIdentifier.generate(self, sub_account)
|
76
114
|
end
|
@@ -78,17 +116,70 @@ module IcAgent
|
|
78
116
|
def to_s
|
79
117
|
to_str
|
80
118
|
end
|
119
|
+
|
120
|
+
# Compares the Principal with another Principal.
|
121
|
+
#
|
122
|
+
# Parameters:
|
123
|
+
# - other: The other Principal to compare with.
|
124
|
+
#
|
125
|
+
# Returns: The comparison result as a string ('lt', 'eq', or 'gt').
|
126
|
+
def compare_to(other)
|
127
|
+
(0...[self.bytes.length, other.bytes.length].min).each do |i|
|
128
|
+
if self.bytes[i] < other.bytes[i]
|
129
|
+
return 'lt'
|
130
|
+
elsif self.bytes[i] > other.bytes[i]
|
131
|
+
return 'gt'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
if self.bytes.length < other.bytes.length
|
136
|
+
'lt'
|
137
|
+
elsif self.bytes.length > other.bytes.length
|
138
|
+
'gt'
|
139
|
+
else
|
140
|
+
'eq'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Utility method checking whether a provided Principal is less than or equal to the current one using the `compare_to` method.
|
145
|
+
#
|
146
|
+
# Parameters:
|
147
|
+
# - other: The other Principal to compare with.
|
148
|
+
#
|
149
|
+
# Returns: `true` if the current Principal is less than or equal to the provided Principal, otherwise `false`.
|
150
|
+
def lt_eq(other)
|
151
|
+
cmp = compare_to(other)
|
152
|
+
%w[lt eq].include?(cmp)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Utility method checking whether a provided Principal is greater than or equal to the current one using the `compare_to` method.
|
156
|
+
#
|
157
|
+
# Parameters:
|
158
|
+
# - other: The other Principal to compare with.
|
159
|
+
#
|
160
|
+
# Returns: `true` if the current Principal is greater than or equal to the provided Principal, otherwise `false`.
|
161
|
+
def gt_eq(other)
|
162
|
+
cmp = compare_to(other)
|
163
|
+
%w[gt eq].include?(cmp)
|
164
|
+
end
|
81
165
|
end
|
82
166
|
|
83
167
|
class AccountIdentifier
|
84
168
|
attr_reader :bytes
|
85
169
|
|
170
|
+
# Initializes a new instance of the AccountIdentifier class.
|
171
|
+
#
|
172
|
+
# Parameters:
|
173
|
+
# - hash: The hash representing the AccountIdentifier.
|
86
174
|
def initialize(hash)
|
87
175
|
raise 'Invalid hash length' unless hash.length == 32
|
88
176
|
|
89
177
|
@bytes = hash
|
90
178
|
end
|
91
179
|
|
180
|
+
# Converts the AccountIdentifier to a string representation.
|
181
|
+
#
|
182
|
+
# Returns: The string representation of the AccountIdentifier.
|
92
183
|
def to_str
|
93
184
|
'0x' + @bytes.unpack1('H*')
|
94
185
|
end
|
@@ -97,12 +188,19 @@ module IcAgent
|
|
97
188
|
to_str
|
98
189
|
end
|
99
190
|
|
191
|
+
# Generates a new AccountIdentifier from a Principal.
|
192
|
+
#
|
193
|
+
# Parameters:
|
194
|
+
# - principal: The Principal associated with the AccountIdentifier.
|
195
|
+
# - sub_account: The sub-account identifier. Defaults to 0.
|
196
|
+
#
|
197
|
+
# Returns: The AccountIdentifier instance.
|
100
198
|
def self.generate(principal, sub_account = 0)
|
101
199
|
sha224 = OpenSSL::Digest::SHA224.new
|
102
200
|
sha224 << "\naccount-id"
|
103
201
|
sha224 << principal.bytes
|
104
202
|
format_sub_account = "%08d" % sub_account
|
105
|
-
sub_account = format_sub_account.chars.map {|c| c.to_i}.pack('N*')
|
203
|
+
sub_account = format_sub_account.chars.map { |c| c.to_i }.pack('N*')
|
106
204
|
sha224 << sub_account
|
107
205
|
hash = sha224.digest
|
108
206
|
checksum = Zlib.crc32(hash) & 0xFFFFFFFF
|
@@ -5,6 +5,13 @@ require 'cbor'
|
|
5
5
|
|
6
6
|
module IcAgent
|
7
7
|
class SyetemState
|
8
|
+
# Retrieves the system time from a canister's state.
|
9
|
+
#
|
10
|
+
# Parameters:
|
11
|
+
# - agent: The IcAgent::Client instance.
|
12
|
+
# - canister_id: The ID of the canister.
|
13
|
+
#
|
14
|
+
# Returns: The system time as a timestamp.
|
8
15
|
def self.time(agent, canister_id)
|
9
16
|
cert = agent.read_state_raw(canister_id, [['time']])
|
10
17
|
timestamp = Certificate.lookup(['time'], cert)
|
@@ -12,6 +19,14 @@ module IcAgent
|
|
12
19
|
LEB128.decode_signed(str_io)
|
13
20
|
end
|
14
21
|
|
22
|
+
# Retrieves the public key of a subnet from a canister's state.
|
23
|
+
#
|
24
|
+
# Parameters:
|
25
|
+
# - agent: The IcAgent::Client instance.
|
26
|
+
# - canister_id: The ID of the canister.
|
27
|
+
# - subnet_id: The ID of the subnet.
|
28
|
+
#
|
29
|
+
# Returns: The public key of the subnet in hexadecimal format.
|
15
30
|
def self.subnet_public_key(agent, canister_id, subnet_id)
|
16
31
|
path = ['subnet', Principal.from_str(subnet_id).bytes, 'public_key']
|
17
32
|
cert = agent.read_state_raw(canister_id, [path])
|
@@ -19,6 +34,14 @@ module IcAgent
|
|
19
34
|
pubkey.str2hex
|
20
35
|
end
|
21
36
|
|
37
|
+
# Retrieves the canister ranges of a subnet from a canister's state.
|
38
|
+
#
|
39
|
+
# Parameters:
|
40
|
+
# - agent: The IcAgent::Client instance.
|
41
|
+
# - canister_id: The ID of the canister.
|
42
|
+
# - subnet_id: The ID of the subnet.
|
43
|
+
#
|
44
|
+
# Returns: An array of canister ranges, where each range is represented as an array of Principal instances.
|
22
45
|
def self.subnet_canister_ranges(agent, canister_id, subnet_id)
|
23
46
|
path = ['subnet', Principal.from_str(subnet_id).bytes, 'canister_ranges']
|
24
47
|
cert = agent.read_state_raw(canister_id, [path])
|
@@ -26,6 +49,13 @@ module IcAgent
|
|
26
49
|
CBOR.decode(ranges).value.map { |range| range.map { |item| Principal.new(bytes: item) } }
|
27
50
|
end
|
28
51
|
|
52
|
+
# Retrieves the module hash of a canister from a canister's state.
|
53
|
+
#
|
54
|
+
# Parameters:
|
55
|
+
# - agent: The IcAgent::Client instance.
|
56
|
+
# - canister_id: The ID of the canister.
|
57
|
+
#
|
58
|
+
# Returns: The module hash of the canister in hexadecimal format.
|
29
59
|
def self.canister_module_hash(agent, canister_id)
|
30
60
|
path = ['canister', Principal.from_str(canister_id).bytes, 'module_hash']
|
31
61
|
cert = agent.read_state_raw(canister_id, [path])
|
@@ -33,6 +63,13 @@ module IcAgent
|
|
33
63
|
module_hash.str2hex
|
34
64
|
end
|
35
65
|
|
66
|
+
# Retrieves the controllers of a canister from a canister's state.
|
67
|
+
#
|
68
|
+
# Parameters:
|
69
|
+
# - agent: The IcAgent::Client instance.
|
70
|
+
# - canister_id: The ID of the canister.
|
71
|
+
#
|
72
|
+
# Returns: An array of Principal instances representing the controllers of the canister.
|
36
73
|
def self.canister_controllers(agent, canister_id)
|
37
74
|
path = ['canister', Principal.from_str(canister_id).bytes, 'controllers']
|
38
75
|
cert = agent.read_state_raw(canister_id, [path])
|
data/lib/ic_agent/utils.rb
CHANGED
@@ -3,6 +3,12 @@ require 'leb128'
|
|
3
3
|
|
4
4
|
module IcAgent
|
5
5
|
module Utils
|
6
|
+
# Encodes a list of items into a binary string.
|
7
|
+
#
|
8
|
+
# Parameters:
|
9
|
+
# - l: The list of items to encode.
|
10
|
+
#
|
11
|
+
# Returns: The binary string representation of the encoded list.
|
6
12
|
def self.encode_list(l)
|
7
13
|
ret = ''
|
8
14
|
l.each do |item|
|
@@ -21,7 +27,12 @@ module IcAgent
|
|
21
27
|
ret
|
22
28
|
end
|
23
29
|
|
24
|
-
#
|
30
|
+
# Computes a hash value for sorting records by key.
|
31
|
+
#
|
32
|
+
# Parameters:
|
33
|
+
# - s: The key to hash.
|
34
|
+
#
|
35
|
+
# Returns: The computed hash value.
|
25
36
|
def self.label_hash(s)
|
26
37
|
if s =~ /(^_\d+_$)|(^_0x[0-9a-fA-F]+_$)/
|
27
38
|
num = s[1..-2]
|
@@ -41,6 +52,12 @@ module IcAgent
|
|
41
52
|
idl_hash(s)
|
42
53
|
end
|
43
54
|
|
55
|
+
# Computes a hash value for an IDL string.
|
56
|
+
#
|
57
|
+
# Parameters:
|
58
|
+
# - s: The IDL string to hash.
|
59
|
+
#
|
60
|
+
# Returns: The computed hash value.
|
44
61
|
def self.idl_hash(s)
|
45
62
|
h = 0
|
46
63
|
s.bytes.each do |c|
|
@@ -49,6 +66,12 @@ module IcAgent
|
|
49
66
|
h
|
50
67
|
end
|
51
68
|
|
69
|
+
# Converts a data structure into a request ID.
|
70
|
+
#
|
71
|
+
# Parameters:
|
72
|
+
# - d: The data structure to convert.
|
73
|
+
#
|
74
|
+
# Returns: The request ID as a binary string.
|
52
75
|
def self.to_request_id(d)
|
53
76
|
return nil unless d.is_a?(Hash)
|
54
77
|
|
@@ -67,9 +90,14 @@ module IcAgent
|
|
67
90
|
Digest::SHA256.digest(s)
|
68
91
|
end
|
69
92
|
|
93
|
+
# Decodes a binary blob into a string.
|
94
|
+
#
|
95
|
+
# Parameters:
|
96
|
+
# - blob_bytes: The binary blob to decode.
|
97
|
+
#
|
98
|
+
# Returns: The decoded string.
|
70
99
|
def self.decode_blob(blob_bytes)
|
71
100
|
blob_bytes.pack('C*')
|
72
101
|
end
|
73
102
|
end
|
74
103
|
end
|
75
|
-
|
data/lib/ic_agent/version.rb
CHANGED