Ejemplo n.º 1
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;
 }
Ejemplo n.º 2
0
 private static Block createGenesis(NetworkParameters n) {
   Block genesisBlock = new Block(n);
   Transaction t = new Transaction(n);
   try {
     // A script containing the difficulty bits and the following message:
     //
     //   "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
     byte[] bytes =
         Utils.HEX.decode(
             "04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73");
     t.addInput(new TransactionInput(n, t, bytes));
     ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
     Script.writeBytes(
         scriptPubKeyBytes,
         Utils.HEX.decode(
             "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"));
     scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG);
     t.addOutput(new TransactionOutput(n, t, FIFTY_COINS, scriptPubKeyBytes.toByteArray()));
   } catch (Exception e) {
     // Cannot happen.
     throw new RuntimeException(e);
   }
   genesisBlock.addTransaction(t);
   return genesisBlock;
 }
Ejemplo n.º 3
0
  @Test
  public void getLargeBlock() throws Exception {
    connect();

    Block b1 = createFakeBlock(blockStore).block;
    blockChain.add(b1);
    Block b2 = makeSolvedTestBlock(b1);
    Transaction t = new Transaction(unitTestParams);
    t.addInput(b1.getTransactions().get(0).getOutput(0));
    t.addOutput(
        new TransactionOutput(
            unitTestParams, t, BigInteger.ZERO, new byte[Block.MAX_BLOCK_SIZE - 1000]));
    b2.addTransaction(t);

    // Request the block.
    Future<Block> resultFuture = peer.getBlock(b2.getHash());
    assertFalse(resultFuture.isDone());
    // Peer asks for it.
    GetDataMessage message = (GetDataMessage) outbound(writeTarget);
    assertEquals(message.getItems().get(0).hash, b2.getHash());
    assertFalse(resultFuture.isDone());
    // Peer receives it.
    inbound(writeTarget, b2);
    Block b = resultFuture.get();
    assertEquals(b, b2);
  }
Ejemplo n.º 4
0
 private static Block createGenesis(NetworkParameters n) {
   Block genesisBlock = new Block(n);
   Transaction t = new Transaction(n);
   try {
     // A script containing the difficulty bits and the following message:
     //
     //   "Mar-17-2014 Harvard Scientists: First Direct Evidence of Cosmic Inflation"
     byte[] bytes =
         Hex.decode(
             "04ffff001d0104494d61722d31372d32303134204861727661726420536369656e74697374733a204669727374204469726563742045766964656e6365206f6620436f736d696320496e666c6174696f6e");
     t.addInput(new TransactionInput(n, t, bytes));
     ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
     Script.writeBytes(
         scriptPubKeyBytes,
         Hex.decode(
             "04c6c6a01450f2b8bbb176b98f593623b04a5f5761a7d0adae4d21f77c366e01e147217a9d8864a871d95d182e01d2b003168f0fa7851278a86ac0aa682919f9a6"));
     scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG);
     t.addOutput(
         new TransactionOutput(n, t, Utils.toNanoCoins(1, 0), scriptPubKeyBytes.toByteArray()));
   } catch (Exception e) {
     // Cannot happen.
     throw new RuntimeException(e);
   }
   genesisBlock.addTransaction(t);
   return genesisBlock;
 }
Ejemplo n.º 5
0
  @Test
  public void testSimplePayment() throws Exception {
    // Create a PaymentRequest and make sure the correct values are parsed by the PaymentSession.
    MockPaymentSession paymentSession = new MockPaymentSession(newSimplePaymentRequest());
    assertEquals(paymentRequestMemo, paymentSession.getMemo());
    assertEquals(nanoCoins, paymentSession.getValue());
    assertEquals(simplePaymentUrl, paymentSession.getPaymentUrl());
    assertTrue(new Date(time * 1000L).equals(paymentSession.getDate()));
    assertTrue(paymentSession.getSendRequest().tx.equals(tx));
    assertFalse(paymentSession.isExpired());

    // Send the payment and verify that the correct information is sent.
    // Add a dummy input to tx so it is considered valid.
    tx.addInput(new TransactionInput(params, tx, outputToMe.getScriptBytes()));
    ArrayList<Transaction> txns = new ArrayList<Transaction>();
    txns.add(tx);
    Address refundAddr = new Address(params, serverKey.getPubKeyHash());
    paymentSession.sendPayment(txns, refundAddr, paymentMemo);
    assertEquals(1, paymentSession.getPaymentLog().size());
    assertEquals(simplePaymentUrl, paymentSession.getPaymentLog().get(0).getUrl().toString());
    Protos.Payment payment = paymentSession.getPaymentLog().get(0).getPayment();
    assertEquals(paymentMemo, payment.getMemo());
    assertEquals(merchantData, payment.getMerchantData());
    assertEquals(1, payment.getRefundToCount());
    assertEquals(nanoCoins.longValue(), payment.getRefundTo(0).getAmount());
    TransactionOutput refundOutput = new TransactionOutput(params, null, nanoCoins, refundAddr);
    ByteString refundScript = ByteString.copyFrom(refundOutput.getScriptBytes());
    assertTrue(refundScript.equals(payment.getRefundTo(0).getScript()));
  }
Ejemplo n.º 6
0
 private static Block createGenesis(NetworkParameters n) {
   Block genesisBlock = new Block(n);
   Transaction t = new Transaction(n);
   try {
     // A script containing the difficulty bits and the following message:
     //
     //   "3 Aug 2013 - M&G - Mugabe wins Zim election with more than 60% of votes"
     byte[] bytes =
         Hex.decode(
             "04ffff001d010445434e4e2032332f31302f3230313320536369656e74697374732066696e6420676f6c642067726f77696e67206f6e20747265657320696e204175737472616c6961");
     t.addInput(new TransactionInput(n, t, bytes));
     ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
     Script.writeBytes(
         scriptPubKeyBytes,
         Hex.decode(
             "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"));
     scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG);
     t.addOutput(
         new TransactionOutput(n, t, Utils.toNanoCoins(50, 0), scriptPubKeyBytes.toByteArray()));
   } catch (Exception e) {
     // Cannot happen.
     throw new RuntimeException(e);
   }
   genesisBlock.addTransaction(t);
   // Unable to figure out the exact transaction input script therefore taking the shortcut by
   // setting merkle root directly
   genesisBlock.setMerkleRoot(
       new Sha256Hash("d25dbe3a2852926fc2ec6591a95983bbcde80c449f30ced37fd657361073fa96"));
   return genesisBlock;
 }
Ejemplo n.º 7
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())));
  }
 /**
  * Creates the initial multisig contract and incomplete refund transaction which can be requested
  * at the appropriate time using {@link PaymentChannelClientState#getIncompleteRefundTransaction}
  * and {@link PaymentChannelClientState#getMultisigContract()}. The way the contract is crafted
  * can be adjusted by overriding {@link
  * PaymentChannelClientState#editContractSendRequest(com.google.bitcoin.core.Wallet.SendRequest)}.
  * By default unconfirmed coins are allowed to be used, as for micropayments the risk should be
  * relatively low.
  *
  * @throws ValueOutOfRangeException if the value being used is too small to be accepted by the
  *     network
  * @throws InsufficientMoneyException if the wallet doesn't contain enough balance to initiate
  */
 public synchronized void initiate() throws ValueOutOfRangeException, InsufficientMoneyException {
   final NetworkParameters params = wallet.getParams();
   Transaction template = new Transaction(params);
   // We always place the client key before the server key because, if either side wants some
   // privacy, they can
   // use a fresh key for the the multisig contract and nowhere else
   List<ECKey> keys = Lists.newArrayList(myKey, serverMultisigKey);
   // There is also probably a change output, but we don't bother shuffling them as it's obvious
   // from the
   // format which one is the change. If we start obfuscating the change output better in future
   // this may
   // be worth revisiting.
   TransactionOutput multisigOutput =
       template.addOutput(totalValue, ScriptBuilder.createMultiSigOutputScript(2, keys));
   if (multisigOutput.getMinNonDustValue().compareTo(totalValue) > 0)
     throw new ValueOutOfRangeException("totalValue too small to use");
   Wallet.SendRequest req = Wallet.SendRequest.forTx(template);
   req.coinSelector = AllowUnconfirmedCoinSelector.get();
   editContractSendRequest(req);
   req.shuffleOutputs = false; // TODO: Fix things so shuffling is usable.
   wallet.completeTx(req);
   Coin multisigFee = req.tx.getFee();
   multisigContract = req.tx;
   // Build a refund transaction that protects us in the case of a bad server that's just trying to
   // cause havoc
   // by locking up peoples money (perhaps as a precursor to a ransom attempt). We time lock it so
   // the server
   // has an assurance that we cannot take back our money by claiming a refund before the channel
   // closes - this
   // relies on the fact that since Bitcoin 0.8 time locked transactions are non-final. This will
   // need to change
   // in future as it breaks the intended design of timelocking/tx replacement, but for now it
   // simplifies this
   // specific protocol somewhat.
   refundTx = new Transaction(params);
   refundTx
       .addInput(multisigOutput)
       .setSequenceNumber(0); // Allow replacement when it's eventually reactivated.
   refundTx.setLockTime(expiryTime);
   if (totalValue.compareTo(Coin.CENT) < 0) {
     // Must pay min fee.
     final Coin valueAfterFee = totalValue.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
     if (Transaction.MIN_NONDUST_OUTPUT.compareTo(valueAfterFee) > 0)
       throw new ValueOutOfRangeException("totalValue too small to use");
     refundTx.addOutput(valueAfterFee, myKey.toAddress(params));
     refundFees = multisigFee.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
   } else {
     refundTx.addOutput(totalValue, myKey.toAddress(params));
     refundFees = multisigFee;
   }
   refundTx.getConfidence().setSource(TransactionConfidence.Source.SELF);
   log.info(
       "initiated channel with multi-sig contract {}, refund {}",
       multisigContract.getHashAsString(),
       refundTx.getHashAsString());
   state = State.INITIATED;
   // Client should now call getIncompleteRefundTransaction() and send it to the server.
 }
 // 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);
 }
Ejemplo n.º 10
0
 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;
 }
Ejemplo n.º 11
0
 @Test
 public void testUpdateLength() {
   NetworkParameters params = UnitTestParams.get();
   Block block =
       params
           .getGenesisBlock()
           .createNextBlockWithCoinbase(
               Block.BLOCK_VERSION_GENESIS, new ECKey().getPubKey(), Block.BLOCK_HEIGHT_GENESIS);
   assertEquals(block.bitcoinSerialize().length, block.length);
   final int origBlockLen = block.length;
   Transaction tx = new Transaction(params);
   // this is broken until the transaction has > 1 input + output (which is required anyway...)
   // assertTrue(tx.length == tx.bitcoinSerialize().length && tx.length == 8);
   byte[] outputScript = new byte[10];
   Arrays.fill(outputScript, (byte) ScriptOpCodes.OP_FALSE);
   tx.addOutput(new TransactionOutput(params, null, Coin.SATOSHI, outputScript));
   tx.addInput(
       new TransactionInput(
           params,
           null,
           new byte[] {(byte) ScriptOpCodes.OP_FALSE},
           new TransactionOutPoint(params, 0, Sha256Hash.of(new byte[] {1}))));
   int origTxLength = 8 + 2 + 8 + 1 + 10 + 40 + 1 + 1;
   assertEquals(tx.bitcoinSerialize().length, tx.length);
   assertEquals(origTxLength, tx.length);
   block.addTransaction(tx);
   assertEquals(block.bitcoinSerialize().length, block.length);
   assertEquals(origBlockLen + tx.length, block.length);
   block
       .getTransactions()
       .get(1)
       .getInputs()
       .get(0)
       .setScriptBytes(new byte[] {(byte) ScriptOpCodes.OP_FALSE, (byte) ScriptOpCodes.OP_FALSE});
   assertEquals(block.length, origBlockLen + tx.length);
   assertEquals(tx.length, origTxLength + 1);
   block.getTransactions().get(1).getInputs().get(0).setScriptBytes(new byte[] {});
   assertEquals(block.length, block.bitcoinSerialize().length);
   assertEquals(block.length, origBlockLen + tx.length);
   assertEquals(tx.length, origTxLength - 1);
   block
       .getTransactions()
       .get(1)
       .addInput(
           new TransactionInput(
               params,
               null,
               new byte[] {(byte) ScriptOpCodes.OP_FALSE},
               new TransactionOutPoint(params, 0, Sha256Hash.of(new byte[] {1}))));
   assertEquals(block.length, origBlockLen + tx.length);
   assertEquals(tx.length, origTxLength + 41); // - 1 + 40 + 1 + 1
 }
