module Bdb

Constants

ADDRESS_REGEXP

Public Class Methods

balance_asset(ipdb, public_key, asset_id) click to toggle source
# File lib/bigchaindb.rb, line 132
def self.balance_asset(ipdb, public_key, asset_id)
  unspent = Bdb.unspent_outputs(ipdb, public_key)
  balance = 0
  unspent.each do |u|
    txn = JSON.parse(Bdb.get_transaction_by_id(ipdb, u["transaction_id"]).body)
    next unless ((txn["operation"] == "CREATE") && (txn["id"] == asset_id)) || (txn["asset"]["id"] == asset_id)
    balance += txn["outputs"][u["output_index"]]["amount"].to_i
  end
  return balance
end
create_asset(ipdb, public_key, private_key, asset_data, amount = 1, metadata = {"x"=> "y"}) click to toggle source
# File lib/bigchaindb.rb, line 143
def self.create_asset(ipdb, public_key, private_key, asset_data, amount = 1, metadata = {"x"=> "y"})
  output = Bdb.generate_output(public_key, amount)
  create = Bdb.create_txn(public_key, output, asset_data, metadata)
  signed_create = Bdb.sign(create, private_key)
  resp = post_transaction(ipdb, signed_create)
  if resp.code == 202
    txn = JSON.parse(resp.body)
    puts "Transaction #{txn["id"]} posted. Now checking status..."
    sleep(5)
    status_resp = get_transaction_status(ipdb, txn["id"])
    if (status_resp.code == 200) && JSON.parse(status_resp.body)["status"] == "valid"
      return {"create" => create, "txn" => txn}
    else
      puts "Trying again: #{status_resp.code} #{status_resp.body}"
      sleep(5)
      status_resp = get_transaction_status(ipdb, txn["id"])
      if (status_resp.code == 200) && JSON.parse(status_resp.body)["status"] == "valid"
        return {"create" => create, "txn" => txn}
      else
        puts "Tried twice but failed. #{status_resp.code} #{status_resp.body}"
        return nil
      end
    end
  else
    puts "Error in create_asset: #{resp.code} #{resp.body}"
    return nil
  end
end
create_txn(owner_before, output, asset_data, metadata) click to toggle source
# File lib/bigchaindb.rb, line 17
def self.create_txn(owner_before, output, asset_data, metadata)
  # owner_before is the issuer of the asset
  args = [
    "--asset-data", asset_data.to_json,
    "--metadata", metadata.to_json,
    owner_before,
    output.to_json
  ]
  command = "bdb create #{args.shelljoin}"
  JSON.parse(`#{command}`)
end
generate_keys() click to toggle source
# File lib/bigchaindb.rb, line 7
def self.generate_keys
  JSON.parse(`bdb generate_keys`)
  # {"public"=> "x", "private"=> "y"} #Base58 256-bit numbers
end
generate_output(owner_after, amount = 1) click to toggle source
# File lib/bigchaindb.rb, line 12
def self.generate_output(owner_after, amount = 1)
  # owner_after = who can consume this output? Can be a single pubkey or a m-of-n ThresholdSha256 condition
  JSON.parse(`bdb generate_output --amount #{[amount,owner_after].shelljoin}`)
end
get_asset(txn) click to toggle source
# File lib/bigchaindb.rb, line 40
def self.get_asset(txn)
  args = [txn.to_json]
  JSON.parse(`bdb get_asset #{args.shelljoin}`)
end
get_assets(ipdb, query) click to toggle source
# File lib/bigchaindb.rb, line 246
def self.get_assets(ipdb, query)
  HTTParty.get(ipdb["url"] + "/assets?search=#{query}")
end
get_outputs_by_pubkey(ipdb, pubkey, spent = :both) click to toggle source
# File lib/bigchaindb.rb, line 237
def self.get_outputs_by_pubkey(ipdb, pubkey, spent = :both)
  return HTTParty.get(ipdb["url"] + "/outputs?public_key=#{pubkey}") if spent == :both
  return HTTParty.get(ipdb["url"] + "/outputs?public_key=#{pubkey}&spent=#{spent}") # true or false
end
get_transaction_by_id(ipdb, txn_id) click to toggle source
# File lib/bigchaindb.rb, line 229
def self.get_transaction_by_id(ipdb, txn_id)
  HTTParty.get(ipdb["url"] + "/transactions/#{txn_id}")
end
get_transaction_status(ipdb, txn_id) click to toggle source
# File lib/bigchaindb.rb, line 242
def self.get_transaction_status(ipdb, txn_id)
  HTTParty.get(ipdb["url"] + "/statuses?transaction_id=#{txn_id}")
end
get_transactions_by_asset(ipdb, asset_id) click to toggle source
# File lib/bigchaindb.rb, line 233
def self.get_transactions_by_asset(ipdb, asset_id)
  HTTParty.get(ipdb["url"] + "/transactions?asset_id=#{asset_id}")
end
post_transaction(ipdb, txn) click to toggle source
# File lib/bigchaindb.rb, line 225
def self.post_transaction(ipdb, txn)
  HTTParty.post(ipdb["url"] + "/transactions/", {:body => txn.to_json, :headers => {"Content-Type" => "application/json", "app_id" => ipdb["app_id"], "app_key" => ipdb["app_key"]}})
end
sign(txn,privkey) click to toggle source
# File lib/bigchaindb.rb, line 45
def self.sign(txn,privkey)
  args = [txn.to_json, privkey]
  JSON.parse(`bdb sign #{args.shelljoin}`)
end
spend(txn, output_ids = []) click to toggle source
# File lib/bigchaindb.rb, line 50
def self.spend(txn, output_ids = [])
  # Convert outputs in txn to signable/spendable inputs.
  if output_ids.any?
    args = [txn.to_json, output_ids.to_json]
  else
    args = [txn.to_json]
  end
  JSON.parse(`bdb spend #{args.shelljoin}`)
end
test() click to toggle source
# File lib/bigchaindb.rb, line 172
def self.test
  # Generate key-pairs
  alice = Bdb.generate_keys
  puts "alice = #{alice.to_json}\n\n"
  bob = Bdb.generate_keys
  puts "bob = #{bob.to_json}\n\n"

  # Define the metadata for an asset, null should work too
  asset_data = { "name" => "mycoin", "symbol" => "MC" }
  puts "asset = #{asset_data.to_json}\n\n"

  # To create a CREATE txn, we need output and asset. metadata is optional
  # Let's generate the output first: we'll need receiver and amount
  output = Bdb.generate_output(alice["public"],100)
  puts "output = #{output.to_json}\n\n"

  metadata = {"msg" => "creating mycoin asset"}
  create = Bdb.create_txn(alice["public"], output, asset_data, metadata)
  puts "create = #{create.to_json}\n\n"

  signed_create = Bdb.sign(create, alice["private"])
  puts "signed_create = #{signed_create.to_json}\n\n"

  # This signed CREATE txn can be sent to BigChainDB over HTTP api
  ipdb = { "url" => ENV["IPDB_URL"], "app_id" => ENV["IPDB_APP_ID"], "app_key" => ENV["IPDB_APP_KEY"]}
  resp = post_transaction(ipdb, signed_create)
  puts "resp = #{resp.code} #{resp.body}"

  # Now let's create a TRANSFER txn and transfer 10 coins to bob
  # we'll need: inputs, outputs, asset and metadata
  # asset for transfer is just { "id": "id of the create txn"}
  asset = { "id" => signed_create["id"]}
  output = Bdb.generate_output(bob["public"],10)
  input = Bdb.spend(create)
  puts "input = #{input.to_json}\n\n"

  transfer = Bdb.transfer_txn(input, output, asset, {"msg" => "txferring"})
  puts "transfer = #{transfer.to_json}\n\n"

  signed_transfer = Bdb.sign(transfer, alice["private"])
  puts "signed_transfer = #{signed_transfer.to_json}\n\n"
  # Now send this to server
  resp = post_transaction(ipdb, signed_transfer)
  puts "resp = #{resp.code} #{resp.body}"

  # Get all txns for this asset
  txns = JSON.parse(get_transactions_by_asset(root_url, asset["id"]).body)

  # txfr status
  puts get_transaction_status(ipdb, txns.first["id"]).body
  puts get_transaction_status(ipdb, txns.last["id"]).body
