diff --git a/core/src/main/java/org/bitcoinj/core/PeerAddress.java b/core/src/main/java/org/bitcoinj/core/PeerAddress.java index 35f9ffa3..48cd1a13 100644 --- a/core/src/main/java/org/bitcoinj/core/PeerAddress.java +++ b/core/src/main/java/org/bitcoinj/core/PeerAddress.java @@ -28,8 +28,6 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; -import static org.bitcoinj.core.Utils.uint32ToByteStreamLE; -import static org.bitcoinj.core.Utils.uint64ToByteStreamLE; import static com.google.common.base.Preconditions.checkNotNull; /** @@ -126,9 +124,9 @@ public class PeerAddress extends ChildMessage { //so assumes itself to be up. For a fuller implementation this needs to be dynamic only if //the address refers to this client. int secs = (int) (Utils.currentTimeSeconds()); - uint32ToByteStreamLE(secs, stream); + Utils.uint32ToByteStreamLE(secs, stream); } - uint64ToByteStreamLE(services, stream); // nServices. + Utils.uint64ToByteStreamLE(services, stream); // nServices. // Java does not provide any utility to map an IPv4 address into IPv6 space, so we have to do it by hand. byte[] ipBytes = addr.getAddress(); if (ipBytes.length == 4) { @@ -140,8 +138,7 @@ public class PeerAddress extends ChildMessage { } stream.write(ipBytes); // And write out the port. Unlike the rest of the protocol, address and port is in big endian byte order. - stream.write((byte) (0xFF & port >> 8)); - stream.write((byte) (0xFF & port)); + Utils.uint16ToByteStreamBE(port, stream); } @Override @@ -162,7 +159,8 @@ public class PeerAddress extends ChildMessage { } catch (UnknownHostException e) { throw new RuntimeException(e); // Cannot happen. } - port = ((0xFF & payload[cursor++]) << 8) | (0xFF & payload[cursor++]); + port = Utils.readUint16BE(payload, cursor); + cursor += 2; // The 4 byte difference is the uint32 timestamp that was introduced in version 31402 length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4; } diff --git a/core/src/main/java/org/bitcoinj/core/TransactionOutputChanges.java b/core/src/main/java/org/bitcoinj/core/TransactionOutputChanges.java index f17bc68b..431dedcd 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionOutputChanges.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionOutputChanges.java @@ -38,18 +38,12 @@ public class TransactionOutputChanges { } public TransactionOutputChanges(InputStream in) throws IOException { - int numOutsCreated = (in.read() & 0xFF) | - ((in.read() & 0xFF) << 8) | - ((in.read() & 0xFF) << 16) | - ((in.read() & 0xFF) << 24); + int numOutsCreated = (int) Utils.readUint32FromStream(in); txOutsCreated = new LinkedList<>(); for (int i = 0; i < numOutsCreated; i++) txOutsCreated.add(new UTXO(in)); - int numOutsSpent = (in.read() & 0xFF) | - ((in.read() & 0xFF) << 8) | - ((in.read() & 0xFF) << 16) | - ((in.read() & 0xFF) << 24); + int numOutsSpent = (int) Utils.readUint32FromStream(in); txOutsSpent = new LinkedList<>(); for (int i = 0; i < numOutsSpent; i++) txOutsSpent.add(new UTXO(in)); @@ -57,19 +51,13 @@ public class TransactionOutputChanges { public void serializeToStream(OutputStream bos) throws IOException { int numOutsCreated = txOutsCreated.size(); - bos.write(0xFF & numOutsCreated); - bos.write(0xFF & (numOutsCreated >> 8)); - bos.write(0xFF & (numOutsCreated >> 16)); - bos.write(0xFF & (numOutsCreated >> 24)); + Utils.uint32ToByteStreamLE(numOutsCreated, bos); for (UTXO output : txOutsCreated) { output.serializeToStream(bos); } int numOutsSpent = txOutsSpent.size(); - bos.write(0xFF & numOutsSpent); - bos.write(0xFF & (numOutsSpent >> 8)); - bos.write(0xFF & (numOutsSpent >> 16)); - bos.write(0xFF & (numOutsSpent >> 24)); + Utils.uint32ToByteStreamLE(numOutsSpent, bos); for (UTXO output : txOutsSpent) { output.serializeToStream(bos); } diff --git a/core/src/main/java/org/bitcoinj/core/UTXO.java b/core/src/main/java/org/bitcoinj/core/UTXO.java index 8aa15113..292854d4 100644 --- a/core/src/main/java/org/bitcoinj/core/UTXO.java +++ b/core/src/main/java/org/bitcoinj/core/UTXO.java @@ -146,35 +146,22 @@ public class UTXO implements Serializable { public void serializeToStream(OutputStream bos) throws IOException { Utils.uint64ToByteStreamLE(BigInteger.valueOf(value.value), bos); - byte[] scriptBytes = script.getProgram(); - bos.write(0xFF & scriptBytes.length); - bos.write(0xFF & scriptBytes.length >> 8); - bos.write(0xFF & (scriptBytes.length >> 16)); - bos.write(0xFF & (scriptBytes.length >> 24)); + Utils.uint32ToByteStreamLE(scriptBytes.length, bos); bos.write(scriptBytes); - bos.write(hash.getBytes()); Utils.uint32ToByteStreamLE(index, bos); - - bos.write(0xFF & (height)); - bos.write(0xFF & (height >> 8)); - bos.write(0xFF & (height >> 16)); - bos.write(0xFF & (height >> 24)); - + Utils.uint32ToByteStreamLE(height, bos); bos.write(new byte[] { (byte)(coinbase ? 1 : 0) }); } - + public void deserializeFromStream(InputStream in) throws IOException { byte[] valueBytes = new byte[8]; if (in.read(valueBytes, 0, 8) != 8) throw new EOFException(); value = Coin.valueOf(Utils.readInt64(valueBytes, 0)); - int scriptBytesLength = ((in.read() & 0xFF)) | - ((in.read() & 0xFF) << 8) | - ((in.read() & 0xFF) << 16) | - ((in.read() & 0xFF) << 24); + int scriptBytesLength = (int) Utils.readUint32FromStream(in); byte[] scriptBytes = new byte[scriptBytesLength]; if (in.read(scriptBytes) != scriptBytesLength) throw new EOFException(); @@ -190,10 +177,7 @@ public class UTXO implements Serializable { throw new EOFException(); index = Utils.readUint32(indexBytes, 0); - height = ((in.read() & 0xFF)) | - ((in.read() & 0xFF) << 8) | - ((in.read() & 0xFF) << 16) | - ((in.read() & 0xFF) << 24); + height = (int) Utils.readUint32FromStream(in); byte[] coinbaseByte = new byte[1]; in.read(coinbaseByte); diff --git a/core/src/main/java/org/bitcoinj/core/Utils.java b/core/src/main/java/org/bitcoinj/core/Utils.java index b75a4342..dab17405 100644 --- a/core/src/main/java/org/bitcoinj/core/Utils.java +++ b/core/src/main/java/org/bitcoinj/core/Utils.java @@ -18,6 +18,7 @@ package org.bitcoinj.core; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; import java.text.DateFormat; @@ -88,12 +89,10 @@ public class Utils { return dest; } - /** Write 4 bytes to the byte array (starting at the offset) as unsigned 32-bit integer in big endian format. */ - public static void uint32ToByteArrayBE(long val, byte[] out, int offset) { - out[offset] = (byte) (0xFF & (val >> 24)); - out[offset + 1] = (byte) (0xFF & (val >> 16)); - out[offset + 2] = (byte) (0xFF & (val >> 8)); - out[offset + 3] = (byte) (0xFF & val); + /** Write 2 bytes to the byte array (starting at the offset) as unsigned 16-bit integer in little endian format. */ + public static void uint16ToByteArrayLE(int val, byte[] out, int offset) { + out[offset] = (byte) (0xFF & val); + out[offset + 1] = (byte) (0xFF & (val >> 8)); } /** Write 4 bytes to the byte array (starting at the offset) as unsigned 32-bit integer in little endian format. */ @@ -104,6 +103,14 @@ public class Utils { out[offset + 3] = (byte) (0xFF & (val >> 24)); } + /** Write 4 bytes to the byte array (starting at the offset) as unsigned 32-bit integer in big endian format. */ + public static void uint32ToByteArrayBE(long val, byte[] out, int offset) { + out[offset] = (byte) (0xFF & (val >> 24)); + out[offset + 1] = (byte) (0xFF & (val >> 16)); + out[offset + 2] = (byte) (0xFF & (val >> 8)); + out[offset + 3] = (byte) (0xFF & val); + } + /** Write 8 bytes to the byte array (starting at the offset) as signed 64-bit integer in little endian format. */ public static void int64ToByteArrayLE(long val, byte[] out, int offset) { out[offset] = (byte) (0xFF & val); @@ -116,6 +123,18 @@ public class Utils { out[offset + 7] = (byte) (0xFF & (val >> 56)); } + /** Write 2 bytes to the output stream as unsigned 16-bit integer in little endian format. */ + public static void uint16ToByteStreamLE(int val, OutputStream stream) throws IOException { + stream.write((int) (0xFF & val)); + stream.write((int) (0xFF & (val >> 8))); + } + + /** Write 2 bytes to the output stream as unsigned 16-bit integer in big endian format. */ + public static void uint16ToByteStreamBE(int val, OutputStream stream) throws IOException { + stream.write((int) (0xFF & (val >> 8))); + stream.write((int) (0xFF & val)); + } + /** Write 4 bytes to the output stream as unsigned 32-bit integer in little endian format. */ public static void uint32ToByteStreamLE(long val, OutputStream stream) throws IOException { stream.write((int) (0xFF & val)); @@ -123,7 +142,15 @@ public class Utils { stream.write((int) (0xFF & (val >> 16))); stream.write((int) (0xFF & (val >> 24))); } - + + /** Write 4 bytes to the output stream as unsigned 32-bit integer in big endian format. */ + public static void uint32ToByteStreamBE(long val, OutputStream stream) throws IOException { + stream.write((int) (0xFF & (val >> 24))); + stream.write((int) (0xFF & (val >> 16))); + stream.write((int) (0xFF & (val >> 8))); + stream.write((int) (0xFF & val)); + } + /** Write 8 bytes to the output stream as signed 64-bit integer in little endian format. */ public static void int64ToByteStreamLE(long val, OutputStream stream) throws IOException { stream.write((int) (0xFF & val)); @@ -150,6 +177,12 @@ public class Utils { } } + /** Parse 2 bytes from the byte array (starting at the offset) as unsigned 16-bit integer in little endian format. */ + public static int readUint16(byte[] bytes, int offset) { + return (bytes[offset] & 0xff) | + ((bytes[offset + 1] & 0xff) << 8); + } + /** Parse 4 bytes from the byte array (starting at the offset) as unsigned 32-bit integer in little endian format. */ public static long readUint32(byte[] bytes, int offset) { return (bytes[offset] & 0xffl) | @@ -184,6 +217,28 @@ public class Utils { (bytes[offset + 1] & 0xff); } + /** Parse 2 bytes from the stream as unsigned 16-bit integer in little endian format. */ + public static int readUint16FromStream(InputStream is) { + try { + return (is.read() & 0xff) | + ((is.read() & 0xff) << 8); + } catch (IOException x) { + throw new RuntimeException(x); + } + } + + /** Parse 4 bytes from the stream as unsigned 32-bit integer in little endian format. */ + public static long readUint32FromStream(InputStream is) { + try { + return (is.read() & 0xffl) | + ((is.read() & 0xffl) << 8) | + ((is.read() & 0xffl) << 16) | + ((is.read() & 0xffl) << 24); + } catch (IOException x) { + throw new RuntimeException(x); + } + } + /** * Returns a copy of the given byte array in reverse order. */ diff --git a/core/src/main/java/org/bitcoinj/core/VarInt.java b/core/src/main/java/org/bitcoinj/core/VarInt.java index b990c237..05759267 100644 --- a/core/src/main/java/org/bitcoinj/core/VarInt.java +++ b/core/src/main/java/org/bitcoinj/core/VarInt.java @@ -45,7 +45,7 @@ public class VarInt { value = first; originallyEncodedSize = 1; // 1 data byte (8 bits) } else if (first == 253) { - value = (0xFF & buf[offset + 1]) | ((0xFF & buf[offset + 2]) << 8); + value = Utils.readUint16(buf, offset + 1); originallyEncodedSize = 3; // 1 marker + 2 data bytes (16 bits) } else if (first == 254) { value = Utils.readUint32(buf, offset + 1); @@ -96,7 +96,10 @@ public class VarInt { case 1: return new byte[]{(byte) value}; case 3: - return new byte[]{(byte) 253, (byte) (value), (byte) (value >> 8)}; + bytes = new byte[3]; + bytes[0] = (byte) 253; + Utils.uint16ToByteArrayLE((int) value, bytes, 1); + return bytes; case 5: bytes = new byte[5]; bytes[0] = (byte) 254; diff --git a/core/src/main/java/org/bitcoinj/net/discovery/SeedPeers.java b/core/src/main/java/org/bitcoinj/net/discovery/SeedPeers.java index b44b4ee7..a0b903fe 100644 --- a/core/src/main/java/org/bitcoinj/net/discovery/SeedPeers.java +++ b/core/src/main/java/org/bitcoinj/net/discovery/SeedPeers.java @@ -17,6 +17,7 @@ package org.bitcoinj.net.discovery; import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.Utils; import javax.annotation.Nullable; import java.net.InetAddress; @@ -104,10 +105,7 @@ public class SeedPeers implements PeerDiscovery { private InetAddress convertAddress(int seed) throws UnknownHostException { byte[] v4addr = new byte[4]; - v4addr[0] = (byte) (0xFF & (seed)); - v4addr[1] = (byte) (0xFF & (seed >> 8)); - v4addr[2] = (byte) (0xFF & (seed >> 16)); - v4addr[3] = (byte) (0xFF & (seed >> 24)); + Utils.uint32ToByteArrayLE(seed, v4addr, 0); return InetAddress.getByAddress(v4addr); } diff --git a/core/src/main/java/org/bitcoinj/script/Script.java b/core/src/main/java/org/bitcoinj/script/Script.java index b1d10dd1..aa8742f7 100644 --- a/core/src/main/java/org/bitcoinj/script/Script.java +++ b/core/src/main/java/org/bitcoinj/script/Script.java @@ -200,12 +200,12 @@ public class Script { } else if (opcode == OP_PUSHDATA2) { // Read a short, then read that many bytes of data. if (bis.available() < 2) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script"); - dataToRead = bis.read() | (bis.read() << 8); + dataToRead = Utils.readUint16FromStream(bis); } else if (opcode == OP_PUSHDATA4) { // Read a uint32, then read that many bytes of data. // Though this is allowed, because its value cannot be > 520, it should never actually be used if (bis.available() < 4) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script"); - dataToRead = ((long)bis.read()) | (((long)bis.read()) << 8) | (((long)bis.read()) << 16) | (((long)bis.read()) << 24); + dataToRead = Utils.readUint32FromStream(bis); } ScriptChunk chunk; @@ -384,8 +384,7 @@ public class Script { os.write(buf); } else if (buf.length < 65536) { os.write(OP_PUSHDATA2); - os.write(0xFF & (buf.length)); - os.write(0xFF & (buf.length >> 8)); + Utils.uint16ToByteStreamLE(buf.length, os); os.write(buf); } else { throw new RuntimeException("Unimplemented"); @@ -722,13 +721,9 @@ public class Script { } else if (opcode == OP_PUSHDATA1) { additionalBytes = (0xFF & inputScript[cursor]) + 1; } else if (opcode == OP_PUSHDATA2) { - additionalBytes = ((0xFF & inputScript[cursor]) | - ((0xFF & inputScript[cursor+1]) << 8)) + 2; + additionalBytes = Utils.readUint16(inputScript, cursor) + 2; } else if (opcode == OP_PUSHDATA4) { - additionalBytes = ((0xFF & inputScript[cursor]) | - ((0xFF & inputScript[cursor+1]) << 8) | - ((0xFF & inputScript[cursor+1]) << 16) | - ((0xFF & inputScript[cursor+1]) << 24)) + 4; + additionalBytes = (int) Utils.readUint32(inputScript, cursor) + 4; } if (!skip) { try { diff --git a/core/src/main/java/org/bitcoinj/script/ScriptChunk.java b/core/src/main/java/org/bitcoinj/script/ScriptChunk.java index 9c21ca73..a8b2d298 100644 --- a/core/src/main/java/org/bitcoinj/script/ScriptChunk.java +++ b/core/src/main/java/org/bitcoinj/script/ScriptChunk.java @@ -125,8 +125,7 @@ public class ScriptChunk { } else if (opcode == OP_PUSHDATA2) { checkState(data.length <= 0xFFFF); stream.write(OP_PUSHDATA2); - stream.write(0xFF & data.length); - stream.write(0xFF & (data.length >> 8)); + Utils.uint16ToByteStreamLE(data.length, stream); } else if (opcode == OP_PUSHDATA4) { checkState(data.length <= Script.MAX_SCRIPT_ELEMENT_SIZE); stream.write(OP_PUSHDATA4); diff --git a/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java index 6a2f1376..fc914122 100644 --- a/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java @@ -668,10 +668,7 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto txOutChanges = bos.toByteArray(); } else { int numTxn = undoableBlock.getTransactions().size(); - bos.write(0xFF & numTxn); - bos.write(0xFF & (numTxn >> 8)); - bos.write(0xFF & (numTxn >> 16)); - bos.write(0xFF & (numTxn >> 24)); + Utils.uint32ToByteStreamLE(numTxn, bos); for (Transaction tx : undoableBlock.getTransactions()) tx.bitcoinSerialize(bos); transactions = bos.toByteArray(); @@ -805,11 +802,8 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto byte[] transactions = results.getBytes(2); StoredUndoableBlock block; if (txOutChanges == null) { - int offset = 0; - int numTxn = ((transactions[offset++] & 0xFF)) | - ((transactions[offset++] & 0xFF) << 8) | - ((transactions[offset++] & 0xFF) << 16) | - ((transactions[offset++] & 0xFF) << 24); + int numTxn = (int) Utils.readUint32(transactions, 0); + int offset = 4; List transactionList = new LinkedList<>(); for (int i = 0; i < numTxn; i++) { Transaction tx = params.getDefaultSerializer().makeTransaction(transactions, offset); diff --git a/core/src/main/java/org/bitcoinj/store/LevelDBFullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/LevelDBFullPrunedBlockStore.java index 6e954f71..ac647fa6 100644 --- a/core/src/main/java/org/bitcoinj/store/LevelDBFullPrunedBlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/LevelDBFullPrunedBlockStore.java @@ -38,6 +38,7 @@ import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionOutputChanges; import org.bitcoinj.core.UTXO; import org.bitcoinj.core.UTXOProviderException; +import org.bitcoinj.core.Utils; import org.bitcoinj.core.VerificationException; import org.bitcoinj.script.Script; import org.bitcoinj.script.ScriptException; @@ -513,10 +514,7 @@ public class LevelDBFullPrunedBlockStore implements FullPrunedBlockStore { txOutChanges = bos.toByteArray(); } else { int numTxn = undoableBlock.getTransactions().size(); - bos.write((int) (0xFF & (numTxn >> 0))); - bos.write((int) (0xFF & (numTxn >> 8))); - bos.write((int) (0xFF & (numTxn >> 16))); - bos.write((int) (0xFF & (numTxn >> 24))); + Utils.uint32ToByteStreamLE(numTxn, bos); for (Transaction tx : undoableBlock.getTransactions()) tx.bitcoinSerialize(bos); transactions = bos.toByteArray(); @@ -671,9 +669,8 @@ public class LevelDBFullPrunedBlockStore implements FullPrunedBlockStore { int txSize = bb.getInt(); byte[] transactions = new byte[txSize]; bb.get(transactions); - int offset = 0; - int numTxn = ((transactions[offset++] & 0xFF) << 0) | ((transactions[offset++] & 0xFF) << 8) - | ((transactions[offset++] & 0xFF) << 16) | ((transactions[offset++] & 0xFF) << 24); + int numTxn = (int) Utils.readUint32(transactions, 0); + int offset = 4; List transactionList = new LinkedList<>(); for (int i = 0; i < numTxn; i++) { Transaction tx = new Transaction(params, transactions, offset); diff --git a/core/src/main/java/org/bitcoinj/store/PostgresFullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/PostgresFullPrunedBlockStore.java index 2f3ad9d9..87a895d0 100644 --- a/core/src/main/java/org/bitcoinj/store/PostgresFullPrunedBlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/PostgresFullPrunedBlockStore.java @@ -181,10 +181,7 @@ public class PostgresFullPrunedBlockStore extends DatabaseFullPrunedBlockStore { txOutChanges = bos.toByteArray(); } else { int numTxn = undoableBlock.getTransactions().size(); - bos.write(0xFF & numTxn); - bos.write(0xFF & (numTxn >> 8)); - bos.write(0xFF & (numTxn >> 16)); - bos.write(0xFF & (numTxn >> 24)); + Utils.uint32ToByteStreamLE(numTxn, bos); for (Transaction tx : undoableBlock.getTransactions()) tx.bitcoinSerialize(bos); transactions = bos.toByteArray(); diff --git a/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java b/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java index 570735f8..20b2251b 100644 --- a/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java +++ b/core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java @@ -173,10 +173,7 @@ public class FullBlockTestGenerator { public boolean add(Rule element) { if (outStream != null && element instanceof BlockAndValidity) { try { - outStream.write((int) (params.getPacketMagic() >>> 24)); - outStream.write((int) (params.getPacketMagic() >>> 16)); - outStream.write((int) (params.getPacketMagic() >>> 8)); - outStream.write((int) params.getPacketMagic()); + Utils.uint32ToByteStreamBE(params.getPacketMagic(), outStream); byte[] block = ((BlockAndValidity)element).block.bitcoinSerialize(); byte[] length = new byte[4]; Utils.uint32ToByteArrayBE(block.length, length, 0);