RecordsKeeperRubyLib 0.1.0 → 0.1.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.
Files changed (39) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -1
  3. data/CODE_OF_CONDUCT.md +0 -8
  4. data/Gemfile +0 -1
  5. data/Gemfile.lock +4 -62
  6. data/LICENSE.txt +2 -2
  7. data/README.md +92 -1
  8. data/{RecordsKeeperRuby.gemspec → RecordsKeeperRubyLib.gemspec} +4 -5
  9. data/bin/console +1 -1
  10. data/config.yaml +8 -0
  11. data/docs/{addressdoc.rb → address_doc.rb} +105 -116
  12. data/docs/{assetsdoc.rb → asset_doc.rb} +54 -65
  13. data/docs/block_doc.rb +63 -0
  14. data/docs/blockchain_doc.rb +77 -0
  15. data/docs/{permissionsdoc.rb → permissions_doc.rb} +54 -65
  16. data/docs/{streamdoc.rb → stream_doc.rb} +79 -84
  17. data/docs/{transactiondoc.rb → transaction_doc.rb} +110 -121
  18. data/docs/{walletdoc.rb → wallet_doc.rb} +122 -133
  19. data/lib/RecordsKeeperRubyLib.rb +9 -0
  20. data/lib/{RecordsKeeperRuby → RecordsKeeperRubyLib}/address.rb +198 -186
  21. data/lib/{RecordsKeeperRuby → RecordsKeeperRubyLib}/assets.rb +99 -87
  22. data/lib/{RecordsKeeperRuby → RecordsKeeperRubyLib}/block.rb +94 -89
  23. data/lib/{RecordsKeeperRuby → RecordsKeeperRubyLib}/blockchain.rb +148 -135
  24. data/lib/RecordsKeeperRubyLib/config.yaml +8 -0
  25. data/lib/{RecordsKeeperRuby → RecordsKeeperRubyLib}/permissions.rb +77 -77
  26. data/lib/RecordsKeeperRubyLib/stream.rb +220 -0
  27. data/lib/{RecordsKeeperRuby → RecordsKeeperRubyLib}/transaction.rb +213 -182
  28. data/lib/RecordsKeeperRubyLib/version.rb +3 -0
  29. data/lib/{RecordsKeeperRuby → RecordsKeeperRubyLib}/wallet.rb +263 -252
  30. data/lib/config.yaml +8 -0
  31. metadata +27 -41
  32. data/docs/blockchaindoc.rb +0 -77
  33. data/docs/blockdoc.rb +0 -76
  34. data/lib/RecordsKeeperRuby.rb +0 -9
  35. data/lib/RecordsKeeperRuby/sample_config.yaml +0 -60
  36. data/lib/RecordsKeeperRuby/stream.rb +0 -169
  37. data/lib/RecordsKeeperRuby/version.rb +0 -3
  38. data/lib/sample_config.yaml +0 -60
  39. data/sample_config.yaml +0 -60
