Updated on 2026-08-14
This commit is contained in:
parent
092eb2dd68
commit
250ceba99f
78 changed files with 229 additions and 247 deletions
228
app/src/main/java/com/tangem/wallet/RLP.java
Normal file
228
app/src/main/java/com/tangem/wallet/RLP.java
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
package com.tangem.wallet;
|
||||
|
||||
/**
|
||||
* Created by Ilia on 07.01.2018.
|
||||
*/
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.tangem.util.ByteUtil.isNullOrZeroArray;
|
||||
import static com.tangem.util.ByteUtil.isSingleZero;
|
||||
|
||||
public class RLP {
|
||||
public static final byte[] EMPTY_ELEMENT_RLP = encodeElement(new byte[0]);
|
||||
|
||||
/**
|
||||
* Allow for content up to size of 2^64 bytes *
|
||||
*/
|
||||
private static final double MAX_ITEM_LENGTH = Math.pow(256, 8);
|
||||
|
||||
/**
|
||||
* Reason for threshold according to Vitalik Buterin:
|
||||
* - 56 bytes maximizes the benefit of both options
|
||||
* - if we went with 60 then we would have only had 4 slots for long strings
|
||||
* so RLP would not have been able to store objects above 4gb
|
||||
* - if we went with 48 then RLP would be fine for 2^128 space, but that's way too much
|
||||
* - so 56 and 2^64 space seems like the right place to put the cutoff
|
||||
* - also, that's where Bitcoin's varint does the cutof
|
||||
*/
|
||||
private static final int SIZE_THRESHOLD = 56;
|
||||
|
||||
/** RLP encoding rules are defined as follows: */
|
||||
|
||||
/*
|
||||
* For a single byte whose value is in the [0x00, 0x7f] range, that byte is
|
||||
* its own RLP encoding.
|
||||
*/
|
||||
|
||||
/**
|
||||
* [0x80]
|
||||
* If a string is 0-55 bytes long, the RLP encoding consists of a single
|
||||
* byte with value 0x80 plus the length of the string followed by the
|
||||
* string. The range of the first byte is thus [0x80, 0xb7].
|
||||
*/
|
||||
private static final int OFFSET_SHORT_ITEM = 0x80;
|
||||
|
||||
/**
|
||||
* [0xb7]
|
||||
* If a string is more than 55 bytes long, the RLP encoding consists of a
|
||||
* single byte with value 0xb7 plus the length of the length of the string
|
||||
* in binary form, followed by the length of the string, followed by the
|
||||
* string. For example, a length-1024 string would be encoded as
|
||||
* \xb9\x04\x00 followed by the string. The range of the first byte is thus
|
||||
* [0xb8, 0xbf].
|
||||
*/
|
||||
private static final int OFFSET_LONG_ITEM = 0xb7;
|
||||
|
||||
/**
|
||||
* [0xc0]
|
||||
* If the total payload of a list (i.e. the combined length of all its
|
||||
* items) is 0-55 bytes long, the RLP encoding consists of a single byte
|
||||
* with value 0xc0 plus the length of the list followed by the concatenation
|
||||
* of the RLP encodings of the items. The range of the first byte is thus
|
||||
* [0xc0, 0xf7].
|
||||
*/
|
||||
private static final int OFFSET_SHORT_LIST = 0xc0;
|
||||
|
||||
public static byte[] encodeByte(byte singleByte) {
|
||||
if ((singleByte & 0xFF) == 0) {
|
||||
return new byte[]{(byte) OFFSET_SHORT_ITEM};
|
||||
} else if ((singleByte & 0xFF) <= 0x7F) {
|
||||
return new byte[]{singleByte};
|
||||
} else {
|
||||
return new byte[]{(byte) (OFFSET_SHORT_ITEM + 1), singleByte};
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] encodeShort(short singleShort) {
|
||||
if ((singleShort & 0xFF) == singleShort)
|
||||
return encodeByte((byte) singleShort);
|
||||
else {
|
||||
return new byte[]{(byte) (OFFSET_SHORT_ITEM + 2),
|
||||
(byte) (singleShort >> 8 & 0xFF),
|
||||
(byte) (singleShort >> 0 & 0xFF)};
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] encodeInt(int singleInt) {
|
||||
if ((singleInt & 0xFF) == singleInt)
|
||||
return encodeByte((byte) singleInt);
|
||||
else if ((singleInt & 0xFFFF) == singleInt)
|
||||
return encodeShort((short) singleInt);
|
||||
else if ((singleInt & 0xFFFFFF) == singleInt)
|
||||
return new byte[]{(byte) (OFFSET_SHORT_ITEM + 3),
|
||||
(byte) (singleInt >>> 16),
|
||||
(byte) (singleInt >>> 8),
|
||||
(byte) singleInt};
|
||||
else {
|
||||
return new byte[]{(byte) (OFFSET_SHORT_ITEM + 4),
|
||||
(byte) (singleInt >>> 24),
|
||||
(byte) (singleInt >>> 16),
|
||||
(byte) (singleInt >>> 8),
|
||||
(byte) singleInt};
|
||||
}
|
||||
}
|
||||
|
||||
private static final int OFFSET_LONG_LIST = 0xf7;
|
||||
|
||||
public static byte[] encodeElement2(byte[] srcData) {
|
||||
if (srcData == null)
|
||||
return new byte[]{(byte) OFFSET_SHORT_ITEM};
|
||||
else if (srcData.length == 1 && (srcData[0] & 0xFF) < 0x80) {
|
||||
return srcData;
|
||||
} else if (srcData.length < SIZE_THRESHOLD) {
|
||||
// length = 8X
|
||||
byte length = (byte) (OFFSET_SHORT_ITEM + srcData.length);
|
||||
byte[] data = Arrays.copyOf(srcData, srcData.length + 1);
|
||||
System.arraycopy(data, 0, data, 1, srcData.length);
|
||||
data[0] = length;
|
||||
|
||||
return data;
|
||||
} else {
|
||||
// length of length = BX
|
||||
// prefix = [BX, [length]]
|
||||
int tmpLength = srcData.length;
|
||||
byte byteNum = 0;
|
||||
while (tmpLength != 0) {
|
||||
++byteNum;
|
||||
tmpLength = tmpLength >> 8;
|
||||
}
|
||||
byte[] lenBytes = new byte[byteNum];
|
||||
for (int i = 0; i < byteNum; ++i) {
|
||||
lenBytes[byteNum - 1 - i] = (byte) ((srcData.length >> (8 * i)) & 0xFF);
|
||||
}
|
||||
// first byte = F7 + bytes.length
|
||||
byte[] data = Arrays.copyOf(srcData, srcData.length + 1 + byteNum);
|
||||
System.arraycopy(data, 0, data, 1 + byteNum, srcData.length);
|
||||
data[0] = (byte) (OFFSET_LONG_ITEM + byteNum);
|
||||
System.arraycopy(lenBytes, 0, data, 1, lenBytes.length);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
public static byte[] encodeElement(byte[] srcData) {
|
||||
|
||||
if (isNullOrZeroArray(srcData))
|
||||
return new byte[]{(byte) OFFSET_SHORT_ITEM};
|
||||
else if (isSingleZero(srcData))
|
||||
return srcData;
|
||||
else if (srcData.length == 1 && (srcData[0] & 0xFF) < 0x80) {
|
||||
return srcData;
|
||||
} else if (srcData.length < SIZE_THRESHOLD) {
|
||||
// length = 8X
|
||||
byte length = (byte) (OFFSET_SHORT_ITEM + srcData.length);
|
||||
byte[] data = Arrays.copyOf(srcData, srcData.length + 1);
|
||||
System.arraycopy(data, 0, data, 1, srcData.length);
|
||||
data[0] = length;
|
||||
|
||||
return data;
|
||||
} else {
|
||||
// length of length = BX
|
||||
// prefix = [BX, [length]]
|
||||
int tmpLength = srcData.length;
|
||||
byte byteNum = 0;
|
||||
while (tmpLength != 0) {
|
||||
++byteNum;
|
||||
tmpLength = tmpLength >> 8;
|
||||
}
|
||||
byte[] lenBytes = new byte[byteNum];
|
||||
for (int i = 0; i < byteNum; ++i) {
|
||||
lenBytes[byteNum - 1 - i] = (byte) ((srcData.length >> (8 * i)) & 0xFF);
|
||||
}
|
||||
// first byte = F7 + bytes.length
|
||||
byte[] data = Arrays.copyOf(srcData, srcData.length + 1 + byteNum);
|
||||
System.arraycopy(data, 0, data, 1 + byteNum, srcData.length);
|
||||
data[0] = (byte) (OFFSET_LONG_ITEM + byteNum);
|
||||
System.arraycopy(lenBytes, 0, data, 1, lenBytes.length);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte[] encodeList(byte[]... elements) {
|
||||
|
||||
if (elements == null) {
|
||||
return new byte[]{(byte) OFFSET_SHORT_LIST};
|
||||
}
|
||||
|
||||
int totalLength = 0;
|
||||
for (byte[] element1 : elements) {
|
||||
totalLength += element1.length;
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
int copyPos;
|
||||
if (totalLength < SIZE_THRESHOLD) {
|
||||
|
||||
data = new byte[1 + totalLength];
|
||||
data[0] = (byte) (OFFSET_SHORT_LIST + totalLength);
|
||||
copyPos = 1;
|
||||
} else {
|
||||
// length of length = BX
|
||||
// prefix = [BX, [length]]
|
||||
int tmpLength = totalLength;
|
||||
byte byteNum = 0;
|
||||
while (tmpLength != 0) {
|
||||
++byteNum;
|
||||
tmpLength = tmpLength >> 8;
|
||||
}
|
||||
tmpLength = totalLength;
|
||||
byte[] lenBytes = new byte[byteNum];
|
||||
for (int i = 0; i < byteNum; ++i) {
|
||||
lenBytes[byteNum - 1 - i] = (byte) ((tmpLength >> (8 * i)) & 0xFF);
|
||||
}
|
||||
// first byte = F7 + bytes.length
|
||||
data = new byte[1 + lenBytes.length + totalLength];
|
||||
data[0] = (byte) (OFFSET_LONG_LIST + byteNum);
|
||||
System.arraycopy(lenBytes, 0, data, 1, lenBytes.length);
|
||||
|
||||
copyPos = lenBytes.length + 1;
|
||||
}
|
||||
for (byte[] element : elements) {
|
||||
System.arraycopy(element, 0, data, copyPos, element.length);
|
||||
copyPos += element.length;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue