/**
   * Called when the client provides the multi-sig contract. Checks that the previously-provided
   * refund transaction spends this transaction (because we will use it as a base to create payment
   * transactions) as well as output value and form (ie it is a 2-of-2 multisig to the correct
   * keys).
   *
   * @param multisigContract The provided multisig contract. Do not mutate this object after this
   *     call.
   * @return A future which completes when the provided multisig contract successfully broadcasts,
   *     or throws if the broadcast fails for some reason Note that if the network simply rejects
   *     the transaction, this future will never complete, a timeout should be used.
   * @throws VerificationException If the provided multisig contract is not well-formed or does not
   *     meet previously-specified parameters
   */
  public synchronized ListenableFuture<PaymentChannelServerState> provideMultiSigContract(
      final Transaction multisigContract) throws VerificationException {
    checkNotNull(multisigContract);
    checkState(state == State.WAITING_FOR_MULTISIG_CONTRACT);
    try {
      multisigContract.verify();
      this.multisigContract = multisigContract;
      this.multisigScript = multisigContract.getOutput(0).getScriptPubKey();

      // Check that multisigContract's first output is a 2-of-2 multisig to the correct pubkeys in
      // the correct order
      final Script expectedScript =
          ScriptBuilder.createMultiSigOutputScript(2, Lists.newArrayList(clientKey, serverKey));
      if (!Arrays.equals(multisigScript.getProgram(), expectedScript.getProgram()))
        throw new VerificationException(
            "Multisig contract's first output was not a standard 2-of-2 multisig to client and server in that order.");

      this.totalValue = multisigContract.getOutput(0).getValue();
      if (this.totalValue.signum() <= 0)
        throw new VerificationException(
            "Not accepting an attempt to open a contract with zero value.");
    } catch (VerificationException e) {
      // We couldn't parse the multisig transaction or its output.
      log.error("Provided multisig contract did not verify: {}", multisigContract.toString());
      throw e;
    }
    log.info("Broadcasting multisig contract: {}", multisigContract);
    state = State.WAITING_FOR_MULTISIG_ACCEPTANCE;
    final SettableFuture<PaymentChannelServerState> future = SettableFuture.create();
    Futures.addCallback(
        broadcaster.broadcastTransaction(multisigContract).future(),
        new FutureCallback<Transaction>() {
          @Override
          public void onSuccess(Transaction transaction) {
            log.info(
                "Successfully broadcast multisig contract {}. Channel now open.",
                transaction.getHashAsString());
            try {
              // Manually add the multisigContract to the wallet, overriding the isRelevant checks
              // so we can track
              // it and check for double-spends later
              wallet.receivePending(multisigContract, null, true);
            } catch (VerificationException e) {
              throw new RuntimeException(
                  e); // Cannot happen, we already called multisigContract.verify()
            }
            state = State.READY;
            future.set(PaymentChannelServerState.this);
          }

          @Override
          public void onFailure(Throwable throwable) {
            // Couldn't broadcast the transaction for some reason.
            log.error("Broadcast multisig contract failed", throwable);
            state = State.ERROR;
            future.setException(throwable);
          }
        });
    return future;
  }
 /**
  * For a connected transaction, runs the script against the connected pubkey and verifies they are
  * correct.
  *
  * @throws ScriptException if the script did not verify.
  * @throws VerificationException If the outpoint doesn't match the given output.
  */
 public void verify() throws VerificationException {
   final Transaction fromTx = getOutpoint().fromTx;
   long spendingIndex = getOutpoint().getIndex();
   checkNotNull(fromTx, "Not connected");
   final TransactionOutput output = fromTx.getOutput((int) spendingIndex);
   verify(output);
 }
 /**
  * When the servers signature for the refund transaction is received, call this to verify it and
  * sign the complete refund ourselves.
  *
  * <p>If this does not throw an exception, we are secure against the loss of funds and can safely
  * provide the server with the multi-sig contract to lock in the agreement. In this case, both the
  * multisig contract and the refund transaction are automatically committed to wallet so that it
  * can handle broadcasting the refund transaction at the appropriate time if necessary.
  */
 public synchronized void provideRefundSignature(byte[] theirSignature)
     throws VerificationException {
   checkNotNull(theirSignature);
   checkState(state == State.WAITING_FOR_SIGNED_REFUND);
   TransactionSignature theirSig = TransactionSignature.decodeFromBitcoin(theirSignature, true);
   if (theirSig.sigHashMode() != Transaction.SigHash.NONE || !theirSig.anyoneCanPay())
     throw new VerificationException("Refund signature was not SIGHASH_NONE|SIGHASH_ANYONECANPAY");
   // Sign the refund transaction ourselves.
   final TransactionOutput multisigContractOutput = multisigContract.getOutput(0);
   try {
     multisigScript = multisigContractOutput.getScriptPubKey();
   } catch (ScriptException e) {
     throw new RuntimeException(e); // Cannot happen: we built this ourselves.
   }
   TransactionSignature ourSignature =
       refundTx.calculateSignature(0, myKey, multisigScript, Transaction.SigHash.ALL, false);
   // Insert the signatures.
   Script scriptSig = ScriptBuilder.createMultiSigInputScript(ourSignature, theirSig);
   log.info("Refund scriptSig: {}", scriptSig);
   log.info("Multi-sig contract scriptPubKey: {}", multisigScript);
   TransactionInput refundInput = refundTx.getInput(0);
   refundInput.setScriptSig(scriptSig);
   refundInput.verify(multisigContractOutput);
   state = State.SAVE_STATE_IN_WALLET;
 }
