module TxCatcher class Server < Goliath::API use Goliath::Rack::Params def response(env) uri = env["REQUEST_URI"] begin return route_for(uri) rescue BitcoinRPC::JSONRPCError => e return [400, {}, { error: e.data }.to_json] rescue Exception => e return [500, {}, { error: e.to_s }.to_json] end end def route_for(path) puts "[REQUEST: #{path}]" if path =~ /\A\/addr\/[0-9a-zA-Z]+\/utxo/ utxo(path) elsif path.start_with? "/addr/" address(path) elsif path.start_with? "/tx/send" broadcast_tx(params["rawtx"]) elsif path.start_with? "/feerate" feerate(params["blocks_target"] || 2) elsif path == "/" || path.empty? version = File.read(File.expand_path(File.dirname(__FILE__) + "../../../VERSION")) [200, {}, "TxCatcher server v#{version.strip}, #{TxCatcher::Config.rpcnode["name"]}, current_block_height: #{TxCatcher.current_block_height}"] else [404, {}, { error: "404, not found" }.to_json] end end def address(path) path = path.split("/").delete_if { |i| i.empty? } addr = path.last address = Address.where(address: addr).eager(deposits: :transactions).first if address transactions_ids = address.deposits.map { |d| d.transaction.txid } deposits = address.deposits.map do |d| t = d.transaction t.update(protected: true) unless t.protected t.update_block_height! { txid: t.txid, amount: d.amount_in_btc, satoshis: d.amount, confirmations: t.confirmations, block_height: t.block_height } end [200, {}, { address: address.address, received: address.received, deposits: deposits }.to_json] else [200, {}, { address: addr, received: 0, deposits: [] }.to_json] end end def utxo(path) path = path.split("/").delete_if { |i| i.empty? } path.pop addr = path.last model = Address.where(address: addr).eager(deposits: :transactions).first return [200, {}, "{}"] unless model transactions = model.deposits.map { |d| d.transaction } utxos = transactions.map do |t| outs = t.tx_hash["vout"].select { |out| out["scriptPubKey"]["addresses"] == [addr] } outs.map! do |out| t.update(protected: true) unless t.protected t.update_block_height! out["confirmations"] = t.confirmations || 0 out["txid"] = t.txid out end outs end.flatten [200, {}, utxos.to_json] end def broadcast_tx(txhex) TxCatcher.rpc_node.sendrawtransaction(txhex) tx = TxCatcher.rpc_node.decoderawtransaction(txhex) [200, {}, tx.to_json] end def feerate(blocks_target) result = TxCatcher.rpc_node.estimatesmartfee(blocks_target.to_i) # This is for older versions of bitcoind/litecoind clients if result.to_s.include?("error") result = TxCatcher.rpc_node.estimatefee(blocks_target.to_i) result = { "feerate" => result, "blocks" => blocks_target} end [200, {}, result["feerate"].to_s] end end end