package jelectrum; import java.security.MessageDigest; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.params.MainNetParams; import org.bitcoinj.params.TestNet3Params; import java.util.Collection; import java.util.ArrayList; import org.json.JSONObject; import org.json.JSONArray; import java.util.Scanner; import java.util.TreeMap; import java.util.Map; import java.net.URL; public class Util { public static final long FEE_MAP_UPDATE_TIME=300000L; //5min public static String getHexString(byte[] data) { StringBuilder sb = new StringBuilder(); for(byte b : data) { sb.append(String.format("%02x", b)); } return sb.toString(); } public static String SHA256(byte[] P) { try { MessageDigest SIG=MessageDigest.getInstance("SHA-256"); SIG.update(P, 0, P.length); byte D[]=SIG.digest(); return getHexString(D); } catch (java.security.NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public static String SHA256(String s) { return SHA256(s.getBytes()); } public static int measureSerialization(Object obj) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream obj_out = new ObjectOutputStream(out); obj_out.writeObject(obj); obj_out.flush(); obj_out.close(); return out.size(); } catch(Exception e) { throw new RuntimeException(e); } } public static Sha256Hash treeHash(Sha256Hash a, Sha256Hash b) { try { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(swapEndian(a).getBytes()); md.update(swapEndian(b).getBytes()); byte[] pass = md.digest(); md = MessageDigest.getInstance("SHA-256"); md.update(pass); return swapEndian(new Sha256Hash(md.digest())); } catch(java.security.NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public static Sha256Hash doubleHash(Sha256Hash a) { try { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(a.getBytes()); byte[] pass = md.digest(); md = MessageDigest.getInstance("SHA-256"); md.update(pass); return new Sha256Hash(md.digest()); } catch(java.security.NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public static Sha256Hash swapEndian(Sha256Hash a) { return new Sha256Hash(swapEndianHexString(a.toString())); } public static String swapEndianHexString(String in) { StringBuilder sb=new StringBuilder(); for(int i=0; i<in.length(); i+=2) { String s = in.substring(i,i+2); sb.insert(0,s); } return sb.toString(); } public static JSONObject getMerkleTreeForTransaction(Collection<Transaction> tx_list, Sha256Hash tx_hash) { try { ArrayList<Sha256Hash> tx_hash_list=new ArrayList<Sha256Hash>(); int target_pos=-1; for(Transaction tx : tx_list) { if (tx.getHash().equals(tx_hash)) target_pos=tx_hash_list.size(); tx_hash_list.add(tx.getHash()); } if (target_pos < 0) throw new RuntimeException("Target transaction not in collection"); JSONObject result = new JSONObject(); result.put("pos", target_pos); ArrayList<Sha256Hash> out_list = new ArrayList<Sha256Hash>(); int span=1; while(span < tx_hash_list.size()) span = span*2; Sha256Hash root = getInternalMerkleTreeMadness(tx_hash_list, target_pos, out_list); //result.put("root", root); JSONArray merkle = new JSONArray(); for(Sha256Hash h : out_list) { merkle.put(h.toString()); } result.put("merkle", merkle); return result; } catch(org.json.JSONException e) { throw new RuntimeException(e); } } private static Sha256Hash getInternalMerkleTreeMadness(ArrayList<Sha256Hash> tx_list, int target_pos, ArrayList<Sha256Hash> out_list) { int magic_pos = target_pos; while(tx_list.size() > 1) { ArrayList<Sha256Hash> next_list = new ArrayList<Sha256Hash>(); if (tx_list.size() % 2 == 1) { tx_list.add(tx_list.get(tx_list.size()-1)); } for(int i=0; i<tx_list.size(); i+=2) { if (magic_pos==i) { out_list.add(tx_list.get(i+1)); } if (magic_pos==i+1) { out_list.add(tx_list.get(i)); } next_list.add(treeHash(tx_list.get(i), tx_list.get(i+1))); } magic_pos = magic_pos / 2; tx_list=next_list; } return tx_list.get(0); } private static Sha256Hash getInternalMerkleTreeMadness(ArrayList<Sha256Hash> tx_list, int target_pos, int start, int span, ArrayList<Sha256Hash> out_list) { if (start >= tx_list.size()) return getInternalMerkleTreeMadness(tx_list, target_pos, start-span, span, null); if (span == 1) { return tx_list.get(start); } int mid = start + span/2; if ((start <= target_pos) && (target_pos < (start+span))) { Sha256Hash first = getInternalMerkleTreeMadness(tx_list,target_pos, start, span/2, out_list); Sha256Hash second = getInternalMerkleTreeMadness(tx_list,target_pos, mid, span/2, out_list); if (out_list != null) { if (target_pos < mid) { out_list.add(second); } else { out_list.add(first); } } return treeHash(first, second); } else { return treeHash( getInternalMerkleTreeMadness(tx_list, target_pos, start, span/2, null), getInternalMerkleTreeMadness(tx_list, target_pos, mid, span/2, null)); } } public static NetworkParameters getNetworkParameters(Config config) { NetworkParameters network_params = MainNetParams.get(); if (config.getBoolean("testnet")) { network_params = TestNet3Params.get(); } return network_params; } private static Map<Integer, Double> last_fee_map; private static long last_fee_map_time; public static Map<Integer, Double> getFeeEstimateMap() { updateFeeMap(); return last_fee_map; } private synchronized static void updateFeeMap() { try { if (last_fee_map_time + FEE_MAP_UPDATE_TIME < System.currentTimeMillis()) { URL url = new URL("https://ds73ipzb70zbz.cloudfront.net/fee_estimates"); Scanner scan = new Scanner(url.openStream()); StringBuilder sb = new StringBuilder(); while(scan.hasNextLine()) { String line = scan.nextLine(); sb.append(line); } scan.close(); JSONObject obj = new JSONObject(sb.toString()); JSONObject fees = obj.getJSONObject("fees"); TreeMap<Integer, Double> map = new TreeMap<>(); for(int i=1; i<120; i++) { double v = fees.getDouble("" + i); map.put(i, v); } last_fee_map = map; last_fee_map_time = System.currentTimeMillis(); } } catch(Throwable t) { System.out.println("Error getting fee estimates: " + t); } } }