Exemple #4
0
  @Test
  public void testCreateMultiSigInputScript() throws AddressFormatException {
    // Setup transaction and signatures
    ECKey key1 =
        new DumpedPrivateKey(params, "cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT")
            .getKey();
    ECKey key2 =
        new DumpedPrivateKey(params, "cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo")
            .getKey();
    ECKey key3 =
        new DumpedPrivateKey(params, "cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg")
            .getKey();
    Script multisigScript =
        ScriptBuilder.createMultiSigOutputScript(2, Arrays.asList(key1, key2, key3));
    byte[] bytes =
        Hex.decode(
            "01000000013df681ff83b43b6585fa32dd0e12b0b502e6481e04ee52ff0fdaf55a16a4ef61000000006b483045022100a84acca7906c13c5895a1314c165d33621cdcf8696145080895cbf301119b7cf0220730ff511106aa0e0a8570ff00ee57d7a6f24e30f592a10cae1deffac9e13b990012102b8d567bcd6328fd48a429f9cf4b315b859a58fd28c5088ef3cb1d98125fc4e8dffffffff02364f1c00000000001976a91439a02793b418de8ec748dd75382656453dc99bcb88ac40420f000000000017a9145780b80be32e117f675d6e0ada13ba799bf248e98700000000");
    Transaction transaction = new Transaction(params, bytes);
    TransactionOutput output = transaction.getOutput(1);
    Transaction spendTx = new Transaction(params);
    Address address = new Address(params, "n3CFiCmBXVt5d3HXKQ15EFZyhPz4yj5F3H");
    Script outputScript = ScriptBuilder.createOutputScript(address);
    spendTx.addOutput(output.getValue(), outputScript);
    spendTx.addInput(output);
    Sha256Hash sighash = spendTx.hashForSignature(0, multisigScript, SigHash.ALL, false);
    ECKey.ECDSASignature party1Signature = key1.sign(sighash);
    ECKey.ECDSASignature party2Signature = key2.sign(sighash);
    TransactionSignature party1TransactionSignature =
        new TransactionSignature(party1Signature, SigHash.ALL, false);
    TransactionSignature party2TransactionSignature =
        new TransactionSignature(party2Signature, SigHash.ALL, false);

    // Create p2sh multisig input script
    Script inputScript =
        ScriptBuilder.createP2SHMultiSigInputScript(
            ImmutableList.of(party1TransactionSignature, party2TransactionSignature),
            multisigScript.getProgram());

    // Assert that the input script contains 4 chunks
    assertTrue(inputScript.getChunks().size() == 4);

    // Assert that the input script created contains the original multisig
    // script as the last chunk
    ScriptChunk scriptChunk = inputScript.getChunks().get(inputScript.getChunks().size() - 1);
    Assert.assertArrayEquals(scriptChunk.data, multisigScript.getProgram());

    // Create regular multisig input script
    inputScript =
        ScriptBuilder.createMultiSigInputScript(
            ImmutableList.of(party1TransactionSignature, party2TransactionSignature));

    // Assert that the input script only contains 3 chunks
    assertTrue(inputScript.getChunks().size() == 3);

    // Assert that the input script created does not end with the original
    // multisig script
    scriptChunk = inputScript.getChunks().get(inputScript.getChunks().size() - 1);
    Assert.assertThat(scriptChunk.data, IsNot.not(IsEqual.equalTo(multisigScript.getProgram())));
  }
 // Create a payment transaction with valueToMe going back to us
 private synchronized Wallet.SendRequest makeUnsignedChannelContract(Coin valueToMe) {
   Transaction tx = new Transaction(wallet.getParams());
   if (!totalValue.subtract(valueToMe).equals(Coin.ZERO)) {
     clientOutput.setValue(totalValue.subtract(valueToMe));
     tx.addOutput(clientOutput);
   }
   tx.addInput(multisigContract.getOutput(0));
   return Wallet.SendRequest.forTx(tx);
 }
 /** Returns true if the tx is a valid settlement transaction. */
 public synchronized boolean isSettlementTransaction(Transaction tx) {
   try {
     tx.verify();
     tx.getInput(0).verify(multisigContract.getOutput(0));
     return true;
   } catch (VerificationException e) {
     return false;
   }
 }
 PaymentChannelClientState(StoredClientChannel storedClientChannel, Wallet wallet)
     throws VerificationException {
   // The PaymentChannelClientConnection handles storedClientChannel.active and ensures we aren't
   // resuming channels
   this.wallet = checkNotNull(wallet);
   this.multisigContract = checkNotNull(storedClientChannel.contract);
   this.multisigScript = multisigContract.getOutput(0).getScriptPubKey();
   this.refundTx = checkNotNull(storedClientChannel.refund);
   this.refundFees = checkNotNull(storedClientChannel.refundFees);
   this.expiryTime = refundTx.getLockTime();
   this.myKey = checkNotNull(storedClientChannel.myKey);
   this.serverMultisigKey = null;
   this.totalValue = multisigContract.getOutput(0).getValue();
   this.valueToMe = checkNotNull(storedClientChannel.valueToMe);
   this.storedChannel = storedClientChannel;
   this.state = State.READY;
   initWalletListeners();
 }
 private synchronized Transaction makeUnsignedChannelContract(Coin valueToMe)
     throws ValueOutOfRangeException {
   Transaction tx = new Transaction(wallet.getParams());
   tx.addInput(multisigContract.getOutput(0));
   // Our output always comes first.
   // TODO: We should drop myKey in favor of output key + multisig key separation
   // (as its always obvious who the client is based on T2 output order)
   tx.addOutput(valueToMe, myKey.toAddress(wallet.getParams()));
   return tx;
 }
 PaymentChannelServerState(
     StoredServerChannel storedServerChannel, Wallet wallet, TransactionBroadcaster broadcaster)
     throws VerificationException {
   synchronized (storedServerChannel) {
     this.wallet = checkNotNull(wallet);
     this.broadcaster = checkNotNull(broadcaster);
     this.multisigContract = checkNotNull(storedServerChannel.contract);
     this.multisigScript = multisigContract.getOutput(0).getScriptPubKey();
     this.clientKey = ECKey.fromPublicOnly(multisigScript.getChunks().get(1).data);
     this.clientOutput = checkNotNull(storedServerChannel.clientOutput);
     this.refundTransactionUnlockTimeSecs = storedServerChannel.refundTransactionUnlockTimeSecs;
     this.serverKey = checkNotNull(storedServerChannel.myKey);
     this.totalValue = multisigContract.getOutput(0).getValue();
     this.bestValueToMe = checkNotNull(storedServerChannel.bestValueToMe);
     this.bestValueSignature = storedServerChannel.bestValueSignature;
     checkArgument(bestValueToMe.equals(Coin.ZERO) || bestValueSignature != null);
     this.storedServerChannel = storedServerChannel;
     storedServerChannel.state = this;
     this.state = State.READY;
   }
 }
