@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) {
    }
  }
Example #2
0
  @Test
  public void dataDrivenInvalidTransactions() throws Exception {
    BufferedReader in =
        new BufferedReader(
            new InputStreamReader(
                getClass().getResourceAsStream("tx_invalid.json"), Charset.forName("UTF-8")));

    NetworkParameters params = TestNet3Params.get();

    // Poor man's (aka. really, really poor) JSON parser (because pulling in a lib for this is
    // probably overkill)
    List<JSONObject> tx = new ArrayList<JSONObject>(1);
    in.read(); // remove first [
    StringBuffer buffer = new StringBuffer(1000);
    while (in.ready()) {
      String line = in.readLine();
      if (line == null || line.equals("")) continue;
      buffer.append(line);
      if (line.equals("]") && buffer.toString().equals("]") && !in.ready()) break; // ignore last ]
      boolean isFinished = appendToList(tx, buffer);
      while (tx.size() > 0
          && tx.get(0).isList()
          && tx.get(0).list.size() == 1
          && tx.get(0).list.get(0).isString()) tx.remove(0);
      if (isFinished && tx.size() == 1 && tx.get(0).list.size() == 3) {
        HashMap<TransactionOutPoint, Script> scriptPubKeys =
            new HashMap<TransactionOutPoint, Script>();
        for (JSONObject input : tx.get(0).list.get(0).list) {
          String hash = input.list.get(0).string;
          int index = input.list.get(1).integer;
          String script = input.list.get(2).string;
          Sha256Hash sha256Hash =
              new Sha256Hash(Hex.decode(hash.getBytes(Charset.forName("UTF-8"))));
          scriptPubKeys.put(
              new TransactionOutPoint(params, index, sha256Hash), parseScriptString(script));
        }

        byte[] bytes = tx.get(0).list.get(1).string.getBytes(Charset.forName("UTF-8"));
        Transaction transaction = new Transaction(params, Hex.decode(bytes));
        boolean enforceP2SH = tx.get(0).list.get(2).booleanValue;
        assertTrue(tx.get(0).list.get(2).isBoolean());

        boolean valid = true;
        try {
          transaction.verify();
        } catch (VerificationException e) {
          valid = false;
        }

        // The reference client checks this case in CheckTransaction, but we leave it to
        // later where we will see an attempt to double-spend, so we explicitly check here
        HashSet<TransactionOutPoint> set = new HashSet<TransactionOutPoint>();
        for (TransactionInput input : transaction.getInputs()) {
          if (set.contains(input.getOutpoint())) valid = false;
          set.add(input.getOutpoint());
        }

        for (int i = 0; i < transaction.getInputs().size() && valid; i++) {
          TransactionInput input = transaction.getInputs().get(i);
          assertTrue(scriptPubKeys.containsKey(input.getOutpoint()));
          try {
            input
                .getScriptSig()
                .correctlySpends(
                    transaction, i, scriptPubKeys.get(input.getOutpoint()), enforceP2SH);
          } catch (VerificationException e) {
            valid = false;
          }
        }

        if (valid) fail();

        tx.clear();
      }
    }
    in.close();
  }
Example #3
0
  @Test
  public void dataDrivenValidTransactions() throws Exception {
    BufferedReader in =
        new BufferedReader(
            new InputStreamReader(
                getClass().getResourceAsStream("tx_valid.json"), Charset.forName("UTF-8")));

    NetworkParameters params = TestNet3Params.get();

    // Poor man's (aka. really, really poor) JSON parser (because pulling in a lib for this is
    // probably not overkill)
    int lineNum = -1;
    List<JSONObject> tx = new ArrayList<JSONObject>(3);
    in.read(); // remove first [
    StringBuffer buffer = new StringBuffer(1000);
    while (in.ready()) {
      lineNum++;
      String line = in.readLine();
      if (line == null || line.equals("")) continue;
      buffer.append(line);
      if (line.equals("]") && buffer.toString().equals("]") && !in.ready()) break;
      boolean isFinished = appendToList(tx, buffer);
      while (tx.size() > 0
          && tx.get(0).isList()
          && tx.get(0).list.size() == 1
          && tx.get(0).list.get(0).isString()) tx.remove(0); // ignore last ]
      if (isFinished && tx.size() == 1 && tx.get(0).list.size() == 3) {
        Transaction transaction = null;
        try {
          HashMap<TransactionOutPoint, Script> scriptPubKeys =
              new HashMap<TransactionOutPoint, Script>();
          for (JSONObject input : tx.get(0).list.get(0).list) {
            String hash = input.list.get(0).string;
            int index = input.list.get(1).integer;
            String script = input.list.get(2).string;
            Sha256Hash sha256Hash =
                new Sha256Hash(Hex.decode(hash.getBytes(Charset.forName("UTF-8"))));
            scriptPubKeys.put(
                new TransactionOutPoint(params, index, sha256Hash), parseScriptString(script));
          }

          byte[] bytes = tx.get(0).list.get(1).string.getBytes(Charset.forName("UTF-8"));
          transaction = new Transaction(params, Hex.decode(bytes));
          boolean enforceP2SH = tx.get(0).list.get(2).booleanValue;
          assertTrue(tx.get(0).list.get(2).isBoolean());

          transaction.verify();

          for (int i = 0; i < transaction.getInputs().size(); i++) {
            TransactionInput input = transaction.getInputs().get(i);
            if (input.getOutpoint().getIndex() == 0xffffffffL) input.getOutpoint().setIndex(-1);
            assertTrue(scriptPubKeys.containsKey(input.getOutpoint()));
            input
                .getScriptSig()
                .correctlySpends(
                    transaction, i, scriptPubKeys.get(input.getOutpoint()), enforceP2SH);
          }
          tx.clear();
        } catch (Exception e) {
          System.err.println("Exception processing line " + lineNum + ": " + line);
          if (transaction != null) System.err.println(transaction);
          throw e;
        }
      }
    }
    in.close();
  }
  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);
    }
  }
  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);
    }
  }