Skip to content

Commit

Permalink
Merge pull request hyperledger-web3j#1961 from web3j/offset_encode
Browse files Browse the repository at this point in the history
fix Dynamic Arrays encode
  • Loading branch information
gtebrean authored Sep 19, 2023
2 parents e4e1dd7 + a426d7d commit 2a2da15
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 11 deletions.
80 changes: 75 additions & 5 deletions abi/src/main/java/org/web3j/abi/TypeDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -636,8 +636,8 @@ private static <T extends Type> T decodeArrayElements(

try {
Class<T> cls = Utils.getParameterizedTypeFromArray(typeReference);
List<T> elements = new ArrayList<>(length);
if (StructType.class.isAssignableFrom(cls)) {
List<T> elements = new ArrayList<>(length);
for (int i = 0, currOffset = offset;
i < length;
i++,
Expand All @@ -663,11 +663,81 @@ private static <T extends Type> T decodeArrayElements(

return consumer.apply(elements, typeName);
} else if (Array.class.isAssignableFrom(cls)) {
throw new UnsupportedOperationException(
"Arrays of arrays are not currently supported for external functions, see"
+ "http://solidity.readthedocs.io/en/develop/types.html#members");
for (int i = 0, currOffset = offset; i < length; i++) {
T value;
if (DynamicArray.class.isAssignableFrom(cls)) {
value =
(T)
TypeDecoder.decodeDynamicArray(
input,
offset
+ getDataOffset(
input, currOffset, typeReference),
Utils.getDynamicArrayTypeReference(
Utils.getFullParameterizedTypeFromArray(
typeReference)));
currOffset +=
getSingleElementLength(input, currOffset, cls)
* MAX_BYTE_LENGTH_FOR_HEX_STRING;
} else {
String typeName = cls.getSimpleName();
String extractedLength =
typeName.substring(typeName.replaceAll("[0-9]+$", "").length());
int staticLength =
extractedLength.isEmpty() ? 0 : Integer.parseInt(extractedLength);
TypeReference innerType =
TypeReference.create(
Utils.getFullParameterizedTypeFromArray(typeReference));

TypeReference.StaticArrayTypeReference staticReference =
new TypeReference.StaticArrayTypeReference<StaticArray>(
staticLength) {

@Override
TypeReference getSubTypeReference() {
return innerType;
}

@Override
public boolean isIndexed() {
return false;
}

@Override
public java.lang.reflect.Type getType() {
return new ParameterizedType() {
@Override
public java.lang.reflect.Type[]
getActualTypeArguments() {
return new java.lang.reflect.Type[] {
innerType.getType()
};
}

@Override
public java.lang.reflect.Type getRawType() {
return cls;
}

@Override
public java.lang.reflect.Type getOwnerType() {
return Class.class;
}
};
}
};
value =
(T)
TypeDecoder.decodeStaticArray(
input, currOffset, staticReference, staticLength);
currOffset +=
((decodeUintAsInt(input, currOffset) / Type.MAX_BYTE_LENGTH) + 2)
* MAX_BYTE_LENGTH_FOR_HEX_STRING;
}
elements.add(value);
}
return consumer.apply(elements, cls.getName());
} else {
List<T> elements = new ArrayList<>(length);
int currOffset = offset;
for (int i = 0; i < length; i++) {
T value;
Expand Down
14 changes: 8 additions & 6 deletions abi/src/main/java/org/web3j/abi/TypeEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ static String removePadding(String encodedValue, Type parameter) {
* @return
*/
private static <T extends Type> String encodeStaticArrayWithDynamicStruct(Array<T> value) {
String valuesOffsets = encodeStructsArraysOffsets(value);
String valuesOffsets = encodeDynamicsTypesArraysOffsets(value);
String encodedValues = encodeArrayValues(value);

StringBuilder result = new StringBuilder();
Expand Down Expand Up @@ -334,6 +334,8 @@ private static <T extends Type> String encodeArrayValuesOffsets(DynamicArray<T>
!value.getValue().isEmpty() && value.getValue().get(0) instanceof Utf8String;
boolean arrayOfDynamicStructs =
!value.getValue().isEmpty() && value.getValue().get(0) instanceof DynamicStruct;
boolean arrayOfDynamicArrays =
!value.getValue().isEmpty() && value.getValue().get(0) instanceof DynamicArray;
if (arrayOfBytes || arrayOfString) {
long offset = 0;
for (int i = 0; i < value.getValue().size(); i++) {
Expand All @@ -353,20 +355,20 @@ private static <T extends Type> String encodeArrayValuesOffsets(DynamicArray<T>
Numeric.toBytesPadded(
new BigInteger(Long.toString(offset)), MAX_BYTE_LENGTH)));
}
} else if (arrayOfDynamicStructs) {
result.append(encodeStructsArraysOffsets(value));
} else if (arrayOfDynamicArrays || arrayOfDynamicStructs) {
result.append(encodeDynamicsTypesArraysOffsets(value));
}
return result.toString();
}

/**
* Encodes arrays of structs elements offsets. To be used when encoding a dynamic array or a
* static array containing dynamic structs,
* Encodes arrays of structs or dynamic arrays elements offsets. To be used when encoding a
* dynamic arrays or a static array containing dynamic structs,
*
* @param value DynamicArray or StaticArray containing dynamic structs
* @return encoded array offset
*/
private static <T extends Type> String encodeStructsArraysOffsets(Array<T> value) {
private static <T extends Type> String encodeDynamicsTypesArraysOffsets(Array<T> value) {
StringBuilder result = new StringBuilder();
long offset = value.getValue().size();
List<String> tailsEncoding =
Expand Down
19 changes: 19 additions & 0 deletions abi/src/main/java/org/web3j/abi/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,29 @@ static <T extends Type> Class<T> getParameterizedTypeFromArray(TypeReference typ
java.lang.reflect.Type[] typeArguments =
((ParameterizedType) type).getActualTypeArguments();

if (typeArguments[0] instanceof ParameterizedType) {
return (Class<T>)
Class.forName(getTypeName(((ParameterizedType) typeArguments[0]).getRawType()));
}

String parameterizedTypeName = getTypeName(typeArguments[0]);
return (Class<T>) Class.forName(parameterizedTypeName);
}

static <T extends Type> Class<T> getFullParameterizedTypeFromArray(TypeReference typeReference)
throws ClassNotFoundException {

java.lang.reflect.Type type = typeReference.getType();

java.lang.reflect.Type typeArgument =
((ParameterizedType) type).getActualTypeArguments()[0];

return (Class<T>)
Class.forName(
((ParameterizedType) typeArgument)
.getActualTypeArguments()[0].getTypeName());
}

@SuppressWarnings("unchecked")
public static List<TypeReference<Type>> convert(List<TypeReference<?>> input) {
List<TypeReference<Type>> result = new ArrayList<>(input.size());
Expand Down
58 changes: 58 additions & 0 deletions abi/src/test/java/org/web3j/abi/TypeDecoderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.web3j.abi;

import java.math.BigInteger;
import java.util.Arrays;

import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -61,6 +62,7 @@
import org.web3j.abi.datatypes.generated.Int80;
import org.web3j.abi.datatypes.generated.Int88;
import org.web3j.abi.datatypes.generated.Int96;
import org.web3j.abi.datatypes.generated.StaticArray1;
import org.web3j.abi.datatypes.generated.StaticArray2;
import org.web3j.abi.datatypes.generated.StaticArray3;
import org.web3j.abi.datatypes.generated.Uint104;
Expand Down Expand Up @@ -1141,6 +1143,62 @@ public void testDynamicArray() throws Exception {
assertEquals(dynamicArray.getValue().get(1), (new Utf8String("world! Hello,")));
}

@Test
public void testDynamicArrayOfDynamicArrays() throws Exception {
assertEquals(
TypeDecoder.decodeDynamicArray(
"0000000000000000000000000000000000000000000000000000000000000002"
+ "0000000000000000000000000000000000000000000000000000000000000040"
+ "00000000000000000000000000000000000000000000000000000000000000a0"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000000",
0,
new TypeReference<DynamicArray<DynamicArray<AbiV2TestFixture.Bar>>>() {}),
new DynamicArray(
DynamicArray.class,
Arrays.asList(
new DynamicArray(
AbiV2TestFixture.Bar.class,
new AbiV2TestFixture.Bar(
new Uint256(BigInteger.ZERO),
new Uint256(BigInteger.ZERO))),
new DynamicArray(
AbiV2TestFixture.Bar.class,
new AbiV2TestFixture.Bar(
new Uint256(BigInteger.ONE),
new Uint256(BigInteger.ZERO))))));
}

@Test
public void testDynamicArrayOfStaticArrays() throws Exception {
assertEquals(
TypeDecoder.decodeDynamicArray(
"0000000000000000000000000000000000000000000000000000000000000002"
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000000",
0,
new TypeReference<DynamicArray<StaticArray1<AbiV2TestFixture.Bar>>>() {}),
new DynamicArray(
StaticArray1.class,
Arrays.asList(
new StaticArray1(
AbiV2TestFixture.Bar.class,
new AbiV2TestFixture.Bar(
new Uint256(BigInteger.ZERO),
new Uint256(BigInteger.ZERO))),
new StaticArray1(
AbiV2TestFixture.Bar.class,
new AbiV2TestFixture.Bar(
new Uint256(BigInteger.ONE),
new Uint256(BigInteger.ZERO))))));
}

@SuppressWarnings("unchecked")
@Test
public void multiDimArrays() throws Exception {
Expand Down
31 changes: 31 additions & 0 deletions abi/src/test/java/org/web3j/abi/TypeEncoderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.web3j.abi;

import java.math.BigInteger;
import java.util.Arrays;

import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -85,6 +86,7 @@
import org.web3j.abi.datatypes.generated.Uint24;
import org.web3j.abi.datatypes.generated.Uint240;
import org.web3j.abi.datatypes.generated.Uint248;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint32;
import org.web3j.abi.datatypes.generated.Uint40;
import org.web3j.abi.datatypes.generated.Uint48;
Expand Down Expand Up @@ -1217,6 +1219,35 @@ public void testDynamicStringsArray() {
TypeEncoder.encodeDynamicArray(array));
}

@Test
public void testDynamicArrayOfDynamicArraysOfStaticStructs() {
DynamicArray<DynamicArray<Bar>> array =
new DynamicArray(
DynamicArray.class,
Arrays.asList(
new DynamicArray(
Bar.class,
new Bar(
new Uint256(BigInteger.ZERO),
new Uint256(BigInteger.ZERO))),
new DynamicArray(
Bar.class,
new Bar(
new Uint256(BigInteger.ONE),
new Uint256(BigInteger.ZERO)))));
assertEquals(
("0000000000000000000000000000000000000000000000000000000000000002"
+ "0000000000000000000000000000000000000000000000000000000000000040"
+ "00000000000000000000000000000000000000000000000000000000000000a0"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000000"),
TypeEncoder.encodeDynamicArray(array));
}

@Test
public void testStructsDynamicArray() {
DynamicArray<Foo> array =
Expand Down

0 comments on commit 2a2da15

Please sign in to comment.