class Bluzelle::Client
Public Class Methods
new(options)
click to toggle source
# File lib/bluzelle.rb, line 64 def initialize(options) @options = options end
Public Instance Methods
api_mutate(method, endpoint, payload)
click to toggle source
# File lib/bluzelle.rb, line 284 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)
click to toggle source
# File lib/bluzelle.rb, line 273 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, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 326 def broadcast_transaction(data, gas_info: nil) # fee fee = data['fee'] fee_gas = fee['gas'].to_i if gas_info == nil gas_info = @options['gas_info'] end if gas_info == nil raise OptionsError, 'please provide gas_info when initializing the client or in the transaction' end Bluzelle::validate_gas_info 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, gas_info: gas_info) return end raise APIError, raw_log end
build_txn(fee:, msg:)
click to toggle source
# File lib/bluzelle.rb, line 417 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()
click to toggle source
# File lib/bluzelle.rb, line 201 def count() url = "/crud/count/#{@options["uuid"]}" api_query(url)["result"]["count"].to_i end
create(key, value, lease_info: nil, gas_info: nil)
click to toggle source
mutate
# File lib/bluzelle.rb, line 105 def create(key, value, lease_info: nil, gas_info: nil) payload = {"Key" => key} payload["Lease"] = Bluzelle::lease_info_to_blocks(lease_info).to_s if lease_info payload["Value"] = value send_transaction( 'post', '/crud/create', payload, gas_info: gas_info ) end
delete(key, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 129 def delete(key, gas_info: nil) send_transaction( "delete", "/crud/delete", {"Key" => key}, gas_info: gas_info ) end
delete_all(gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 147 def delete_all(gas_info: nil) send_transaction( "post", "/crud/deleteall", {}, gas_info: gas_info ) end
get_lease(key)
click to toggle source
# File lib/bluzelle.rb, line 216 def get_lease(key) url = "/crud/getlease/#{@options["uuid"]}/#{key}" Bluzelle::lease_blocks_to_seconds api_query(url)["result"]["lease"].to_i end
get_n_shortest_leases(n)
click to toggle source
# File lib/bluzelle.rb, line 221 def get_n_shortest_leases(n) url = "/crud/getnshortestleases/#{@options["uuid"]}/#{n}" kls = api_query(url)["result"]["keyleases"] kls.each do |kl| kl["lease"] = Bluzelle::lease_blocks_to_seconds kl["lease"] end kls end
get_response_error(response)
click to toggle source
# File lib/bluzelle.rb, line 447 def get_response_error(response) error = response['error'] return APIError.new(error) if error end
has(key)
click to toggle source
# File lib/bluzelle.rb, line 196 def has(key) url = "/crud/has/#{@options["uuid"]}/#{key}" api_query(url)["result"]["has"] end
key_values()
click to toggle source
# File lib/bluzelle.rb, line 211 def key_values() url = "/crud/keyvalues/#{@options["uuid"]}" api_query(url)["result"]["keyvalues"] end
keys()
click to toggle source
# File lib/bluzelle.rb, line 206 def keys() url = "/crud/keys/#{@options["uuid"]}" api_query(url)["result"]["keys"] end
multi_update(payload, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 156 def multi_update(payload, gas_info: nil) list = [] payload.each do |key, value| list.append({"key" => key, "value" => value}) end send_transaction( "post", "/crud/multiupdate", {"KeyValues" => list}, gas_info: gas_info ) end
proven_read(key)
click to toggle source
# File lib/bluzelle.rb, line 191 def proven_read(key) url = "/crud/pread/#{@options["uuid"]}/#{key}" api_query(url)["result"]["value"] end
read(key)
click to toggle source
query
# File lib/bluzelle.rb, line 186 def read(key) url = "/crud/read/#{@options["uuid"]}/#{key}" api_query(url)["result"]["value"] end
read_account()
click to toggle source
# File lib/bluzelle.rb, line 93 def read_account url = "/auth/accounts/#{@options['address']}" api_query(url)['result']['value'] end
rename(key, new_key, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 138 def rename(key, new_key, gas_info: nil) send_transaction( "post", "/crud/rename", {"Key" => key, "NewKey" => new_key}, gas_info: gas_info ) end
renew_all_leases(lease_info, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 176 def renew_all_leases(lease_info, gas_info: nil) send_transaction( "post", "/crud/renewleaseall", {"Lease" => Bluzelle::lease_info_to_blocks(lease_info).to_s}, gas_info: gas_info ) end
renew_lease(key, lease_info, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 168 def renew_lease(key, lease_info, gas_info: nil) send_transaction( "post", "/crud/renewlease", {"Key" => key, "Lease" => Bluzelle::lease_info_to_blocks(lease_info).to_s}, gas_info: gas_info ) end
send_transaction(method, endpoint, payload, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 307 def send_transaction(method, endpoint, payload, gas_info: nil) @broadcast_retries = 0 txn = validate_transaction(method, endpoint, payload) broadcast_transaction(txn, gas_info: gas_info) end
set_account()
click to toggle source
# File lib/bluzelle.rb, line 87 def set_account @account = read_account end
set_private_key()
click to toggle source
# File lib/bluzelle.rb, line 73 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()
click to toggle source
# File lib/bluzelle.rb, line 68 def setup_logging @logger = Logger.new(STDOUT) @logger.level = if @options['debug'] then Logger::DEBUG else Logger::FATAL end end
sign_transaction(txn)
click to toggle source
# File lib/bluzelle.rb, line 398 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(gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 242 def tx_count(gas_info: nil) res = send_transaction("post", "/crud/count", {}, gas_info: gas_info) res["count"].to_i end
tx_get_lease(key, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 257 def tx_get_lease(key, gas_info: nil) res = send_transaction("post", "/crud/getlease", {"Key" => key}, gas_info: gas_info) Bluzelle::lease_blocks_to_seconds res["lease"].to_i end
tx_get_n_shortest_leases(n, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 262 def tx_get_n_shortest_leases(n, gas_info: nil) res = send_transaction("post", "/crud/getnshortestleases", {"N" => n.to_s}, gas_info: gas_info) kls = res["keyleases"] kls.each do |kl| kl["lease"] = Bluzelle::lease_blocks_to_seconds kl["lease"] end kls end
tx_has(key, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 237 def tx_has(key, gas_info: nil) res = send_transaction("post", "/crud/has", {"Key" => key}, gas_info: gas_info) res["has"] end
tx_key_values(gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 252 def tx_key_values(gas_info: nil) res = send_transaction("post", "/crud/keyvalues", {}, gas_info: gas_info) res["keyvalues"] end
tx_keys(gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 247 def tx_keys(gas_info: nil) res = send_transaction("post", "/crud/keys", {}, gas_info: gas_info) res["keys"] end
tx_read(key, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 232 def tx_read(key, gas_info: nil) res = send_transaction("post", "/crud/read", {"Key" => key}, gas_info: gas_info) res["value"] end
update(key, value, lease_info: nil, gas_info: nil)
click to toggle source
# File lib/bluzelle.rb, line 117 def update(key, value, lease_info: nil, gas_info: nil) payload = {"Key" => key} payload["Lease"] = Bluzelle::lease_info_to_blocks(lease_info).to_s if lease_info payload["Value"] = value send_transaction( "post", "/crud/update", payload, gas_info: gas_info ) end
validate_transaction(method, endpoint, payload)
click to toggle source
# File lib/bluzelle.rb, line 313 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()
click to toggle source
# File lib/bluzelle.rb, line 79 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()
click to toggle source
# File lib/bluzelle.rb, line 98 def version() url = "/node_info" api_query(url)["application_version"]["version"] end