Esempio n. 1
0
  public Address getAddressForOutput(TransactionOutput out) {
    try {
      Script script = out.getScriptPubKey();
      if (script.isSentToRawPubKey()) {
        byte[] key = out.getScriptPubKey().getPubKey();
        byte[] address_bytes = com.google.bitcoin.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;
  }
Esempio n. 2
0
 /** Returns a human readable debug string. */
 @Override
 public String toString() {
   try {
     Script script = getScriptPubKey();
     StringBuilder buf = new StringBuilder("TxOut of ");
     buf.append(Coin.valueOf(value).toFriendlyString());
     if (script.isSentToAddress() || script.isPayToScriptHash())
       buf.append(" to ").append(script.getToAddress(params));
     else if (script.isSentToRawPubKey())
       buf.append(" to pubkey ").append(Utils.HEX.encode(script.getPubKey()));
     else if (script.isSentToMultiSig()) buf.append(" to multisig");
     else buf.append(" (unknown type)");
     buf.append(" script:");
     buf.append(script);
     return buf.toString();
   } catch (ScriptException e) {
     throw new RuntimeException(e);
   }
 }
Esempio n. 3
0
 /** Returns true if this output is to a key, or an address we have the keys for, in the wallet. */
 public boolean isMine(Wallet wallet) {
   try {
     Script script = getScriptPubKey();
     if (script.isSentToRawPubKey()) {
       byte[] pubkey = script.getPubKey();
       return wallet.isPubKeyMine(pubkey);
     }
     if (script.isPayToScriptHash()) {
       return wallet.isPayToScriptHashMine(script.getPubKeyHash());
     } else {
       byte[] pubkeyHash = script.getPubKeyHash();
       return wallet.isPubKeyHashMine(pubkeyHash);
     }
   } catch (ScriptException e) {
     // Just means we didn't understand the output of this transaction: ignore it.
     log.debug("Could not parse tx output script: {}", e.toString());
     return false;
   }
 }
  @Override
  /** Used during reorgs to connect a block previously on a fork */
  protected synchronized TransactionOutputChanges connectTransactions(StoredBlock newBlock)
      throws VerificationException, BlockStoreException, PrunedException {
    checkState(lock.isHeldByCurrentThread());
    if (!params.passesCheckpoint(newBlock.getHeight(), newBlock.getHeader().getHash()))
      throw new VerificationException("Block failed checkpoint lockin at " + newBlock.getHeight());

    blockStore.beginDatabaseBatchWrite();
    StoredUndoableBlock block = blockStore.getUndoBlock(newBlock.getHeader().getHash());
    if (block == null) {
      // We're trying to re-org too deep and the data needed has been deleted.
      blockStore.abortDatabaseBatchWrite();
      throw new PrunedException(newBlock.getHeader().getHash());
    }
    TransactionOutputChanges txOutChanges;
    try {
      List<Transaction> transactions = block.getTransactions();
      if (transactions != null) {
        LinkedList<StoredTransactionOutput> txOutsSpent = new LinkedList<StoredTransactionOutput>();
        LinkedList<StoredTransactionOutput> txOutsCreated =
            new LinkedList<StoredTransactionOutput>();
        long sigOps = 0;
        final boolean enforcePayToScriptHash =
            newBlock.getHeader().getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME;
        if (!params.isCheckpoint(newBlock.getHeight())) {
          for (Transaction tx : transactions) {
            Sha256Hash hash = tx.getHash();
            if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
              throw new VerificationException("Block failed BIP30 test!");
          }
        }
        Coin totalFees = Coin.ZERO;
        Coin coinbaseValue = null;

        if (scriptVerificationExecutor.isShutdown())
          scriptVerificationExecutor =
              Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        List<Future<VerificationException>> listScriptVerificationResults =
            new ArrayList<Future<VerificationException>>(transactions.size());
        for (final Transaction tx : transactions) {
          boolean isCoinBase = tx.isCoinBase();
          Coin valueIn = Coin.ZERO;
          Coin valueOut = Coin.ZERO;
          final List<Script> prevOutScripts = new LinkedList<Script>();
          if (!isCoinBase) {
            for (int index = 0; index < tx.getInputs().size(); index++) {
              final TransactionInput in = tx.getInputs().get(index);
              final StoredTransactionOutput prevOut =
                  blockStore.getTransactionOutput(
                      in.getOutpoint().getHash(), in.getOutpoint().getIndex());
              if (prevOut == null)
                throw new VerificationException(
                    "Attempted spend of a non-existent or already spent output!");
              if (newBlock.getHeight() - prevOut.getHeight() < params.getSpendableCoinbaseDepth())
                throw new VerificationException(
                    "Tried to spend coinbase at depth "
                        + (newBlock.getHeight() - prevOut.getHeight()));
              valueIn = valueIn.add(prevOut.getValue());
              if (enforcePayToScriptHash) {
                Script script = new Script(prevOut.getScriptBytes());
                if (script.isPayToScriptHash())
                  sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
                if (sigOps > Block.MAX_BLOCK_SIGOPS)
                  throw new VerificationException("Too many P2SH SigOps in block");
              }

              prevOutScripts.add(new Script(prevOut.getScriptBytes()));

              blockStore.removeUnspentTransactionOutput(prevOut);
              txOutsSpent.add(prevOut);
            }
          }
          Sha256Hash hash = tx.getHash();
          for (TransactionOutput out : tx.getOutputs()) {
            valueOut = valueOut.add(out.getValue());
            StoredTransactionOutput newOut =
                new StoredTransactionOutput(
                    hash,
                    out.getIndex(),
                    out.getValue(),
                    newBlock.getHeight(),
                    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.signum() < 0 || valueOut.compareTo(NetworkParameters.MAX_MONEY) > 0)
            throw new VerificationException("Transaction output value out of range");
          if (isCoinBase) {
            coinbaseValue = valueOut;
          } else {
            if (valueIn.compareTo(valueOut) < 0
                || valueIn.compareTo(NetworkParameters.MAX_MONEY) > 0)
              throw new VerificationException("Transaction input value out of range");
            totalFees = totalFees.add(valueIn.subtract(valueOut));
          }

          if (!isCoinBase) {
            // Because correctlySpends modifies transactions, this must come after we are done with
            // tx
            FutureTask<VerificationException> future =
                new FutureTask<VerificationException>(
                    new Verifier(tx, prevOutScripts, enforcePayToScriptHash));
            scriptVerificationExecutor.execute(future);
            listScriptVerificationResults.add(future);
          }
        }
        if (totalFees.compareTo(NetworkParameters.MAX_MONEY) > 0
            || newBlock
                    .getHeader()
                    .getBlockInflation(newBlock.getHeight())
                    .add(totalFees)
                    .compareTo(coinbaseValue)
                < 0) throw new VerificationException("Transaction fees out of range");
        txOutChanges = new TransactionOutputChanges(txOutsCreated, txOutsSpent);
        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.",
                thrownE);
          }
          if (e != null) throw e;
        }
      } else {
        txOutChanges = block.getTxOutChanges();
        if (!params.isCheckpoint(newBlock.getHeight()))
          for (StoredTransactionOutput out : txOutChanges.txOutsCreated) {
            Sha256Hash hash = out.getHash();
            if (blockStore.getTransactionOutput(hash, out.getIndex()) != null)
              throw new VerificationException("Block failed BIP30 test!");
          }
        for (StoredTransactionOutput out : txOutChanges.txOutsCreated)
          blockStore.addUnspentTransactionOutput(out);
        for (StoredTransactionOutput out : txOutChanges.txOutsSpent)
          blockStore.removeUnspentTransactionOutput(out);
      }
    } catch (VerificationException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    } catch (BlockStoreException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    }
    return txOutChanges;
  }
  @Override
  protected TransactionOutputChanges connectTransactions(int height, Block block)
      throws VerificationException, BlockStoreException {
    checkState(lock.isHeldByCurrentThread());
    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 enforcePayToScriptHash =
        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://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki
        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 (enforcePayToScriptHash) // We already check non-BIP16 sigops in
            // Block.verifyTransactions(true)
            sigOps += tx.getSigOpCount();
        }
      }
      Coin totalFees = Coin.ZERO;
      Coin coinbaseValue = null;
      for (final Transaction tx : block.transactions) {
        boolean isCoinBase = tx.isCoinBase();
        Coin valueIn = Coin.ZERO;
        Coin valueOut = Coin.ZERO;
        final List<Script> prevOutScripts = new LinkedList<Script>();
        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 (enforcePayToScriptHash) {
              if (new Script(prevOut.getScriptBytes()).isPayToScriptHash())
                sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
              if (sigOps > Block.MAX_BLOCK_SIGOPS)
                throw new VerificationException("Too many P2SH SigOps in block");
            }

            prevOutScripts.add(new Script(prevOut.getScriptBytes()));

            // 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.signum() < 0 || valueOut.compareTo(NetworkParameters.MAX_MONEY) > 0)
          throw new VerificationException("Transaction output value out of range");
        if (isCoinBase) {
          coinbaseValue = valueOut;
        } else {
          if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(NetworkParameters.MAX_MONEY) > 0)
            throw new VerificationException("Transaction input value out of range");
          totalFees = totalFees.add(valueIn.subtract(valueOut));
        }

        if (!isCoinBase && runScripts) {
          // Because correctlySpends modifies transactions, this must come after we are done with tx
          FutureTask<VerificationException> future =
              new FutureTask<VerificationException>(
                  new Verifier(tx, prevOutScripts, enforcePayToScriptHash));
          scriptVerificationExecutor.execute(future);
          listScriptVerificationResults.add(future);
        }
      }
      if (totalFees.compareTo(NetworkParameters.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.",
              thrownE);
        }
        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);
  }
  public static List<Transaction> getTransactionsFromBither(
      JSONObject jsonObject, int storeBlockHeight)
      throws JSONException, WrongNetworkException, AddressFormatException, VerificationException,
          ParseException, NoSuchFieldException, IllegalAccessException, IllegalArgumentException {
    List<Transaction> transactions = new ArrayList<Transaction>();

    if (!jsonObject.isNull(TXS)) {
      JSONArray txArray = jsonObject.getJSONArray(TXS);
      double count = 0;
      double size = txArray.length();

      for (int j = 0; j < txArray.length(); j++) {
        JSONObject tranJsonObject = txArray.getJSONObject(j);
        String blockHash = tranJsonObject.getString(BITHER_BLOCK_HASH);
        String txHash = tranJsonObject.getString(TX_HASH);
        int height = tranJsonObject.getInt(BITHER_BLOCK_NO);
        if (height > storeBlockHeight && storeBlockHeight > 0) {
          continue;
        }
        int version = 1;
        Date updateTime = new Date();
        if (!tranJsonObject.isNull(EXPLORER_TIME)) {
          updateTime = DateTimeUtil.getDateTimeForTimeZone(tranJsonObject.getString(EXPLORER_TIME));
        }
        if (!tranJsonObject.isNull(EXPLORER_VERSION)) {
          version = tranJsonObject.getInt(EXPLORER_VERSION);
        }
        Transaction transaction =
            new Transaction(BitherSetting.NETWORK_PARAMETERS, version, new Sha256Hash(txHash));
        transaction.addBlockAppearance(new Sha256Hash(blockHash), height);
        if (!tranJsonObject.isNull(EXPLORER_OUT)) {
          JSONArray tranOutArray = tranJsonObject.getJSONArray(EXPLORER_OUT);
          for (int i = 0; i < tranOutArray.length(); i++) {
            JSONObject tranOutJson = tranOutArray.getJSONObject(i);
            BigInteger value = BigInteger.valueOf(tranOutJson.getLong(BITHER_VALUE));
            if (!tranOutJson.isNull(SCRIPT_PUB_KEY)) {
              String str = tranOutJson.getString(SCRIPT_PUB_KEY);
              // Script script = new Script(
              // );
              // byte[] bytes1 = ScriptBuilder.createOutputScript(
              // address).getProgram();
              // byte[] bytes2 = StringUtil
              // .hexStringToByteArray(str);
              // LogUtil.d("tx", Arrays.equals(bytes1, bytes2) +
              // ";");
              TransactionOutput transactionOutput =
                  new TransactionOutput(
                      BitherSetting.NETWORK_PARAMETERS,
                      transaction,
                      value,
                      StringUtil.hexStringToByteArray(str));
              transaction.addOutput(transactionOutput);
            }
          }
        }

        if (!tranJsonObject.isNull(EXPLORER_IN)) {
          JSONArray tranInArray = tranJsonObject.getJSONArray(EXPLORER_IN);
          for (int i = 0; i < tranInArray.length(); i++) {
            JSONObject tranInJson = tranInArray.getJSONObject(i);
            TransactionOutPoint transactionOutPoint = null;
            if (!tranInJson.isNull(EXPLORER_COINBASE)) {
              long index = 0;
              if (!tranInJson.isNull(EXPLORER_SEQUENCE)) {
                index = tranInJson.getLong(EXPLORER_SEQUENCE);
              }
              transactionOutPoint =
                  new TransactionOutPoint(
                      BitherSetting.NETWORK_PARAMETERS, index, Sha256Hash.ZERO_HASH);

            } else {

              String prevOutHash = tranInJson.getString(PREV_TX_HASH);
              long n = 0;
              if (!tranInJson.isNull(PREV_OUTPUT_SN)) {
                n = tranInJson.getLong(PREV_OUTPUT_SN);
              }
              transactionOutPoint =
                  new TransactionOutPoint(
                      BitherSetting.NETWORK_PARAMETERS, n, new Sha256Hash(prevOutHash));
            }
            // Log.d("transaction", transaction.toString());
            if (transactionOutPoint != null) {
              TransactionInput transactionInput =
                  new TransactionInput(
                      BitherSetting.NETWORK_PARAMETERS,
                      transaction,
                      Script.createInputScript(EMPTY_BYTES, EMPTY_BYTES),
                      transactionOutPoint);

              transaction.addInput(transactionInput);
            }
          }
        }
        transaction.getConfidence().setAppearedAtChainHeight(height);
        transaction.getConfidence().setConfidenceType(ConfidenceType.BUILDING);
        transaction.getConfidence().setDepthInBlocks(storeBlockHeight - height + 1);
        transaction.setUpdateTime(updateTime);
        // Log.d("transaction", "transaction.num:" + transaction);
        Field txField = Transaction.class.getDeclaredField("hash");
        txField.setAccessible(true);
        txField.set(transaction, new Sha256Hash(txHash));
        transactions.add(transaction);
        count++;
        double progress =
            BitherSetting.SYNC_TX_PROGRESS_BLOCK_HEIGHT
                + BitherSetting.SYNC_TX_PROGRESS_STEP1
                + BitherSetting.SYNC_TX_PROGRESS_STEP2 * (count / size);
        BroadcastUtil.sendBroadcastProgressState(progress);
      }
    }

    LogUtil.d("transaction", "transactions.num:" + transactions.size());
    return transactions;
  }