Ejemplo n.º 12
0
  @Before
  public void setUp() throws Exception {
    unitTestParams = UnitTestParams.get();
    wallet = new Wallet(unitTestParams);
    wallet.addKey(new ECKey());

    resetBlockStore();

    Transaction tx1 =
        createFakeTx(
            unitTestParams,
            Utils.toNanoCoins(2, 0),
            wallet.getKeys().get(0).toAddress(unitTestParams));

    // add a second input so can test granularity of byte cache.
    Transaction prevTx = new Transaction(unitTestParams);
    TransactionOutput prevOut =
        new TransactionOutput(
            unitTestParams,
            prevTx,
            Utils.toNanoCoins(1, 0),
            wallet.getKeys().get(0).toAddress(unitTestParams));
    prevTx.addOutput(prevOut);
    // Connect it.
    tx1.addInput(prevOut);

    Transaction tx2 =
        createFakeTx(
            unitTestParams, Utils.toNanoCoins(1, 0), new ECKey().toAddress(unitTestParams));

    Block b1 = createFakeBlock(blockStore, tx1, tx2).block;

    BitcoinSerializer bs = new BitcoinSerializer(unitTestParams);

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    bs.serialize(tx1, bos);
    tx1BytesWithHeader = bos.toByteArray();
    tx1Bytes = tx1.bitcoinSerialize();

    bos.reset();
    bs.serialize(tx2, bos);
    tx2BytesWithHeader = bos.toByteArray();
    tx2Bytes = tx2.bitcoinSerialize();

    bos.reset();
    bs.serialize(b1, bos);
    b1BytesWithHeader = bos.toByteArray();
    b1Bytes = b1.bitcoinSerialize();
  }
Ejemplo n.º 13
0
  private void readTransaction(Protos.Transaction txProto, NetworkParameters params) {
    Transaction tx = new Transaction(params);
    if (txProto.hasUpdatedAt()) {
      tx.setUpdateTime(new Date(txProto.getUpdatedAt()));
    }

    for (Protos.TransactionOutput outputProto : txProto.getTransactionOutputList()) {
      BigInteger value = BigInteger.valueOf(outputProto.getValue());
      byte[] scriptBytes = outputProto.getScriptBytes().toByteArray();
      TransactionOutput output = new TransactionOutput(params, tx, value, scriptBytes);
      tx.addOutput(output);
    }

    for (Protos.TransactionInput transactionInput : txProto.getTransactionInputList()) {
      byte[] scriptBytes = transactionInput.getScriptBytes().toByteArray();
      TransactionOutPoint outpoint =
          new TransactionOutPoint(
              params,
              transactionInput.getTransactionOutPointIndex(),
              byteStringToHash(transactionInput.getTransactionOutPointHash()));
      TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint);
      if (transactionInput.hasSequence()) {
        input.setSequenceNumber(transactionInput.getSequence());
      }
      tx.addInput(input);
    }

    for (ByteString blockHash : txProto.getBlockHashList()) {
      tx.addBlockAppearance(byteStringToHash(blockHash));
    }

    if (txProto.hasLockTime()) {
      tx.setLockTime(0xffffffffL & txProto.getLockTime());
    }

    // Transaction should now be complete.
    Sha256Hash protoHash = byteStringToHash(txProto.getHash());
    Preconditions.checkState(
        tx.getHash().equals(protoHash),
        "Transaction did not deserialize completely: %s vs %s",
        tx.getHash(),
        protoHash);
    Preconditions.checkState(
        !txMap.containsKey(txProto.getHash()),
        "Wallet contained duplicate transaction %s",
        byteStringToHash(txProto.getHash()));
    txMap.put(txProto.getHash(), tx);
  }
Ejemplo n.º 14
0
 @Test
 public void testExpiredPaymentRequest() throws Exception {
   MockPaymentSession paymentSession = new MockPaymentSession(newExpiredPaymentRequest());
   assertTrue(paymentSession.isExpired());
   // Send the payment and verify that an exception is thrown.
   // Add a dummy input to tx so it is considered valid.
   tx.addInput(new TransactionInput(params, tx, outputToMe.getScriptBytes()));
   ArrayList<Transaction> txns = new ArrayList<Transaction>();
   txns.add(tx);
   try {
     paymentSession.sendPayment(txns, null, null);
   } catch (PaymentRequestException.Expired e) {
     assertEquals(0, paymentSession.getPaymentLog().size());
     assertEquals(e.getMessage(), "PaymentRequest is expired");
     return;
   }
   fail("Expected exception due to expired PaymentRequest");
 }
  @Test
  public void skipScripts() throws Exception {
    store = createStore(params, 10);
    chain = new FullPrunedBlockChain(params, store);

    // Check that we aren't accidentally leaving any references
    // to the full StoredUndoableBlock's lying around (ie memory leaks)

    ECKey outKey = new ECKey();
    int height = 1;

    // Build some blocks on genesis block to create a spendable output
    Block rollingBlock =
        params
            .getGenesisBlock()
            .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
    chain.add(rollingBlock);
    TransactionOutput spendableOutput = rollingBlock.getTransactions().get(0).getOutput(0);
    for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
      rollingBlock =
          rollingBlock.createNextBlockWithCoinbase(
              Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
      chain.add(rollingBlock);
    }

    rollingBlock = rollingBlock.createNextBlock(null);
    Transaction t = new Transaction(params);
    t.addOutput(new TransactionOutput(params, t, FIFTY_COINS, new byte[] {}));
    TransactionInput input = t.addInput(spendableOutput);
    // Invalid script.
    input.clearScriptBytes();
    rollingBlock.addTransaction(t);
    rollingBlock.solve();
    chain.setRunScripts(false);
    try {
      chain.add(rollingBlock);
    } catch (VerificationException e) {
      fail();
    }
    try {
      store.close();
    } catch (Exception e) {
    }
  }
