Beispiel #1
0
 public int getOptimalEncodingMessageSize() {
   if (optimalEncodingMessageSize != 0) return optimalEncodingMessageSize;
   maybeParseTransactions();
   if (optimalEncodingMessageSize != 0) return optimalEncodingMessageSize;
   optimalEncodingMessageSize = getMessageSize();
   return optimalEncodingMessageSize;
 }
Beispiel #2
0
 /**
  * In lazy parsing mode access to getters and setters may throw an unchecked LazyParseException.
  * If guaranteed safe access is required this method will force parsing to occur immediately thus
  * ensuring LazyParseExeption will never be thrown from this Message. If the Message contains
  * child messages (e.g. a Block containing Transaction messages) this will not force child
  * messages to parse.
  *
  * <p>This method ensures parsing of transactions only.
  *
  * @throws ProtocolException
  */
 public void ensureParsedTransactions() throws ProtocolException {
   try {
     maybeParseTransactions();
   } catch (LazyParseException e) {
     if (e.getCause() instanceof ProtocolException) throw (ProtocolException) e.getCause();
     throw new ProtocolException(e);
   }
 }
Beispiel #3
0
 private void unCacheTransactions() {
   maybeParseTransactions();
   transactionBytesValid = false;
   if (!headerBytesValid) bytes = null;
   // Current implementation has to uncache headers as well as any change to a tx will alter the
   // merkle root. In
   // future we can go more granular and cache merkle root separately so rest of the header does
   // not need to be
   // rewritten.
   unCacheHeader();
   // Clear merkleRoot last as it may end up being parsed during unCacheHeader().
   merkleRoot = null;
 }
Beispiel #4
0
 /**
  * Checks the block contents
  *
  * @throws VerificationException
  */
 public void verifyTransactions() throws VerificationException {
   // Now we need to check that the body of the block actually matches the headers. The network
   // won't generate
   // an invalid block, but if we didn't validate this then an untrusted man-in-the-middle could
   // obtain the next
   // valid block from the network and simply replace the transactions in it with their own
   // fictional
   // transactions that reference spent or non-existant inputs.
   if (transactions.isEmpty()) throw new VerificationException("Block had no transactions");
   maybeParseTransactions();
   if (this.getOptimalEncodingMessageSize() > MAX_BLOCK_SIZE)
     throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
   checkTransactions();
   checkMerkleRoot();
   checkSigOps();
   for (Transaction transaction : transactions) transaction.verify();
 }
Beispiel #5
0
 public List<Transaction> getTransactions() {
   maybeParseTransactions();
   return Collections.unmodifiableList(transactions);
 }
Beispiel #6
0
 private List<byte[]> buildMerkleTree() {
   // The Merkle root is based on a tree of hashes calculated from the transactions:
   //
   //     root
   //      / \
   //   A      B
   //  / \    / \
   // t1 t2 t3 t4
   //
   // The tree is represented as a list: t1,t2,t3,t4,A,B,root where each
   // entry is a hash.
   //
   // The hashing algorithm is double SHA-256. The leaves are a hash of the serialized contents of
   // the transaction.
   // The interior nodes are hashes of the concenation of the two child hashes.
   //
   // This structure allows the creation of proof that a transaction was included into a block
   // without having to
   // provide the full block contents. Instead, you can provide only a Merkle branch. For example
   // to prove tx2 was
   // in a block you can just provide tx2, the hash(tx1) and B. Now the other party has everything
   // they need to
   // derive the root, which can be checked against the block header. These proofs aren't used
   // right now but
   // will be helpful later when we want to download partial block contents.
   //
   // Note that if the number of transactions is not even the last tx is repeated to make it so
   // (see
   // tx3 above). A tree with 5 transactions would look like this:
   //
   //         root
   //        /     \
   //       1        5
   //     /   \     / \
   //    2     3    4  4
   //  / \   / \   / \
   // t1 t2 t3 t4 t5 t5
   maybeParseTransactions();
   ArrayList<byte[]> tree = new ArrayList<byte[]>();
   // Start by adding all the hashes of the transactions as leaves of the tree.
   for (Transaction t : transactions) {
     tree.add(t.getHash().getBytes());
   }
   int levelOffset = 0; // Offset in the list where the currently processed level starts.
   // Step through each level, stopping when we reach the root (levelSize == 1).
   for (int levelSize = transactions.size(); levelSize > 1; levelSize = (levelSize + 1) / 2) {
     // For each pair of nodes on that level:
     for (int left = 0; left < levelSize; left += 2) {
       // The right hand node can be the same as the left hand, in the case where we don't have
       // enough
       // transactions.
       int right = Math.min(left + 1, levelSize - 1);
       byte[] leftBytes = Utils.reverseBytes(tree.get(levelOffset + left));
       byte[] rightBytes = Utils.reverseBytes(tree.get(levelOffset + right));
       tree.add(Utils.reverseBytes(doubleDigestTwoBuffers(leftBytes, 0, 32, rightBytes, 0, 32)));
     }
     // Move to the next level.
     levelOffset += levelSize;
   }
   return tree;
 }