/* * Follow the Bitcoin * Copyright (C) 2014 Danno Ferrin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package com.shemnon.btc.bitcore; import com.shemnon.btc.coinbase.CBPriceHistory; import com.shemnon.btc.model.IBase; import com.shemnon.btc.model.IBlock; import com.shemnon.btc.model.ICoin; import com.shemnon.btc.model.ITx; import javax.json.Json; import javax.json.JsonNumber; import javax.json.JsonObject; import javax.json.JsonReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * * Created by shemnon on 28 Feb 2014. */ public class BCTx extends BitcoreBase implements ITx { static Map<String, BCTx> txcache = new ConcurrentHashMap<>(); protected static final DateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm"); private String txid; List<ICoin> inputs; List<ICoin> outputs; String blockhash; private long timeMs; private double outputValue; private double inputValue; private double fees; private int confirmations; private BCTx() {} public static BCTx query(String txid) { if (txid == null) return null; if (txcache.containsKey(txid)) { return txcache.get(txid); } else { IBase.checkOffThread(); try (InputStream is = new URL(urlbase + "/tx/" + txid).openStream()) { return fromStream(is); } catch (IOException e) { e.printStackTrace(); return null; } } } static BCTx fromJson(String jsonString) { try (InputStream is = new ByteArrayInputStream(jsonString.getBytes("UTF-8"))) { return fromStream(is); } catch (IOException e) { e.printStackTrace(); return null; } } static BCTx fromStream(InputStream is) { JsonObject obj = null; try (JsonReader rdr = Json.createReader(is)) { BCTx tx = new BCTx(); obj = rdr.readObject(); tx.txid = obj.getString("txid"); tx.inputs = obj.getJsonArray("vin").stream() .map(jv -> BCCoin.cacheFromInput((JsonObject)jv, tx.txid)) .collect(Collectors.toList()); tx.outputs = obj.getJsonArray("vout").stream() .map(jv -> BCCoin.cacheFromOutput((JsonObject) jv, tx.txid)) .collect(Collectors.toList()); tx.blockhash = obj.getString("blockhash"); tx.confirmations = obj.getInt("confirmations"); JsonNumber num = obj.getJsonNumber("time"); tx.timeMs = num == null ? 0 : (num.longValue() * 1000L); num = obj.getJsonNumber("valueOut"); tx.outputValue = (num == null) ? 0.0 : num.doubleValue(); num = obj.getJsonNumber("valueIn"); tx.inputValue = (num == null) ? 0.0 : num.doubleValue(); num = obj.getJsonNumber("fees"); tx.fees = (num == null) ? 0.0 : num.doubleValue(); txcache.put(tx.txid, tx); return tx; } catch (Exception e){ e.printStackTrace(); System.out.println(obj); return null; } } public List<ICoin> getInputs() { return inputs; } public List<ICoin> getOutputs() { return outputs; } List<String> getInputAddresses() { return inputs.stream().map(ICoin::getAddr).collect(Collectors.toList()); } List<String> getOutputAddresses() { return outputs.stream().map(ICoin::getAddr).collect(Collectors.toList()); } public double getInputValue() { return inputValue; } public long getInputValueSatoshi() { return (long)(inputValue * 100000000); } public double getInputValueUSD() { return CBPriceHistory.getInstance().getPrice(timeMs).orElse(0.0) * inputValue; } public long getOutputValueSatoshi() { return (long)(outputValue * 100000000); } public double getOutputValue() { return outputValue; } public double getOutputValueUSD() { return CBPriceHistory.getInstance().getPrice(timeMs).orElse(0.0) * outputValue; } long getFeePaidSatoshi() { return (long)(fees * 100000000); } double getFeePaid() { return fees; } double getFeePaidUSD() { return CBPriceHistory.getInstance().getPrice(timeMs).orElse(0.0) * fees; } public boolean isConfirmed() { return confirmations > 0; } IBlock getBlock() { return BCBlock.query(blockhash); } public String getHash() { return txid; } public int getBlockHeight() { IBlock block = getBlock(); return (block != null) ? block.getHeight() : -1; } public List<ICoin> getUnspentCoins() { return outputs.stream().filter(c -> !c.isSpent()).collect(Collectors.toList()); } public long getTimeMs() { return timeMs; } public String getTimeString() { if (timeMs > 0) { return dateFormat.format(timeMs); } else { return "?"; } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BCTx bcTx = (BCTx) o; return !(txid != null ? !txid.equals(bcTx.txid) : bcTx.txid != null); } @Override public int hashCode() { return txid != null ? txid.hashCode() : 0; } @Override public String dump() { return "{\"type\":\"BCBlock\", \"txid\":\"" + txid + "\"}"; } }