package jelectrum;
import jelectrum.db.DBFace;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.core.TransactionOutPoint;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.ScriptException;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.script.Script;
import java.util.HashSet;
import java.util.Map;
import java.util.Collection;
import java.util.Set;
import org.junit.Assert;
public class TXUtil
{
private DBFace db;
private NetworkParameters params;
private LRUCache<Sha256Hash, Transaction> transaction_cache;
public TXUtil(DBFace db, NetworkParameters params)
{
this.db = db;
this.params = params;
}
public synchronized void saveTxCache(Transaction tx)
{
if (transaction_cache == null)
{
transaction_cache = new LRUCache<Sha256Hash, Transaction>(64000);
}
transaction_cache.put(tx.getHash(), SerializedTransaction.scrubTransaction(params,tx));
}
public synchronized void putTxCacheIfOpen(Transaction tx)
{
if (transaction_cache != null)
{
transaction_cache.put(tx.getHash(), SerializedTransaction.scrubTransaction(params,tx));
}
}
public synchronized Transaction getTransaction(Sha256Hash hash)
{
Transaction tx = null;
if (transaction_cache != null)
{
tx = transaction_cache.get(hash);
}
if (tx != null) return tx;
SerializedTransaction s_tx = db.getTransaction(hash);
if (s_tx != null)
{
tx = s_tx.getTx(params);
putTxCacheIfOpen(tx);
return tx;
}
return null;
}
public Address getAddressForOutput(TransactionOutput out)
{
try
{
Script script = out.getScriptPubKey();
if (script.isSentToRawPubKey())
{
byte[] key = out.getScriptPubKey().getPubKey();
byte[] address_bytes = org.bitcoinj.core.Utils.sha256hash160(key);
Address a = new Address(params, address_bytes);
return a;
}
else
{
Address a = script.getToAddress(params);
return a;
}
}
catch(ScriptException e)
{
//System.out.println(out.getParentTransaction().getHash() + " - " + out);
//e.printStackTrace();
//jelly.getEventLog().log("Unable process tx output: " + out.getParentTransaction().getHash());
}
return null;
}
public HashSet<String> getAllAddresses(Transaction tx, boolean confirmed, Map<Sha256Hash, Transaction> block_tx_map)
{
HashSet<String> lst = new HashSet<String>();
boolean detail = false;
for(TransactionInput in : tx.getInputs())
{
Address a = getAddressForInput(in, confirmed, block_tx_map);
if (a!=null) lst.add(a.toString());
}
for(TransactionOutput out : tx.getOutputs())
{
Address a = getAddressForOutput(out);
if (a!=null) lst.add(a.toString());
}
return lst;
}
public Address getAddressForInput(TransactionInput in, boolean confirmed, Map<Sha256Hash, Transaction> block_tx_map)
{
if (in.isCoinBase()) return null;
try
{
Address a = in.getFromAddress();
return a;
}
catch(ScriptException e)
{
//Lets try this the other way
try
{
TransactionOutPoint out_p = in.getOutpoint();
Transaction src_tx = null;
int fail_count =0;
while(src_tx == null)
{
if (block_tx_map != null)
{
src_tx = block_tx_map.get(out_p.getHash());
}
if (src_tx == null)
{
src_tx = getTransaction(out_p.getHash());
if (src_tx == null)
{
if (!confirmed)
{
return null;
}
fail_count++;
if (fail_count > 30)
{
System.out.println("Unable to get source transaction: " + out_p.getHash());
}
if (fail_count > 240)
{
throw new RuntimeException("Waited too long to get transaction: " + out_p.getHash());
}
try{Thread.sleep(500);}catch(Exception e7){}
}
}
}
TransactionOutput out = src_tx.getOutput((int)out_p.getIndex());
Address a = getAddressForOutput(out);
return a;
}
catch(ScriptException e2)
{
return null;
}
}
}
public String getAddressForInputViaSummary(TransactionInput in, boolean confirmed, Map<Sha256Hash, TransactionSummary> block_tx_map)
{
if (in.isCoinBase()) return null;
try
{
Address a = in.getFromAddress();
return a.toString();
}
catch(ScriptException e)
{
//Lets try this the other way
try
{
TransactionOutPoint out_p = in.getOutpoint();
TransactionSummary src_tx = null;
while(src_tx == null)
{
if (block_tx_map != null)
{
synchronized(block_tx_map)
{
src_tx = block_tx_map.get(out_p.getHash());
}
}
if (src_tx == null)
{
src_tx = db.getTransactionSummary(out_p.getHash());
if (src_tx == null)
{
if (!confirmed)
{
return null;
}
System.out.println("Unable to get source transaction summary: " + out_p.getHash() + " - " + block_tx_map.size());
try{Thread.sleep(500);}catch(Exception e7){}
}
}
}
Assert.assertNotNull(src_tx);
Assert.assertNotNull(src_tx.getOutputs());
int out_p_idx = (int)out_p.getIndex();
Assert.assertNotNull("" + out_p.getHash() + " should have output " + out_p.getIndex() + " " + src_tx, src_tx.getOutputs().get(out_p_idx));
return src_tx.getOutputs().get(out_p_idx).getToAddress();
}
catch(ScriptException e2)
{
return null;
}
}
}
public int getTXBlockHeight(Transaction tx, BlockChainCache chain_cache)
{
Set<Sha256Hash> block_list = db.getTxToBlockMap(tx.getHash());
if (block_list != null)
{
for(Sha256Hash block_hash : block_list)
{
if (chain_cache.isBlockInMainChain(block_hash))
{
return db.getBlockStoreMap().get(block_hash).getHeight();
}
}
}
return -1;
}
}