@@ -0,0 +1,3 @@
1
+ module RecordsKeeperRubyLib
2
+ VERSION = "0.1.1"
3
+ end
@@ -1,252 +1,263 @@
1
- # Library to work with RecordsKeeper wallet.
2
-
3
- # You can create wallet, create multisignature wallet, retrieve wallet's information, retrieve private key of a particular
4
- # wallet address, sign message verify message, dump wallet file, backup wallet file, import wallet file, encrypt wallet by
5
- # using wallet class. You just have to pass parameters to invoke the pre-defined functions.
6
-
7
- require 'rubygems'
8
- require 'httparty'
9
- require 'json'
10
- require 'binary_parser'
11
- require 'yaml'
12
- require 'hex_string'
13
-
14
- module RecordsKeeperRuby
15
- class Wallet
16
-
17
- # Entry point for accessing Block class resources.
18
- # Import values from config file.
19
- cfg = YAML::load(File.open('config.yaml','r'))
20
- @network = cfg['testnet'] # Network variable to store the networrk that you want to access
21
- if @network==cfg['testnet']
22
- @url = cfg['testnet']['url']
23
- @user = cfg['testnet']['rkuser']
24
- @password = cfg['testnet']['passwd']
25
- @chain = cfg['testnet']['chain']
26
- else
27
- @url = cfg['mainnet']['url']
28
- @user = cfg['mainnet']['rkuser']
29
- @password = cfg['mainnet']['passwd']
30
- @chain = cfg['mainnet']['chain']
31
- end
32
-
33
- def self.variable
34
- net = @network
35
- return net
36
- end
37
-
38
- # Function to create wallet on RecordsKeeper Blockchain
39
- def self.createWallet
40
- auth = {:username => @user, :password => @password}
41
- options = {
42
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
43
- :basic_auth => auth,
44
- :body => [ {"method":"createkeypairs","params":[],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
45
- }
46
- response = HTTParty.get(@url, options)
47
- out = response.parsed_response
48
- public_address = out[0]['result'][0]['address'] # returns public address of the wallet
49
- private_key = out[0]['result'][0]['privkey'] # returns private key of the wallet
50
- public_key = out[0]['result'][0]['pubkey'] # returns public key of the wallet
51
-
52
- def self.importAddress public_address
53
- auth = {:username => @user, :password => @password}
54
- options = {
55
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
56
- :basic_auth => auth,
57
- :body => [ {"method":"importaddress","params":[public_address, " ", false],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
58
- }
59
- response = HTTParty.get(@url, options)
60
- out = response.parsed_response
61
- result = out[0]['result']
62
- return result;
63
- end
64
- import_address = importAddress public_address
65
-
66
- return public_address, private_key, public_key; #returns public and private key
67
- end
68
-
69
- # Function to retrieve private key of a wallet on RecordsKeeper Blockchain
70
- def self.getPrivateKey public_address
71
- auth = {:username => @user, :password => @password}
72
- options = {
73
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
74
- :basic_auth => auth,
75
- :body => [ {"method":"dumpprivkey","params":[public_address],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
76
- }
77
- response = HTTParty.get(@url, options)
78
- out = response.parsed_response
79
- result = out[0]['result']
80
- if result.nil?
81
- private_key = out[0]['error']['message']
82
- else
83
- private_key = out[0]['result']
84
- end
85
- return private_key; #returns private key
86
- end
87
-
88
- # Function to retrieve wallet's information on RecordsKeeper Blockchain
89
- def self.retrieveWalletinfo
90
- auth = {:username => @user, :password => @password}
91
- options = {
92
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
93
- :basic_auth => auth,
94
- :body => [ {"method":"getwalletinfo","params":[],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
95
- }
96
- response = HTTParty.get(@url, options)
97
- out = response.parsed_response
98
- balance = out[0]['result']['balance']
99
- tx_count = out[0]['result']['txcount']
100
- unspent_tx = out[0]['result']['utxocount']
101
- return balance, tx_count, unspent_tx; #returns balance, tx count, unspent tx
102
- end
103
-
104
- # Function to create wallet's backup on RecordsKeeper Blockchain
105
- def self.backupWallet filename
106
- auth = {:username => @user, :password => @password}
107
- options = {
108
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
109
- :basic_auth => auth,
110
- :body => [ {"method":"backupwallet","params":[filename],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
111
- }
112
- response = HTTParty.get(@url, options)
113
- out = response.parsed_response
114
- result = out[0]['result']
115
- if result.nil?
116
- res = "Backup successful!"
117
- else
118
- res = out[0]['error']['message']
119
- end
120
- return res; #returns result
121
- end
122
-
123
- # Function to import wallet's backup on RecordsKeeper Blockchain
124
- def self.importWallet filename
125
- auth = {:username => @user, :password => @password}
126
- options = {
127
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
128
- :basic_auth => auth,
129
- :body => [ {"method":"importwallet","params":[filename],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
130
- }
131
- response = HTTParty.get(@url, options)
132
- out = response.parsed_response
133
- result = out[0]['result']
134
- if result.nil?
135
- res = "Wallet is successfully imported"
136
- else
137
- res = out[0]['error']['message']
138
- end
139
- return res; #returns result
140
- end
141
-
142
- # Function to dump wallet on RecordsKeeper Blockchain
143
- def self.dumpWallet filename
144
- auth = {:username => @user, :password => @password}
145
- options = {
146
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
147
- :basic_auth => auth,
148
- :body => [ {"method":"dumpwallet","params":[filename],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
149
- }
150
- response = HTTParty.get(@url, options)
151
- out = response.parsed_response
152
- result = out[0]['result']
153
- if result.nil?
154
- res = "Wallet is successfully dumped"
155
- else
156
- res = out[0]['error']['message']
157
- end
158
- return res; #returns result
159
- end
160
-
161
- # Function to lock wallet on RecordsKeeper Blockchain
162
- def self.lockWallet password
163
- auth = {:username => @user, :password => @password}
164
- options = {
165
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
166
- :basic_auth => auth,
167
- :body => [ {"method":"encryptwallet","params":[password],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
168
- }
169
- response = HTTParty.get(@url, options)
170
- out = response.parsed_response
171
- result = out[0]['result']
172
- if result.nil?
173
- res = "Wallet is successfully encrypted."
174
- else
175
- res = out[0]['error']['message']
176
- end
177
- return res; #returns result
178
- end
179
-
180
- # Function to unlock wallet on RecordsKeeper Blockchain
181
- def self.unlockWallet password, unlocktime
182
- auth = {:username => @user, :password => @password}
183
- options = {
184
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
185
- :basic_auth => auth,
186
- :body => [ {"method":"walletpassphrase","params":[password, unlocktime],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
187
- }
188
- response = HTTParty.get(@url, options)
189
- out = response.parsed_response
190
- result = out[0]['result']
191
- if result.nil?
192
- res = "Wallet is successfully unlocked."
193
- else
194
- res = out[0]['error']['message']
195
- end
196
- return res; #returns result
197
- end
198
-
199
- # Function to change password for wallet on RecordsKeeper Blockchain
200
- def self.changeWalletPassword old_password, new_password
201
- auth = {:username => @user, :password => @password}
202
- options = {
203
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
204
- :basic_auth => auth,
205
- :body => [ {"method":"walletpassphrasechange","params":[old_password, new_password],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
206
- }
207
- response = HTTParty.get(@url, options)
208
- out = response.parsed_response
209
- result = out[0]['result']
210
- if result.nil?
211
- res = "Password successfully changed!"
212
- else
213
- res = out[0]['error']['message']
214
- end
215
- return res; #returns result
216
- end
217
-
218
- # Function to sign message on RecordsKeeper Blockchain
219
- def self.signMessage private_key, message
220
- auth = {:username => @user, :password => @password}
221
- options = {
222
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
223
- :basic_auth => auth,
224
- :body => [ {"method":"signmessage","params":[private_key, message],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
225
- }
226
- response = HTTParty.get(@url, options)
227
- out = response.parsed_response
228
- signedMessage = out[0]['result']
229
- return signedMessage; #returns private key
230
- end
231
-
232
- # Function to verify message on RecordsKeeper Blockchain
233
- def self.verifyMessage address, signedMessage, message
234
- auth = {:username => @user, :password => @password}
235
- options = {
236
- :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
237
- :basic_auth => auth,
238
- :body => [ {"method":"verifymessage","params":[address, signedMessage, message],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
239
- }
240
- response = HTTParty.get(@url, options)
241
- out = response.parsed_response
242
- verifiedMessage = out[0]['result']
243
- if verifiedMessage
244
- validity = "Yes, message is verified"
245
- else
246
- validity = "No, signedMessage is not correct"
247
- end
248
- return validity; #returns validity
249
- end
250
- end
251
-
252
- end
1
+ # Library to work with RecordsKeeper wallet.
2
+
3
+ # You can create wallet, create multisignature wallet, retrieve wallet's information, retrieve private key of a particular
4
+ # wallet address, sign message verify message, dump wallet file, backup wallet file, import wallet file, encrypt wallet by
5
+ # using wallet class. You just have to pass parameters to invoke the pre-defined functions.
6
+
7
+ require 'rubygems'
8
+ require 'httparty'
9
+ require 'json'
10
+ require 'binary_parser'
11
+ require 'yaml'
12
+ require 'hex_string'
13
+
14
+ module RecordsKeeperRubyLib
15
+ class Wallet
16
+
17
+ # # Entry point for accessing Wallet class functions
18
+ if File.exist?('config.yaml')
19
+ # Import values from configuration file.
20
+ cfg = YAML::load(File.open('config.yaml','r'))
21
+
22
+ @url = cfg['url']
23
+ @user = cfg['rkuser']
24
+ @password = cfg['passwd']
25
+ @chain = cfg['chain']
26
+
27
+ else
28
+ #Import using ENV variables
29
+
30
+ @url = ENV['url']
31
+ @user = ENV['rkuser']
32
+ @password = ENV['passwd']
33
+ @chain = ENV['chain']
34
+ end
35
+
36
+ # Function to create wallet on RecordsKeeper Blockchain
37
+ def self.createWallet
38
+ auth = {:username => @user, :password => @password}
39
+ options = {
40
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
41
+ :basic_auth => auth,
42
+ :body => [ {"method":"createkeypairs","params":[],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
43
+ }
44
+ response = HTTParty.get(@url, options)
45
+ out = response.parsed_response
46
+ public_address = out[0]['result'][0]['address'] # returns public address of the wallet
47
+ private_key = out[0]['result'][0]['privkey'] # returns private key of the wallet
48
+ public_key = out[0]['result'][0]['pubkey'] # returns public key of the wallet
49
+
50
+ def self.importAddress public_address
51
+ auth = {:username => @user, :password => @password}
52
+ options = {
53
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
54
+ :basic_auth => auth,
55
+ :body => [ {"method":"importaddress","params":[public_address, " ", false],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
56
+ }
57
+ response = HTTParty.get(@url, options)
58
+ out = response.parsed_response
59
+ result = out[0]['result']
60
+ return result;
61
+ end
62
+ import_address = importAddress public_address
63
+ retrieve = {:public_address => public_address,:private_key => private_key,:public_key => public_key}
64
+ retrievedinfo = JSON.generate retrieve
65
+ return retrievedinfo
66
+ end
67
+
68
+ # Function to retrieve private key of a wallet address on RecordsKeeper Blockchain
69
+ def self.getPrivateKey public_address
70
+ auth = {:username => @user, :password => @password}
71
+ options = {
72
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
73
+ :basic_auth => auth,
74
+ :body => [ {"method":"dumpprivkey","params":[public_address],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
75
+ }
76
+ response = HTTParty.get(@url, options)
77
+ out = response.parsed_response
78
+ result = out[0]['result']
79
+ if result.nil?
80
+ private_key = out[0]['error']['message']
81
+ else
82
+ private_key = out[0]['result']
83
+ end
84
+ return private_key; #returns private key
85
+ end
86
+
87
+ # Function to retrieve wallet's information on RecordsKeeper Blockchain
88
+ def self.retrieveWalletinfo
89
+ auth = {:username => @user, :password => @password}
90
+ options = {
91
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
92
+ :basic_auth => auth,
93
+ :body => [ {"method":"getwalletinfo","params":[],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
94
+ }
95
+ response = HTTParty.get(@url, options)
96
+ out = response.parsed_response
97
+ balance = out[0]['result']['balance']
98
+ tx_count = out[0]['result']['txcount']
99
+ unspent_tx = out[0]['result']['utxocount']
100
+ retrieve = {:balance => balance,:tx_count => tx_count,:unspent_tx => unspent_tx}
101
+ retrievedinfo = JSON.generate retrieve
102
+ return retrievedinfo
103
+ end
104
+
105
+ # Function to create wallet's backup on RecordsKeeper Blockchain
106
+ def self.backupWallet filename
107
+ auth = {:username => @user, :password => @password}
108
+ options = {
109
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
110
+ :basic_auth => auth,
111
+ :body => [ {"method":"backupwallet","params":[filename],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
112
+ }
113
+ response = HTTParty.get(@url, options)
114
+ out = response.parsed_response
115
+ result = out[0]['result']
116
+ if result.nil?
117
+ res = "Backup successful!"
118
+ else
119
+ res = out[0]['error']['message']
120
+ end
121
+ return res; #returns result
122
+ end
123
+
124
+ # Function to import wallet's backup on RecordsKeeper Blockchain
125
+ def self.importWallet filename
126
+ auth = {:username => @user, :password => @password}
127
+ options = {
128
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
129
+ :basic_auth => auth,
130
+ :body => [ {"method":"importwallet","params":[filename],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
131
+ }
132
+ response = HTTParty.get(@url, options)
133
+ out = response.parsed_response
134
+ result = out[0]['result']
135
+ if result.nil?
136
+ res = "Wallet is successfully imported"
137
+ else
138
+ res = out[0]['error']['message']
139
+ end
140
+ return res; #returns result
141
+ end
142
+
143
+ # Function to dump wallet on RecordsKeeper Blockchain
144
+ def self.dumpWallet filename
145
+ auth = {:username => @user, :password => @password}
146
+ options = {
147
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
148
+ :basic_auth => auth,
149
+ :body => [ {"method":"dumpwallet","params":[filename],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
150
+ }
151
+ response = HTTParty.get(@url, options)
152
+ out = response.parsed_response
153
+ result = out[0]['result']
154
+ if result.nil?
155
+ res = "Wallet is successfully dumped"
156
+ else
157
+ res = out[0]['error']['message']
158
+ end
159
+ return res; #returns result
160
+ end
161
+
162
+ # Function to lock wallet on RecordsKeeper Blockchain
163
+ def self.lockWallet password
164
+ auth = {:username => @user, :password => @password}
165
+ options = {
166
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
167
+ :basic_auth => auth,
168
+ :body => [ {"method":"encryptwallet","params":[password],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
169
+ }
170
+ response = HTTParty.get(@url, options)
171
+ out = response.parsed_response
172
+ result = out[0]['result']
173
+ if result.nil?
174
+ res = "Wallet is successfully encrypted."
175
+ else
176
+ res = out[0]['error']['message']
177
+ end
178
+ return res; #returns result
179
+ end
180
+
181
+ # Function to unlock wallet on RecordsKeeper Blockchain
182
+ def self.unlockWallet password, unlocktime
183
+ auth = {:username => @user, :password => @password}
184
+ options = {
185
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
186
+ :basic_auth => auth,
187
+ :body => [ {"method":"walletpassphrase","params":[password, unlocktime],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
188
+ }
189
+ response = HTTParty.get(@url, options)
190
+ out = response.parsed_response
191
+ result = out[0]['result']
192
+ if result.nil?
193
+ res = "Wallet is successfully unlocked."
194
+ else
195
+ res = out[0]['error']['message']
196
+ end
197
+ return res; #returns result
198
+ end
199
+
200
+ # Function to change password for wallet on RecordsKeeper Blockchain
201
+ def self.changeWalletPassword old_password, new_password
202
+ auth = {:username => @user, :password => @password}
203
+ options = {
204
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
205
+ :basic_auth => auth,
206
+ :body => [ {"method":"walletpassphrasechange","params":[old_password, new_password],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
207
+ }
208
+ response = HTTParty.get(@url, options)
209
+ out = response.parsed_response
210
+ result = out[0]['result']
211
+ if result.nil?
212
+ res = "Password successfully changed!"
213
+ else
214
+ res = out[0]['error']['message']
215
+ end
216
+ return res; #returns result
217
+ end
218
+
219
+ # Function to sign message on RecordsKeeper Blockchain
220
+ def self.signMessage private_key, message
221
+ auth = {:username => @user, :password => @password}
222
+ options = {
223
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
224
+ :basic_auth => auth,
225
+ :body => [ {"method":"signmessage","params":[private_key, message],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
226
+ }
227
+ response = HTTParty.get(@url, options)
228
+ out = response.parsed_response
229
+ check = out[0]['result']
230
+
231
+ if check.nil?
232
+ signedMessage = out[0]['error']['message']
233
+ else
234
+ signedMessage = out[0]['result']
235
+ end
236
+ return signedMessage; #returns private key
237
+ end
238
+
239
+ # Function to verify message on RecordsKeeper Blockchain
240
+ def self.verifyMessage address, signedMessage, message
241
+ auth = {:username => @user, :password => @password}
242
+ options = {
243
+ :headers => headers= {"Content-Type"=> "application/json","Cache-Control" => "no-cache"},
244
+ :basic_auth => auth,
245
+ :body => [ {"method":"verifymessage","params":[address, signedMessage, message],"jsonrpc":2.0,"id":"curltext","chain_name":@chain}].to_json
246
+ }
247
+ response = HTTParty.get(@url, options)
248
+ out = response.parsed_response
249
+ verifiedMessage = out[0]['result']
250
+ error = out[0]['error']
251
+ if verifiedMessage
252
+ validity = "Yes, message is verified"
253
+ elsif error.nil?
254
+ validity = "No, signedMessage is not correct"
255
+ else
256
+ validity = error['message']
257
+ end
258
+ return validity; #returns validity
259
+ end
260
+ end
261
+
262
+ end
263
+