Exemple #10
0
 @Test
 public void exceptionListener() throws Exception {
   wallet.addEventListener(
       new AbstractWalletEventListener() {
         @Override
         public void onCoinsReceived(
             Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
           throw new NullPointerException("boo!");
         }
       });
   final Throwable[] throwables = new Throwable[1];
   Threading.uncaughtExceptionHandler =
       new Thread.UncaughtExceptionHandler() {
         @Override
         public void uncaughtException(Thread thread, Throwable throwable) {
           throwables[0] = throwable;
         }
       };
   // In real usage we're not really meant to adjust the uncaught exception handler after stuff
   // started happening
   // but in the unit test environment other tests have just run so the thread is probably still
   // kicking around.
   // Force it to crash so it'll be recreated with our new handler.
   Threading.USER_THREAD.execute(
       new Runnable() {
         @Override
         public void run() {
           throw new RuntimeException();
         }
       });
   connect();
   Transaction t1 = new Transaction(unitTestParams);
   t1.addInput(new TransactionInput(unitTestParams, t1, new byte[] {}));
   t1.addOutput(Utils.toNanoCoins(1, 0), new ECKey().toAddress(unitTestParams));
   Transaction t2 = new Transaction(unitTestParams);
   t2.addInput(t1.getOutput(0));
   t2.addOutput(Utils.toNanoCoins(1, 0), wallet.getChangeAddress());
   inbound(writeTarget, t2);
   final InventoryItem inventoryItem =
       new InventoryItem(InventoryItem.Type.Transaction, t2.getInput(0).getOutpoint().getHash());
   final NotFoundMessage nfm =
       new NotFoundMessage(unitTestParams, Lists.newArrayList(inventoryItem));
   inbound(writeTarget, nfm);
   pingAndWait(writeTarget);
   Threading.waitForUserCode();
   assertTrue(throwables[0] instanceof NullPointerException);
   Threading.uncaughtExceptionHandler = null;
 }
  /**
   * Called when the client provides the refund transaction. The refund transaction must have one
   * input from the multisig contract (that we don't have yet) and one output that the client
   * creates to themselves. This object will later be modified when we start getting paid.
   *
   * @param refundTx The refund transaction, this object will be mutated when payment is
   *     incremented.
   * @param clientMultiSigPubKey The client's pubkey which is required for the multisig output
   * @return Our signature that makes the refund transaction valid
   * @throws VerificationException If the transaction isnt valid or did not meet the requirements of
   *     a refund transaction.
   */
  public synchronized byte[] provideRefundTransaction(
      Transaction refundTx, byte[] clientMultiSigPubKey) throws VerificationException {
    checkNotNull(refundTx);
    checkNotNull(clientMultiSigPubKey);
    checkState(state == State.WAITING_FOR_REFUND_TRANSACTION);
    log.info("Provided with refund transaction: {}", refundTx);
    // Do a few very basic syntax sanity checks.
    refundTx.verify();
    // Verify that the refund transaction has a single input (that we can fill to sign the multisig
    // output).
    if (refundTx.getInputs().size() != 1)
      throw new VerificationException("Refund transaction does not have exactly one input");
    // Verify that the refund transaction has a time lock on it and a sequence number of zero.
    if (refundTx.getInput(0).getSequenceNumber() != 0)
      throw new VerificationException("Refund transaction's input's sequence number is non-0");
    if (refundTx.getLockTime() < minExpireTime)
      throw new VerificationException("Refund transaction has a lock time too soon");
    // Verify the transaction has one output (we don't care about its contents, its up to the
    // client)
    // Note that because we sign with SIGHASH_NONE|SIGHASH_ANYOENCANPAY the client can later add
    // more outputs and
    // inputs, but we will need only one output later to create the paying transactions
    if (refundTx.getOutputs().size() != 1)
      throw new VerificationException("Refund transaction does not have exactly one output");

    refundTransactionUnlockTimeSecs = refundTx.getLockTime();

    // Sign the refund tx with the scriptPubKey and return the signature. We don't have the spending
    // transaction
    // so do the steps individually.
    clientKey = ECKey.fromPublicOnly(clientMultiSigPubKey);
    Script multisigPubKey =
        ScriptBuilder.createMultiSigOutputScript(2, ImmutableList.of(clientKey, serverKey));
    // We are really only signing the fact that the transaction has a proper lock time and don't
    // care about anything
    // else, so we sign SIGHASH_NONE and SIGHASH_ANYONECANPAY.
    TransactionSignature sig =
        refundTx.calculateSignature(0, serverKey, multisigPubKey, Transaction.SigHash.NONE, true);
    log.info("Signed refund transaction.");
    this.clientOutput = refundTx.getOutput(0);
    state = State.WAITING_FOR_MULTISIG_CONTRACT;
    return sig.encodeToBitcoin();
  }
 /**
  * Connects this input to the relevant output of the referenced transaction. Connecting means
  * updating the internal pointers and spent flags. If the mode is to ABORT_ON_CONFLICT then the
  * spent output won't be changed, but the outpoint.fromTx pointer will still be updated.
  *
  * @param transaction The transaction to try.
  * @param mode Whether to abort if there's a pre-existing connection or not.
  * @return NO_SUCH_TX if transaction is not the prevtx, ALREADY_SPENT if there was a conflict,
  *     SUCCESS if not.
  */
 public ConnectionResult connect(Transaction transaction, ConnectMode mode) {
   if (!transaction.getHash().equals(outpoint.getHash())) return ConnectionResult.NO_SUCH_TX;
   checkElementIndex(
       (int) outpoint.getIndex(), transaction.getOutputs().size(), "Corrupt transaction");
   TransactionOutput out = transaction.getOutput((int) outpoint.getIndex());
   if (!out.isAvailableForSpending()) {
     if (getParentTransaction().equals(outpoint.fromTx)) {
       // Already connected.
       return ConnectionResult.SUCCESS;
     } else if (mode == ConnectMode.DISCONNECT_ON_CONFLICT) {
       out.markAsUnspent();
     } else if (mode == ConnectMode.ABORT_ON_CONFLICT) {
       outpoint.fromTx = out.getParentTransaction();
       return TransactionInput.ConnectionResult.ALREADY_SPENT;
     }
   }
   connect(out);
   return TransactionInput.ConnectionResult.SUCCESS;
 }
  /**
   * This is required for signatures which use a sigHashType which cannot be represented using
   * SigHash and anyoneCanPay See transaction
   * c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73, which has sigHashType 0
   */
  public static synchronized byte[] serializeForSignature(
      Transaction spendingTx, int inputIndex, byte[] connectedScript, byte sigHashType) {
    NetworkParameters params = TestNet3Params.get();
    // The SIGHASH flags are used in the design of contracts, please see this page for a further
    // understanding of
    // the purposes of the code in this method:
    //
    //   https://en.bitcoin.it/wiki/Contracts

    try {

      Transaction tx = new Transaction(params, spendingTx.bitcoinSerialize());
      // Store all the input scripts and clear them in preparation for signing. If we're signing a
      // fresh
      // transaction that step isn't very helpful, but it doesn't add much cost relative to the
      // actual
      // EC math so we'll do it anyway.
      //
      // Also store the input sequence numbers in case we are clearing them with SigHash.NONE/SINGLE

      byte[][] inputScripts = new byte[tx.getInputs().size()][];
      long[] inputSequenceNumbers = new long[tx.getInputs().size()];
      for (int i = 0; i < tx.getInputs().size(); i++) {
        inputScripts[i] = tx.getInputs().get(i).getScriptBytes();
        inputSequenceNumbers[i] = tx.getInputs().get(i).getSequenceNumber();
        tx.getInput(i).setScriptSig(new Script(new byte[0]));
      }

      // This step has no purpose beyond being synchronized with the reference clients bugs.
      // OP_CODESEPARATOR
      // is a legacy holdover from a previous, broken design of executing scripts that shipped in
      // Bitcoin 0.1.
      // It was seriously flawed and would have let anyone take anyone elses money. Later versions
      // switched to
      // the design we use today where scripts are executed independently but share a stack. This
      // left the
      // OP_CODESEPARATOR instruction having no purpose as it was only meant to be used internally,
      // not actually
      // ever put into scripts. Deleting OP_CODESEPARATOR is a step that should never be required
      // but if we don't
      // do it, we could split off the main chain.

      connectedScript =
          Script.removeAllInstancesOfOp(connectedScript, ScriptOpCodes.OP_CODESEPARATOR);
      // Set the input to the script of its output. Satoshi does this but the step has no obvious
      // purpose as
      // the signature covers the hash of the prevout transaction which obviously includes the
      // output script
      // already. Perhaps it felt safer to him in some way, or is another leftover from how the code
      // was written.
      TransactionInput input = tx.getInputs().get(inputIndex);
      input.setScriptSig(new Script(connectedScript));
      List<TransactionOutput> outputs = tx.getOutputs();

      if ((sigHashType & 0x1f) == (Transaction.SigHash.NONE.ordinal() + 1)) {
        // SIGHASH_NONE means no outputs are signed at all - the signature is effectively for a
        // "blank cheque".
        // this.outputs = new ArrayList<TransactionOutput>(0);
        tx.clearOutputs();
        // The signature isn't broken by new versions of the transaction issued by other parties.
        for (int i = 0; i < tx.getInputs().size(); i++)
          if (i != inputIndex) tx.getInputs().get(i).setSequenceNumber(0);
      } else if ((sigHashType & 0x1f) == (Transaction.SigHash.SINGLE.ordinal() + 1)) {
        // SIGHASH_SINGLE means only sign the output at the same index as the input (ie, my output).
        if (inputIndex >= tx.getOutputs().size()) {
          // The input index is beyond the number of outputs, it's a buggy signature made by a
          // broken
          // Bitcoin implementation. The reference client also contains a bug in handling this case:
          // any transaction output that is signed in this case will result in both the signed
          // output
          // and any future outputs to this public key being steal-able by anyone who has
          // the resulting signature and the public key (both of which are part of the signed tx
          // input).
          // Put the transaction back to how we found it.
          //
          // TODO: Only allow this to happen if we are checking a signature, not signing a
          // transactions
          for (int i = 0; i < tx.getInputs().size(); i++) {
            // tx.getInputs().get(i).setScriptSig(inputScripts[i]);
            /*                        tx.getInputs().get(i).setScriptSig(ScriptBuilder.createMultiSigInputScriptBytes(
            Arrays.asList(inputScripts[i])));*/
            tx.getInput(i).setScriptSig(new Script(inputScripts[i]));
            tx.getInputs().get(i).setSequenceNumber(inputSequenceNumbers[i]);
          }
          // this.outputs = outputs;

          // Satoshis bug is that SignatureHash was supposed to return a hash and on this codepath
          // it
          // actually returns the constant "1" to indicate an error, which is never checked for.
          // Oops.
          return Utils.HEX.decode(
              "0100000000000000000000000000000000000000000000000000000000000000");
        }

        // In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs
        // before
        // that position are "nulled out". Unintuitively, the value in a "null" transaction is set
        // to -1.
        /*                this.outputs = new ArrayList<TransactionOutput>(this.outputs.subList(0, inputIndex + 1));
        for (int i = 0; i < inputIndex; i++)
            this.outputs.set(i, new TransactionOutput(params, this, Coin.NEGATIVE_SATOSHI, new byte[] {}));
        // The signature isn't broken by new versions of the transaction issued by other parties.
        for (int i = 0; i < inputs.size(); i++)
            if (i != inputIndex)
                inputs.get(i).setSequenceNumber(0);*/
        // In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs
        // before
        // that position are "nulled out". Unintuitively, the value in a "null" transaction is set
        // to -1.
        // tx.outputs = new ArrayList<TransactionOutput>(tx.getOutputs().subList(0, inputIndex +
        // 1));
        tx.clearOutputs();
        for (int i = 0; i <= inputIndex; i++)
          if (i == inputIndex) {
            // need to make sure the output at inputIndex stays the same
            tx.addOutput(spendingTx.getOutput(inputIndex));
          } else {
            // this.outputs.set(i, new TransactionOutput(params, this, Coin.NEGATIVE_SATOSHI, new
            // byte[] {}));
            tx.addOutput(new TransactionOutput(params, tx, Coin.NEGATIVE_SATOSHI, new byte[] {}));
          }

        // The signature isn't broken by new versions of the transaction issued by other parties.
        for (int i = 0; i < tx.getInputs().size(); i++)
          if (i != inputIndex) tx.getInputs().get(i).setSequenceNumber(0);
      }

      List<TransactionInput> inputs = tx.getInputs();
      if ((sigHashType & (byte) 0x80) == 0x80) {
        // SIGHASH_ANYONECANPAY means the signature in the input is not broken by
        // changes/additions/removals
        // of other inputs. For example, this is useful for building assurance contracts.
        tx.clearInputs();
        tx.getInputs().add(input);
      }

      ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(256);
      tx.bitcoinSerialize(bos);
      // We also have to write a hash type (sigHashType is actually an unsigned char)
      uint32ToByteStreamLE(0x000000ff & sigHashType, bos);
      // Note that this is NOT reversed to ensure it will be signed correctly. If it were to be
      // printed out
      // however then we would expect that it is IS reversed.
      byte[] txSignatureBytes = bos.toByteArray();
      bos.close();

      // Put the transaction back to how we found it.
      // tx.inputs = inputs;
      tx.clearInputs();
      for (int i = 0; i < inputs.size(); i++) {
        tx.addInput(inputs.get(i));
      }
      for (int i = 0; i < inputs.size(); i++) {
        inputs.get(i).setScriptSig(new Script(inputScripts[i]));
        inputs.get(i).setSequenceNumber(inputSequenceNumbers[i]);
      }
      // this.outputs = outputs;
      tx.clearOutputs();
      for (int i = 0; i < outputs.size(); i++) {
        tx.addOutput(outputs.get(i));
      }
      return txSignatureBytes;
    } catch (IOException e) {
      throw new RuntimeException(e); // Cannot happen.
    }
  }