Ejemplo n.º 16
0
  /**
   * Returns a solved block that builds on top of this one. This exists for unit tests. In this
   * variant you can specify a public key (pubkey) for use in generating coinbase blocks.
   */
  Block createNextBlock(
      Address to, TransactionOutPoint prevOut, long time, byte[] pubKey, BigInteger coinbaseValue) {
    Block b = new Block(params);
    b.setDifficultyTarget(difficultyTarget);
    b.addCoinbaseTransaction(pubKey, coinbaseValue);

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

    b.setPrevBlockHash(getHash());
    // Don't let timestamp go backwards
    if (getTimeSeconds() >= time) b.setTime(getTimeSeconds() + 1);
    else b.setTime(time);
    b.solve();
    try {
      b.verifyHeader();
    } catch (VerificationException e) {
      throw new RuntimeException(e); // Cannot happen.
    }
    return b;
  }
Ejemplo n.º 17
0
 /** Adds a coinbase transaction to the block. This exists for unit tests. */
 void addCoinbaseTransaction(byte[] pubKeyTo, BigInteger value) {
   unCacheTransactions();
   transactions = new ArrayList<Transaction>();
   Transaction coinbase = new Transaction(params);
   // A real coinbase transaction has some stuff in the scriptSig like the extraNonce and
   // difficulty. The
   // transactions are distinguished by every TX output going to a different key.
   //
   // Here we will do things a bit differently so a new address isn't needed every time. We'll put
   // a simple
   // counter in the scriptSig so every transaction has a different hash.
   coinbase.addInput(
       new TransactionInput(params, coinbase, new byte[] {(byte) txCounter++, (byte) 1}));
   coinbase.addOutput(
       new TransactionOutput(params, coinbase, value, Script.createOutputScript(pubKeyTo)));
   transactions.add(coinbase);
   coinbase.setParent(this);
   coinbase.length = coinbase.litecoinSerialize().length;
   adjustLength(transactions.size(), coinbase.length);
 }
Ejemplo n.º 18
0
  public void testBlock(byte[] blockBytes, boolean isChild, boolean lazy, boolean retain)
      throws Exception {
    // reference serializer to produce comparison serialization output after changes to
    // message structure.
    BitcoinSerializer bsRef = new BitcoinSerializer(unitTestParams, false, false);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    BitcoinSerializer bs = new BitcoinSerializer(unitTestParams, lazy, retain);
    Block b1;
    Block bRef;
    b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes));
    bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes));

    // verify our reference BitcoinSerializer produces matching byte array.
    bos.reset();
    bsRef.serialize(bRef, bos);
    assertTrue(Arrays.equals(bos.toByteArray(), blockBytes));

    // check lazy and retain status survive both before and after a serialization
    assertEquals(!lazy, b1.isParsedTransactions());
    assertEquals(!lazy, b1.isParsedHeader());
    if (b1.isParsedHeader()) assertEquals(retain, b1.isHeaderBytesValid());
    if (b1.isParsedTransactions()) assertEquals(retain, b1.isTransactionBytesValid());

    serDeser(bs, b1, blockBytes, null, null);

    assertEquals(!lazy, b1.isParsedTransactions());
    assertEquals(!lazy, b1.isParsedHeader());
    if (b1.isParsedHeader()) assertEquals(retain, b1.isHeaderBytesValid());
    if (b1.isParsedTransactions()) assertEquals(retain, b1.isTransactionBytesValid());

    // compare to ref block
    bos.reset();
    bsRef.serialize(bRef, bos);
    serDeser(bs, b1, bos.toByteArray(), null, null);

    // retrieve a value from a child
    b1.getTransactions();
    assertTrue(b1.isParsedTransactions());
    if (b1.getTransactions().size() > 0) {
      assertTrue(b1.isParsedTransactions());
      Transaction tx1 = b1.getTransactions().get(0);

      // this will always be true for all children of a block once they are retrieved.
      // the tx child inputs/outputs may not be parsed however.

      // no longer forced to parse if length not provided.
      // assertEquals(true, tx1.isParsed());
      if (tx1.isParsed()) assertEquals(retain, tx1.isCached());
      else assertTrue(tx1.isCached());

      // does it still match ref block?
      serDeser(bs, b1, bos.toByteArray(), null, null);
    }

    // refresh block
    b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes));
    bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes));

    // retrieve a value from header
    b1.getDifficultyTarget();
    assertTrue(b1.isParsedHeader());
    assertEquals(lazy, !b1.isParsedTransactions());

    // does it still match ref block?
    serDeser(bs, b1, bos.toByteArray(), null, null);

    // refresh block
    b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes));
    bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes));

    // retrieve a value from a child and header
    b1.getDifficultyTarget();
    assertTrue(b1.isParsedHeader());
    assertEquals(lazy, !b1.isParsedTransactions());

    b1.getTransactions();
    assertTrue(b1.isParsedTransactions());
    if (b1.getTransactions().size() > 0) {
      assertTrue(b1.isParsedTransactions());
      Transaction tx1 = b1.getTransactions().get(0);

      // no longer forced to parse if length not provided.
      // assertEquals(true, tx1.isParsed());

      if (tx1.isParsed()) assertEquals(retain, tx1.isCached());
      else assertTrue(tx1.isCached());
    }
    // does it still match ref block?
    serDeser(bs, b1, bos.toByteArray(), null, null);

    // refresh block
    b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes));
    bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes));

    // change a value in header
    b1.setNonce(23);
    bRef.setNonce(23);
    assertTrue(b1.isParsedHeader());
    assertEquals(lazy, !b1.isParsedTransactions());
    assertFalse(b1.isHeaderBytesValid());
    if (b1.isParsedTransactions()) assertEquals(retain, b1.isTransactionBytesValid());
    else assertEquals(true, b1.isTransactionBytesValid());
    // does it still match ref block?
    bos.reset();
    bsRef.serialize(bRef, bos);
    serDeser(bs, b1, bos.toByteArray(), null, null);

    // refresh block
    b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes));
    bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes));

    // retrieve a value from a child of a child
    b1.getTransactions();
    if (b1.getTransactions().size() > 0) {
      Transaction tx1 = b1.getTransactions().get(0);

      TransactionInput tin = tx1.getInputs().get(0);

      assertTrue(tx1.isParsed());
      assertTrue(b1.isParsedTransactions());
      assertEquals(!lazy, b1.isParsedHeader());

      assertEquals(!lazy, tin.isParsed());
      assertEquals(tin.isParsed() ? retain : true, tin.isCached());

      // does it still match ref tx?
      bos.reset();
      bsRef.serialize(bRef, bos);
      serDeser(bs, b1, bos.toByteArray(), null, null);
    }

    // refresh block
    b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes));
    bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes));

    // add an input
    b1.getTransactions();
    if (b1.getTransactions().size() > 0) {
      Transaction tx1 = b1.getTransactions().get(0);

      if (tx1.getInputs().size() > 0) {
        tx1.addInput(tx1.getInputs().get(0));
        // replicate on reference tx
        bRef.getTransactions().get(0).addInput(bRef.getTransactions().get(0).getInputs().get(0));

        assertFalse(tx1.isCached());
        assertTrue(tx1.isParsed());
        assertFalse(b1.isTransactionBytesValid());
        assertTrue(b1.isParsedHeader());

        // confirm sibling cache status was unaffected
        if (tx1.getInputs().size() > 1) {
          boolean parsed = tx1.getInputs().get(1).isParsed();
          assertEquals(parsed ? retain : true, tx1.getInputs().get(1).isCached());
          assertEquals(!lazy, parsed);
        }

        // this has to be false. Altering a tx invalidates the merkle root.
        // when we have seperate merkle caching then the entire header won't need to be
        // invalidated.
        assertFalse(b1.isHeaderBytesValid());

        bos.reset();
        bsRef.serialize(bRef, bos);
        byte[] source = bos.toByteArray();
        // confirm we still match the reference tx.
        serDeser(bs, b1, source, null, null);
      }

      // does it still match ref tx?
      bos.reset();
      bsRef.serialize(bRef, bos);
      serDeser(bs, b1, bos.toByteArray(), null, null);
    }

    // refresh block
    b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes));
    Block b2 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes));
    bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes));
    Block bRef2 = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes));

    // reparent an input
    b1.getTransactions();
    if (b1.getTransactions().size() > 0) {
      Transaction tx1 = b1.getTransactions().get(0);
      Transaction tx2 = b2.getTransactions().get(0);

      if (tx1.getInputs().size() > 0) {
        TransactionInput fromTx1 = tx1.getInputs().get(0);
        tx2.addInput(fromTx1);

        // replicate on reference tx
        TransactionInput fromTxRef = bRef.getTransactions().get(0).getInputs().get(0);
        bRef2.getTransactions().get(0).addInput(fromTxRef);

        // b1 hasn't changed but it's no longer in the parent
        // chain of fromTx1 so has to have been uncached since it won't be
        // notified of changes throught the parent chain anymore.
        assertFalse(b1.isTransactionBytesValid());

        // b2 should have it's cache invalidated because it has changed.
        assertFalse(b2.isTransactionBytesValid());

        bos.reset();
        bsRef.serialize(bRef2, bos);
        byte[] source = bos.toByteArray();
        // confirm altered block matches altered ref block.
        serDeser(bs, b2, source, null, null);
      }

      // does unaltered block still match ref block?
      bos.reset();
      bsRef.serialize(bRef, bos);
      serDeser(bs, b1, bos.toByteArray(), null, null);

      // how about if we refresh it?
      bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes));
      bos.reset();
      bsRef.serialize(bRef, bos);
      serDeser(bs, b1, bos.toByteArray(), null, null);
    }
  }
Ejemplo n.º 19
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));
  }
  /**
   * 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.
    }
  }
  private void readTransaction(Protos.Transaction txProto, NetworkParameters params)
      throws UnreadableWalletException {
    Transaction tx = new Transaction(params);
    if (txProto.hasUpdatedAt()) {
      tx.setUpdateTime(new Date(txProto.getUpdatedAt()));
    }

    for (Protos.TransactionOutput outputProto : txProto.getTransactionOutputList()) {
      Coin value = Coin.valueOf(outputProto.getValue());
      byte[] scriptBytes = outputProto.getScriptBytes().toByteArray();
      TransactionOutput output = new TransactionOutput(params, tx, value, scriptBytes);
      tx.addOutput(output);
    }

    for (Protos.TransactionInput inputProto : txProto.getTransactionInputList()) {
      byte[] scriptBytes = inputProto.getScriptBytes().toByteArray();
      TransactionOutPoint outpoint =
          new TransactionOutPoint(
              params,
              inputProto.getTransactionOutPointIndex() & 0xFFFFFFFFL,
              byteStringToHash(inputProto.getTransactionOutPointHash()));
      Coin value = inputProto.hasValue() ? Coin.valueOf(inputProto.getValue()) : null;
      TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint, value);
      if (inputProto.hasSequence()) {
        input.setSequenceNumber(inputProto.getSequence());
      }
      tx.addInput(input);
    }

    for (int i = 0; i < txProto.getBlockHashCount(); i++) {
      ByteString blockHash = txProto.getBlockHash(i);
      int relativityOffset = 0;
      if (txProto.getBlockRelativityOffsetsCount() > 0)
        relativityOffset = txProto.getBlockRelativityOffsets(i);
      tx.addBlockAppearance(byteStringToHash(blockHash), relativityOffset);
    }

    if (txProto.hasLockTime()) {
      tx.setLockTime(0xffffffffL & txProto.getLockTime());
    }

    if (txProto.hasPurpose()) {
      switch (txProto.getPurpose()) {
        case UNKNOWN:
          tx.setPurpose(Transaction.Purpose.UNKNOWN);
          break;
        case USER_PAYMENT:
          tx.setPurpose(Transaction.Purpose.USER_PAYMENT);
          break;
        case KEY_ROTATION:
          tx.setPurpose(Transaction.Purpose.KEY_ROTATION);
          break;
        case ASSURANCE_CONTRACT_CLAIM:
          tx.setPurpose(Transaction.Purpose.ASSURANCE_CONTRACT_CLAIM);
          break;
        case ASSURANCE_CONTRACT_PLEDGE:
          tx.setPurpose(Transaction.Purpose.ASSURANCE_CONTRACT_PLEDGE);
          break;
        case ASSURANCE_CONTRACT_STUB:
          tx.setPurpose(Transaction.Purpose.ASSURANCE_CONTRACT_STUB);
          break;
        default:
          throw new RuntimeException("New purpose serialization not implemented");
      }
    } else {
      // Old wallet: assume a user payment as that's the only reason a new tx would have been
      // created back then.
      tx.setPurpose(Transaction.Purpose.USER_PAYMENT);
    }

    if (txProto.hasExchangeRate()) {
      Protos.ExchangeRate exchangeRateProto = txProto.getExchangeRate();
      tx.setExchangeRate(
          new ExchangeRate(
              Coin.valueOf(exchangeRateProto.getCoinValue()),
              Fiat.valueOf(
                  exchangeRateProto.getFiatCurrencyCode(), exchangeRateProto.getFiatValue())));
    }

    if (txProto.hasMemo()) tx.setMemo(txProto.getMemo());

    // Peercoin: Include time
    tx.setTime(txProto.getTime());

    // Transaction should now be complete.
    Sha256Hash protoHash = byteStringToHash(txProto.getHash());
    if (!tx.getHash().equals(protoHash))
      throw new UnreadableWalletException(
          String.format(
              "Transaction did not deserialize completely: %s vs %s", tx.getHash(), protoHash));
    if (txMap.containsKey(txProto.getHash()))
      throw new UnreadableWalletException(
          "Wallet contained duplicate transaction " + byteStringToHash(txProto.getHash()));
    txMap.put(txProto.getHash(), tx);
  }
Ejemplo n.º 22
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]);
 }
Ejemplo n.º 23
0
  public void testTransaction(
      NetworkParameters params, byte[] txBytes, boolean isChild, boolean lazy, boolean retain)
      throws Exception {

    // reference serializer to produce comparison serialization output after changes to
    // message structure.
    BitcoinSerializer bsRef = new BitcoinSerializer(params, false, false);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    BitcoinSerializer bs = new BitcoinSerializer(params, lazy, retain);
    Transaction t1;
    Transaction tRef;
    t1 = (Transaction) bs.deserialize(new ByteArrayInputStream(txBytes));
    tRef = (Transaction) bsRef.deserialize(new ByteArrayInputStream(txBytes));

    // verify our reference BitcoinSerializer produces matching byte array.
    bos.reset();
    bsRef.serialize(tRef, bos);
    assertTrue(Arrays.equals(bos.toByteArray(), txBytes));

    // check lazy and retain status survive both before and after a serialization
    assertEquals(!lazy, t1.isParsed());
    if (t1.isParsed()) assertEquals(retain, t1.isCached());

    serDeser(bs, t1, txBytes, null, null);

    assertEquals(lazy, !t1.isParsed());
    if (t1.isParsed()) assertEquals(retain, t1.isCached());

    // compare to ref tx
    bos.reset();
    bsRef.serialize(tRef, bos);
    serDeser(bs, t1, bos.toByteArray(), null, null);

    // retrieve a value from a child
    t1.getInputs();
    assertTrue(t1.isParsed());
    if (t1.getInputs().size() > 0) {
      assertTrue(t1.isParsed());
      TransactionInput tin = t1.getInputs().get(0);
      assertEquals(!lazy, tin.isParsed());
      if (tin.isParsed()) assertEquals(retain, tin.isCached());

      // does it still match ref tx?
      serDeser(bs, t1, bos.toByteArray(), null, null);
    }

    // refresh tx
    t1 = (Transaction) bs.deserialize(new ByteArrayInputStream(txBytes));
    tRef = (Transaction) bsRef.deserialize(new ByteArrayInputStream(txBytes));

    // add an input
    if (t1.getInputs().size() > 0) {

      t1.addInput(t1.getInputs().get(0));

      // replicate on reference tx
      tRef.addInput(tRef.getInputs().get(0));

      assertFalse(t1.isCached());
      assertTrue(t1.isParsed());

      bos.reset();
      bsRef.serialize(tRef, bos);
      byte[] source = bos.toByteArray();
      // confirm we still match the reference tx.
      serDeser(bs, t1, source, null, null);
    }
  }