use of snowblossom.proto.TransactionInput in project snowblossom by snowblossomcoin.
the class MemPool method buildTXCluster.
/**
* Attemped to build an ordered list of transactions
* that can confirm. In the simple case, it is just
* a single transaction that has all outputs already in utxo.
* In the more complex case, a chain of transactions needs to go
* in for the transaction in question to be confirmed.
* TODO - make faster, this thing sucks out loud.
* Probably need to actually build the graph and do graph
* theory things.
*/
private TXCluster buildTXCluster(Transaction target_tx) throws ValidationException {
HashMap<ChainHash, Transaction> working_map = new HashMap<>();
HashMultimap<ChainHash, ChainHash> depends_on_map = HashMultimap.<ChainHash, ChainHash>create();
LinkedList<TransactionInput> needed_inputs = new LinkedList<>();
addInputRequirements(target_tx, depends_on_map, needed_inputs);
working_map.put(new ChainHash(target_tx.getTxHash()), target_tx);
long t1;
while (needed_inputs.size() > 0) {
TransactionInput in = needed_inputs.pop();
ChainHash needed_tx = new ChainHash(in.getSrcTxId());
if (!working_map.containsKey(needed_tx)) {
ByteString key = UtxoUpdateBuffer.getKey(in);
t1 = System.nanoTime();
ByteString matching_output = utxo_hashed_trie.getLeafData(utxo_for_pri_map.getBytes(), key);
TimeRecord.record(t1, "utxo_lookup");
if (matching_output == null) {
if (known_transactions.containsKey(needed_tx)) {
t1 = System.nanoTime();
// TODO Check shard IDs
Transaction found_tx = known_transactions.get(needed_tx).tx;
TransactionInner found_tx_inner = TransactionUtil.getInner(found_tx);
TransactionOutput tx_out = found_tx_inner.getOutputs(in.getSrcTxOutIdx());
if (!shard_cover_set.contains(tx_out.getTargetShard())) {
throw new ValidationException(String.format("Transaction %s depends on %s which seems to be in other shard", new ChainHash(target_tx.getTxHash()), in.toString()));
}
working_map.put(needed_tx, found_tx);
addInputRequirements(found_tx, depends_on_map, needed_inputs);
TimeRecord.record(t1, "input_add");
} else {
throw new ValidationException(String.format("Unable to find source tx %s", needed_tx.toString()));
}
}
}
}
// At this point we have all the inputs satisfied. Now to figure out ordering.
t1 = System.nanoTime();
LinkedList<Transaction> ordered_list = getOrderdTxList(working_map, depends_on_map, new ChainHash(target_tx.getTxHash()));
TimeRecord.record(t1, "get_order");
t1 = System.nanoTime();
UtxoUpdateBuffer test_buffer = new UtxoUpdateBuffer(utxo_hashed_trie, utxo_for_pri_map);
int header_version = 1;
if (chain_state_source.getParams().getActivationHeightShards() <= chain_state_source.getHeight() + 1) {
header_version = 2;
}
BlockHeader dummy_header = BlockHeader.newBuilder().setBlockHeight(chain_state_source.getHeight() + 1).setTimestamp(System.currentTimeMillis()).setVersion(header_version).build();
// TODO - assign shard correctly
Map<Integer, UtxoUpdateBuffer> export_utxo_buffer = new TreeMap<>();
for (Transaction t : ordered_list) {
Validation.deepTransactionCheck(t, test_buffer, dummy_header, chain_state_source.getParams(), shard_cover_set, export_utxo_buffer);
}
TimeRecord.record(t1, "utxo_sim");
return new TXCluster(ordered_list);
}
use of snowblossom.proto.TransactionInput in project snowblossom by snowblossomcoin.
the class MemPool method addInputRequirements.
private static void addInputRequirements(Transaction tx, HashMultimap<ChainHash, ChainHash> depends_on_map, List<TransactionInput> needed_inputs) {
ChainHash tx_id = new ChainHash(tx.getTxHash());
TransactionInner inner = TransactionUtil.getInner(tx);
for (TransactionInput in : inner.getInputsList()) {
depends_on_map.put(tx_id, new ChainHash(in.getSrcTxId()));
needed_inputs.add(in);
}
}
use of snowblossom.proto.TransactionInput in project snowblossom by snowblossomcoin.
the class MemPool method addTransaction.
/**
* @return true iff this seems to be a new and valid tx
*/
public boolean addTransaction(Transaction tx, boolean p2p_source) throws ValidationException {
try (MetricLog mlog = new MetricLog()) {
long t1 = System.nanoTime();
Validation.checkTransactionBasics(tx, false);
mlog.set("basic_validation", 1);
TimeRecord.record(t1, "mempool:tx_validation");
long t_lock = System.nanoTime();
synchronized (this) {
TimeRecord.record(t_lock, "mempool:have_lock");
mlog.setOperation("add_transaction");
mlog.setModule("mem_pool");
mlog.set("added", 0);
if ((p2p_source) && (!accepts_p2p_tx)) {
mlog.set("reject_p2p", 1);
return false;
}
ChainHash tx_hash = new ChainHash(tx.getTxHash());
mlog.set("tx_id", tx_hash.toString());
if (known_transactions.containsKey(tx_hash)) {
mlog.set("already_known", 1);
return false;
}
if (known_transactions.size() >= MEM_POOL_MAX) {
throw new ValidationException("mempool is full");
}
TransactionMempoolInfo info = new TransactionMempoolInfo(tx);
TransactionInner inner = info.inner;
double tx_ratio = (double) inner.getFee() / (double) tx.toByteString().size();
mlog.set("fee", inner.getFee());
mlog.set("fee_ratio", tx_ratio);
if (tx_ratio < Globals.LOW_FEE) {
mlog.set("low_fee", 1);
if (known_transactions.size() >= MEM_POOL_MAX_LOW) {
throw new ValidationException("mempool is too full for low fee transactions");
}
}
TreeSet<String> used_outputs = new TreeSet<>();
TimeRecord.record(t1, "mempool:p1");
long t3 = System.nanoTime();
mlog.set("input_count", inner.getInputsCount());
mlog.set("output_count", inner.getOutputsCount());
for (TransactionInput in : inner.getInputsList()) {
String key = HexUtil.getHexString(in.getSrcTxId()) + ":" + in.getSrcTxOutIdx();
used_outputs.add(key);
if (claimed_outputs.containsKey(key)) {
if (!claimed_outputs.get(key).equals(tx_hash)) {
throw new ValidationException("Discarding as double-spend");
}
}
}
TimeRecord.record(t3, "mempool:input_proc");
long output_total = inner.getFee();
for (TransactionOutput out : inner.getOutputsList()) {
output_total += out.getValue();
}
mlog.set("total_output", output_total);
if (utxo_for_pri_map != null) {
long t2 = System.nanoTime();
TXCluster cluster = buildTXCluster(tx);
mlog.set("cluster_tx_count", cluster.tx_list.size());
mlog.set("cluster_tx_size", cluster.total_size);
TimeRecord.record(t2, "mempool:build_cluster");
if (cluster == null) {
throw new ValidationException("Unable to find a tx cluster that makes this work");
}
double ratio = (double) cluster.total_fee / (double) cluster.total_size;
// Random rnd = new Random();
// ratio = ratio * 1e9 + rnd.nextDouble();
long t4 = System.nanoTime();
priority_map.put(ratio, cluster);
TimeRecord.record(t4, "mempool:primapput");
}
TimeRecord.record(t1, "mempool:p2");
known_transactions.put(tx_hash, info);
for (AddressSpecHash spec_hash : info.involved_addresses) {
address_tx_map.put(spec_hash, tx_hash);
}
// Claim outputs used by inputs
for (String key : used_outputs) {
claimed_outputs.put(key, tx_hash);
}
TimeRecord.record(t1, "mempool:tx_add");
TimeRecord.record(t1, "mempool:p3");
for (MemPoolTickleInterface listener : mempool_listener) {
listener.tickleMemPool(tx, info.involved_addresses);
}
mlog.set("added", 1);
return true;
}
}
}
use of snowblossom.proto.TransactionInput in project snowblossom by snowblossomcoin.
the class MemPoolTest method testBasicTxDoubleSpend.
@Test
public void testBasicTxDoubleSpend() throws Exception {
HashedTrie utxo_trie = newMemoryTrie();
KeyPair keys = KeyUtil.generateECCompressedKey();
UtxoUpdateBuffer utxo_buffer = new UtxoUpdateBuffer(utxo_trie, UtxoUpdateBuffer.EMPTY);
TransactionInput in = addUtxoToUseAtInput(utxo_buffer, keys, 100000L);
ChainHash utxo_root = utxo_buffer.commit();
TransactionOutput out = TransactionOutput.newBuilder().setRecipientSpecHash(in.getSpecHash()).setValue(100000L).build();
Transaction tx = TransactionUtil.createTransaction(ImmutableList.of(in), ImmutableList.of(out), keys);
MemPool mem_pool = new MemPool(utxo_trie, new DummyChainState(100));
mem_pool.rebuildPriorityMap(utxo_root);
// Pool starts empty
Assert.assertEquals(0, mem_pool.getTransactionsForBlock(utxo_root, 1048576).size());
mem_pool.addTransaction(tx, false);
TransactionOutput out_a = TransactionOutput.newBuilder().setRecipientSpecHash(in.getSpecHash()).setValue(50000L).build();
Transaction tx2 = TransactionUtil.createTransaction(ImmutableList.of(in), ImmutableList.of(out_a, out_a), keys);
Assert.assertNotEquals(tx.getTxHash(), tx2.getTxHash());
try {
mem_pool.addTransaction(tx2, false);
Assert.fail();
} catch (ValidationException e) {
System.out.println(e);
}
}
use of snowblossom.proto.TransactionInput in project snowblossom by snowblossomcoin.
the class MemPoolTest method addUtxoToUseAtInput.
public static TransactionInput addUtxoToUseAtInput(UtxoUpdateBuffer utxo_buffer, KeyPair keys, long value) throws Exception {
Random rnd = new Random();
byte[] tx_id_buff = new byte[Globals.BLOCKCHAIN_HASH_LEN];
rnd.nextBytes(tx_id_buff);
ChainHash tx_id = new ChainHash(tx_id_buff);
AddressSpec claim = AddressUtil.getSimpleSpecForKey(keys.getPublic(), SignatureUtil.SIG_TYPE_ECDSA_COMPRESSED);
AddressSpecHash addr = AddressUtil.getHashForSpec(claim);
TransactionInput tx_in = TransactionInput.newBuilder().setSpecHash(addr.getBytes()).setSrcTxId(tx_id.getBytes()).setSrcTxOutIdx(0).build();
TransactionOutput tx_out = TransactionOutput.newBuilder().setRecipientSpecHash(addr.getBytes()).setValue(value).build();
utxo_buffer.addOutput(ImmutableList.of(tx_out.toByteString()), tx_out, tx_id, 0);
return tx_in;
}
Aggregations