All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bitcoinj.core.Utils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2011 Google Inc.
 * Copyright 2014 Andreas Schildbach
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bitcoinj.core;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.digests.RIPEMD160Digest;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.io.BaseEncoding;

/**
 * A collection of various utility methods that are helpful for working with the Bitcoin protocol.
 * To enable debug logging from the library, run with -Dbitcoinj.logging=true on your command line.
 */
public class Utils {

    /** Joiner for concatenating words with a space inbetween. */
    public static final Joiner SPACE_JOINER = Joiner.on(" ");
    /** Splitter for splitting words on whitespaces. */
    public static final Splitter WHITESPACE_SPLITTER = Splitter.on(Pattern.compile("\\s+"));
    /** Hex encoding used throughout the framework. Use with HEX.encode(byte[]) or HEX.decode(CharSequence). */
    public static final BaseEncoding HEX = BaseEncoding.base16().lowerCase();

    /**
     * Max initial size of variable length arrays and ArrayLists that could be attacked.
     * Avoids this attack: Attacker sends a msg indicating it will contain a huge number (eg 2 billion) elements (eg transaction inputs) and
     * forces bitcoinj to try to allocate a huge piece of the memory resulting in OutOfMemoryError.
    */
    public static final int MAX_INITIAL_ARRAY_LENGTH = 20;

    private static BlockingQueue mockSleepQueue;

    private static final Logger log = LoggerFactory.getLogger(Utils.class);

