/** * Returns the amount of bitcoin ever received via output. <b>This is not the balance!</b> If an output spends from a * transaction whose inputs are also to our wallet, the input amounts are deducted from the outputs contribution, with a minimum of zero * contribution. The idea behind this is we avoid double counting money sent to us. * @return the total amount of satoshis received, regardless of whether it was spent or not. */ public Coin getTotalReceived() { Coin total = Coin.ZERO; // Include outputs to us if they were not just change outputs, ie the inputs to us summed to less // than the outputs to us. for (Transaction tx: transactions.values()) { Coin txTotal = Coin.ZERO; for (TransactionOutput output : tx.getOutputs()) { if (output.isMine(this)) { txTotal = txTotal.add(output.getValue()); } } for (TransactionInput in : tx.getInputs()) { TransactionOutput prevOut = in.getConnectedOutput(); if (prevOut != null && prevOut.isMine(this)) { txTotal = txTotal.subtract(prevOut.getValue()); } } if (txTotal.isPositive()) { total = total.add(txTotal); } } return total; }
/** Checks if the given input passes some of the AreInputsStandard checks. Not complete. */ public static RuleViolation isInputStandard(TransactionInput input) { for (ScriptChunk chunk : input.getScriptSig().getChunks()) { if (chunk.data != null && !chunk.isShortestPossiblePushData()) return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA; if (chunk.isPushData()) { ECDSASignature signature; try { signature = ECKey.ECDSASignature.decodeFromDER(chunk.data); } catch (IllegalArgumentException x) { // Doesn't look like a signature. signature = null; } if (signature != null) { if (!TransactionSignature.isEncodingCanonical(chunk.data)) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; if (!signature.isCanonical()) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; } } } return RuleViolation.NONE; }
private void assembleTransaction() throws PaymentException { int numClientSigs = clientTxSignatures.size(); int numServerSigs = serverTxSignatures.size(); if (numClientSigs != numServerSigs) { throw new PaymentException(PaymentError.TRANSACTION_ERROR); } for (int i = 0; i < numClientSigs; ++i) { TransactionInput txIn = transaction.getInput(i); byte[] hash = txIn.getConnectedOutput().getScriptPubKey().getPubKeyHash(); TimeLockedAddress tla = walletService.findTimeLockedAddressByHash(hash); Script scriptSig = tla.createScriptSigBeforeLockTime( clientTxSignatures.get(i), serverTxSignatures.get(i)); txIn.setScriptSig(scriptSig); txIn.verify(); } transaction.verify(); }
private List<TransactionSignature> signTransaction(Transaction tx) throws CoinbleskException { final List<TransactionInput> inputs = tx.getInputs(); final List<TransactionSignature> signatures = new ArrayList<>(inputs.size()); for (int i = 0; i < inputs.size(); ++i) { TransactionInput txIn = inputs.get(i); TransactionOutput prevTxOut = txIn.getConnectedOutput(); byte[] sentToHash = prevTxOut.getScriptPubKey().getPubKeyHash(); TimeLockedAddress tla = findTimeLockedAddressByHash(sentToHash); if (tla == null) { throw new CoinbleskException(String.format(Locale.US, "Could not sign input (index=%d, pubKeyHash=%s)", i, org.bitcoinj.core.Utils.HEX.encode(sentToHash))); } byte[] redeemScript = tla.createRedeemScript().getProgram(); TransactionSignature signature = tx.calculateSignature( i, multisigClientKey, redeemScript, Transaction.SigHash.ALL, false); signatures.add(signature); } return signatures; }
/** Checks if the given input passes some of the AreInputsStandard checks. Not complete. */ public static RuleViolation isInputStandard(TransactionInput input) { for (ScriptChunk chunk : input.getScriptSig().getChunks()) { if (chunk.data != null && !chunk.isShortestPossiblePushData()) return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA; if (chunk.isPushData()) { ECDSASignature signature; try { signature = ECKey.ECDSASignature.decodeFromDER(chunk.data); } catch (RuntimeException x) { // Doesn't look like a signature. signature = null; } if (signature != null) { if (!TransactionSignature.isEncodingCanonical(chunk.data)) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; if (!signature.isCanonical()) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; } } } return RuleViolation.NONE; }
public void completeTxPartiallySignedMarried(Wallet.MissingSigsMode missSigMode, byte[] expectedSig) throws Exception { // create married wallet without signer createMarriedWallet(2, 2, false); myAddress = wallet.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN, myAddress); SendRequest req = SendRequest.emptyWallet(OTHER_ADDRESS); req.missingSigsMode = missSigMode; wallet.completeTx(req); TransactionInput input = req.tx.getInput(0); boolean firstSigIsMissing = Arrays.equals(expectedSig, input.getScriptSig().getChunks().get(1).data); boolean secondSigIsMissing = Arrays.equals(expectedSig, input.getScriptSig().getChunks().get(2).data); assertTrue("Only one of the signatures should be missing/dummy", firstSigIsMissing ^ secondSigIsMissing); int localSigIndex = firstSigIsMissing ? 2 : 1; int length = input.getScriptSig().getChunks().get(localSigIndex).data.length; assertTrue("Local sig should be present: " + length, length >= 70); }
private static Transaction shuffleTransaction (Transaction transaction) { Transaction shuffledTransaction = new Transaction(Constants.getNetwork()); List<TransactionInput> shuffledInputs = new ArrayList<>(transaction.getInputs()); List<TransactionOutput> shuffledOutputs = new ArrayList<>(transaction.getOutputs()); Collections.shuffle(shuffledInputs); Collections.shuffle(shuffledOutputs); for (TransactionInput input : shuffledInputs) { shuffledTransaction.addInput(input); } for (TransactionOutput output : shuffledOutputs) { shuffledTransaction.addOutput(output); } return shuffledTransaction; }
@Nullable public Value getRawTxFee(TransactionWatcherWallet wallet) { Preconditions.checkState(!isTrimmed, "Cannot get raw tx fee from a trimmed transaction"); Value fee = type.value(0); for (TransactionInput input : tx.getInputs()) { TransactionOutPoint outPoint = input.getOutpoint(); BitTransaction inTx = wallet.getTransaction(outPoint.getHash()); if (inTx == null || !inTx.isOutputAvailable((int) outPoint.getIndex())) { return null; } TransactionOutput txo = inTx.getOutput((int) outPoint.getIndex()); fee = fee.add(txo.getValue()); } for (TransactionOutput output : getOutputs()) { fee = fee.subtract(output.getValue()); } return fee; }
private boolean isInputMine(TransactionInput input) { lock.lock(); try { try { Script script = input.getScriptSig(); // TODO check multi sig scripts return isPubKeyMine(script.getPubKey()); } catch (ScriptException e) { // We didn't understand this input ScriptSig: ignore it. log.debug("Could not parse tx input script: {}", e.toString()); return false; } } finally { lock.unlock(); } }
/** * Takes in a transaction and a private key and returns a signature (if possible) * as a Bytestring object. */ public Bytestring getSignature(org.bitcoinj.core.Transaction signTx, ECKey privKey) { org.bitcoinj.core.Transaction copyTx = signTx; for (int i = 0; i < copyTx.getInputs().size(); i++) { TransactionInput input = copyTx.getInput(i); TransactionOutput connectedOutput = input.getConnectedOutput(); Sha256Hash hash = copyTx.hashForSignature(i, connectedOutput.getScriptPubKey(), org.bitcoinj.core.Transaction.SigHash.ALL, false); ECKey.ECDSASignature ecSig = privKey.sign(hash); TransactionSignature txSig = new TransactionSignature(ecSig, org.bitcoinj.core.Transaction.SigHash.ALL, false); byte[] originalScript = input.getScriptBytes().clone(); Script inputScript = ScriptBuilder.createInputScript(txSig, ECKey.fromPublicOnly(privKey.getPubKey())); input.setScriptSig(inputScript); try { input.verify(connectedOutput); return new Bytestring(inputScript.getProgram()); } catch (VerificationException e) { input.setScriptSig(this.bytestringToInputScript(new Bytestring(originalScript))); } } return null; }
private static AltcoinBlock createGenesis(NetworkParameters params) { AltcoinBlock genesisBlock = new AltcoinBlock(params, Block.BLOCK_VERSION_GENESIS); Transaction t = new Transaction(params); try { byte[] bytes = Utils.HEX.decode ("04ffff001d0104084e696e746f6e646f"); t.addInput(new TransactionInput(params, t, bytes)); ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream(); Script.writeBytes(scriptPubKeyBytes, Utils.HEX.decode ("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9")); scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG); t.addOutput(new TransactionOutput(params, t, COIN.multiply(88), scriptPubKeyBytes.toByteArray())); } catch (Exception e) { // Cannot happen. throw new RuntimeException(e); } genesisBlock.addTransaction(t); return genesisBlock; }
private static AltcoinBlock createGenesis(NetworkParameters params) { AltcoinBlock genesisBlock = new AltcoinBlock(params, Block.BLOCK_VERSION_GENESIS); Transaction t = new Transaction(params); try { byte[] bytes = Hex.decode ("04ffff001d0104404e592054696d65732030352f4f63742f32303131205374657665204a6f62732c204170706c65e280997320566973696f6e6172792c2044696573206174203536"); t.addInput(new TransactionInput(params, t, bytes)); ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream(); Script.writeBytes(scriptPubKeyBytes, Hex.decode ("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9")); scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG); t.addOutput(new TransactionOutput(params, t, COIN.multiply(50), scriptPubKeyBytes.toByteArray())); } catch (Exception e) { // Cannot happen. throw new RuntimeException(e); } genesisBlock.addTransaction(t); genesisBlock.setTime(1317798646L); genesisBlock.setDifficultyTarget(0x1e0ffff0L); genesisBlock.setNonce(385270584); return genesisBlock; }
private static AltcoinBlock createGenesis(NetworkParameters params) { AltcoinBlock genesisBlock = new AltcoinBlock(params, Block.BLOCK_VERSION_GENESIS); Transaction t = new Transaction(params); try { byte[] bytes = Hex.decode ("04ffff001d0104404e592054696d65732030352f4f63742f32303131205374657665204a6f62732c204170706c65e280997320566973696f6e6172792c2044696573206174203536"); t.addInput(new TransactionInput(params, t, bytes)); ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream(); Script.writeBytes(scriptPubKeyBytes, Hex.decode ("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9")); scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG); t.addOutput(new TransactionOutput(params, t, COIN.multiply(50), scriptPubKeyBytes.toByteArray())); } catch (Exception e) { // Cannot happen. throw new RuntimeException(e); } genesisBlock.addTransaction(t); genesisBlock.setTime(1317972665L); genesisBlock.setDifficultyTarget(0x1e0ffff0L); genesisBlock.setNonce(2084524493); return genesisBlock; }
private static AltcoinBlock createGenesis(NetworkParameters params) { AltcoinBlock genesisBlock = new AltcoinBlock(params, Block.BLOCK_VERSION_GENESIS); Transaction t = new Transaction(params); try { // "... choose what comes next. Lives of your own, or a return to chains. -- V" byte[] bytes = Utils.HEX.decode ("04ff7f001c020a024b2e2e2e2063686f6f7365207768617420636f6d6573206e6578742e20204c69766573206f6620796f7572206f776e2c206f7220612072657475726e20746f20636861696e732e202d2d2056"); t.addInput(new TransactionInput(params, t, bytes)); ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream(); Script.writeBytes(scriptPubKeyBytes, Utils.HEX.decode ("04b620369050cd899ffbbc4e8ee51e8c4534a855bb463439d63d235d4779685d8b6f4870a238cf365ac94fa13ef9a2a22cd99d0d5ee86dcabcafce36c7acf43ce5")); scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG); t.addOutput(new TransactionOutput(params, t, COIN.multiply(50), scriptPubKeyBytes.toByteArray())); } catch (Exception e) { // Cannot happen. throw new RuntimeException(e); } genesisBlock.addTransaction(t); return genesisBlock; }
/** * Construct from a bitcoinj transaction * @param transaction A bitcoinj confirmed or unconfirmed transaction */ public RawTransactionInfo(Transaction transaction) { this.hex = TransactionHexSerializer.bytesToHexString(transaction.bitcoinSerialize()); this.txid = transaction.getHash(); this.version = transaction.getVersion(); this.locktime = transaction.getLockTime(); this.blockhash = null; // For now this.confirmations = transaction.getConfidence().getDepthInBlocks(); this.time = 0; // TODO: block header time of block including transaction this.blocktime = this.time; // same as time (see API doc) vin = new VinList(); for (TransactionInput input : transaction.getInputs()) { vin.add(new Vin(input.getHash(), input.getOutpoint().getIndex(), input.getScriptSig().toString(), input.getSequenceNumber())); } vout = new VoutList(); for (TransactionOutput output : transaction.getOutputs()) { vout.add(new Vout(output.getValue(), output.getIndex(), output.getScriptPubKey().toString())); } }
@Test public void pshTx() { final List<TransactionInput> inputs = tx1.getInputs(); for (TransactionInput input : inputs) { try { Address current = input.getScriptSig().getFromAddress(MainNetParams.get()); System.out.println(current.toString()); } catch (ScriptException e) { System.out.println("exception"); } } }
public Coin getPendingChannelFees(Account account) { if (account.getChannelTransaction() == null) { return Coin.ZERO; } Transaction tx = new Transaction(appConfig.getNetworkParameters(), account.getChannelTransaction()); long inputSum = 0L; long outputSum = 0L; for(TransactionInput in : tx.getInputs()) { inputSum += walletService.findOutputFor(in).getValue().longValue(); } for(TransactionOutput out : tx.getOutputs()) { outputSum += out.getValue().longValue(); } return Coin.valueOf(inputSum - outputSum); }
public static void main(String[] args) throws InterruptedException, ExecutionException { // 85.25.198.97 94.242.229.209 //ClientBase clientBase = new ClientBase("tcp://85.25.198.97:9091"); ClientBase clientBase = new ClientBase("tcp://192.168.8.108:9091"); clientBase.setDumpedExecutorsCleanInterval(1000); clientBase.setMonitorOutputInterval(3 * 1000); clientBase.setSocketPoolSize(5); clientBase.setMinSocketPoolSize(5); clientBase.setup(); final Client client = new Client(clientBase); Future<Object> f = client.getBlockchain().fetchTransaction(Hex.decode("516a4fe33d99edcfb500aab499d93d9a6d4133d46860f0d7716781ab65002e69")); Transaction tx = (Transaction)f.get(); System.out.println(tx.getMessageSize()); //System.out.println(tx.getInput(0).getOutpoint().toString()); for (TransactionInput input : tx.getInputs()) { System.out.println(input.getOutpoint().toString()); } for (TransactionOutput output : tx.getOutputs()) { System.out.println(output.getAddressFromP2PKHScript(MainNetParams.get())); System.out.println(output.getAddressFromP2SH(MainNetParams.get())); } }
private Transaction signStandardTx(Transaction tx, Map<String,ECKey> keys){ // sign for(int index=0;index < tx.getInputs().size(); index++){ TransactionInput in = tx.getInput(index); TransactionOutput connectedOutput = in.getConnectedOutput(); String addFrom = connectedOutput.getScriptPubKey().getToAddress(getNetworkParams()).toString(); TransactionSignature sig = tx.calculateSignature(index, keys.get(addFrom), connectedOutput.getScriptPubKey(), Transaction.SigHash.ALL, false); Script inputScript = ScriptBuilder.createInputScript(sig, keys.get(addFrom)); in.setScriptSig(inputScript); try { in.getScriptSig().correctlySpends(tx, (long)index, connectedOutput.getScriptPubKey()); } catch (ScriptException e) { return null; } } return tx; }
private TransactionInput addInput(Coin amount, String privateKeyStringHex, String addressString, int keyIndex) throws Exception{ ECKey inKey = ECKey.fromPrivate(Hex.decode(privateKeyStringHex)); Address inAddress = inKey.toAddress(MainNetParams.get()); assertTrue(inAddress.toString().equals(addressString)); ATAddress.Builder bAddress = ATAddress.newBuilder(); bAddress.setAccountIndex(0); bAddress.setAddressStr(inAddress.toString()); bAddress.setIsUsed(false); bAddress.setKeyIndex(keyIndex); bAddress.setType(HierarchyAddressTypes.External); TransactionOutput in = new TransactionOutput(MainNetParams.get(), null, amount, inAddress); TransactionInput inMock = Mockito.mock(TransactionInput.class); Mockito.when(inMock.getConnectedOutput()).thenReturn(in); Mockito.when(wallet.findAddressInAccounts(inAddress.toString())).thenReturn(bAddress.build()); Mockito.when(wallet.getPrivECKeyFromAccount(bAddress.getAccountIndex(), bAddress.getType(), bAddress.getKeyIndex(), null, true)).thenReturn(inKey); return inMock; }
public TransactionSummary(Transaction tx, TXUtil tx_util, boolean confirmed, Map<Sha256Hash, Transaction> block_tx_map) { tx_hash = tx.getHash(); involved_scripthashes = new HashSet<>(); ins = new TreeMap<>(); outs = new TreeMap<>(); for(TransactionOutput tx_out : tx.getOutputs()) { TransactionOutSummary os = new TransactionOutSummary(tx_out, tx_util); outs.put( tx_out.getIndex(), os ); involved_scripthashes.add(os.getScriptHash()); } int idx=0; for(TransactionInput tx_in : tx.getInputs()) { TransactionInSummary is = new TransactionInSummary(tx_in, tx_util, confirmed, block_tx_map); ByteString addr = is.getScriptHash(); if (addr != null) involved_scripthashes.add(addr); ins.put(idx, is); idx++; } }
public void putTxOutSpents(Transaction tx) { LinkedList<String> tx_outs = new LinkedList<String>(); for(TransactionInput in : tx.getInputs()) { if (!in.isCoinBase()) { TransactionOutPoint out = in.getOutpoint(); String key = out.getHash().toString() + ":" + out.getIndex(); //file_db.addTxOutSpentByMap(key, tx.getHash()); tx_outs.add(key); } } }
public boolean areSomeInputsPending(Transaction tx) { MemPoolInfo info = latest_info; if (info == null) return false; //Hard to say for(TransactionInput tx_in : tx.getInputs()) { if (!tx_in.isCoinBase()) { TransactionOutPoint tx_out = tx_in.getOutpoint(); Sha256Hash parent_hash = tx_out.getHash(); if (info.tx_set.contains(parent_hash)) return true; } } return false; }
public static boolean isEntirelySelf(final Transaction tx, final Wallet wallet) { for (final TransactionInput input : tx.getInputs()) { final TransactionOutput connectedOutput = input.getConnectedOutput(); if (connectedOutput == null || !connectedOutput.isMine(wallet)) return false; } for (final TransactionOutput output : tx.getOutputs()) { if (!output.isMine(wallet)) return false; } return true; }
/** Finds if tx is NOT spending other txns which are in the specified confidence type */ private boolean isNotSpendingTxnsInConfidenceType(Transaction tx, ConfidenceType confidenceType) { for (TransactionInput txInput : tx.getInputs()) { Transaction connectedTx = this.getTransaction(txInput.getOutpoint().getHash()); if (connectedTx != null && connectedTx.getConfidence().getConfidenceType().equals(confidenceType)) { return false; } } return true; }
/** Finds whether txA spends txB */ boolean spends(Transaction txA, Transaction txB) { for (TransactionInput txInput : txA.getInputs()) { if (txInput.getOutpoint().getHash().equals(txB.getHash())) { return true; } } return false; }
/** * Returns the amount of bitcoin ever sent via output. If an output is sent to our own wallet, because of change or * rotating keys or whatever, we do not count it. If the wallet was * involved in a shared transaction, i.e. there is some input to the transaction that we don't have the key for, then * we multiply the sum of the output values by the proportion of satoshi coming in to our inputs. Essentially we treat * inputs as pooling into the transaction, becoming fungible and being equally distributed to all outputs. * @return the total amount of satoshis sent by us */ public Coin getTotalSent() { Coin total = Coin.ZERO; for (Transaction tx: transactions.values()) { // Count spent outputs to only if they were not to us. This means we don't count change outputs. Coin txOutputTotal = Coin.ZERO; for (TransactionOutput out : tx.getOutputs()) { if (out.isMine(this) == false) { txOutputTotal = txOutputTotal.add(out.getValue()); } } // Count the input values to us Coin txOwnedInputsTotal = Coin.ZERO; for (TransactionInput in : tx.getInputs()) { TransactionOutput prevOut = in.getConnectedOutput(); if (prevOut != null && prevOut.isMine(this)) { txOwnedInputsTotal = txOwnedInputsTotal.add(prevOut.getValue()); } } // If there is an input that isn't from us, i.e. this is a shared transaction Coin txInputsTotal = tx.getInputSum(); if (txOwnedInputsTotal != txInputsTotal) { // multiply our output total by the appropriate proportion to account for the inputs that we don't own BigInteger txOutputTotalNum = new BigInteger(txOutputTotal.toString()); txOutputTotalNum = txOutputTotalNum.multiply(new BigInteger(txOwnedInputsTotal.toString())); txOutputTotalNum = txOutputTotalNum.divide(new BigInteger(txInputsTotal.toString())); txOutputTotal = Coin.valueOf(txOutputTotalNum.longValue()); } total = total.add(txOutputTotal); } return total; }
private Map<String, Long> createLockTimeForInputsMap(List<TransactionInput> inputs) { Map<String, Long> timeLocksOfInputs = new HashMap<>(inputs.size()); for (TransactionInput txIn : inputs) { byte[] pubKeyHash = txIn.getConnectedOutput().getScriptPubKey().getPubKeyHash(); String addressHashHex = org.bitcoinj.core.Utils.HEX.encode(pubKeyHash); TimeLockedAddress txInAddress = addressHashes.get(addressHashHex); if (txInAddress != null) { long lockTime = txInAddress.getLockTime(); timeLocksOfInputs.put(addressHashHex, lockTime); } } return timeLocksOfInputs; }
@Test public void isTxConsistentReturnsFalseAsExpected() { Wallet wallet = new Wallet(PARAMS); TransactionOutput to = createMock(TransactionOutput.class); EasyMock.expect(to.isAvailableForSpending()).andReturn(true); EasyMock.expect(to.isMineOrWatched(wallet)).andReturn(true); EasyMock.expect(to.getSpentBy()).andReturn(new TransactionInput(PARAMS, null, new byte[0])); Transaction tx = FakeTxBuilder.createFakeTxWithoutChange(PARAMS, to); replay(to); boolean isConsistent = wallet.isTxConsistent(tx, false); assertFalse(isConsistent); }
@Test (expected = ECKey.MissingPrivateKeyException.class) public void completeTxPartiallySignedThrows() throws Exception { sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, wallet.freshReceiveKey()); SendRequest req = SendRequest.emptyWallet(OTHER_ADDRESS); wallet.completeTx(req); // Delete the sigs for (TransactionInput input : req.tx.getInputs()) input.clearScriptBytes(); Wallet watching = Wallet.fromWatchingKey(PARAMS, wallet.getWatchingKey().dropParent().dropPrivateBytes()); watching.completeTx(SendRequest.forTx(req.tx)); }