From e5c88cda8b511cfa9dd0720390b5d6ca6f5297b5 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 2 Aug 2012 18:00:04 +0200 Subject: [PATCH] Fix Utils.decodeMPI and add a Utils.encodeMPI --- .../java/com/google/bitcoin/core/Script.java | 1 + .../java/com/google/bitcoin/core/Utils.java | 65 +++++++++++++++++-- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/google/bitcoin/core/Script.java b/core/src/main/java/com/google/bitcoin/core/Script.java index f348be872..ab3d28a04 100644 --- a/core/src/main/java/com/google/bitcoin/core/Script.java +++ b/core/src/main/java/com/google/bitcoin/core/Script.java @@ -612,6 +612,7 @@ public class Script { /** * Writes out the given byte buffer to the output stream with the correct opcode prefix + * To write an integer call writeBytes(out, Utils.reverseBytes(Utils.encodeMPI(val, false))); */ static void writeBytes(OutputStream os, byte[] buf) throws IOException { if (buf.length < OP_PUSHDATA1) { diff --git a/core/src/main/java/com/google/bitcoin/core/Utils.java b/core/src/main/java/com/google/bitcoin/core/Utils.java index 302138187..0b254848b 100644 --- a/core/src/main/java/com/google/bitcoin/core/Utils.java +++ b/core/src/main/java/com/google/bitcoin/core/Utils.java @@ -323,13 +323,64 @@ public class Utils { /** * 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. + * 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 */ - static BigInteger decodeMPI(byte[] mpi) { - int length = (int) readUint32BE(mpi, 0); - byte[] buf = new byte[length]; - System.arraycopy(mpi, 4, buf, 0, length); - return new BigInteger(buf); + 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 hasLength indicates whether the 4 byte length field should be included + */ + 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.compareTo(BigInteger.ZERO) < 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 representation of nBits uses another home-brew encoding, as a way to represent a large @@ -341,7 +392,7 @@ public class Utils { 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 >> 0) & 0xFF); - return decodeMPI(bytes); + return decodeMPI(bytes, true); } /**