Class: Bluzelle::Client
- Inherits:
-
Object
- Object
- Bluzelle::Client
- Defined in:
- lib/bluzelle.rb
Instance Method Summary collapse
- #api_mutate(method, endpoint, payload) ⇒ Object
- #api_query(endpoint) ⇒ Object
- #broadcast_transaction(data) ⇒ Object
- #build_txn(fee:, msg:) ⇒ Object
- #count ⇒ Object
-
#create(key, value, lease: 0) ⇒ Object
mutate.
- #delete(key) ⇒ Object
- #delete_all ⇒ Object
- #get_lease(key) ⇒ Object
- #get_n_shortest_leases(n) ⇒ Object
- #get_response_error(response) ⇒ Object
- #has(key) ⇒ Object
-
#initialize(options) ⇒ Client
constructor
A new instance of Client.
- #key_values ⇒ Object
- #keys ⇒ Object
- #multi_update(payload) ⇒ Object
- #proven_read(key) ⇒ Object
-
#read(key) ⇒ Object
query.
- #read_account ⇒ Object
- #rename(key, new_key) ⇒ Object
- #renew_all_leases(lease) ⇒ Object
- #renew_lease(key, lease) ⇒ Object
- #send_transaction(method, endpoint, payload) ⇒ Object
- #set_account ⇒ Object
- #set_private_key ⇒ Object
- #setup_logging ⇒ Object
- #sign_transaction(txn) ⇒ Object
- #tx_count ⇒ Object
- #tx_get_lease(key) ⇒ Object
- #tx_get_n_shortest_leases(n) ⇒ Object
- #tx_has(key) ⇒ Object
- #tx_key_values ⇒ Object
- #tx_keys ⇒ Object
- #tx_read(key) ⇒ Object
- #update(key, value, lease: nil) ⇒ Object
- #validate_transaction(method, endpoint, payload) ⇒ Object
- #verify_address ⇒ Object
- #version ⇒ Object
Constructor Details
#initialize(options) ⇒ Client
Returns a new instance of Client.
76 77 78 |
# File 'lib/bluzelle.rb', line 76 def initialize() @options = end |
Instance Method Details
#api_mutate(method, endpoint, payload) ⇒ Object
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/bluzelle.rb', line 248 def api_mutate(method, endpoint, payload) url = @options['endpoint'] + endpoint @logger.debug("mutating url(#{url}), method(#{method})") uri = URI(url) http = Net::HTTP.new(uri.host, uri.port) if method == "delete" request = Net::HTTP::Delete.new(uri.request_uri) else request = Net::HTTP::Post.new(uri.request_uri) end request['Accept'] = 'application/json' request.content_type = 'application/json' data = Bluzelle::json_dumps payload @logger.debug("data(#{data})") request.body = data response = http.request(request) @logger.debug("response (#{response.body})...") data = JSON.parse(response.body) error = get_response_error(data) raise error if error data end |
#api_query(endpoint) ⇒ Object
237 238 239 240 241 242 243 244 245 246 |
# File 'lib/bluzelle.rb', line 237 def api_query(endpoint) url = @options['endpoint'] + endpoint @logger.debug("querying url(#{url})...") response = Net::HTTP.get_response URI(url) data = JSON.parse(response.body) error = get_response_error(data) raise error if error @logger.debug("response (#{data})...") data end |
#broadcast_transaction(data) ⇒ Object
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/bluzelle.rb', line 290 def broadcast_transaction(data) # fee fee = data['fee'] fee_gas = fee['gas'].to_i gas_info = @options['gas_info'] if gas_info['max_gas'] != 0 && fee_gas > gas_info['max_gas'] fee['gas'] = gas_info['max_gas'].to_s end if gas_info['max_fee'] != 0 fee['amount'] = [{ 'denom' => TOKEN_NAME, 'amount' => gas_info['max_fee'].to_s }] elsif gasInfo['gas_price'] != 0 fee['amount'] = [{ 'denom' => TOKEN_NAME, 'amount' => (fee_gas * gas_info['gas_price']).to_s }] end # sort txn = build_txn( fee: fee, msg: data['msg'][0] ) # signatures txn['signatures'] = [{ 'account_number' => @account['account_number'].to_s, 'pub_key' => { 'type' => PUB_KEY_TYPE, 'value' => Bluzelle::base64_encode(@wallet.public_key.compressed.to_bytes) }, 'sequence' => @account['sequence'].to_s, 'signature' => sign_transaction(txn) }] payload = { 'mode' => 'block', 'tx' => txn } response = api_mutate('post', TX_COMMAND, payload) # https://github.com/bluzelle/blzjs/blob/45fe51f6364439fa88421987b833102cc9bcd7c0/src/swarmClient/cosmos.js#L240-L246 # note - as of right now (3/6/20) the responses returned by the Cosmos REST interface now look like this: # success case: {"height":"0","txhash":"3F596D7E83D514A103792C930D9B4ED8DCF03B4C8FD93873AB22F0A707D88A9F","raw_log":"[]"} # failure case: {"height":"0","txhash":"DEE236DEF1F3D0A92CB7EE8E442D1CE457EE8DB8E665BAC1358E6E107D5316AA","code":4, # "raw_log":"unauthorized: signature verification failed; verify correct account sequence and chain-id"} # # this is far from ideal, doesn't match their docs, and is probably going to change (again) in the future. unless response.fetch('code', nil) @account['sequence'] += 1 if response.fetch('data', nil) return JSON.parse Bluzelle::hex_to_ascii response['data'] end return end raw_log = response["raw_log"] if raw_log.include?("signature verification failed") @broadcast_retries += 1 @logger.warn("transaction failed ... retrying(#{@broadcast_retries}) ...") if @broadcast_retries >= BROADCAST_MAX_RETRIES raise APIError, "transaction failed after max retry attempts" end sleep BROADCAST_RETRY_INTERVAL_SECONDS set_account() broadcast_transaction(txn) return end raise APIError, raw_log end |
#build_txn(fee:, msg:) ⇒ Object
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/bluzelle.rb', line 375 def build_txn(fee:, msg:) # TODO: find a better way to sort fee_amount = fee['amount'][0] txn = { 'fee' => { "amount" => [ { "amount" => fee_amount['amount'], "denom" => fee_amount['denom'] } ], "gas" => fee['gas'] }, 'memo' => Bluzelle::make_random_string(32) } msg_value = msg['value'] sorted_msg_value = {} MSG_KEYS_ORDER.each do |key| val = msg_value.fetch(key, nil) sorted_msg_value[key] = val if val end txn['msg'] = [ { "type" => msg['type'], "value" => sorted_msg_value } ] txn end |
#count ⇒ Object
173 174 175 176 |
# File 'lib/bluzelle.rb', line 173 def count() url = "/crud/count/#{@options["uuid"]}" api_query(url)["result"]["count"].to_i end |
#create(key, value, lease: 0) ⇒ Object
mutate
117 118 119 |
# File 'lib/bluzelle.rb', line 117 def create(key, value, lease: 0) send_transaction('post', '/crud/create', { 'Key' => key, 'Lease' => lease.to_s, 'Value' => value }) end |
#delete(key) ⇒ Object
128 129 130 |
# File 'lib/bluzelle.rb', line 128 def delete(key) send_transaction("delete", "/crud/delete", {"Key" => key}) end |
#delete_all ⇒ Object
136 137 138 |
# File 'lib/bluzelle.rb', line 136 def delete_all() send_transaction("post", "/crud/deleteall", {}) end |
#get_lease(key) ⇒ Object
188 189 190 191 |
# File 'lib/bluzelle.rb', line 188 def get_lease(key) url = "/crud/getlease/#{@options["uuid"]}/#{key}" api_query(url)["result"]["lease"].to_i end |
#get_n_shortest_leases(n) ⇒ Object
193 194 195 196 |
# File 'lib/bluzelle.rb', line 193 def get_n_shortest_leases(n) url = "/crud/getnshortestlease/#{@options["uuid"]}/#{n}" api_query(url)["result"]["keyleases"] end |
#get_response_error(response) ⇒ Object
405 406 407 408 |
# File 'lib/bluzelle.rb', line 405 def get_response_error(response) error = response['error'] return APIError.new(error) if error end |
#has(key) ⇒ Object
168 169 170 171 |
# File 'lib/bluzelle.rb', line 168 def has(key) url = "/crud/has/#{@options["uuid"]}/#{key}" api_query(url)["result"]["has"] end |
#key_values ⇒ Object
183 184 185 186 |
# File 'lib/bluzelle.rb', line 183 def key_values() url = "/crud/keyvalues/#{@options["uuid"]}" api_query(url)["result"]["keyvalues"] end |
#keys ⇒ Object
178 179 180 181 |
# File 'lib/bluzelle.rb', line 178 def keys() url = "/crud/keys/#{@options["uuid"]}" api_query(url)["result"]["keys"] end |
#multi_update(payload) ⇒ Object
140 141 142 143 144 145 146 |
# File 'lib/bluzelle.rb', line 140 def multi_update(payload) list = [] payload.each do |key, value| list.append({"key" => key, "value" => value}) end send_transaction("post", "/crud/multiupdate", {"KeyValues" => list}) end |
#proven_read(key) ⇒ Object
163 164 165 166 |
# File 'lib/bluzelle.rb', line 163 def proven_read(key) url = "/crud/pread/#{@options["uuid"]}/#{key}" api_query(url)["result"]["value"] end |
#read(key) ⇒ Object
query
158 159 160 161 |
# File 'lib/bluzelle.rb', line 158 def read(key) url = "/crud/read/#{@options["uuid"]}/#{key}" api_query(url)["result"]["value"] end |
#read_account ⇒ Object
105 106 107 108 |
# File 'lib/bluzelle.rb', line 105 def read_account url = "/auth/accounts/#{@options['address']}" api_query(url)['result']['value'] end |
#rename(key, new_key) ⇒ Object
132 133 134 |
# File 'lib/bluzelle.rb', line 132 def rename(key, new_key) send_transaction("post", "/crud/rename", {"Key" => key, "NewKey" => new_key}) end |
#renew_all_leases(lease) ⇒ Object
152 153 154 |
# File 'lib/bluzelle.rb', line 152 def renew_all_leases(lease) send_transaction("post", "/crud/renewleaseall", {"Lease" => lease.to_s}) end |
#renew_lease(key, lease) ⇒ Object
148 149 150 |
# File 'lib/bluzelle.rb', line 148 def renew_lease(key, lease) send_transaction("post", "/crud/renewlease", {"Key" => key, "Lease" => lease.to_s}) end |
#send_transaction(method, endpoint, payload) ⇒ Object
271 272 273 274 275 |
# File 'lib/bluzelle.rb', line 271 def send_transaction(method, endpoint, payload) @broadcast_retries = 0 txn = validate_transaction(method, endpoint, payload) broadcast_transaction(txn) end |
#set_account ⇒ Object
99 100 101 |
# File 'lib/bluzelle.rb', line 99 def set_account @account = read_account end |
#set_private_key ⇒ Object
85 86 87 88 89 |
# File 'lib/bluzelle.rb', line 85 def set_private_key seed = BipMnemonic.to_seed(mnemonic: @options['mnemonic']) master = MoneyTree::Master.new(seed_hex: seed) @wallet = master.node_for_path(HD_PATH) end |
#setup_logging ⇒ Object
80 81 82 83 |
# File 'lib/bluzelle.rb', line 80 def setup_logging @logger = Logger.new(STDOUT) @logger.level = if @options['debug'] then Logger::DEBUG else Logger::FATAL end end |
#sign_transaction(txn) ⇒ Object
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/bluzelle.rb', line 356 def sign_transaction(txn) payload = { 'account_number' => @account['account_number'].to_s, 'chain_id' => @options['chain_id'], 'fee' => txn['fee'], 'memo' => txn['memo'], 'msgs' => txn['msg'], 'sequence' => @account['sequence'].to_s } payload = Bluzelle::json_dumps(payload) pk = Secp256k1::PrivateKey.new(privkey: @wallet.private_key.to_bytes, raw: true) rs = pk.ecdsa_sign payload r = rs.slice(0, 32).read_string.reverse s = rs.slice(32, 32).read_string.reverse sig = "#{r}#{s}" Bluzelle::base64_encode sig end |
#tx_count ⇒ Object
210 211 212 213 |
# File 'lib/bluzelle.rb', line 210 def tx_count() res = send_transaction("post", "/crud/count", {}) res["count"].to_i end |
#tx_get_lease(key) ⇒ Object
225 226 227 228 |
# File 'lib/bluzelle.rb', line 225 def tx_get_lease(key) res = send_transaction("post", "/crud/getlease", {"Key" => key}) res["lease"].to_i end |
#tx_get_n_shortest_leases(n) ⇒ Object
230 231 232 233 |
# File 'lib/bluzelle.rb', line 230 def tx_get_n_shortest_leases(n) res = send_transaction("post", "/crud/getnshortestlease", {"N" => n.to_s}) res["keyleases"] end |
#tx_has(key) ⇒ Object
205 206 207 208 |
# File 'lib/bluzelle.rb', line 205 def tx_has(key) res = send_transaction("post", "/crud/has", {"Key" => key}) res["has"] end |
#tx_key_values ⇒ Object
220 221 222 223 |
# File 'lib/bluzelle.rb', line 220 def tx_key_values() res = send_transaction("post", "/crud/keyvalues", {}) res["keyvalues"] end |
#tx_keys ⇒ Object
215 216 217 218 |
# File 'lib/bluzelle.rb', line 215 def tx_keys() res = send_transaction("post", "/crud/keys", {}) res["keys"] end |
#tx_read(key) ⇒ Object
200 201 202 203 |
# File 'lib/bluzelle.rb', line 200 def tx_read(key) res = send_transaction("post", "/crud/read", {"Key" => key}) res["value"] end |
#update(key, value, lease: nil) ⇒ Object
121 122 123 124 125 126 |
# File 'lib/bluzelle.rb', line 121 def update(key, value, lease: nil) payload = {"Key" => key} payload["Lease"] = lease.to_s if lease.is_a? Integer payload["Value"] = value send_transaction("post", "/crud/update", payload) end |
#validate_transaction(method, endpoint, payload) ⇒ Object
277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/bluzelle.rb', line 277 def validate_transaction(method, endpoint, payload) address = @options['address'] payload = payload.merge({ 'BaseReq' => { 'chain_id' => @options['chain_id'], 'from' => address }, 'Owner' => address, 'UUID' => @options['uuid'] }) api_mutate(method, endpoint, payload)['value'] end |
#verify_address ⇒ Object
91 92 93 94 95 96 97 |
# File 'lib/bluzelle.rb', line 91 def verify_address b = Digest::RMD160.digest(Digest::SHA256.digest(@wallet.public_key.compressed.to_bytes)) address = Bech32.encode(ADDRESS_PREFIX, Bech32.convert_bits(b, from_bits: 8, to_bits: 5, pad: true)) if address != @options['address'] raise OptionsError, 'bad credentials(verify your address and mnemonic)' end end |
#version ⇒ Object
110 111 112 113 |
# File 'lib/bluzelle.rb', line 110 def version() url = "/node_info" api_query(url)["application_version"]["version"] end |