ic_agent 0.1.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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