end
transfer_asset(ipdb, receiver_pubkeys_amounts, sender_pubkey, sender_privkey, inputs, asset_id, metadata = {"ts"=> Time.now.to_s}) click to toggle source
# File lib/bigchaindb.rb, line 65
def self.transfer_asset(ipdb, receiver_pubkeys_amounts, sender_pubkey, sender_privkey, inputs, asset_id, metadata = {"ts"=> Time.now.to_s})
  asset = { "id" => asset_id}
  new_inputs = []
  input_amount = 0

  if inputs.nil? || inputs.none?
    # ask IPDB for unspent outputs
    unspent = Bdb.unspent_outputs(ipdb, sender_pubkey)
    if unspent.none?
      return nil, "#{sender_pubkey} does not have any unspent outputs for asset #{asset_id}"
    end
    unspent.each do |u|
      txn = JSON.parse(Bdb.get_transaction_by_id(ipdb, u["transaction_id"]).body)
      next unless ((txn["operation"] == "CREATE") && txn["id"] == asset_id) || ((txn["operation"] == "TRANSFER") && txn["asset"]["id"] == asset_id)
      input_amount += txn["outputs"][u["output_index"]]["amount"].to_i
      new_inputs += Bdb.spend(txn, [u["output_index"]])
    end
  else
    # assume that every output for sender_pubkey in given inputs is unspent and can be used as input
    inputs.each do |inp|
      input_amount += inp["outputs"].select { |o| o["condition"]["details"]["public_key"] == sender_pubkey }.inject(0) { |sum,out| sum + out["amount"].to_i }
      new_inputs += Bdb.spend(inp, inp["outputs"].each_with_index.select { |o,i| o["condition"]["details"]["public_key"] == sender_pubkey }.map(&:last))
    end
  end

  outgoing_amount = 0
  receiver_pubkeys_amounts.each do |pa|
    if pa[:amount] <= 0
      return nil, "Invalid amount (<=0) found for #{pa[:pubkey]}"
    end
    outgoing_amount += pa[:amount]
  end

  if outgoing_amount > input_amount
    return nil, "input_amount #{input_amount} < outgoing_amount #{outgoing_amount}"
  end
  outputs = receiver_pubkeys_amounts.collect { |pa| Bdb.generate_output(pa[:pubkey],pa[:amount]) }
  if outgoing_amount < input_amount
    # left-over amount should be transferred back to sender
    outputs.push(Bdb.generate_output(sender_pubkey,input_amount - outgoing_amount))
  end
  
  transfer = Bdb.transfer_txn(new_inputs, outputs, asset, metadata)
  signed_transfer = Bdb.sign(transfer, sender_privkey)
  resp = post_transaction(ipdb, signed_transfer)
  if resp.code == 202
    txn = JSON.parse(resp.body)
    puts "Transaction #{txn["id"]} posted. Now checking status..."
    sleep(5)
    status_resp = get_transaction_status(ipdb, txn["id"])
    if (status_resp.code == 200) && JSON.parse(status_resp.body)["status"] == "valid"
      return txn, "success"
    else
      puts "Trying again: #{status_resp.code} #{status_resp.body}"
      sleep(5)
      status_resp = get_transaction_status(ipdb, txn["id"])
      if (status_resp.code == 200) && JSON.parse(status_resp.body)["status"] == "valid"
        return txn, "success"
      else
        return nil, "Tried twice but failed. #{status_resp.code} #{status_resp.body}"
      end
    end
  else
    return nil, "Error in transfer_asset: #{resp.code} #{resp.body}"
  end
end
transfer_txn(inputs, outputs, asset, metadata = nil) click to toggle source
# File lib/bigchaindb.rb, line 29
def self.transfer_txn(inputs, outputs, asset, metadata = nil)
  args = {inputs: inputs, outputs: outputs, asset: asset, metadata: metadata}
  puts "In transfer_txn, args = #{args.inspect}"
  if metadata
    args = [inputs.to_json, outputs.to_json, asset.to_json, metadata.to_json]
  else
    args = [inputs.to_json, outputs.to_json, asset.to_json]
  end
  JSON.parse(`bdb transfer #{args.shelljoin}`)
end
unspent_outputs(ipdb, pubkey) click to toggle source
# File lib/bigchaindb.rb, line 60
def self.unspent_outputs(ipdb, pubkey)
  resp = self.get_outputs_by_pubkey(ipdb, pubkey, spent = false)
  JSON.parse(resp.body)
end