    /**
     * 

* The regular {@link BigInteger#toByteArray()} includes the sign bit of the number and * might result in an extra byte addition. This method removes this extra byte. *

*

* Assuming only positive numbers, it's possible to discriminate if an extra byte * is added by checking if the first element of the array is 0 (0000_0000). * Due to the minimal representation provided by BigInteger, it means that the bit sign * is the least significant bit 0000_0000 . * Otherwise the representation is not minimal. * For example, if the sign bit is 0000_0000, then the representation is not minimal due to the rightmost zero. *

* @param b the integer to format into a byte array * @param numBytes the desired size of the resulting byte array * @return numBytes byte long array. */ public static byte[] bigIntegerToBytes(BigInteger b, int numBytes) { checkArgument(b.signum() >= 0, "b must be positive or zero"); checkArgument(numBytes > 0, "numBytes must be positive"); byte[] src = b.toByteArray(); byte[] dest = new byte[numBytes]; boolean isFirstByteOnlyForSign = src[0] == 0; int length = isFirstByteOnlyForSign ? src.length - 1 : src.length; checkArgument(length <= numBytes, "The given number does not fit in " + numBytes); int srcPos = isFirstByteOnlyForSign ? 1 : 0; int destPos = numBytes - length; System.arraycopy(src, srcPos, dest, destPos, length); return dest; } /** 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. */ public static void uint32ToByteArrayLE(long val, byte[] out, int offset) { out[offset] = (byte) (0xFF & val); out[offset + 1] = (byte) (0xFF & (val >> 8)); out[offset + 2] = (byte) (0xFF & (val >> 16)); 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); out[offset + 1] = (byte) (0xFF & (val >> 8)); out[offset + 2] = (byte) (0xFF & (val >> 16)); out[offset + 3] = (byte) (0xFF & (val >> 24)); out[offset + 4] = (byte) (0xFF & (val >> 32)); out[offset + 5] = (byte) (0xFF & (val >> 40)); out[offset + 6] = (byte) (0xFF & (val >> 48)); 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)); stream.write((int) (0xFF & (val >> 8))); 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)); stream.write((int) (0xFF & (val >> 8))); stream.write((int) (0xFF & (val >> 16))); stream.write((int) (0xFF & (val >> 24))); stream.write((int) (0xFF & (val >> 32))); stream.write((int) (0xFF & (val >> 40))); stream.write((int) (0xFF & (val >> 48))); stream.write((int) (0xFF & (val >> 56))); } /** Write 8 bytes to the output stream as unsigned 64-bit integer in little endian format. */ public static void uint64ToByteStreamLE(BigInteger val, OutputStream stream) throws IOException { byte[] bytes = val.toByteArray(); if (bytes.length > 8) { throw new RuntimeException("Input too large to encode into a uint64"); } bytes = reverseBytes(bytes); stream.write(bytes); if (bytes.length < 8) { for (int i = 0; i < 8 - bytes.length; i++) stream.write(0); } } /** 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) | ((bytes[offset + 1] & 0xffl) << 8) | ((bytes[offset + 2] & 0xffl) << 16) | ((bytes[offset + 3] & 0xffl) << 24); } /** Parse 8 bytes from the byte array (starting at the offset) as signed 64-bit integer in little endian format. */ public static long readInt64(byte[] bytes, int offset) { return (bytes[offset] & 0xffl) | ((bytes[offset + 1] & 0xffl) << 8) | ((bytes[offset + 2] & 0xffl) << 16) | ((bytes[offset + 3] & 0xffl) << 24) | ((bytes[offset + 4] & 0xffl) << 32) | ((bytes[offset + 5] & 0xffl) << 40) | ((bytes[offset + 6] & 0xffl) << 48) | ((bytes[offset + 7] & 0xffl) << 56); } /** Parse 4 bytes from the byte array (starting at the offset) as unsigned 32-bit integer in big endian format. */ public static long readUint32BE(byte[] bytes, int offset) { return ((bytes[offset] & 0xffl) << 24) | ((bytes[offset + 1] & 0xffl) << 16) | ((bytes[offset + 2] & 0xffl) << 8) | (bytes[offset + 3] & 0xffl); } /** Parse 2 bytes from the byte array (starting at the offset) as unsigned 16-bit integer in big endian format. */ public static int readUint16BE(byte[] bytes, int offset) { return ((bytes[offset] & 0xff) << 8) | (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. */ public static byte[] reverseBytes(byte[] bytes) { // We could use the XOR trick here but it's easier to understand if we don't. If we find this is really a // performance issue the matter can be revisited. byte[] buf = new byte[bytes.length]; for (int i = 0; i < bytes.length; i++) buf[i] = bytes[bytes.length - 1 - i]; return buf; } /** * Calculates RIPEMD160(SHA256(input)). This is used in Address calculations. */ public static byte[] sha256hash160(byte[] input) { byte[] sha256 = Sha256Hash.hash(input); RIPEMD160Digest digest = new RIPEMD160Digest(); digest.update(sha256, 0, sha256.length); byte[] out = new byte[20]; digest.doFinal(out, 0); return out; } /** * MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function. They consist of * a 4 byte big endian length field, followed by the stated number of bytes representing * the number in big endian format (with a sign bit). * @param hasLength can be set to false if the given array is missing the 4 byte length field */ public static BigInteger decodeMPI(byte[] mpi, boolean hasLength) { byte[] buf; if (hasLength) { int length = (int) readUint32BE(mpi, 0); buf = new byte[length]; System.arraycopy(mpi, 4, buf, 0, length); } else buf = mpi; if (buf.length == 0) return BigInteger.ZERO; boolean isNegative = (buf[0] & 0x80) == 0x80; if (isNegative) buf[0] &= 0x7f; BigInteger result = new BigInteger(buf); return isNegative ? result.negate() : result; } /** * MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function. They consist of * a 4 byte big endian length field, followed by the stated number of bytes representing * the number in big endian format (with a sign bit). * @param includeLength indicates whether the 4 byte length field should be included */ public static byte[] encodeMPI(BigInteger value, boolean includeLength) { if (value.equals(BigInteger.ZERO)) { if (!includeLength) return new byte[] {}; else return new byte[] {0x00, 0x00, 0x00, 0x00}; } boolean isNegative = value.signum() < 0; if (isNegative) value = value.negate(); byte[] array = value.toByteArray(); int length = array.length; if ((array[0] & 0x80) == 0x80) length++; if (includeLength) { byte[] result = new byte[length + 4]; System.arraycopy(array, 0, result, length - array.length + 3, array.length); uint32ToByteArrayBE(length, result, 0); if (isNegative) result[4] |= 0x80; return result; } else { byte[] result; if (length != array.length) { result = new byte[length]; System.arraycopy(array, 0, result, 1, array.length); }else result = array; if (isNegative) result[0] |= 0x80; return result; } } /** *

The "compact" format is a representation of a whole number N using an unsigned 32 bit number similar to a * floating point format. The most significant 8 bits are the unsigned exponent of base 256. This exponent can * be thought of as "number of bytes of N". The lower 23 bits are the mantissa. Bit number 24 (0x800000) represents * the sign of N. Therefore, N = (-1^sign) * mantissa * 256^(exponent-3).

* *

Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). MPI uses the most significant bit of the * first byte as sign. Thus 0x1234560000 is compact 0x05123456 and 0xc0de000000 is compact 0x0600c0de. Compact * 0x05c0de00 would be -0x40de000000.

* *

Bitcoin only uses this "compact" format for encoding difficulty targets, which are unsigned 256bit quantities. * Thus, all the complexities of the sign bit and using base 256 are probably an implementation accident.

*/ public static BigInteger decodeCompactBits(long compact) { int size = ((int) (compact >> 24)) & 0xFF; byte[] bytes = new byte[4 + size]; bytes[3] = (byte) size; if (size >= 1) bytes[4] = (byte) ((compact >> 16) & 0xFF); if (size >= 2) bytes[5] = (byte) ((compact >> 8) & 0xFF); if (size >= 3) bytes[6] = (byte) (compact & 0xFF); return decodeMPI(bytes, true); } /** * @see Utils#decodeCompactBits(long) */ public static long encodeCompactBits(BigInteger value) { long result; int size = value.toByteArray().length; if (size <= 3) result = value.longValue() << 8 * (3 - size); else result = value.shiftRight(8 * (size - 3)).longValue(); // The 0x00800000 bit denotes the sign. // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. if ((result & 0x00800000L) != 0) { result >>= 8; size++; } result |= size << 24; result |= value.signum() == -1 ? 0x00800000 : 0; return result; } /** * If non-null, overrides the return value of now(). */ private static volatile Date mockTime; /** * Advances (or rewinds) the mock clock by the given number of seconds. */ public static Date rollMockClock(int seconds) { return rollMockClockMillis(seconds * 1000); } /** * Advances (or rewinds) the mock clock by the given number of milliseconds. */ public static Date rollMockClockMillis(long millis) { if (mockTime == null) throw new IllegalStateException("You need to use setMockClock() first."); mockTime = new Date(mockTime.getTime() + millis); return mockTime; } /** * Sets the mock clock to the current time. */ public static void setMockClock() { mockTime = new Date(); } /** * Sets the mock clock to the given time (in seconds). */ public static void setMockClock(long mockClockSeconds) { mockTime = new Date(mockClockSeconds * 1000); } /** * Clears the mock clock and sleep */ public static void resetMocking() { mockTime = null; mockSleepQueue = null; } /** * Returns the current time, or a mocked out equivalent. */ public static Date now() { return mockTime != null ? mockTime : new Date(); } /** * Returns the current time in milliseconds since the epoch, or a mocked out equivalent. */ public static long currentTimeMillis() { return mockTime != null ? mockTime.getTime() : System.currentTimeMillis(); } /** * Returns the current time in seconds since the epoch, or a mocked out equivalent. */ public static long currentTimeSeconds() { return currentTimeMillis() / 1000; } private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); /** * Formats a given date+time value to an ISO 8601 string. * @param dateTime value to format, as a Date */ public static String dateTimeFormat(Date dateTime) { DateFormat iso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); iso8601.setTimeZone(UTC); return iso8601.format(dateTime); } /** * Formats a given date+time value to an ISO 8601 string. * @param dateTime value to format, unix time (ms) */ public static String dateTimeFormat(long dateTime) { DateFormat iso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); iso8601.setTimeZone(UTC); return iso8601.format(dateTime); } // 00000001, 00000010, 00000100, 00001000, ... private static final int[] bitMask = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; /** Checks if the given bit is set in data, using little endian (not the same as Java native big endian) */ public static boolean checkBitLE(byte[] data, int index) { return (data[index >>> 3] & bitMask[7 & index]) != 0; } /** Sets the given bit in data to one, using little endian (not the same as Java native big endian) */ public static void setBitLE(byte[] data, int index) { data[index >>> 3] |= bitMask[7 & index]; } /** Sleep for a span of time, or mock sleep if enabled */ public static void sleep(long millis) { if (mockSleepQueue == null) { sleepUninterruptibly(millis, TimeUnit.MILLISECONDS); } else { try { boolean isMultiPass = mockSleepQueue.take(); rollMockClockMillis(millis); if (isMultiPass) mockSleepQueue.offer(true); } catch (InterruptedException e) { // Ignored. } } } /** Enable or disable mock sleep. If enabled, set mock time to current time. */ public static void setMockSleep(boolean isEnable) { if (isEnable) { mockSleepQueue = new ArrayBlockingQueue<>(1); mockTime = new Date(System.currentTimeMillis()); } else { mockSleepQueue = null; } } /** Let sleeping thread pass the synchronization point. */ public static void passMockSleep() { mockSleepQueue.offer(false); } /** Let the sleeping thread pass the synchronization point any number of times. */ public static void finishMockSleep() { if (mockSleepQueue != null) { mockSleepQueue.offer(true); } } private enum Runtime { ANDROID, OPENJDK, ORACLE_JAVA } private enum OS { LINUX, WINDOWS, MAC_OS } private static Runtime runtime = null; private static OS os = null; static { String runtimeProp = System.getProperty("java.runtime.name", "").toLowerCase(Locale.US); if (runtimeProp.equals("")) runtime = null; else if (runtimeProp.contains("android")) runtime = Runtime.ANDROID; else if (runtimeProp.contains("openjdk")) runtime = Runtime.OPENJDK; else if (runtimeProp.contains("java(tm) se")) runtime = Runtime.ORACLE_JAVA; else log.info("Unknown java.runtime.name '{}'", runtimeProp); String osProp = System.getProperty("os.name", "").toLowerCase(Locale.US); if (osProp.equals("")) os = null; else if (osProp.contains("linux")) os = OS.LINUX; else if (osProp.contains("win")) os = OS.WINDOWS; else if (osProp.contains("mac")) os = OS.MAC_OS; else log.info("Unknown os.name '{}'", runtimeProp); } public static boolean isAndroidRuntime() { return runtime == Runtime.ANDROID; } public static boolean isOpenJDKRuntime() { return runtime == Runtime.OPENJDK; } public static boolean isOracleJavaRuntime() { return runtime == Runtime.ORACLE_JAVA; } public static boolean isLinux() { return os == OS.LINUX; } public static boolean isWindows() { return os == OS.WINDOWS; } public static boolean isMac() { return os == OS.MAC_OS; } public static String toString(List stack) { List parts = new ArrayList<>(stack.size()); for (byte[] push : stack) parts.add('[' + HEX.encode(push) + ']'); return SPACE_JOINER.join(parts); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy