Example #1
0
 /** Returns a copy of the block, but without any transactions. */
 public Block cloneAsHeader() {
   maybeParseHeader();
   Block block = new Block(params);
   block.nonce = nonce;
   block.prevBlockHash = prevBlockHash.duplicate();
   block.merkleRoot = getMerkleRoot().duplicate();
   block.version = version;
   block.time = time;
   block.difficultyTarget = difficultyTarget;
   block.transactions = null;
   block.hash = getHash().duplicate();
   return block;
 }
Example #2
0
  /**
   * Returns a solved block that builds on top of this one. This exists for unit tests. In this
   * variant you can specify a public key (pubkey) for use in generating coinbase blocks.
   */
  Block createNextBlock(
      Address to, TransactionOutPoint prevOut, long time, byte[] pubKey, BigInteger coinbaseValue) {
    Block b = new Block(params);
    b.setDifficultyTarget(difficultyTarget);
    b.addCoinbaseTransaction(pubKey, coinbaseValue);

    if (to != null) {
      // Add a transaction paying 50 coins to the "to" address.
      Transaction t = new Transaction(params);
      t.addOutput(new TransactionOutput(params, t, Utils.toNanoCoins(50, 0), to));
      // The input does not really need to be a valid signature, as long as it has the right general
      // form.
      TransactionInput input;
      if (prevOut == null) {
        input = new TransactionInput(params, t, Script.createInputScript(EMPTY_BYTES, EMPTY_BYTES));
        // Importantly the outpoint hash cannot be zero as that's how we detect a coinbase
        // transaction in isolation
        // but it must be unique to avoid 'different' transactions looking the same.
        byte[] counter = new byte[32];
        counter[0] = (byte) txCounter++;
        counter[1] = 1;
        input.getOutpoint().setHash(new Sha256Hash(counter));
      } else {
        input =
            new TransactionInput(
                params, t, Script.createInputScript(EMPTY_BYTES, EMPTY_BYTES), prevOut);
      }
      t.addInput(input);
      b.addTransaction(t);
    }

    b.setPrevBlockHash(getHash());
    // Don't let timestamp go backwards
    if (getTimeSeconds() >= time) b.setTime(getTimeSeconds() + 1);
    else b.setTime(time);
    b.solve();
    try {
      b.verifyHeader();
    } catch (VerificationException e) {
      throw new RuntimeException(e); // Cannot happen.
    }
    return b;
  }
  @Override
  protected TransactionOutputChanges connectTransactions(int height, Block block)
      throws VerificationException, BlockStoreException {
    checkState(lock.isLocked());
    if (block.transactions == null)
      throw new RuntimeException(
          "connectTransactions called with Block that didn't have transactions!");
    if (!params.passesCheckpoint(height, block.getHash()))
      throw new VerificationException("Block failed checkpoint lockin at " + height);

    blockStore.beginDatabaseBatchWrite();

    LinkedList<StoredTransactionOutput> txOutsSpent = new LinkedList<StoredTransactionOutput>();
    LinkedList<StoredTransactionOutput> txOutsCreated = new LinkedList<StoredTransactionOutput>();
    long sigOps = 0;
    final boolean enforceBIP16 = block.getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME;

    if (scriptVerificationExecutor.isShutdown())
      scriptVerificationExecutor =
          Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    List<Future<VerificationException>> listScriptVerificationResults =
        new ArrayList<Future<VerificationException>>(block.transactions.size());
    try {
      if (!params.isCheckpoint(height)) {
        // BIP30 violator blocks are ones that contain a duplicated transaction. They are all in the
        // checkpoints list and we therefore only check non-checkpoints for duplicated transactions
        // here. See the
        // BIP30 document for more details on this: https://en.litecoin.it/wiki/BIP_0030
        for (Transaction tx : block.transactions) {
          Sha256Hash hash = tx.getHash();
          // If we already have unspent outputs for this hash, we saw the tx already. Either the
          // block is
          // being added twice (bug) or the block is a BIP30 violator.
          if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
            throw new VerificationException("Block failed BIP30 test!");
          if (enforceBIP16) // We already check non-BIP16 sigops in Block.verifyTransactions(true)
          sigOps += tx.getSigOpCount();
        }
      }
      BigInteger totalFees = BigInteger.ZERO;
      BigInteger coinbaseValue = null;
      for (Transaction tx : block.transactions) {
        boolean isCoinBase = tx.isCoinBase();
        BigInteger valueIn = BigInteger.ZERO;
        BigInteger valueOut = BigInteger.ZERO;
        if (!isCoinBase) {
          // For each input of the transaction remove the corresponding output from the set of
          // unspent
          // outputs.
          for (int index = 0; index < tx.getInputs().size(); index++) {
            TransactionInput in = tx.getInputs().get(index);
            StoredTransactionOutput prevOut =
                blockStore.getTransactionOutput(
                    in.getOutpoint().getHash(), in.getOutpoint().getIndex());
            if (prevOut == null)
              throw new VerificationException(
                  "Attempted to spend a non-existent or already spent output!");
            // Coinbases can't be spent until they mature, to avoid re-orgs destroying entire
            // transaction
            // chains. The assumption is there will ~never be re-orgs deeper than the spendable
            // coinbase
            // chain depth.
            if (height - prevOut.getHeight() < params.getSpendableCoinbaseDepth())
              throw new VerificationException(
                  "Tried to spend coinbase at depth " + (height - prevOut.getHeight()));
            // TODO: Check we're not spending the genesis transaction here. Satoshis code won't
            // allow it.
            valueIn = valueIn.add(prevOut.getValue());
            if (enforceBIP16) {
              if (new Script(params, prevOut.getScriptBytes(), 0, prevOut.getScriptBytes().length)
                  .isPayToScriptHash()) sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
              if (sigOps > Block.MAX_BLOCK_SIGOPS)
                throw new VerificationException("Too many P2SH SigOps in block");
            }

            // All of these copies are terribly ugly, however without them,
            // I see some odd concurrency issues where scripts throw exceptions
            // (mostly "Attempted OP_* on empty stack" or similar) when they shouldn't.
            // In my tests, total time spent in net.usecredits.credits.core when
            // downloading the chain is < 0.5%, so doing this is no big efficiency issue.
            // TODO: Find out the underlying issue and create a better work-around
            final int currentIndex = index;
            final Transaction txCache;
            try {
              txCache = new Transaction(params, tx.unsafeLitecoinSerialize());
            } catch (ProtocolException e1) {
              throw new RuntimeException(e1);
            }
            final Script scriptSig = in.getScriptSig();
            final Script scriptPubKey =
                new Script(params, prevOut.getScriptBytes(), 0, prevOut.getScriptBytes().length);
            FutureTask<VerificationException> future =
                new FutureTask<VerificationException>(
                    new Callable<VerificationException>() {
                      public VerificationException call() {
                        try {
                          scriptSig.correctlySpends(
                              txCache, currentIndex, scriptPubKey, enforceBIP16);
                        } catch (VerificationException e) {
                          return e;
                        }
                        return null;
                      }
                    });
            scriptVerificationExecutor.execute(future);
            listScriptVerificationResults.add(future);

            // in.getScriptSig().correctlySpends(tx, index, new Script(params,
            // prevOut.getScriptBytes(), 0, prevOut.getScriptBytes().length));

            blockStore.removeUnspentTransactionOutput(prevOut);
            txOutsSpent.add(prevOut);
          }
        }
        Sha256Hash hash = tx.getHash();
        for (TransactionOutput out : tx.getOutputs()) {
          valueOut = valueOut.add(out.getValue());
          // For each output, add it to the set of unspent outputs so it can be consumed in future.
          StoredTransactionOutput newOut =
              new StoredTransactionOutput(
                  hash, out.getIndex(), out.getValue(), height, isCoinBase, out.getScriptBytes());
          blockStore.addUnspentTransactionOutput(newOut);
          txOutsCreated.add(newOut);
        }
        // All values were already checked for being non-negative (as it is verified in
        // Transaction.verify())
        // but we check again here just for defence in depth. Transactions with zero output value
        // are OK.
        if (valueOut.compareTo(BigInteger.ZERO) < 0 || valueOut.compareTo(params.MAX_MONEY) > 0)
          throw new VerificationException("Transaction output value out of rage");
        if (isCoinBase) {
          coinbaseValue = valueOut;
        } else {
          if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(params.MAX_MONEY) > 0)
            throw new VerificationException("Transaction input value out of range");
          totalFees = totalFees.add(valueIn.subtract(valueOut));
        }
      }
      if (totalFees.compareTo(params.MAX_MONEY) > 0
          || block.getBlockInflation(height).add(totalFees).compareTo(coinbaseValue) < 0)
        throw new VerificationException("Transaction fees out of range");
      for (Future<VerificationException> future : listScriptVerificationResults) {
        VerificationException e;
        try {
          e = future.get();
        } catch (InterruptedException thrownE) {
          throw new RuntimeException(thrownE); // Shouldn't happen
        } catch (ExecutionException thrownE) {
          log.error("Script.correctlySpends threw a non-normal exception: " + thrownE.getCause());
          throw new VerificationException(
              "Bug in Script.correctlySpends, likely script malformed in some new and interesting way.");
        }
        if (e != null) throw e;
      }
    } catch (VerificationException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    } catch (BlockStoreException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    }
    return new TransactionOutputChanges(txOutsCreated, txOutsSpent);
  }
Example #4
0
 @Override
 public boolean equals(Object o) {
   if (!(o instanceof Block)) return false;
   Block other = (Block) o;
   return getHash().equals(other.getHash());
 }