class Xgt::Ruby::Auth

Public Class Methods

canonical?(sig) click to toggle source
# File lib/xgt/ruby.rb, line 188
def self.canonical?(sig)
  sig = sig.unpack('C*')

  !(
    ((sig[0] & 0x80 ) != 0) || ( sig[0] == 0 ) ||
    ((sig[1] & 0x80 ) != 0) ||
    ((sig[32] & 0x80 ) != 0) || ( sig[32] == 0 ) ||
    ((sig[33] & 0x80 ) != 0)
  )
end
from_base_58(string) click to toggle source
# File lib/xgt/ruby.rb, line 170
def self.from_base_58(string)
  unhexlify(Bitcoin.decode_base58(string))
end
generate_wallet_name(*public_keys) click to toggle source
# File lib/xgt/ruby.rb, line 159
def self.generate_wallet_name(*public_keys)
  hashed = Digest::SHA256.hexdigest(public_keys.join(''))
  base58 = Base58.binary_to_base58([hashed].pack('H*'), :bitcoin, true)
  address_prefix = 'XGT'
  "#{address_prefix}#{base58[0...40]}"
end
generate_wif(name, password, role) click to toggle source
# File lib/xgt/ruby.rb, line 138
def self.generate_wif(name, password, role)
  brain_key = (name + role + password).strip.split(/[\t\n\v\f\r ]+/).join(' ')
  key = "\x80".b + Digest::SHA256.digest(brain_key)
  checksum = Digest::SHA256.digest(Digest::SHA256.digest(key))[0...4]
  to_base_58(key + checksum)
end
hexlify(s) click to toggle source
# File lib/xgt/ruby.rb, line 174
def self.hexlify(s)
  a = []
  if s.respond_to?(:each_byte)
    s.each_byte { |b| a << sprintf('%02X', b) }
  else
    s.each { |b| a << sprintf('%02X', b) }
  end
  a.join.downcase
end
random_wif() click to toggle source
# File lib/xgt/ruby.rb, line 132
def self.random_wif()
  private_key = unhexlify('80' + SecureRandom.hex(32))
  checksum = Digest::SHA256.digest( Digest::SHA256.digest(private_key) ).byteslice(0, 4)
  to_base_58(private_key + checksum)
end
sign_transaction(rpc, txn, wifs, chain_id) click to toggle source
# File lib/xgt/ruby.rb, line 89
def self.sign_transaction(rpc, txn, wifs, chain_id)
  # Get the last irreversible block number
  response = rpc.call('database_api.get_dynamic_global_properties', {})
  chain_date = response['time'] + 'Z'
  last_irreversible_block_num = response['last_irreversible_block_num']
  ref_block_num = (last_irreversible_block_num - 1) & 0xffff
  # Get ref block info to append to the transaction
  response = rpc.call('block_api.get_block_header', { 'block_num' => last_irreversible_block_num })
  header = response['header']
  head_block_id = (header && header['previous']) ? header['previous'] : '0000000000000000000000000000000000000000'
  ref_block_prefix = [head_block_id].pack('H*')[4...8].unpack('V').first
  expiration = (Time.parse(chain_date) + 600).iso8601.gsub(/Z$/, '')
  # Append ref block info to the transaction
  txn['ref_block_num'] = ref_block_num
  txn['ref_block_prefix'] = ref_block_prefix
  txn['expiration'] = expiration
  # Get a hex digest of the transactioon
  response = rpc.call('transaction_api.get_transaction_hex', [txn])
  transaction_hex = response[0..-3]
  unhexed = unhexlify(chain_id + transaction_hex)
  digest_hex = Digest::SHA256.hexdigest(unhexed)
  private_keys = wifs.map { |wif| Bitcoin::Key.from_base58(wif) }
  ec = Bitcoin::OpenSSL_EC
  count = 0
  sig = nil

  # Calculate signatures and add them to the transaction
  txn['signatures'] ||= []
  private_keys.each do |private_key|
    loop do
      count += 1
      # TODO: Periodically check that count doesn't spin out of control
      public_key_hex = private_key.pub
      digest = unhexlify(digest_hex)
      sig = ec.sign_compact(digest, private_key.priv, public_key_hex, false)
      next if public_key_hex != ec.recover_compact(digest, sig)
      break if canonical?(sig)
    end
    txn['signatures'] << hexlify(sig)
  end
  txn
end
to_base_58(bytes) click to toggle source
# File lib/xgt/ruby.rb, line 166
def self.to_base_58(bytes)
  Bitcoin.encode_base58(hexlify(bytes))
end
unhexlify(s) click to toggle source
# File lib/xgt/ruby.rb, line 184
def self.unhexlify(s)
  s.split.pack('H*')
end
wif_to_public_key(wif, address_prefix) click to toggle source
# File lib/xgt/ruby.rb, line 145
def self.wif_to_public_key(wif, address_prefix)
  private_wif = unhexlify(Bitcoin.decode_base58(wif))
  version = private_wif[0]
  checksum = private_wif[-4..-1]
  # TODO: Verify version and checksum
  private_key = private_wif[1...-4]
  big_num = OpenSSL::BN.new(hexlify(private_key).to_i(16))
  group = OpenSSL::PKey::EC::Group.new('secp256k1')
  product = group.generator.mul(big_num).to_bn
  public_key_buffer = OpenSSL::PKey::EC::Point.new(group, product).to_octet_string(:compressed)
  checksum = Digest::RMD160.digest(public_key_buffer)
  address_prefix + to_base_58(public_key_buffer + checksum[0...4])
end