Search in sources :

Example 6 with TransactionInput

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);
}
Also used : TransactionOutput(snowblossom.proto.TransactionOutput) ByteString(com.google.protobuf.ByteString) TransactionInput(snowblossom.proto.TransactionInput) Transaction(snowblossom.proto.Transaction) TransactionInner(snowblossom.proto.TransactionInner) BlockHeader(snowblossom.proto.BlockHeader)

Example 7 with TransactionInput

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);
    }
}
Also used : TransactionInner(snowblossom.proto.TransactionInner) TransactionInput(snowblossom.proto.TransactionInput)

Example 8 with TransactionInput

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;
        }
    }
}
Also used : TransactionOutput(snowblossom.proto.TransactionOutput) MetricLog(duckutil.MetricLog) ByteString(com.google.protobuf.ByteString) TransactionInput(snowblossom.proto.TransactionInput) TransactionInner(snowblossom.proto.TransactionInner)

Example 9 with TransactionInput

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);
    }
}
Also used : HashedTrie(snowblossom.lib.trie.HashedTrie) KeyPair(java.security.KeyPair) TransactionOutput(snowblossom.proto.TransactionOutput) Transaction(snowblossom.proto.Transaction) TransactionInput(snowblossom.proto.TransactionInput) MemPool(snowblossom.node.MemPool) Test(org.junit.Test)

Example 10 with TransactionInput

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;
}
Also used : TransactionOutput(snowblossom.proto.TransactionOutput) Random(java.util.Random) AddressSpec(snowblossom.proto.AddressSpec) TransactionInput(snowblossom.proto.TransactionInput)

Aggregations

TransactionInput (snowblossom.proto.TransactionInput)12 TransactionOutput (snowblossom.proto.TransactionOutput)10 Transaction (snowblossom.proto.Transaction)9 KeyPair (java.security.KeyPair)6 Test (org.junit.Test)6 HashedTrie (snowblossom.lib.trie.HashedTrie)6 MemPool (snowblossom.node.MemPool)6 TransactionInner (snowblossom.proto.TransactionInner)5 ByteString (com.google.protobuf.ByteString)4 Random (java.util.Random)2 MetricLog (duckutil.MetricLog)1 TimeRecord (duckutil.TimeRecord)1 TreeMap (java.util.TreeMap)1 AddressSpec (snowblossom.proto.AddressSpec)1 BlockHeader (snowblossom.proto.BlockHeader)1 RequestTransaction (snowblossom.proto.RequestTransaction)1