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; }
/** 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); } }
/** 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; }