Exemple #14
0
 private void checkTimeLockedDependency(boolean shouldAccept, boolean useNotFound)
     throws Exception {
   // Initial setup.
   connectWithVersion(useNotFound ? 70001 : 60001);
   ECKey key = new ECKey();
   Wallet wallet = new Wallet(unitTestParams);
   wallet.addKey(key);
   wallet.setAcceptRiskyTransactions(shouldAccept);
   peer.addWallet(wallet);
   final Transaction[] vtx = new Transaction[1];
   wallet.addEventListener(
       new AbstractWalletEventListener() {
         @Override
         public void onCoinsReceived(
             Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
           vtx[0] = tx;
         }
       });
   // t1 -> t2 [locked] -> t3 (not available)
   Transaction t2 = new Transaction(unitTestParams);
   t2.setLockTime(999999);
   // Add a fake input to t3 that goes nowhere.
   Sha256Hash t3 = Sha256Hash.create("abc".getBytes(Charset.forName("UTF-8")));
   t2.addInput(
       new TransactionInput(
           unitTestParams, t2, new byte[] {}, new TransactionOutPoint(unitTestParams, 0, t3)));
   t2.getInput(0).setSequenceNumber(0xDEADBEEF);
   t2.addOutput(Utils.toNanoCoins(1, 0), new ECKey());
   Transaction t1 = new Transaction(unitTestParams);
   t1.addInput(t2.getOutput(0));
   t1.addOutput(Utils.toNanoCoins(1, 0), key); // Make it relevant.
   // Announce t1.
   InventoryMessage inv = new InventoryMessage(unitTestParams);
   inv.addTransaction(t1);
   inbound(writeTarget, inv);
   // Send it.
   GetDataMessage getdata = (GetDataMessage) outbound(writeTarget);
   assertEquals(t1.getHash(), getdata.getItems().get(0).hash);
   inbound(writeTarget, t1);
   // Nothing arrived at our event listener yet.
   assertNull(vtx[0]);
   // We request t2.
   getdata = (GetDataMessage) outbound(writeTarget);
   assertEquals(t2.getHash(), getdata.getItems().get(0).hash);
   inbound(writeTarget, t2);
   if (!useNotFound) bouncePing();
   // We request t3.
   getdata = (GetDataMessage) outbound(writeTarget);
   assertEquals(t3, getdata.getItems().get(0).hash);
   // Can't find it: bottom of tree.
   if (useNotFound) {
     NotFoundMessage notFound = new NotFoundMessage(unitTestParams);
     notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, t3));
     inbound(writeTarget, notFound);
   } else {
     bouncePing();
   }
   pingAndWait(writeTarget);
   Threading.waitForUserCode();
   // We're done but still not notified because it was timelocked.
   if (shouldAccept) assertNotNull(vtx[0]);
   else assertNull(vtx[0]);
 }
