class Bluzelle::Swarm::Cosmos

Attributes

account_info[RW]
address[R]
chain_id[R]
endpoint[R]
mnemonic[R]

Public Class Methods

new(options = {}) click to toggle source
# File lib/bluzelle/swarm/cosmos.rb, line 17
def initialize(options = {})
  @mnemonic = options[:mnemonic]
  @chain_id = options[:chain_id]
  @endpoint = options[:endpoint]
  @account_info = {}

  @private_key = get_ec_private_key(@mnemonic)
  @address = address_from_mnemonic

  account
end

Public Instance Methods

query(endpoint) click to toggle source
# File lib/bluzelle/swarm/cosmos.rb, line 29
def query(endpoint)
  Request.execute(method: 'get', url: "#{@endpoint}/#{endpoint}")
end
send_transaction(method, endpoint, data, gas_info) click to toggle source
# File lib/bluzelle/swarm/cosmos.rb, line 33
def send_transaction(method, endpoint, data, gas_info)
  txn = Transaction.new(method, endpoint, data)
  txn.set_gas(gas_info)

  # fetch skeleton
  skeleton = fetch_txn_skeleton(txn)
  # set gas
  skeleton = update_gas(txn, skeleton)
  # sort
  skeleton = sort_hash(skeleton)

  broadcast_transaction(Transaction.new('post', TX_COMMAND, skeleton))
end

Private Instance Methods

account() click to toggle source

Account query

# File lib/bluzelle/swarm/cosmos.rb, line 50
def account
  url = "#{@endpoint}/auth/accounts/#{@address}"
  res = Request.execute(method: 'get', url: url)

  set_account_details(res.dig('result', 'value'))
end
address_from_mnemonic() click to toggle source

Check if address and mnemonic are valid

# File lib/bluzelle/swarm/cosmos.rb, line 115
def address_from_mnemonic
  pub_key = get_ec_public_key_from_priv(@private_key)
  get_address(pub_key)
end
broadcast_transaction(txn) click to toggle source

Broadcasts a transaction

@param [Bluzelle::Swarm::Transaction] txn

# File lib/bluzelle/swarm/cosmos.rb, line 60
def broadcast_transaction(txn)
  txn.data['memo'] = make_random_string

  txn.data['signatures'] = [{
    'account_number' => @account_info['account_number'].to_s,
    'pub_key' => {
      'type' => 'tendermint/PubKeySecp256k1',
      'value' => to_base64(
        [compressed_pub_key(open_key(@private_key))].pack('H*')
      )
    },
    'sequence' => @account_info['sequence'].to_s,
    'signature' => sign_transaction(txn.data)
  }]

  url = "#{@endpoint}/#{txn.endpoint}"
  payload = { 'mode' => 'block', 'tx' => txn.data }

  res = Request.execute(method: txn.method, url: url, payload: payload)

  if res.dig('code').nil?
    update_sequence
    decode_json(hex_to_bin(res.dig('data'))) if res.key?('data')
  else
    handle_broadcast_error(res.dig('raw_log'), txn)
  end
end
fetch_txn_skeleton(txn) click to toggle source

Fetch transaction skeleton

# File lib/bluzelle/swarm/cosmos.rb, line 101
def fetch_txn_skeleton(txn)
  url = "#{@endpoint}/#{txn.endpoint}"

  data = Request.execute(
    method: txn.method,
    url: url,
    payload: txn.data,
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
  )

  data['value']
end
handle_broadcast_error(raw_log, txn) click to toggle source

Handle broadcast error

@param [String] raw_log @param [Bluzelle::Swarm::Transaction] txn

# File lib/bluzelle/swarm/cosmos.rb, line 152
def handle_broadcast_error(raw_log, txn)
  if raw_log.include?('signature verification failed')
    update_account_sequence(txn)
  else
    raise Error::ApiError, extract_error_message(raw_log)
  end
end
retry_broadcast(txn) click to toggle source

Retry broadcast after failure

@param [Bluzelle::Swarm::Transaction]

# File lib/bluzelle/swarm/cosmos.rb, line 140
def retry_broadcast(txn)
  txn.retries_left -= 1

  sleep BROADCAST_RETRY_SECONDS

  broadcast_transaction(txn)
end
set_account_details(data) click to toggle source

Updates account details

@param [Hash] data

# File lib/bluzelle/swarm/cosmos.rb, line 123
def set_account_details(data)
  account_number = data.dig('account_number')
  sequence = data.dig('sequence')

  @account_info['account_number'] = account_number

  if @account_info['sequence'] != sequence
    @account_info['sequence'] = sequence
    return true
  end

  false
end
sign_transaction(txn) click to toggle source

Signs a transaction

@param txn

# File lib/bluzelle/swarm/cosmos.rb, line 168
def sign_transaction(txn)
  payload = {
    'account_number' => @account_info['account_number'].to_s,
    'chain_id' => @chain_id,
    'fee' => txn['fee'],
    'memo' => txn['memo'],
    'msgs' => txn['msg'],
    'sequence' => @account_info['sequence'].to_s
  }

  to_base64(ecdsa_sign(encode_json(payload), @private_key))
end
update_account_sequence(txn) click to toggle source

Updates account sequence and retries broadcast

@param [Bluzelle::Swarm::Transaction] txn

# File lib/bluzelle/swarm/cosmos.rb, line 91
def update_account_sequence(txn)
  if txn.retries_left != 0
    account
    retry_broadcast(txn)
  else
    raise Error::ApiError, 'Invalid chain id'
  end
end
update_gas(txn, data) click to toggle source
# File lib/bluzelle/swarm/cosmos.rb, line 181
def update_gas(txn, data)
  res = data.clone

  if res.dig('fee', 'gas').to_i > txn.max_gas && txn.max_gas != 0
    res['fee']['gas'] = txn.max_gas.to_s
  end

  if !txn.max_fee.nil?
    res['fee']['amount'] = [{
      'denom': TOKEN_NAME,
      'amount': txn.max_fee.to_s
    }]
  elsif !txn.gas_price.nil?
    res['fee']['amount'] = [{
      'denom': TOKEN_NAME,
      'amount': (res['fee']['gas'] * txn.gas_price).to_s
    }]
  end

  res
end
update_memo(txn) click to toggle source
# File lib/bluzelle/swarm/cosmos.rb, line 203
def update_memo(txn)
  txn['memo'] = make_random_string
  txn
end
update_sequence() click to toggle source

Update account sequence

# File lib/bluzelle/swarm/cosmos.rb, line 161
def update_sequence
  @account_info['sequence'] = @account_info['sequence'].to_i + 1
end