Exemple #15
0
  public void recursiveDownload(boolean useNotFound) throws Exception {
    // Using ping or notfound?
    connectWithVersion(useNotFound ? 70001 : 60001);
    // Check that we can download all dependencies of an unconfirmed relevant transaction from the
    // mempool.
    ECKey to = new ECKey();

    final Transaction[] onTx = new Transaction[1];
    peer.addEventListener(
        new AbstractPeerEventListener() {
          @Override
          public void onTransaction(Peer peer1, Transaction t) {
            onTx[0] = t;
          }
        },
        Threading.SAME_THREAD);

    // Make the some fake transactions in the following graph:
    //   t1 -> t2 -> [t5]
    //      -> t3 -> t4 -> [t6]
    //      -> [t7]
    //      -> [t8]
    // The ones in brackets are assumed to be in the chain and are represented only by hashes.
    Transaction t2 = TestUtils.createFakeTx(unitTestParams, Utils.toNanoCoins(1, 0), to);
    Sha256Hash t5 = t2.getInput(0).getOutpoint().getHash();
    Transaction t4 = TestUtils.createFakeTx(unitTestParams, Utils.toNanoCoins(1, 0), new ECKey());
    Sha256Hash t6 = t4.getInput(0).getOutpoint().getHash();
    t4.addOutput(Utils.toNanoCoins(1, 0), new ECKey());
    Transaction t3 = new Transaction(unitTestParams);
    t3.addInput(t4.getOutput(0));
    t3.addOutput(Utils.toNanoCoins(1, 0), new ECKey());
    Transaction t1 = new Transaction(unitTestParams);
    t1.addInput(t2.getOutput(0));
    t1.addInput(t3.getOutput(0));
    Sha256Hash someHash =
        new Sha256Hash("2b801dd82f01d17bbde881687bf72bc62e2faa8ab8133d36fcb8c3abe7459da6");
    t1.addInput(
        new TransactionInput(
            unitTestParams,
            t1,
            new byte[] {},
            new TransactionOutPoint(unitTestParams, 0, someHash)));
    Sha256Hash anotherHash =
        new Sha256Hash("3b801dd82f01d17bbde881687bf72bc62e2faa8ab8133d36fcb8c3abe7459da6");
    t1.addInput(
        new TransactionInput(
            unitTestParams,
            t1,
            new byte[] {},
            new TransactionOutPoint(unitTestParams, 1, anotherHash)));
    t1.addOutput(Utils.toNanoCoins(1, 0), to);
    t1 = TestUtils.roundTripTransaction(unitTestParams, t1);
    t2 = TestUtils.roundTripTransaction(unitTestParams, t2);
    t3 = TestUtils.roundTripTransaction(unitTestParams, t3);
    t4 = TestUtils.roundTripTransaction(unitTestParams, t4);

    // Announce the first one. Wait for it to be downloaded.
    InventoryMessage inv = new InventoryMessage(unitTestParams);
    inv.addTransaction(t1);
    inbound(writeTarget, inv);
    GetDataMessage getdata = (GetDataMessage) outbound(writeTarget);
    Threading.waitForUserCode();
    assertEquals(t1.getHash(), getdata.getItems().get(0).hash);
    inbound(writeTarget, t1);
    pingAndWait(writeTarget);
    assertEquals(t1, onTx[0]);
    // We want its dependencies so ask for them.
    ListenableFuture<List<Transaction>> futures = peer.downloadDependencies(t1);
    assertFalse(futures.isDone());
    // It will recursively ask for the dependencies of t1: t2, t3, someHash and anotherHash.
    getdata = (GetDataMessage) outbound(writeTarget);
    assertEquals(4, getdata.getItems().size());
    assertEquals(t2.getHash(), getdata.getItems().get(0).hash);
    assertEquals(t3.getHash(), getdata.getItems().get(1).hash);
    assertEquals(someHash, getdata.getItems().get(2).hash);
    assertEquals(anotherHash, getdata.getItems().get(3).hash);
    long nonce = -1;
    if (!useNotFound) nonce = ((Ping) outbound(writeTarget)).getNonce();
    // For some random reason, t4 is delivered at this point before it's needed - perhaps it was a
    // Bloom filter
    // false positive. We do this to check that the mempool is being checked for seen transactions
    // before
    // requesting them.
    inbound(writeTarget, t4);
    // Deliver the requested transactions.
    inbound(writeTarget, t2);
    inbound(writeTarget, t3);
    if (useNotFound) {
      NotFoundMessage notFound = new NotFoundMessage(unitTestParams);
      notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, someHash));
      notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, anotherHash));
      inbound(writeTarget, notFound);
    } else {
      inbound(writeTarget, new Pong(nonce));
    }
    assertFalse(futures.isDone());
    // It will recursively ask for the dependencies of t2: t5 and t4, but not t3 because it already
    // found t4.
    getdata = (GetDataMessage) outbound(writeTarget);
    assertEquals(getdata.getItems().get(0).hash, t2.getInput(0).getOutpoint().getHash());
    // t5 isn't found and t4 is.
    if (useNotFound) {
      NotFoundMessage notFound = new NotFoundMessage(unitTestParams);
      notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, t5));
      inbound(writeTarget, notFound);
    } else {
      bouncePing();
    }
    assertFalse(futures.isDone());
    // Continue to explore the t4 branch and ask for t6, which is in the chain.
    getdata = (GetDataMessage) outbound(writeTarget);
    assertEquals(t6, getdata.getItems().get(0).hash);
    if (useNotFound) {
      NotFoundMessage notFound = new NotFoundMessage(unitTestParams);
      notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, t6));
      inbound(writeTarget, notFound);
    } else {
      bouncePing();
    }
    pingAndWait(writeTarget);
    // That's it, we explored the entire tree.
    assertTrue(futures.isDone());
    List<Transaction> results = futures.get();
    assertTrue(results.contains(t2));
    assertTrue(results.contains(t3));
    assertTrue(results.contains(t4));
  }
Exemple #16
0
  private static void send(
      List<String> outputs, BigInteger fee, String lockTimeStr, boolean allowUnconfirmed)
      throws VerificationException {
    try {
      // Convert the input strings to outputs.
      Transaction t = new Transaction(params);
      for (String spec : outputs) {
        String[] parts = spec.split(":");
        if (parts.length != 2) {
          System.err.println("Malformed output specification, must have two parts separated by :");
          return;
        }
        String destination = parts[0];
        try {
          BigInteger value = Utils.toNanoCoins(parts[1]);
          if (destination.startsWith("0")) {
            boolean compressed = destination.startsWith("02") || destination.startsWith("03");
            // Treat as a raw public key.
            BigInteger pubKey = new BigInteger(destination, 16);
            ECKey key = new ECKey(null, pubKey.toByteArray(), compressed);
            t.addOutput(value, key);
          } else {
            // Treat as an address.
            Address addr = new Address(params, destination);
            t.addOutput(value, addr);
          }
        } catch (WrongNetworkException e) {
          System.err.println(
              "Malformed output specification, address is for a different network: " + parts[0]);
          return;
        } catch (AddressFormatException e) {
          System.err.println(
              "Malformed output specification, could not parse as address: " + parts[0]);
          return;
        } catch (NumberFormatException e) {
          System.err.println(
              "Malformed output specification, could not parse as value: " + parts[1]);
        }
      }
      Wallet.SendRequest req = Wallet.SendRequest.forTx(t);
      if (t.getOutputs().size() == 1 && t.getOutput(0).getValue().equals(wallet.getBalance())) {
        log.info("Emptying out wallet, recipient may get less than what you expect");
        req.emptyWallet = true;
      }
      req.fee = fee;
      if (allowUnconfirmed) {
        wallet.allowSpendingUnconfirmedTransactions();
      }
      if (password != null) {
        if (!wallet.checkPassword(password)) {
          System.err.println("Password is incorrect.");
          return;
        }
        req.aesKey = wallet.getKeyCrypter().deriveKey(password);
      }
      wallet.completeTx(req);

      try {
        if (lockTimeStr != null) {
          t.setLockTime(Transaction.parseLockTimeStr(lockTimeStr));
          // For lock times to take effect, at least one output must have a non-final sequence
          // number.
          t.getInputs().get(0).setSequenceNumber(0);
          // And because we modified the transaction after it was completed, we must re-sign the
          // inputs.
          t.signInputs(Transaction.SigHash.ALL, wallet);
        }
      } catch (ParseException e) {
        System.err.println("Could not understand --locktime of " + lockTimeStr);
        return;
      } catch (ScriptException e) {
        throw new RuntimeException(e);
      }
      t = req.tx; // Not strictly required today.
      System.out.println(t.getHashAsString());
      if (options.has("offline")) {
        wallet.commitTx(t);
        return;
      }

      setup();
      peers.startAndWait();
      // Wait for peers to connect, the tx to be sent to one of them and for it to be propagated
      // across the
      // network. Once propagation is complete and we heard the transaction back from all our peers,
      // it will
      // be committed to the wallet.
      peers.broadcastTransaction(t).get();
      // Hack for regtest/single peer mode, as we're about to shut down and won't get an ACK from
      // the remote end.
      List<Peer> peerList = peers.getConnectedPeers();
      if (peerList.size() == 1) peerList.get(0).ping().get();
    } catch (BlockStoreException e) {
      throw new RuntimeException(e);
    } catch (KeyCrypterException e) {
      throw new RuntimeException(e);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    } catch (ExecutionException e) {
      throw new RuntimeException(e);
    } catch (InsufficientMoneyException e) {
      System.err.println(
          "Insufficient funds: have " + Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
    }
  }