diff --git a/abi/build.gradle b/abi/build.gradle index 3541772c..c9ac2bd3 100644 --- a/abi/build.gradle +++ b/abi/build.gradle @@ -6,4 +6,5 @@ description 'TRON Application Binary Interface (ABI) for working with smart cont dependencies { implementation project(':utils') + testImplementation "com.fasterxml.jackson.core:jackson-databind:2.18.6" } diff --git a/abi/src/main/java/org/tron/trident/abi/CustomErrorEncoder.java b/abi/src/main/java/org/tron/trident/abi/CustomErrorEncoder.java new file mode 100644 index 00000000..315d31e3 --- /dev/null +++ b/abi/src/main/java/org/tron/trident/abi/CustomErrorEncoder.java @@ -0,0 +1,43 @@ +package org.tron.trident.abi; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; +import org.tron.trident.abi.datatypes.CustomError; +import org.tron.trident.abi.datatypes.Type; +import org.tron.trident.crypto.Hash; +import org.tron.trident.utils.Numeric; + +/** + * Ethereum custom error encoding. Further limited details are available here. + */ +public class CustomErrorEncoder { + + private CustomErrorEncoder() { + } + + public static String encode(CustomError error) { + return calculateSignatureHash( + buildErrorSignature(error.getName(), error.getParameters())); + } + + static String buildErrorSignature( + String errorName, List> parameters) { + + StringBuilder result = new StringBuilder(); + result.append(errorName); + result.append("("); + String params = + parameters.stream().map(Utils::getTypeName).collect(Collectors.joining(",")); + result.append(params); + result.append(")"); + return result.toString(); + } + + public static String calculateSignatureHash(String errorSignature) { + byte[] input = errorSignature.getBytes(StandardCharsets.UTF_8); + byte[] hash = Hash.sha3(input); + return Numeric.toHexString(hash).substring(2); + } +} diff --git a/abi/src/main/java/org/tron/trident/abi/DefaultFunctionEncoder.java b/abi/src/main/java/org/tron/trident/abi/DefaultFunctionEncoder.java index cb79fd8d..cfe4069f 100644 --- a/abi/src/main/java/org/tron/trident/abi/DefaultFunctionEncoder.java +++ b/abi/src/main/java/org/tron/trident/abi/DefaultFunctionEncoder.java @@ -17,6 +17,7 @@ import java.util.List; import org.tron.trident.abi.datatypes.Function; import org.tron.trident.abi.datatypes.StaticArray; +import org.tron.trident.abi.datatypes.StaticStruct; import org.tron.trident.abi.datatypes.Type; import org.tron.trident.abi.datatypes.Uint; @@ -64,11 +65,53 @@ private static String encodeParameters( return result.toString(); } + /** + * Encodes a function call including its method signature (selector) and its parameters. + * + * @param methodId the 4-byte selector (8 hex chars) + * @param parameters the list of parameters to encode + * @return the complete ABI-encoded hex string representing the function call + */ + public String encodeWithSelector(String methodId, List parameters) { + final StringBuilder result = new StringBuilder(methodId); + + return encodeParameters(parameters, result); + } + + /** + * Encodes parameters using tight packing (abi.encodePacked). + * This is a non-standard ABI encoding used primarily for computing hashes, + * where padding is omitted and dynamic types are concatenated without length prefixes. + * + * @param parameters the list of parameters to pack + * @return the packed ABI-encoded hex string + */ + @Override + protected String encodePackedParameters(List parameters) { + final StringBuilder result = new StringBuilder(); + for (Type parameter : parameters) { + result.append(TypeEncoder.encodePacked(parameter)); + } + return result.toString(); + } + + /** + * Calculates the length of the tuple head (in 32-byte slots) required for the given parameters. + * Crucially, all dynamic types (including StaticArrays containing dynamic elements) always occupy exactly + * 1 slot in the head (for the offset pointer). Pure static arrays and structs are flattened to calculate + * their total inline slot requirement. + * + * @param parameters the list of types to be encoded. + * @return the total number of 32-byte slots required for the tuple head. + */ + @SuppressWarnings("unchecked") private static int getLength(final List parameters) { int count = 0; for (final Type type : parameters) { - if (type instanceof StaticArray) { - count += ((StaticArray) type).getValue().size(); + if (TypeEncoder.isDynamic(type)) { + count++; + } else if (type instanceof StaticArray || type instanceof StaticStruct) { + count += type.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH; } else { count++; } diff --git a/abi/src/main/java/org/tron/trident/abi/DefaultFunctionReturnDecoder.java b/abi/src/main/java/org/tron/trident/abi/DefaultFunctionReturnDecoder.java index f2a03bcf..55e79dff 100644 --- a/abi/src/main/java/org/tron/trident/abi/DefaultFunctionReturnDecoder.java +++ b/abi/src/main/java/org/tron/trident/abi/DefaultFunctionReturnDecoder.java @@ -13,6 +13,9 @@ package org.tron.trident.abi; +import static org.tron.trident.abi.TypeDecoder.MAX_BYTE_LENGTH_FOR_HEX_STRING; +import static org.tron.trident.abi.TypeDecoder.isDynamic; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -31,7 +34,7 @@ import org.tron.trident.utils.Strings; /** - * Ethereum Contract Application Binary Interface (ABI) encoding for functions. Further details are + * Ethereum Contract Application Binary Interface (ABI) decoding for functions. Further details are * available here. */ public class DefaultFunctionReturnDecoder extends FunctionReturnDecoder { @@ -73,60 +76,65 @@ public Type decodeEventParameter( } private static List build(String input, List> outputParameters) { + // Reject cyclic or excessively nested TypeReference graphs up front; any + // downstream recursion through subTypeReference / innerTypes is then bounded + // by what passed validation here. + for (TypeReference typeReference : outputParameters) { + Utils.validateTypeReferenceDepth(typeReference); + } + List results = new ArrayList<>(outputParameters.size()); int offset = 0; for (TypeReference typeReference : outputParameters) { try { + int hexStringDataOffset = getDataOffset(input, offset, typeReference); + @SuppressWarnings("unchecked") Class classType = (Class) typeReference.getClassType(); - int hexStringDataOffset = getDataOffset(input, offset, classType); - Type result; if (DynamicStruct.class.isAssignableFrom(classType)) { - if (outputParameters.size() != 1) { - throw new UnsupportedOperationException( - "Multiple return objects containing a struct is not supported"); - } result = - TypeDecoder.decodeDynamicStruct( - input, hexStringDataOffset, typeReference); - offset += TypeDecoder.MAX_BYTE_LENGTH_FOR_HEX_STRING; + TypeDecoder.decodeDynamicStruct( + input, hexStringDataOffset, typeReference); + offset += MAX_BYTE_LENGTH_FOR_HEX_STRING; } else if (DynamicArray.class.isAssignableFrom(classType)) { result = - TypeDecoder.decodeDynamicArray( - input, hexStringDataOffset, typeReference); - offset += TypeDecoder.MAX_BYTE_LENGTH_FOR_HEX_STRING; - - } else if (typeReference instanceof TypeReference.StaticArrayTypeReference) { - int length = ((TypeReference.StaticArrayTypeReference) typeReference).getSize(); - result = - TypeDecoder.decodeStaticArray( - input, hexStringDataOffset, typeReference, length); - offset += length * TypeDecoder.MAX_BYTE_LENGTH_FOR_HEX_STRING; + TypeDecoder.decodeDynamicArray( + input, hexStringDataOffset, typeReference); + offset += MAX_BYTE_LENGTH_FOR_HEX_STRING; } else if (StaticStruct.class.isAssignableFrom(classType)) { result = - TypeDecoder.decodeStaticStruct( - input, hexStringDataOffset, typeReference); - offset += - classType.getDeclaredFields().length * TypeDecoder.MAX_BYTE_LENGTH_FOR_HEX_STRING; + TypeDecoder.decodeStaticStruct( + input, hexStringDataOffset, typeReference); + offset += (result.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else if (StaticArray.class.isAssignableFrom(classType)) { - int length = - Integer.parseInt( - classType - .getSimpleName() - .substring(StaticArray.class.getSimpleName().length())); + int length; + if (typeReference instanceof TypeReference.StaticArrayTypeReference) { + length = ((TypeReference.StaticArrayTypeReference) typeReference).getSize(); + } else { + length = Integer.parseInt( + classType.getSimpleName() + .substring(StaticArray.class.getSimpleName().length())); + } result = - TypeDecoder.decodeStaticArray( - input, hexStringDataOffset, typeReference, length); - offset += length * TypeDecoder.MAX_BYTE_LENGTH_FOR_HEX_STRING; - + TypeDecoder.decodeStaticArray( + input, hexStringDataOffset, typeReference, length); + if (isDynamic(typeReference)) { + offset += MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else { + offset += + (result.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } } else { result = TypeDecoder.decode(input, hexStringDataOffset, classType); - offset += TypeDecoder.MAX_BYTE_LENGTH_FOR_HEX_STRING; + offset += MAX_BYTE_LENGTH_FOR_HEX_STRING; } results.add(result); @@ -137,13 +145,31 @@ private static List build(String input, List> outputPa return results; } - private static int getDataOffset(String input, int offset, Class type) { + public static int getDataOffset( + String input, int offset, TypeReference typeReference) + throws ClassNotFoundException { + @SuppressWarnings("unchecked") + Class type = (Class) typeReference.getClassType(); if (DynamicBytes.class.isAssignableFrom(type) - || Utf8String.class.isAssignableFrom(type) - || DynamicArray.class.isAssignableFrom(type)) { + || Utf8String.class.isAssignableFrom(type) + || DynamicArray.class.isAssignableFrom(type) + || DynamicStruct.class.isAssignableFrom(type) + || hasDynamicOffsetInStaticArray(typeReference)) { return TypeDecoder.decodeUintAsInt(input, offset) << 1; } else { return offset; } } + + private static boolean hasDynamicOffsetInStaticArray(TypeReference typeReference) + throws ClassNotFoundException { + @SuppressWarnings("unchecked") + Class type = (Class) typeReference.getClassType(); + try { + return StaticArray.class.isAssignableFrom(type) + && isDynamic(typeReference); + } catch (ClassCastException e) { + return false; + } + } } diff --git a/abi/src/main/java/org/tron/trident/abi/EventEncoder.java b/abi/src/main/java/org/tron/trident/abi/EventEncoder.java index 85a8c6d2..77ce2e3d 100644 --- a/abi/src/main/java/org/tron/trident/abi/EventEncoder.java +++ b/abi/src/main/java/org/tron/trident/abi/EventEncoder.java @@ -36,6 +36,16 @@ public static String encode(Event event) { return buildEventSignature(methodSignature); } + /** + * Encodes the given {@code Event} and removes '0x' of the encoded string. + * + * @param event the {@code Event} instance to be encoded + * @return a hexadecimal string representing the encoded event, excluding the prefix '0x' + */ + public static String encodeWithOutPrefix(Event event) { + return encode(event).substring(2); + } + static String buildMethodSignature( String methodName, List> parameters) { @@ -54,4 +64,14 @@ public static String buildEventSignature(String methodSignature) { byte[] hash = Hash.sha3(input); return Numeric.toHexString(hash); } + + /** + * Generates the event signature hash without the '0x' prefix + * + * @param methodSignature the string representation of the method signature for the event + * @return a hexadecimal string representing the event signature hash, excluding the prefix '0x' + */ + public static String buildEventSignatureWithOutPrefix(String methodSignature) { + return buildEventSignature(methodSignature).substring(2); + } } diff --git a/abi/src/main/java/org/tron/trident/abi/FunctionEncoder.java b/abi/src/main/java/org/tron/trident/abi/FunctionEncoder.java index 4e326d13..ae60e1a9 100644 --- a/abi/src/main/java/org/tron/trident/abi/FunctionEncoder.java +++ b/abi/src/main/java/org/tron/trident/abi/FunctionEncoder.java @@ -13,6 +13,9 @@ package org.tron.trident.abi; +import static org.tron.trident.abi.TypeDecoder.instantiateType; +import static org.tron.trident.abi.TypeReference.makeTypeReference; + import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Iterator; @@ -32,19 +35,35 @@ * @see DefaultFunctionEncoder * @see FunctionEncoderProvider */ + public abstract class FunctionEncoder { - private static FunctionEncoder DEFAULT_ENCODER; + private static final FunctionEncoder FUNCTION_ENCODER; - private static final ServiceLoader loader = - ServiceLoader.load(FunctionEncoderProvider.class); + static { + ServiceLoader loader = + ServiceLoader.load(FunctionEncoderProvider.class); + final Iterator iterator = loader.iterator(); + + FUNCTION_ENCODER = + iterator.hasNext() ? iterator.next().get() : new DefaultFunctionEncoder(); + } public static String encode(final Function function) { - return encoder().encodeFunction(function); + return FUNCTION_ENCODER.encodeFunction(function); + } + + /** Encode function when we know function method Id / Selector. */ + public static String encode(final String methodId, final List parameters) { + return FUNCTION_ENCODER.encodeWithSelector(methodId, parameters); } public static String encodeConstructor(final List parameters) { - return encoder().encodeParameters(parameters); + return FUNCTION_ENCODER.encodeParameters(parameters); + } + + public static String encodeConstructorPacked(final List parameters) { + return FUNCTION_ENCODER.encodePackedParameters(parameters); } public static Function makeFunction( @@ -52,16 +71,19 @@ public static Function makeFunction( List solidityInputTypes, List arguments, List solidityOutputTypes) - throws ClassNotFoundException, NoSuchMethodException, InstantiationException, - IllegalAccessException, InvocationTargetException { + throws ClassNotFoundException, + NoSuchMethodException, + InstantiationException, + IllegalAccessException, + InvocationTargetException { List encodedInput = new ArrayList<>(); Iterator argit = arguments.iterator(); for (String st : solidityInputTypes) { - encodedInput.add(TypeDecoder.instantiateType(st, argit.next())); + encodedInput.add(instantiateType(st, argit.next())); } List> encodedOutput = new ArrayList<>(); for (String st : solidityOutputTypes) { - encodedOutput.add(TypeReference.makeTypeReference(st)); + encodedOutput.add(makeTypeReference(st)); } return new Function(fnname, encodedInput, encodedOutput); } @@ -70,6 +92,14 @@ public static Function makeFunction( protected abstract String encodeParameters(List parameters); + /** + * @param methodId Callback selector / Abi method Id (Hex format) + */ + protected abstract String encodeWithSelector( + final String methodId, final List parameters); + + protected abstract String encodePackedParameters(List parameters); + protected static String buildMethodSignature( final String methodName, final List parameters) { @@ -88,16 +118,4 @@ protected static String buildMethodId(final String methodSignature) { final byte[] hash = Hash.sha3(input); return Numeric.toHexString(hash).substring(2, 10); } - - private static FunctionEncoder encoder() { - final Iterator iterator = loader.iterator(); - return iterator.hasNext() ? iterator.next().get() : defaultEncoder(); - } - - private static FunctionEncoder defaultEncoder() { - if (DEFAULT_ENCODER == null) { - DEFAULT_ENCODER = new DefaultFunctionEncoder(); - } - return DEFAULT_ENCODER; - } } diff --git a/abi/src/main/java/org/tron/trident/abi/FunctionReturnDecoder.java b/abi/src/main/java/org/tron/trident/abi/FunctionReturnDecoder.java index e07bd048..7b560d14 100644 --- a/abi/src/main/java/org/tron/trident/abi/FunctionReturnDecoder.java +++ b/abi/src/main/java/org/tron/trident/abi/FunctionReturnDecoder.java @@ -13,10 +13,13 @@ package org.tron.trident.abi; +import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; +import org.tron.trident.abi.datatypes.Address; +import org.tron.trident.abi.datatypes.DynamicBytes; import org.tron.trident.abi.datatypes.Type; import org.tron.trident.abi.spi.FunctionReturnDecoderProvider; @@ -32,10 +35,15 @@ */ public abstract class FunctionReturnDecoder { - private static FunctionReturnDecoder DEFAULT_DECODER; + private static final FunctionReturnDecoder decoder; - private static final ServiceLoader loader = - ServiceLoader.load(FunctionReturnDecoderProvider.class); + static { + ServiceLoader loader = + ServiceLoader.load(FunctionReturnDecoderProvider.class); + final Iterator iterator = loader.iterator(); + + decoder = iterator.hasNext() ? iterator.next().get() : new DefaultFunctionReturnDecoder(); + } /** * Decode ABI encoded return values from smart contract function call. @@ -46,9 +54,34 @@ public abstract class FunctionReturnDecoder { * invalid response */ public static List decode(String rawInput, List> outputParameters) { - return decoder().decodeFunctionResult(rawInput, outputParameters); + return decoder.decodeFunctionResult(rawInput, outputParameters); + } + + /** + * Decode ABI encoded return value DynamicBytes from smart contract function call. + * + * @param rawInput ABI encoded input + * @return {@link DynamicBytes} of values returned by function, null if invalid response + */ + public static byte[] decodeDynamicBytes(String rawInput) { + List outputParameters = new ArrayList>(); + outputParameters.add(new TypeReference() {}); + + List typeList = decoder.decodeFunctionResult(rawInput, outputParameters); + + return typeList.isEmpty() ? null : ((DynamicBytes) typeList.get(0)).getValue(); + } + + public static String decodeAddress(String rawInput) { + List outputParameters = new ArrayList>(); + outputParameters.add(new TypeReference
() {}); + + List typeList = decoder.decodeFunctionResult(rawInput, outputParameters); + + return typeList.isEmpty() ? null : ((Address) typeList.get(0)).getValue(); } + /** * Decodes an indexed parameter associated with an event. Indexed parameters are individually * encoded, unlike non-indexed parameters which are encoded as per ABI-encoded function @@ -73,7 +106,7 @@ public static List decode(String rawInput, List> outpu */ public static Type decodeIndexedValue( String rawInput, TypeReference typeReference) { - return decoder().decodeEventParameter(rawInput, typeReference); + return decoder.decodeEventParameter(rawInput, typeReference); } protected abstract List decodeFunctionResult( @@ -82,15 +115,4 @@ protected abstract List decodeFunctionResult( protected abstract Type decodeEventParameter( String rawInput, TypeReference typeReference); - private static FunctionReturnDecoder decoder() { - final Iterator iterator = loader.iterator(); - return iterator.hasNext() ? iterator.next().get() : defaultDecoder(); - } - - private static FunctionReturnDecoder defaultDecoder() { - if (DEFAULT_DECODER == null) { - DEFAULT_DECODER = new DefaultFunctionReturnDecoder(); - } - return DEFAULT_DECODER; - } } diff --git a/abi/src/main/java/org/tron/trident/abi/TypeDecoder.java b/abi/src/main/java/org/tron/trident/abi/TypeDecoder.java index 87f120fb..b8abed6b 100644 --- a/abi/src/main/java/org/tron/trident/abi/TypeDecoder.java +++ b/abi/src/main/java/org/tron/trident/abi/TypeDecoder.java @@ -13,6 +13,12 @@ package org.tron.trident.abi; +import static org.tron.trident.abi.DefaultFunctionReturnDecoder.getDataOffset; +import static org.tron.trident.abi.TypeReference.makeTypeReference; +import static org.tron.trident.abi.Utils.findStructConstructor; +import static org.tron.trident.abi.Utils.getSimpleTypeName; +import static org.tron.trident.abi.Utils.staticStructNestedPublicFieldsFlatList; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; @@ -24,6 +30,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.BiFunction; import org.tron.trident.abi.datatypes.AbiTypes; import org.tron.trident.abi.datatypes.Address; @@ -41,6 +48,7 @@ import org.tron.trident.abi.datatypes.NumericType; import org.tron.trident.abi.datatypes.StaticArray; import org.tron.trident.abi.datatypes.StaticStruct; +import org.tron.trident.abi.datatypes.StructType; import org.tron.trident.abi.datatypes.Type; import org.tron.trident.abi.datatypes.Ufixed; import org.tron.trident.abi.datatypes.Uint; @@ -54,20 +62,30 @@ * Ethereum Contract Application Binary Interface (ABI) decoding for types. Decoding is not * documented, but is the reverse of the encoding details located here. + * + *

The public API is composed of "decode*" methods and provides backward-compatibility. See + * https://github.com/hyperledger/web3j/issues/1591 for a discussion about decoding and possible + * improvements. */ public class TypeDecoder { static final int MAX_BYTE_LENGTH_FOR_HEX_STRING = Type.MAX_BYTE_LENGTH << 1; public static Type instantiateType(String solidityType, Object value) - throws InvocationTargetException, NoSuchMethodException, InstantiationException, - IllegalAccessException, ClassNotFoundException { - return instantiateType(TypeReference.makeTypeReference(solidityType), value); + throws InvocationTargetException, + NoSuchMethodException, + InstantiationException, + IllegalAccessException, + ClassNotFoundException { + return instantiateType(makeTypeReference(solidityType), value); } public static Type instantiateType(TypeReference ref, Object value) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, - InstantiationException, ClassNotFoundException { + throws NoSuchMethodException, + IllegalAccessException, + InvocationTargetException, + InstantiationException, + ClassNotFoundException { Class rc = ref.getClassType(); if (Array.class.isAssignableFrom(rc)) { return instantiateArrayType(ref, value); @@ -116,6 +134,11 @@ static T decode(String input, Class type) { return decode(input, 0, type); } + public static T decode(String input, TypeReference type) + throws ClassNotFoundException { + return decode(input, 0, ((TypeReference) type).getClassType()); + } + public static Address decodeAddress(String input) { return new Address(decodeNumeric(input, Uint160.class)); } @@ -124,17 +147,16 @@ public static T decodeNumeric(String input, Class typ try { byte[] inputByteArray = Numeric.hexStringToByteArray(input); int typeLengthAsBytes = getTypeLengthInBytes(type); - - byte[] resultByteArray = new byte[typeLengthAsBytes + 1]; - - if (Int.class.isAssignableFrom(type) || Fixed.class.isAssignableFrom(type)) { - resultByteArray[0] = inputByteArray[0]; // take MSB as sign bit - } - int valueOffset = Type.MAX_BYTE_LENGTH - typeLengthAsBytes; - System.arraycopy(inputByteArray, valueOffset, resultByteArray, 1, typeLengthAsBytes); + byte[] slice = + Arrays.copyOfRange(inputByteArray, valueOffset, valueOffset + typeLengthAsBytes); - BigInteger numericValue = new BigInteger(resultByteArray); + BigInteger numericValue; + if (Uint.class.isAssignableFrom(type) || Ufixed.class.isAssignableFrom(type)) { + numericValue = new BigInteger(1, slice); + } else { + numericValue = new BigInteger(slice); + } return type.getConstructor(BigInteger.class).newInstance(numericValue); } catch (NoSuchMethodException @@ -172,8 +194,11 @@ static int getTypeLength(Class type) { } static Type instantiateArrayType(TypeReference ref, Object value) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, - InstantiationException, ClassNotFoundException { + throws NoSuchMethodException, + IllegalAccessException, + InvocationTargetException, + InstantiationException, + ClassNotFoundException { List values; if (value instanceof List) { values = (List) value; @@ -183,7 +208,7 @@ static Type instantiateArrayType(TypeReference ref, Object value) throw new ClassCastException( "Arg of type " + value.getClass() - + " should be a list to instantiate web3j Array"); + + " should be a list to instantiate trident Array"); } Constructor listcons; int arraySize = @@ -207,8 +232,11 @@ static Type instantiateArrayType(TypeReference ref, Object value) } static Type instantiateAtomicType(Class referenceClass, Object value) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, - InstantiationException, ClassNotFoundException { + throws NoSuchMethodException, + IllegalAccessException, + InvocationTargetException, + InstantiationException, + ClassNotFoundException { Object constructorArg = null; if (NumericType.class.isAssignableFrom(referenceClass)) { constructorArg = asBigInteger(value); @@ -250,6 +278,7 @@ static Type instantiateAtomicType(Class referenceClass, Object value) return (Type) cons.newInstance(constructorArg); } + @SuppressWarnings("unchecked") static int getSingleElementLength(String input, int offset, Class type) { if (input.length() == offset) { return 0; @@ -257,6 +286,8 @@ static int getSingleElementLength(String input, int offset, Cla || Utf8String.class.isAssignableFrom(type)) { // length field + data value return (decodeUintAsInt(input, offset) / Type.MAX_BYTE_LENGTH) + 2; + } else if (StaticStruct.class.isAssignableFrom(type)) { + return staticStructNestedPublicFieldsFlatList((Class) type).size(); } else { return 1; } @@ -331,7 +362,7 @@ static T decodeStaticArray( throw new UnsupportedOperationException( "Zero length fixed array is invalid type"); } else { - return instantiateStaticArray(typeReference, elements, length); + return instantiateStaticArray(elements, length); } }; @@ -350,9 +381,67 @@ public static T decodeStaticStruct( } }; + if (typeReference.getInnerTypes() != null) { + return decodeStaticStructElementFromInnerTypes(input, offset, typeReference, function); + } + return decodeStaticStructElement(input, offset, typeReference, function); } + private static int extractStaticArrayLength(TypeReference typeReference) { + if (typeReference instanceof TypeReference.StaticArrayTypeReference) { + return ((TypeReference.StaticArrayTypeReference) typeReference).getSize(); + } + try { + Class cls = typeReference.getClassType(); + return Integer.parseInt(cls.getSimpleName().replaceAll("\\D+", "")); + } catch (Exception e) { + throw new UnsupportedOperationException( + "Cannot determine StaticArray length from " + typeReference.getType(), e); + } + } + + @SuppressWarnings("unchecked") + private static T decodeStaticStructElementFromInnerTypes( + final String input, + final int offset, + final TypeReference typeReference, + final BiFunction, String, T> consumer) { + try { + final List> innerTypes = typeReference.getInnerTypes(); + List elements = new ArrayList<>(innerTypes.size()); + + for (int i = 0, currOffset = offset; i < innerTypes.size(); i++) { + T value; + final TypeReference innerType = (TypeReference) innerTypes.get(i); + final Class declaredField = innerType.getClassType(); + + if (StaticStruct.class.isAssignableFrom(declaredField)) { + value = decodeStaticStruct(input, currOffset, innerType); + currOffset += (value.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else if (StaticArray.class.isAssignableFrom(declaredField)) { + int staticLength = extractStaticArrayLength(innerType); + value = (T) decodeStaticArray(input, currOffset, innerType, staticLength); + currOffset += (value.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else { + value = decode(input.substring(currOffset, currOffset + 64), 0, declaredField); + currOffset += MAX_BYTE_LENGTH_FOR_HEX_STRING; + } + elements.add(value); + } + + return consumer.apply(elements, getSimpleTypeName(typeReference.getClassType())); + } catch (ClassNotFoundException e) { + throw new UnsupportedOperationException( + "Unable to access parameterized type " + + Utils.getTypeName(typeReference.getType()), + e); + } + } + + @SuppressWarnings("unchecked") private static T decodeStaticStructElement( final String input, final int offset, @@ -360,23 +449,14 @@ private static T decodeStaticStructElement( final BiFunction, String, T> consumer) { try { Class classType = typeReference.getClassType(); - Constructor constructor = Arrays.stream(classType.getDeclaredConstructors()) - .filter( - declaredConstructor -> - Arrays.stream(declaredConstructor.getParameterTypes()) - .allMatch(Type.class::isAssignableFrom)) - .findAny() - .orElseThrow(() -> - new RuntimeException( - "TypeReferenced struct must contain a constructor with types that extend Type")); + Constructor constructor = findStructConstructor(classType); final int length = constructor.getParameterCount(); List elements = new ArrayList<>(length); - for (int i = 0, currOffset = 0; i < length; i++) { + for (int i = 0, currOffset = offset; i < length; i++) { T value; final Class declaredField = (Class) constructor.getParameterTypes()[i]; - //System.out.println(currOffset); if (StaticStruct.class.isAssignableFrom(declaredField)) { final int nestedStructLength = classType @@ -399,31 +479,31 @@ private static T decodeStaticStructElement( elements.add(value); } - String typeName = Utils.getSimpleTypeName(classType); + String typeName = getSimpleTypeName(classType); return consumer.apply(elements, typeName); } catch (ClassNotFoundException e) { throw new UnsupportedOperationException( - "Unable to access parameterized type " + typeReference.getType().getTypeName(), + "Unable to access parameterized type " + + Utils.getTypeName(typeReference.getType()), e); } } + @SuppressWarnings("unchecked") private static T instantiateStruct( final TypeReference typeReference, final List parameters) { try { - Constructor ctor = Arrays.stream(typeReference.getClassType().getDeclaredConstructors()) - .filter( - declaredConstructor -> - Arrays.stream(declaredConstructor.getParameterTypes()) - .allMatch(Type.class::isAssignableFrom)) - .findAny() - .orElseThrow(() -> - new RuntimeException( - "TypeReference struct must contain a constructor with types that extend Type")); - - ctor.setAccessible(true); - return (T) ctor.newInstance(parameters.toArray()); + Class classType = typeReference.getClassType(); + if (classType.isAssignableFrom(DynamicStruct.class)) { + return (T) new DynamicStruct((List) parameters); + } else if (classType.isAssignableFrom(StaticStruct.class)) { + return (T) new StaticStruct((List) parameters); + } else { + Constructor ctor = findStructConstructor(classType); + ctor.setAccessible(true); + return (T) ctor.newInstance(parameters.toArray()); + } } catch (ReflectiveOperationException e) { throw new UnsupportedOperationException( "Constructor cannot accept" + Arrays.toString(parameters.toArray()), e); @@ -445,21 +525,141 @@ public static T decodeDynamicArray( } public static T decodeDynamicStruct( - String input, int offset, TypeReference typeReference) { + String input, int offset, TypeReference typeReference) + throws ClassNotFoundException { BiFunction, String, T> function = (elements, typeName) -> { if (elements.isEmpty()) { throw new UnsupportedOperationException( "Zero length fixed array is invalid type"); - } else { - return instantiateStruct(typeReference, elements); } + return instantiateStruct(typeReference, elements); }; + if (typeReference.getClassType().isAssignableFrom(DynamicStruct.class) + && typeReference.getInnerTypes() != null) { + return decodeDynamicStructElementsFromInnerTypes( + input, offset, typeReference, function); + } + return decodeDynamicStructElements(input, offset, typeReference, function); } + private static class ParameterOffsetTracker { + public final Map parameters; + public final List parameterOffsets; + public int staticOffset; + public int dynamicParametersToProcess; + + ParameterOffsetTracker( + final Map parametersIn, + final List parameterOffsetsIn, + int staticOffsetIn, + int dynamicParametersToProcessIn) { + this.parameters = parametersIn; + this.parameterOffsets = parameterOffsetsIn; + this.staticOffset = staticOffsetIn; + this.dynamicParametersToProcess = dynamicParametersToProcessIn; + } + } + + private static + ParameterOffsetTracker getDynamicOffsetsAndNonDynamicParameters( + final String input, final int offset, final TypeReference typeReference) + throws ClassNotFoundException { + ParameterOffsetTracker tracker = + new ParameterOffsetTracker(new HashMap<>(), new ArrayList<>(), 0, 0); + + final List> innerTypes = typeReference.getInnerTypes(); + for (int i = 0; i < innerTypes.size(); ++i) { + final TypeReference innerType = (TypeReference) innerTypes.get(i); + final Class declaredField = innerType.getClassType(); + final T value; + final int beginIndex = offset + tracker.staticOffset; + if (isDynamic(innerType)) { + final int parameterOffset = + decodeDynamicStructDynamicParameterOffset( + input.substring(beginIndex, beginIndex + 64)) + + offset; + tracker.parameterOffsets.add(parameterOffset); + tracker.staticOffset += 64; + tracker.dynamicParametersToProcess += 1; + } else { + if (StaticStruct.class.isAssignableFrom(declaredField)) { + value = decodeStaticStruct(input.substring(beginIndex), 0, innerType); + tracker.staticOffset += (value.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else if (StaticArray.class.isAssignableFrom(declaredField)) { + int staticLength = extractStaticArrayLength(innerType); + value = (T) decodeStaticArray(input, beginIndex, innerType, staticLength); + tracker.staticOffset += (value.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else { + value = decode(input.substring(beginIndex), 0, declaredField); + tracker.staticOffset += value.bytes32PaddedLength() * 2; + } + tracker.parameters.put(i, value); + } + } + + return tracker; + } + + private static List getDynamicParametersWithTracker( + final String input, + final TypeReference typeReference, + final ParameterOffsetTracker tracker) + throws ClassNotFoundException { + + final List> innerTypes = typeReference.getInnerTypes(); + int dynamicParametersProcessed = 0; + for (int i = 0; i < innerTypes.size(); ++i) { + final TypeReference parameterTypeReference = (TypeReference) innerTypes.get(i); + if (isDynamic(parameterTypeReference)) { + final boolean isLastParameterInStruct = + dynamicParametersProcessed == (tracker.dynamicParametersToProcess - 1); + final int parameterLength = + isLastParameterInStruct + ? input.length() + - tracker.parameterOffsets.get(dynamicParametersProcessed) + : tracker.parameterOffsets.get(dynamicParametersProcessed + 1) + - tracker.parameterOffsets.get(dynamicParametersProcessed); + + tracker.parameters.put( + i, + decodeDynamicParameterFromStructWithTypeReference( + input, + tracker.parameterOffsets.get(dynamicParametersProcessed), + parameterLength, + parameterTypeReference)); + dynamicParametersProcessed++; + } + } + + final List elements = new ArrayList<>(); + for (int i = 0; i < innerTypes.size(); ++i) { + elements.add(tracker.parameters.get(i)); + } + + return elements; + } + + @SuppressWarnings("unchecked") + private static T decodeDynamicStructElementsFromInnerTypes( + final String input, + final int offset, + final TypeReference typeReference, + final BiFunction, String, T> consumer) + throws ClassNotFoundException { + ParameterOffsetTracker tracker = + getDynamicOffsetsAndNonDynamicParameters(input, offset, typeReference); + final List parameters = getDynamicParametersWithTracker(input, typeReference, tracker); + String typeName = getSimpleTypeName(typeReference.getClassType()); + return consumer.apply(parameters, typeName); + } + + @SuppressWarnings("unchecked") private static T decodeDynamicStructElements( final String input, final int offset, @@ -467,15 +667,7 @@ private static T decodeDynamicStructElements( final BiFunction, String, T> consumer) { try { final Class classType = typeReference.getClassType(); - Constructor constructor = Arrays.stream(classType.getDeclaredConstructors()) - .filter( - declaredConstructor -> - Arrays.stream(declaredConstructor.getParameterTypes()) - .allMatch(Type.class::isAssignableFrom)) - .findAny() - .orElseThrow(() -> - new RuntimeException( - "TypeReferenced struct must contain a constructor with types that extend Type")); + Constructor constructor = findStructConstructor(classType); final int length = constructor.getParameterCount(); final Map parameters = new HashMap<>(); int staticOffset = 0; @@ -485,12 +677,10 @@ private static T decodeDynamicStructElements( final T value; final int beginIndex = offset + staticOffset; if (isDynamic(declaredField)) { - final boolean isOnlyParameterInStruct = length == 1; final int parameterOffset = - isOnlyParameterInStruct - ? offset - : decodeDynamicStructDynamicParameterOffset( - input.substring(beginIndex, beginIndex + 64)); + decodeDynamicStructDynamicParameterOffset( + input.substring(beginIndex, beginIndex + 64)) + + offset; parameterOffsets.add(parameterOffset); staticOffset += 64; } else { @@ -500,11 +690,15 @@ private static T decodeDynamicStructElements( input.substring(beginIndex), 0, TypeReference.create(declaredField)); + staticOffset += + staticStructNestedPublicFieldsFlatList((Class) declaredField) + .size() + * MAX_BYTE_LENGTH_FOR_HEX_STRING; } else { value = decode(input.substring(beginIndex), 0, declaredField); + staticOffset += value.bytes32PaddedLength() * 2; } parameters.put(i, value); - staticOffset += value.bytes32PaddedLength() * 2; } } int dynamicParametersProcessed = 0; @@ -521,18 +715,22 @@ private static T decodeDynamicStructElements( - parameterOffsets.get(dynamicParametersProcessed) : parameterOffsets.get(dynamicParametersProcessed + 1) - parameterOffsets.get(dynamicParametersProcessed); + final Class parameterFromAnnotation = + Utils.extractParameterFromAnnotation( + constructor.getParameterAnnotations()[i]); parameters.put( i, decodeDynamicParameterFromStruct( input, parameterOffsets.get(dynamicParametersProcessed), parameterLength, - declaredField)); + declaredField, + parameterFromAnnotation)); dynamicParametersProcessed++; } } - String typeName = Utils.getSimpleTypeName(classType); + String typeName = getSimpleTypeName(classType); final List elements = new ArrayList<>(); for (int i = 0; i < length; ++i) { @@ -542,7 +740,8 @@ private static T decodeDynamicStructElements( return consumer.apply(elements, typeName); } catch (ClassNotFoundException e) { throw new UnsupportedOperationException( - "Unable to access parameterized type " + typeReference.getType().getTypeName(), + "Unable to access parameterized type " + + Utils.getTypeName(typeReference.getType()), e); } } @@ -557,15 +756,51 @@ private static T decodeDynamicParameterFromStruct( final String input, final int parameterOffset, final int parameterLength, - final Class declaredField) { + final Class declaredField, + final Class parameter) + throws ClassNotFoundException { final String dynamicElementData = input.substring(parameterOffset, parameterOffset + parameterLength); final T value; if (DynamicStruct.class.isAssignableFrom(declaredField)) { + value = decodeDynamicStruct(dynamicElementData, 0, TypeReference.create(declaredField)); + } else if (DynamicArray.class.isAssignableFrom(declaredField)) { + if (parameter == null) { + throw new RuntimeException( + "parameter can not be null, try to use annotation @Parameterized " + + "to specify the parameter type"); + } value = - decodeDynamicStruct( - dynamicElementData, 64, TypeReference.create(declaredField)); + (T) + decodeDynamicArray( + dynamicElementData, + 0, + Utils.getDynamicArrayTypeReference(parameter)); + } else { + value = decode(dynamicElementData, declaredField); + } + return value; + } + + private static T decodeDynamicParameterFromStructWithTypeReference( + final String input, + final int parameterOffset, + final int parameterLength, + final TypeReference parameterTypeReference) + throws ClassNotFoundException { + final String dynamicElementData = + input.substring(parameterOffset, parameterOffset + parameterLength); + final Class declaredField = parameterTypeReference.getClassType(); + + final T value; + if (DynamicStruct.class.isAssignableFrom(declaredField)) { + value = decodeDynamicStruct(dynamicElementData, 0, parameterTypeReference); + } else if (DynamicArray.class.isAssignableFrom(declaredField)) { + value = (T) decodeDynamicArray(dynamicElementData, 0, parameterTypeReference); + } else if (StaticArray.class.isAssignableFrom(declaredField)) { + int staticLength = extractStaticArrayLength(parameterTypeReference); + value = (T) decodeStaticArray(dynamicElementData, 0, parameterTypeReference, staticLength); } else { value = decode(dynamicElementData, declaredField); } @@ -573,13 +808,56 @@ private static T decodeDynamicParameterFromStruct( } private static int decodeDynamicStructDynamicParameterOffset(final String input) { - return (decodeUintAsInt(input, 0) * 2) + 64; + return (decodeUintAsInt(input, 0) * 2); } static boolean isDynamic(Class parameter) { return DynamicBytes.class.isAssignableFrom(parameter) || Utf8String.class.isAssignableFrom(parameter) - || DynamicArray.class.isAssignableFrom(parameter); + || DynamicArray.class.isAssignableFrom(parameter) + || DynamicStruct.class.isAssignableFrom(parameter); + } + + /** + * Recursive ABI-dynamic check: a type is ABI-dynamic if it is itself a dynamic class + * (per {@link #isDynamic(Class)}) OR it is a StaticArray whose element type is ABI-dynamic. + * E.g. {@code string[3]} is ABI-dynamic because {@code string} is dynamic, even though + * its outer Java class is StaticArray. + */ + @SuppressWarnings("unchecked") + static boolean isDynamic(TypeReference typeReference) { + try { + Class cls = (Class) typeReference.getClassType(); + if (isDynamic(cls)) { + return true; + } + if (StaticArray.class.isAssignableFrom(cls)) { + TypeReference subRef = typeReference.getSubTypeReference(); + if (subRef != null) { + return isDynamic(subRef); + } + java.lang.reflect.Type type = typeReference.getType(); + if (type instanceof ParameterizedType) { + final java.lang.reflect.Type elementType = + ((ParameterizedType) type).getActualTypeArguments()[0]; + return isDynamic(new TypeReference() { + @Override + public java.lang.reflect.Type getType() { + return elementType; + } + }); + } + try { + Class paramType = Utils.getParameterizedTypeFromArray(typeReference); + return isDynamic(paramType); + } catch (Exception e) { + return false; + } + } + return false; + } catch (ClassNotFoundException e) { + return false; + } } static BigInteger asBigInteger(Object arg) { @@ -612,8 +890,7 @@ static List arrayToList(Object array) { } @SuppressWarnings("unchecked") - private static T instantiateStaticArray( - TypeReference typeReference, List elements, int length) { + private static T instantiateStaticArray(List elements, int length) { try { Class arrayClass = (Class) @@ -631,33 +908,182 @@ private static T decodeArrayElements( TypeReference typeReference, int length, BiFunction, String, T> consumer) { - try { Class cls = Utils.getParameterizedTypeFromArray(typeReference); - 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"); + List elements = new ArrayList<>(length); + if (StructType.class.isAssignableFrom(cls)) { + int currOffset = offset; + for (int i = 0; i < length; i++) { + T value; + if (DynamicStruct.class.isAssignableFrom(cls)) { + if (Optional.ofNullable(typeReference) + .map(x -> x.getSubTypeReference()) + .map(x -> x.getInnerTypes()) + .isPresent()) { + value = + TypeDecoder.decodeDynamicStruct( + input, + offset + getDataOffset(input, currOffset, typeReference), + (TypeReference) new TypeReference( + typeReference.isIndexed(), + typeReference.getSubTypeReference().getInnerTypes()) {}); + currOffset += + getSingleElementLength(input, currOffset, cls) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else { + value = + TypeDecoder.decodeDynamicStruct( + input, + offset + + getDataOffset( + input, currOffset, typeReference), + TypeReference.create(cls)); + currOffset += + getSingleElementLength(input, currOffset, cls) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } + } else { + if (Optional.ofNullable(typeReference) + .map(x -> x.getSubTypeReference()) + .map(x -> x.getInnerTypes()) + .isPresent()) { + value = TypeDecoder.decodeStaticStruct( + input, + currOffset, + (TypeReference) typeReference.getSubTypeReference()); + currOffset += + (value.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else { + value = + TypeDecoder.decodeStaticStruct( + input, currOffset, TypeReference.create(cls)); + currOffset += + (value.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } + } + elements.add(value); + } + + String typeName = getSimpleTypeName(cls); + + return consumer.apply(elements, typeName); + } else if (Array.class.isAssignableFrom(cls)) { + for (int i = 0, currOffset = offset; i < length; i++) { + T value; + if (DynamicArray.class.isAssignableFrom(cls)) { + TypeReference dynamicTypeRef = + Utils.resolveDynamicArrayElementTypeReference(typeReference); + value = + (T) + TypeDecoder.decodeDynamicArray( + input, + offset + + getDataOffset( + input, currOffset, typeReference), + dynamicTypeRef); + 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); + final TypeReference innerType = + Utils.resolveStaticArrayInnerTypeReference(typeReference); + + TypeReference.StaticArrayTypeReference staticReference = + new TypeReference.StaticArrayTypeReference( + staticLength) { + + @Override + public 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; + } + }; + } + }; + if (isDynamic(staticReference)) { + // StaticArray with dynamic elements (e.g. string[3]) is ABI-dynamic: + // outer container stores an offset pointer (1 slot); actual data + // is at offset + pointerValue, in head/tail form. + int hexStringDataOffset = + getDataOffset(input, currOffset, staticReference); + value = + (T) + TypeDecoder.decodeStaticArray( + input, + offset + hexStringDataOffset, + staticReference, + staticLength); + currOffset += MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else { + // All-static StaticArray: inline, no length prefix. + value = + (T) + TypeDecoder.decodeStaticArray( + input, currOffset, staticReference, staticLength); + currOffset += + (value.bytes32PaddedLength() / Type.MAX_BYTE_LENGTH) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } + } + elements.add(value); + } + return consumer.apply(elements, cls.getName()); } else { - List elements = new ArrayList<>(length); - - for (int i = 0, currOffset = offset; - i < length; - i++, - currOffset += - getSingleElementLength(input, currOffset, cls) - * MAX_BYTE_LENGTH_FOR_HEX_STRING) { - T value = decode(input, currOffset, cls); + int currOffset = offset; + for (int i = 0; i < length; i++) { + T value; + if (isDynamic(cls)) { + int hexStringDataOffset = getDataOffset(input, currOffset, typeReference); + value = decode(input, offset + hexStringDataOffset, cls); + currOffset += MAX_BYTE_LENGTH_FOR_HEX_STRING; + } else { + value = decode(input, currOffset, cls); + currOffset += + getSingleElementLength(input, currOffset, cls) + * MAX_BYTE_LENGTH_FOR_HEX_STRING; + } elements.add(value); } - String typeName = Utils.getSimpleTypeName(cls); + String typeName = getSimpleTypeName(cls); return consumer.apply(elements, typeName); } } catch (ClassNotFoundException e) { throw new UnsupportedOperationException( - "Unable to access parameterized type " + typeReference.getType().getTypeName(), + "Unable to access parameterized type " + + Utils.getTypeName(typeReference.getType()), e); } } diff --git a/abi/src/main/java/org/tron/trident/abi/TypeEncoder.java b/abi/src/main/java/org/tron/trident/abi/TypeEncoder.java index 6ff0b67a..ac0fdcb5 100644 --- a/abi/src/main/java/org/tron/trident/abi/TypeEncoder.java +++ b/abi/src/main/java/org/tron/trident/abi/TypeEncoder.java @@ -20,6 +20,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.tron.trident.abi.datatypes.Address; import org.tron.trident.abi.datatypes.Array; import org.tron.trident.abi.datatypes.Bool; @@ -28,8 +29,11 @@ import org.tron.trident.abi.datatypes.DynamicArray; import org.tron.trident.abi.datatypes.DynamicBytes; import org.tron.trident.abi.datatypes.DynamicStruct; +import org.tron.trident.abi.datatypes.Fixed; +import org.tron.trident.abi.datatypes.FixedPointType; import org.tron.trident.abi.datatypes.NumericType; import org.tron.trident.abi.datatypes.StaticArray; +import org.tron.trident.abi.datatypes.StaticStruct; import org.tron.trident.abi.datatypes.Type; import org.tron.trident.abi.datatypes.Ufixed; import org.tron.trident.abi.datatypes.Uint; @@ -46,10 +50,34 @@ public class TypeEncoder { private TypeEncoder() { } + /** + * Determines if a given ABI type is dynamic. + * According to the ABI specification, dynamic types require an offset pointer in the tuple header + * (occupying exactly 1 slot / 32 bytes) rather than being encoded inline. This method recursively + * checks StaticArrays to properly identify if they contain any dynamic elements. + * + * @param parameter The ABI type parameter to check. + * @return true if the type is dynamic, false otherwise. + */ static boolean isDynamic(Type parameter) { - return parameter instanceof DynamicBytes + if (parameter instanceof DynamicBytes || parameter instanceof Utf8String - || parameter instanceof DynamicArray; + || parameter instanceof DynamicArray + || parameter instanceof DynamicStruct) { + return true; + } + if (parameter instanceof StaticArray) { + StaticArray staticArray = (StaticArray) parameter; + if (!staticArray.getValue().isEmpty()) { + return isDynamic((Type) staticArray.getValue().get(0)); + } + Class componentType = staticArray.getComponentType(); + return DynamicBytes.class.isAssignableFrom(componentType) + || Utf8String.class.isAssignableFrom(componentType) + || DynamicArray.class.isAssignableFrom(componentType) + || DynamicStruct.class.isAssignableFrom(componentType); + } + return false; } @SuppressWarnings("unchecked") @@ -67,7 +95,11 @@ public static String encode(Type parameter) { } else if (parameter instanceof Utf8String) { return encodeString((Utf8String) parameter); } else if (parameter instanceof StaticArray) { - return encodeArrayValues((StaticArray) parameter); + if (isDynamic(parameter)) { + return encodeStaticArrayWithDynamicStruct((StaticArray) parameter); + } else { + return encodeArrayValues((StaticArray) parameter); + } } else if (parameter instanceof DynamicStruct) { return encodeDynamicStruct((DynamicStruct) parameter); } else if (parameter instanceof DynamicArray) { @@ -80,6 +112,87 @@ public static String encode(Type parameter) { } } + /** + * Returns abi.encodePacked hex value for the supported types. First the value is encoded and + * after the padding or length, in arrays cases, is removed resulting the packed encode hex + * value + * + * @param parameter Value to be encoded + * @return + */ + public static String encodePacked(Type parameter) { + if (parameter instanceof Utf8String) { + // removePadding can also be used, but is not necessary + return Numeric.toHexStringNoPrefix( + ((Utf8String) parameter).getValue().getBytes(StandardCharsets.UTF_8)); + } else if (parameter instanceof DynamicBytes) { + // removePadding can also be used, but is not necessary + return Numeric.toHexStringNoPrefix(((DynamicBytes) parameter).getValue()); + } else if (parameter instanceof DynamicArray) { + return arrayEncodePacked((DynamicArray) parameter); + } else if (parameter instanceof StaticArray) { + return arrayEncodePacked((StaticArray) parameter); + } else if (parameter instanceof PrimitiveType) { + return encodePacked(((PrimitiveType) parameter).toSolidityType()); + } else { + return removePadding(encode(parameter), parameter); + } + } + + /** + * Remove padding from the static types and {@link Utf8String} after the encode was applied + * + * @param encodedValue Encoded value of the parameter + * @param parameter Value which was encoded + * @return The encoded value without padding + */ + static String removePadding(String encodedValue, Type parameter) { + if (parameter instanceof NumericType) { + if (parameter instanceof Ufixed || parameter instanceof Fixed) { + return encodedValue; + } + return encodedValue.substring(64 - ((NumericType) parameter).getBitSize() / 4, 64); + } else if (parameter instanceof Address) { + return encodedValue.substring(64 - ((Address) parameter).toUint().getBitSize() / 4, 64); + } else if (parameter instanceof Bool) { + return encodedValue.substring(62, 64); + } + if (parameter instanceof Bytes) { + return encodedValue.substring(0, ((BytesType) parameter).getValue().length * 2); + } + if (parameter instanceof Utf8String) { + int length = + ((Utf8String) parameter).getValue().getBytes(StandardCharsets.UTF_8).length; + return encodedValue.substring(64, 64 + length * 2); + } + if (parameter instanceof DynamicBytes) { + return encodedValue.substring( + 64, 64 + ((DynamicBytes) parameter).getValue().length * 2); + } else { + throw new UnsupportedOperationException( + "Type cannot be encoded: " + parameter.getClass()); + } + } + + /** + * Encodes a static array containing a dynamic struct type. In this case, the array items are + * decoded as dynamic values and have their offsets at the beginning of the encoding. Example: + * For the following static array containing three elements: StaticArray3 + * enc([struct1, struct2, struct2]) = offset(enc(struct1)) offset(enc(struct2)) + * offset(enc(struct3)) enc(struct1) enc(struct2) enc(struct3) + * + **/ + private static String encodeStaticArrayWithDynamicStruct(Array value) { + String valuesOffsets = encodeDynamicsTypesArraysOffsets(value); + String encodedValues = encodeArrayValues(value); + + StringBuilder result = new StringBuilder(); + result.append(valuesOffsets); + result.append(encodedValues); + return result.toString(); + } + + static String encodeAddress(Address address) { return encodeNumeric(address.toUint()); } @@ -199,8 +312,9 @@ private static String encodeDynamicStructValues(final DynamicStruct value) { Numeric.toBytesPadded( new BigInteger(Long.toString(dynamicOffset)), MAX_BYTE_LENGTH))); - dynamicValues.add(encode(type)); - dynamicOffset += type.bytes32PaddedLength(); + String encodedValue = encode(type); + dynamicValues.add(encodedValue); + dynamicOffset += encodedValue.length() >> 1; } else { offsetsAndStaticValues.add(encode(value.getValue().get(i))); } @@ -224,22 +338,48 @@ static String encodeDynamicArray(DynamicArray value) { return result.toString(); } + /** + * Encodes the array values offsets of the to be encrypted dynamic array, which are in our case + * the heads of the encryption. Refer to + * + * @see encoding + * formal specification + *

Dynamic structs array encryption

+ *

An array of dynamic structs (ie, structs containing dynamic datatypes) is encoded in + * the following way: Considering X = [struct1, struct2] for example enc(X) = head(struct1) + * head(struct2) tail(struct1) tail(struct2) with: - tail(struct1) = enc(struct1) - + * tail(struct2) = enc(struct2) - head(struct1) = enc(len( head(struct1) head(struct2))) = + * enc(64), because the heads are 256bits - head(struct2) = enc(len( head(struct1) + * head(struct2) tail(struct1))) + */ + private static String encodeArrayValuesOffsets(DynamicArray value) { StringBuilder result = new StringBuilder(); boolean arrayOfBytes = !value.getValue().isEmpty() && value.getValue().get(0) instanceof DynamicBytes; boolean arrayOfString = !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; + boolean arrayOfDynamicStaticArrays = + !value.getValue().isEmpty() + && value.getValue().get(0) instanceof StaticArray + && isDynamic(value.getValue().get(0)); if (arrayOfBytes || arrayOfString) { long offset = 0; for (int i = 0; i < value.getValue().size(); i++) { if (i == 0) { - offset = value.getValue().size() * MAX_BYTE_LENGTH; + offset = (long) value.getValue().size() * MAX_BYTE_LENGTH; } else { int bytesLength = arrayOfBytes ? ((byte[]) value.getValue().get(i - 1).getValue()).length - : ((String) value.getValue().get(i - 1).getValue()).length(); + : ((String) value.getValue().get(i - 1).getValue()) + .getBytes(StandardCharsets.UTF_8) + .length; int numberOfWords = (bytesLength + MAX_BYTE_LENGTH - 1) / MAX_BYTE_LENGTH; int totalBytesLength = numberOfWords * MAX_BYTE_LENGTH; offset += totalBytesLength + MAX_BYTE_LENGTH; @@ -249,7 +389,70 @@ private static String encodeArrayValuesOffsets(DynamicArray Numeric.toBytesPadded( new BigInteger(Long.toString(offset)), MAX_BYTE_LENGTH))); } + } else if (arrayOfDynamicArrays || arrayOfDynamicStructs || arrayOfDynamicStaticArrays) { + result.append(encodeDynamicsTypesArraysOffsets(value)); } return result.toString(); } + + /** + * 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 String encodeDynamicsTypesArraysOffsets(Array value) { + StringBuilder result = new StringBuilder(); + long offset = value.getValue().size(); + List tailsEncoding = + value.getValue().stream().map(TypeEncoder::encode).collect(Collectors.toList()); + for (int i = 0; i < value.getValue().size(); i++) { + if (i == 0) { + offset = offset * MAX_BYTE_LENGTH; + } else { + offset += tailsEncoding.get(i - 1).length() / 2; + } + result.append( + Numeric.toHexStringNoPrefix( + Numeric.toBytesPadded( + new BigInteger(Long.toString(offset)), MAX_BYTE_LENGTH))); + } + return result.toString(); + } + + /** + * Checks if the received array doesn't contain any element that can make the array unsupported + * for abi.encodePacked + * + * @param value Array to which the abi.encodePacked should be applied + * @param Types of elements from the array + * @return if the encodePacked is supported for the given array + */ + private static boolean isSupportingEncodedPacked(Array value) { + if (Utf8String.class.isAssignableFrom(value.getComponentType()) + || DynamicStruct.class.isAssignableFrom(value.getComponentType()) + || DynamicArray.class.isAssignableFrom(value.getComponentType()) + || StaticStruct.class.isAssignableFrom(value.getComponentType()) + || FixedPointType.class.isAssignableFrom(value.getComponentType()) + || DynamicBytes.class.isAssignableFrom(value.getComponentType())) { + return false; + } + return true; + } + + private static String arrayEncodePacked(Array values) { + if (isSupportingEncodedPacked(values)) { + if (values.getValue().isEmpty()) { + return ""; + } + if (values instanceof DynamicArray) { + return encode(values).substring(64); + } else if (values instanceof StaticArray) { + return encode(values); + } + } + throw new UnsupportedOperationException( + "Type cannot be packed encoded: " + values.getClass()); + } } diff --git a/abi/src/main/java/org/tron/trident/abi/TypeReference.java b/abi/src/main/java/org/tron/trident/abi/TypeReference.java index bd7631d6..549ed3da 100644 --- a/abi/src/main/java/org/tron/trident/abi/TypeReference.java +++ b/abi/src/main/java/org/tron/trident/abi/TypeReference.java @@ -15,6 +15,7 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.tron.trident.abi.datatypes.AbiTypes; @@ -41,10 +42,17 @@ public abstract class TypeReference> innerTypes; + protected TypeReference() { this(false); } + protected TypeReference(boolean indexed, List> innerTypesIn) { + this(indexed); + this.innerTypes = innerTypesIn; + } + protected TypeReference(boolean indexed) { Type superclass = getClass().getGenericSuperclass(); if (superclass instanceof Class) { @@ -65,6 +73,11 @@ TypeReference getSubTypeReference() { return null; } + public List> getInnerTypes() { + return this.innerTypes; + } + + public int compareTo(TypeReference o) { // taken from the blog post comments - this results in an errror if the // type parameter is left out. @@ -93,7 +106,7 @@ public Class getClassType() throws ClassNotFoundException { if (getType() instanceof ParameterizedType) { return (Class) ((ParameterizedType) clsType).getRawType(); } else { - return (Class) Class.forName(clsType.getTypeName()); + return Utils.safeLoadTypeClass(Utils.getTypeName(clsType)); } } @@ -178,7 +191,7 @@ public static TypeReference makeTypeReference( arrayWrappedType = new TypeReference(indexed) { @Override - TypeReference getSubTypeReference() { + public TypeReference getSubTypeReference() { return baseTr; } @@ -216,7 +229,7 @@ public java.lang.reflect.Type getOwnerType() { new TypeReference.StaticArrayTypeReference(arraySizeInt) { @Override - TypeReference getSubTypeReference() { + public TypeReference getSubTypeReference() { return baseTr; } diff --git a/abi/src/main/java/org/tron/trident/abi/Utils.java b/abi/src/main/java/org/tron/trident/abi/Utils.java index 74d51d14..08c0f978 100644 --- a/abi/src/main/java/org/tron/trident/abi/Utils.java +++ b/abi/src/main/java/org/tron/trident/abi/Utils.java @@ -13,21 +13,35 @@ package org.tron.trident.abi; +import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.tron.trident.abi.datatypes.DynamicArray; import org.tron.trident.abi.datatypes.DynamicBytes; import org.tron.trident.abi.datatypes.Fixed; import org.tron.trident.abi.datatypes.Int; import org.tron.trident.abi.datatypes.StaticArray; +import org.tron.trident.abi.datatypes.StaticStruct; +import org.tron.trident.abi.datatypes.StructType; import org.tron.trident.abi.datatypes.Type; import org.tron.trident.abi.datatypes.Ufixed; import org.tron.trident.abi.datatypes.Uint; import org.tron.trident.abi.datatypes.Utf8String; +import org.tron.trident.abi.datatypes.reflection.Parameterized; /** * Utility functions. @@ -37,6 +51,27 @@ public class Utils { private Utils() { } + /** + * Reflectively loads a class by FQN and verifies it is an ABI {@link Type} + * subtype. Uses {@code initialize=false} so loading never triggers the + * target class's static initializer — defense-in-depth against side + * effects from unexpected class names sneaking into a TypeReference graph. + * + * @throws ClassNotFoundException if the named class cannot be located + * @throws UnsupportedOperationException if the named class is not a Type subtype + */ + @SuppressWarnings("unchecked") + static Class safeLoadTypeClass(String fqcn) + throws ClassNotFoundException { + Class loaded = Class.forName(fqcn, false, Utils.class.getClassLoader()); + if (!Type.class.isAssignableFrom(loaded)) { + throw new UnsupportedOperationException( + "Resolved class is not a subtype of " + Type.class.getName() + + ": " + fqcn); + } + return (Class) loaded; + } + static String getTypeName(TypeReference typeReference) { try { java.lang.reflect.Type reflectedType = typeReference.getType(); @@ -45,8 +80,13 @@ static String getTypeName(TypeReference typeReference) { if (reflectedType instanceof ParameterizedType) { type = (Class) ((ParameterizedType) reflectedType).getRawType(); return getParameterizedTypeName(typeReference, type); + } else if (typeReference.getSubTypeReference() != null) { + return getParameterizedTypeName(typeReference, typeReference.getClassType()); } else { - type = Class.forName(reflectedType.getTypeName()); + type = safeLoadTypeClass(getTypeName(reflectedType)); + if (StructType.class.isAssignableFrom(type)) { + return getStructType(type); + } return getSimpleTypeName(type); } } catch (ClassNotFoundException e) { @@ -54,6 +94,115 @@ static String getTypeName(TypeReference typeReference) { } } + /** Ports {@link java.lang.reflect.Type#getTypeName()}. */ + public static String getTypeName(java.lang.reflect.Type type) { + try { + return type.getTypeName(); + } catch (NoSuchMethodError e) { + return getClassName((Class) type); + } + } + + public static String getStructType(Class type) { + final StringBuilder sb = new StringBuilder("("); + Constructor constructor = findStructConstructor(type); + Class[] itemTypes = constructor.getParameterTypes(); + for (int i = 0; i < itemTypes.length; ++i) { + final Class cls = itemTypes[i]; + if (StructType.class.isAssignableFrom(cls)) { + sb.append(getStructType(cls)); + } else { + Class parameterAnnotation = + extractParameterFromAnnotation(constructor.getParameterAnnotations()[i]); + if (parameterAnnotation != null) { + try { + TypeReference typeRef = getTypeReferenceForParameterizedField(cls, parameterAnnotation); + sb.append(getTypeName(typeRef)); + } catch (ClassNotFoundException e) { + throw new RuntimeException( + "Failed to build TypeReference for @Parameterized field of type " + + cls.getName() + " with element type " + + parameterAnnotation.getName(), e); + } + } else { + sb.append(getTypeName(TypeReference.create(cls))); + } + } + if (i < itemTypes.length - 1) { + sb.append(","); + } + } + sb.append(")"); + return sb.toString(); + } + + public static TypeReference getDynamicArrayTypeReference(Class parameter) { + return new TypeReference() { + @Override + public TypeReference getSubTypeReference() { + return TypeReference.create(parameter); + } + }; + } + + public static TypeReference getTypeReferenceForParameterizedField( + Class fieldType, Class elementType) throws ClassNotFoundException { + if (StaticArray.class.isAssignableFrom(fieldType)) { + int size = extractStaticArraySize(fieldType); + String elementTypeName = getSimpleTypeName(elementType); + String solidityType = elementTypeName + "[" + size + "]"; + return TypeReference.makeTypeReference(solidityType); + } else { + return getDynamicArrayTypeReference(elementType); + } + } + + /** + * Extracts the array size from a generated {@code StaticArrayN} subclass. + * The bare {@link StaticArray} base class carries no size information and is rejected. + */ + private static int extractStaticArraySize(Class staticArrayClass) { + String className = staticArrayClass.getSimpleName(); + String prefix = "StaticArray"; + if (!className.startsWith(prefix) || className.length() == prefix.length()) { + throw new IllegalArgumentException( + "Cannot determine static array size from class " + staticArrayClass.getName() + + "; @Parameterized fields must use a generated StaticArrayN subclass."); + } + String sizeStr = className.substring(prefix.length()); + try { + return Integer.parseInt(sizeStr); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "Invalid static array size suffix in class " + staticArrayClass.getName(), e); + } + } + + public static Class extractParameterFromAnnotation( + Annotation[] parameterAnnotation) { + for (Annotation a : parameterAnnotation) { + if (Parameterized.class.isInstance(a)) { + return (Class) ((Parameterized) a).type(); + } + } + return null; + } + + public static Constructor findStructConstructor(Class classType) { + return Arrays.stream(classType.getDeclaredConstructors()) + .filter( + declaredConstructor -> + Arrays.stream(declaredConstructor.getParameterTypes()) + .allMatch(Type.class::isAssignableFrom)) + .findAny() + .orElseThrow( + () -> + new RuntimeException( + "TypeReferenced struct must contain " + + "a constructor with types that extend Type")); + } + + static String getSimpleTypeName(Class type) { String simpleName = type.getSimpleName().toLowerCase(); @@ -66,6 +215,8 @@ static String getSimpleTypeName(Class type) { return "string"; } else if (type.equals(DynamicBytes.class)) { return "bytes"; + } else if (StructType.class.isAssignableFrom(type)) { + return type.getName(); } else { return simpleName; } @@ -76,16 +227,18 @@ static String getParameterizedTypeName( try { if (type.equals(DynamicArray.class)) { - Class parameterizedType = getParameterizedTypeFromArray(typeReference); - String parameterizedTypeName = getSimpleTypeName(parameterizedType); + String parameterizedTypeName = getArrayElementTypeName(typeReference); return parameterizedTypeName + "[]"; - } else if (type.equals(StaticArray.class)) { - Class parameterizedType = getParameterizedTypeFromArray(typeReference); - String parameterizedTypeName = getSimpleTypeName(parameterizedType); - return parameterizedTypeName - + "[" - + ((TypeReference.StaticArrayTypeReference) typeReference).getSize() - + "]"; + } else if (StaticArray.class.isAssignableFrom(type)) { + String parameterizedTypeName = getArrayElementTypeName(typeReference); + final int length; + if (TypeReference.StaticArrayTypeReference.class.isAssignableFrom( + typeReference.getClass())) { + length = ((TypeReference.StaticArrayTypeReference) typeReference).getSize(); + } else { + length = Integer.parseInt(type.getSimpleName().replaceAll("\\D+", "")); + } + return parameterizedTypeName + "[" + length + "]"; } else { throw new UnsupportedOperationException("Invalid type provided " + type.getName()); } @@ -94,16 +247,150 @@ static String getParameterizedTypeName( } } + @SuppressWarnings("unchecked") + private static String getArrayElementTypeName(TypeReference typeReference) + throws ClassNotFoundException { + TypeReference subTypeReference = typeReference.getSubTypeReference(); + if (subTypeReference != null) { + return getTypeName((TypeReference) subTypeReference); + } + + java.lang.reflect.Type reflectedType = typeReference.getType(); + if (!(reflectedType instanceof ParameterizedType)) { + throw new UnsupportedOperationException("Invalid array type provided " + reflectedType); + } + + java.lang.reflect.Type elementType = + ((ParameterizedType) reflectedType).getActualTypeArguments()[0]; + if (elementType instanceof ParameterizedType) { + final java.lang.reflect.Type parameterizedElementType = elementType; + return getTypeName( + new TypeReference() { + @Override + public java.lang.reflect.Type getType() { + return parameterizedElementType; + } + }); + } + + Class elementClass; + if (elementType instanceof Class) { + elementClass = (Class) elementType; + } else { + elementClass = safeLoadTypeClass(elementType.getTypeName()); + } + if (!Type.class.isAssignableFrom(elementClass)) { + throw new UnsupportedOperationException( + "Resolved array element is not a subtype of " + Type.class.getName() + + ": " + elementType); + } + return simpleNameOrStruct((Class) elementClass); + } + + private static String simpleNameOrStruct(Class parameterizedType) { + if (StructType.class.isAssignableFrom(parameterizedType)) { + return getStructType(parameterizedType); + } + return getSimpleTypeName(parameterizedType); + } + + + /** + * Resolves the element TypeReference for a DynamicArray during decoding. + * Prefers the explicit {@code subTypeReference} (set by ABI-JSON path); falls back to + * reflecting on {@code getType()}; last resort synthesizes via + * {@link #getFullParameterizedTypeFromArray}. + */ + @SuppressWarnings("unchecked") + static TypeReference resolveDynamicArrayElementTypeReference(TypeReference outerRef) + throws ClassNotFoundException { + TypeReference sub = outerRef.getSubTypeReference(); + if (sub != null) { + return sub; + } + final java.lang.reflect.Type elementType = + ((ParameterizedType) outerRef.getType()).getActualTypeArguments()[0]; + if (elementType instanceof ParameterizedType) { + return new TypeReference() { + @Override + public java.lang.reflect.Type getType() { + return elementType; + } + }; + } + return getDynamicArrayTypeReference(getFullParameterizedTypeFromArray(outerRef)); + } + + /** + * Resolves the inner element TypeReference for a StaticArray during decoding, + * drilling two levels (outer-of-static-array, then element-of-static-array). + */ + @SuppressWarnings("unchecked") + static TypeReference resolveStaticArrayInnerTypeReference(TypeReference outerRef) { + TypeReference sub = outerRef.getSubTypeReference(); + if (sub != null && sub.getSubTypeReference() != null) { + return sub.getSubTypeReference(); + } + final java.lang.reflect.Type elementType = + ((ParameterizedType) outerRef.getType()).getActualTypeArguments()[0]; + final java.lang.reflect.Type innerReflectType = + ((ParameterizedType) elementType).getActualTypeArguments()[0]; + if (innerReflectType instanceof ParameterizedType) { + return new TypeReference() { + @Override + public java.lang.reflect.Type getType() { + return innerReflectType; + } + }; + } + return TypeReference.create((Class) innerReflectType); + } + @SuppressWarnings("unchecked") static Class getParameterizedTypeFromArray(TypeReference typeReference) throws ClassNotFoundException { + if (typeReference.getSubTypeReference() != null) { + return typeReference.getSubTypeReference().getClassType(); + } + + java.lang.reflect.Type type = typeReference.getType(); java.lang.reflect.Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments(); - String parameterizedTypeName = typeArguments[0].getTypeName(); - return (Class) Class.forName(parameterizedTypeName); + if (typeArguments[0] instanceof ParameterizedType) { + return safeLoadTypeClass( + getTypeName(((ParameterizedType) typeArguments[0]).getRawType())); + } + + return safeLoadTypeClass(typeArguments[0].getTypeName()); + } + + static Class getFullParameterizedTypeFromArray(TypeReference typeReference) + throws ClassNotFoundException { + + TypeReference subRef = typeReference.getSubTypeReference(); + if (subRef != null && subRef.getSubTypeReference() != null) { + return subRef.getSubTypeReference().getClassType(); + } + + java.lang.reflect.Type type = typeReference.getType(); + + java.lang.reflect.Type typeArgument = + ((ParameterizedType) type).getActualTypeArguments()[0]; + + java.lang.reflect.Type innerType = + ((ParameterizedType) typeArgument).getActualTypeArguments()[0]; + + // For 3D+ arrays, innerType may be a ParameterizedType (e.g. DynamicArray). + // Use getRawType() to extract only the class name without generic parameters, + // since Class.forName() cannot parse parameterized type strings. + if (innerType instanceof ParameterizedType) { + return safeLoadTypeClass( + getTypeName(((ParameterizedType) innerType).getRawType())); + } + return safeLoadTypeClass(innerType.getTypeName()); } @SuppressWarnings("unchecked") @@ -156,4 +443,130 @@ public static > List typeMap(List input, Class des } return result; } + + /** + * Returns flat list of canonical fields in a static struct. Example: struct Baz { Struct Bar { + * int a, int b }, int c } will return {a, b, c}. + * + * @param classType Static struct type + * @return Flat list of canonical fields in a nested struct + */ + public static List staticStructNestedPublicFieldsFlatList(Class classType) { + return staticStructsNestedFieldsFlatList(classType).stream() + .filter(field -> Modifier.isPublic(field.getModifiers())) + .collect(Collectors.toList()); + } + + /** + * Goes over a static structs and enumerates all of its fields and nested structs fields + * recursively. + * + * @param classType Static struct type + * @return Flat list of all the fields nested in the struct + */ + @SuppressWarnings("unchecked") + public static List staticStructsNestedFieldsFlatList(Class classType) { + List canonicalFields = + Arrays.stream(classType.getDeclaredFields()) + .filter(field -> !StaticStruct.class.isAssignableFrom(field.getType())) + .collect(Collectors.toList()); + List nestedFields = + Arrays.stream(classType.getDeclaredFields()) + .filter(field -> StaticStruct.class.isAssignableFrom(field.getType())) + .map( + field -> + staticStructsNestedFieldsFlatList( + (Class) field.getType())) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + return Stream.concat(canonicalFields.stream(), nestedFields.stream()) + .collect(Collectors.toList()); + } + + /** Support java version < 8 Copied from {@link Class#getTypeName()}. */ + private static String getClassName(Class type) { + if (type.isArray()) { + try { + Class cl = type; + int dimensions = 0; + while (cl.isArray()) { + dimensions++; + cl = cl.getComponentType(); + } + StringBuilder sb = new StringBuilder(); + sb.append(cl.getName()); + for (int i = 0; i < dimensions; i++) { + sb.append("[]"); + } + return sb.toString(); + } catch (Throwable e) { + /*FALLTHRU*/ + } + } + + return type.getName(); + } + + /** + * Upper bound on TypeReference graph depth accepted by the decoder. + * Real ABIs rarely exceed 6-8 levels of nesting (e.g. DeFi aggregator structs); + * 10 covers realistic use with a small margin while rejecting obviously + * malicious or malformed inputs quickly. Bump if a legitimate case trips this. + */ + private static final int MAX_TYPEREF_DEPTH = 10; + + /** + * Validates a TypeReference graph before decoding: rejects cycles on the + * current traversal path via identity comparison and caps the maximum depth at + * {@link #MAX_TYPEREF_DEPTH}. Walked + * iteratively (explicit stack) so this method itself cannot blow the JVM stack + * on malicious input. + * + *

Called once at the decoder entry; bounds every downstream recursive + * traversal of subTypeReference / innerTypes (in {@code TypeDecoder.isDynamic}, + * {@code buildTypeStringFromTypeReference}, {@code decodeStaticStruct}, + * {@code decodeDynamicStruct}, etc.). All local state, no shared mutable + * fields — safe for concurrent invocation on the same TypeReference. + */ + public static void validateTypeReferenceDepth(TypeReference root) { + if (root == null) { + return; + } + Deque stack = new ArrayDeque<>(); + Set> inPath = + Collections.newSetFromMap(new IdentityHashMap<>()); + stack.push(new Object[]{root, 0, true}); + while (!stack.isEmpty()) { + Object[] curr = stack.pop(); + TypeReference node = (TypeReference) curr[0]; + int depth = (Integer) curr[1]; + boolean entering = (Boolean) curr[2]; + if (!entering) { + inPath.remove(node); + continue; + } + if (depth >= MAX_TYPEREF_DEPTH) { + throw new UnsupportedOperationException( + "TypeReference depth exceeds " + MAX_TYPEREF_DEPTH + + " — possible cycle or excessively nested type"); + } + if (!inPath.add(node)) { + throw new UnsupportedOperationException( + "Cycle detected in TypeReference graph"); + } + stack.push(new Object[]{node, depth, false}); + TypeReference sub = node.getSubTypeReference(); + if (sub != null) { + stack.push(new Object[]{sub, depth + 1, true}); + } + List> inners = node.getInnerTypes(); + if (inners != null) { + for (TypeReference inner : inners) { + if (inner != null) { + stack.push(new Object[]{inner, depth + 1, true}); + } + } + } + } + } } diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/AbiTypes.java b/abi/src/main/java/org/tron/trident/abi/datatypes/AbiTypes.java index a06aa715..132c7f55 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/AbiTypes.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/AbiTypes.java @@ -367,11 +367,34 @@ public static Class getType(String type, boolean primitives) { return Bytes32.class; case "trcToken": return TrcToken.class; - default: - throw new UnsupportedOperationException("Unsupported type encountered: " + type); + default: { + try { + // initialize=false: do not trigger static initializers of arbitrary + // classes during reflective lookup. Explicit ClassLoader keeps + // resolution independent of the calling stack. + Class loaded = + Class.forName(type, false, AbiTypes.class.getClassLoader()); + if (!Type.class.isAssignableFrom(loaded)) { + throw new UnsupportedOperationException( + "Unsupported type encountered: " + type + + " (not a subtype of " + Type.class.getName() + ")"); + } + @SuppressWarnings("unchecked") + Class typed = (Class) loaded; + return typed; + } catch (ClassNotFoundException e) { + throw new UnsupportedOperationException( + "Unsupported type encountered: " + type); + } + } } } + /** + * Returns the provided class type as a string. In case of a struct, it will return the struct + * name. For the tuple notation of a struct, example ((string,uint256)), think of taking an + * instance of the struct and calling the instance.getTypeAsString() method. + */ public static String getTypeAString(Class type) { if (Utf8String.class.equals(type)) { return "string"; diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/Array.java b/abi/src/main/java/org/tron/trident/abi/datatypes/Array.java index fc451f25..24fff903 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/Array.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/Array.java @@ -58,8 +58,8 @@ public abstract class Array implements Type> { @Override public int bytes32PaddedLength() { int length = 0; - for (int i = 0; i < value.size(); i++) { - int valueLength = value.get(i).bytes32PaddedLength(); + for (T t : value) { + int valueLength = t.bytes32PaddedLength(); length += valueLength; } return length; diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/BytesType.java b/abi/src/main/java/org/tron/trident/abi/datatypes/BytesType.java index 172ed35b..82184ca0 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/BytesType.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/BytesType.java @@ -30,7 +30,12 @@ public BytesType(byte[] src, String type) { @Override public int bytes32PaddedLength() { - return value.length * 32; + if (value.length < MAX_BYTE_LENGTH) { + return MAX_BYTE_LENGTH; + } else if (value.length % MAX_BYTE_LENGTH == 0) { + return value.length; + } + return (value.length / MAX_BYTE_LENGTH + 1) * MAX_BYTE_LENGTH; } @Override diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/CustomError.java b/abi/src/main/java/org/tron/trident/abi/datatypes/CustomError.java new file mode 100644 index 00000000..ee87494e --- /dev/null +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/CustomError.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Web3 Labs Ltd. + * + * 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.tron.trident.abi.datatypes; + +import static org.tron.trident.abi.Utils.convert; + +import java.util.List; +import org.tron.trident.abi.TypeReference; + +/** CustomError wrapper type. */ +public class CustomError { + private String name; + private List> parameters; + + public CustomError(String name, List> parameters) { + this.name = name; + this.parameters = convert(parameters); + } + + public String getName() { + return name; + } + + public List> getParameters() { + return parameters; + } +} diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/DynamicArray.java b/abi/src/main/java/org/tron/trident/abi/datatypes/DynamicArray.java index 06e966bf..766a1ae0 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/DynamicArray.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/DynamicArray.java @@ -14,6 +14,7 @@ package org.tron.trident.abi.datatypes; import java.util.List; +import org.tron.trident.abi.Utils; /** * Dynamic array type. @@ -24,13 +25,23 @@ public class DynamicArray extends Array { @SafeVarargs @SuppressWarnings({"unchecked"}) public DynamicArray(T... values) { - super((Class) AbiTypes.getType(values[0].getTypeAsString()), values); + super( + StructType.class.isAssignableFrom(values[0].getClass()) + || Array.class.isAssignableFrom(values[0].getClass()) + ? (Class) values[0].getClass() + : (Class) AbiTypes.getType(values[0].getTypeAsString()), + values); } @Deprecated @SuppressWarnings("unchecked") public DynamicArray(List values) { - super((Class) AbiTypes.getType(values.get(0).getTypeAsString()), values); + super( + StructType.class.isAssignableFrom(values.get(0).getClass()) + || Array.class.isAssignableFrom(values.get(0).getClass()) + ? (Class) values.get(0).getClass() + : (Class) AbiTypes.getType(values.get(0).getTypeAsString()), + values); } @Deprecated @@ -60,6 +71,36 @@ public DynamicArray(Class type, T... values) { @Override public String getTypeAsString() { - return AbiTypes.getTypeAString(getComponentType()) + "[]"; + String type; + if (value.isEmpty()) { + Class componentType = getComponentType(); + boolean genericStruct = + componentType == DynamicStruct.class || componentType == StaticStruct.class; + boolean rawArrayType = + componentType != null + && Array.class.isAssignableFrom(componentType) + && !StructType.class.isAssignableFrom(componentType); + if (componentType == null || genericStruct || rawArrayType) { + throw new UnsupportedOperationException( + "Cannot determine type string for empty array of generic struct " + + "or nested array type. Either construct DynamicArray with a " + + "concrete element type, or compute the type string externally " + + "from a TypeReference."); + } + if (StructType.class.isAssignableFrom(componentType)) { + type = Utils.getStructType(componentType); + } else { + type = AbiTypes.getTypeAString(componentType); + } + } else { + if (StructType.class.isAssignableFrom(value.get(0).getClass())) { + type = value.get(0).getTypeAsString(); + } else if (Array.class.isAssignableFrom(value.get(0).getClass())) { + type = value.get(0).getTypeAsString(); + } else { + type = AbiTypes.getTypeAString(getComponentType()); + } + } + return type + "[]"; } } diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/DynamicStruct.java b/abi/src/main/java/org/tron/trident/abi/datatypes/DynamicStruct.java index 17803f4b..2a491b8d 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/DynamicStruct.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/DynamicStruct.java @@ -25,6 +25,7 @@ public DynamicStruct(List values) { this(Type.class, values); } + @SuppressWarnings("unchecked") private DynamicStruct(Class type, List values) { super(type, values); for (Type value : values) { @@ -51,7 +52,10 @@ public String getTypeAsString() { final StringBuilder type = new StringBuilder("("); for (int i = 0; i < itemTypes.size(); ++i) { final Class cls = itemTypes.get(i); - if (StructType.class.isAssignableFrom(cls)) { + if (StructType.class.isAssignableFrom(cls) + || DynamicArray.class.isAssignableFrom(cls)) { + type.append(getValue().get(i).getTypeAsString()); + } else if (Array.class.isAssignableFrom(cls)) { type.append(getValue().get(i).getTypeAsString()); } else { type.append(AbiTypes.getTypeAString(cls)); diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/Event.java b/abi/src/main/java/org/tron/trident/abi/datatypes/Event.java index 84539853..6a1c793f 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/Event.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/Event.java @@ -13,10 +13,11 @@ package org.tron.trident.abi.datatypes; +import static org.tron.trident.abi.Utils.convert; + import java.util.List; import java.util.stream.Collectors; import org.tron.trident.abi.TypeReference; -import org.tron.trident.abi.Utils; /** * Event wrapper type. @@ -28,7 +29,7 @@ public class Event { public Event(String name, List> parameters) { this.name = name; - this.parameters = Utils.convert(parameters); + this.parameters = convert(parameters); } public String getName() { diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/Function.java b/abi/src/main/java/org/tron/trident/abi/datatypes/Function.java index ebb81aca..a018b92a 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/Function.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/Function.java @@ -13,13 +13,15 @@ package org.tron.trident.abi.datatypes; +import static org.tron.trident.abi.Utils.convert; + import java.util.List; import org.tron.trident.abi.TypeReference; -import org.tron.trident.abi.Utils; /** * Function type. */ + public class Function { private String name; @@ -30,7 +32,7 @@ public Function( String name, List inputParameters, List> outputParameters) { this.name = name; this.inputParameters = inputParameters; - this.outputParameters = Utils.convert(outputParameters); + this.outputParameters = convert(outputParameters); } public String getName() { diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/StaticArray.java b/abi/src/main/java/org/tron/trident/abi/datatypes/StaticArray.java index 6f782a3f..924f793b 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/StaticArray.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/StaticArray.java @@ -48,7 +48,12 @@ public StaticArray(List values) { @Deprecated @SuppressWarnings("unchecked") public StaticArray(int expectedSize, List values) { - super((Class) AbiTypes.getType(values.get(0).getTypeAsString()), values); + super( + StructType.class.isAssignableFrom(values.get(0).getClass()) + || Array.class.isAssignableFrom(values.get(0).getClass()) + ? (Class) values.get(0).getClass() + : (Class) AbiTypes.getType(values.get(0).getTypeAsString()), + values); checkValid(expectedSize); } @@ -79,7 +84,15 @@ public List getValue() { @Override public String getTypeAsString() { - return AbiTypes.getTypeAString(getComponentType()) + "[" + value.size() + "]"; + String type; + if (!value.isEmpty() && StructType.class.isAssignableFrom(value.get(0).getClass())) { + type = value.get(0).getTypeAsString(); + } else if (!value.isEmpty() && Array.class.isAssignableFrom(value.get(0).getClass())) { + type = value.get(0).getTypeAsString(); + } else { + type = AbiTypes.getTypeAString(getComponentType()); + } + return type + "[" + value.size() + "]"; } private void checkValid(int expectedSize) { diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/StaticStruct.java b/abi/src/main/java/org/tron/trident/abi/datatypes/StaticStruct.java index 7992d791..5c4e1583 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/StaticStruct.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/StaticStruct.java @@ -21,6 +21,7 @@ public class StaticStruct extends StaticArray implements StructType { private final List> itemTypes = new ArrayList<>(); + @SuppressWarnings("unchecked") public StaticStruct(List values) { super(Type.class, values.size(), values); for (Type value : values) { @@ -38,7 +39,7 @@ public String getTypeAsString() { final StringBuilder type = new StringBuilder("("); for (int i = 0; i < itemTypes.size(); ++i) { final Class cls = itemTypes.get(i); - if (StructType.class.isAssignableFrom(cls)) { + if (StructType.class.isAssignableFrom(cls) || Array.class.isAssignableFrom(cls)) { type.append(getValue().get(i).getTypeAsString()); } else { type.append(AbiTypes.getTypeAString(cls)); diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/Utf8String.java b/abi/src/main/java/org/tron/trident/abi/datatypes/Utf8String.java index 6b0462f5..a87c2958 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/Utf8String.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/Utf8String.java @@ -13,6 +13,8 @@ package org.tron.trident.abi.datatypes; +import java.nio.charset.StandardCharsets; + /** * UTF-8 encoded string type. */ @@ -27,9 +29,22 @@ public Utf8String(String value) { this.value = value; } + /** + * Returns the Bytes32 Padded length. If the string is empty, we only encode its length. Else, + * we concatenate its length along of its value + */ @Override public int bytes32PaddedLength() { - return 2 * MAX_BYTE_LENGTH; + if (value == null || value.isEmpty()) { + return MAX_BYTE_LENGTH; + } + int length = value.getBytes(StandardCharsets.UTF_8).length; + if (length < MAX_BYTE_LENGTH) { + return MAX_BYTE_LENGTH; + } else if (length % MAX_BYTE_LENGTH == 0) { + return length; + } + return (length / MAX_BYTE_LENGTH + 1) * MAX_BYTE_LENGTH; } @Override diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/generated/StaticArray0.java b/abi/src/main/java/org/tron/trident/abi/datatypes/generated/StaticArray0.java new file mode 100644 index 00000000..b5959777 --- /dev/null +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/generated/StaticArray0.java @@ -0,0 +1,33 @@ +package org.tron.trident.abi.datatypes.generated; + +import java.util.List; +import org.tron.trident.abi.datatypes.StaticArray; +import org.tron.trident.abi.datatypes.Type; + +/** + * Auto generated code. + *

Do not modifiy! + *

Please use org.web3j.codegen.AbiTypesGenerator in the + * codegen module to update. + */ +public class StaticArray0 extends StaticArray { + @Deprecated + public StaticArray0(List values) { + super(0); + } + + @Deprecated + @SafeVarargs + public StaticArray0(T... values) { + super(0, values); + } + + public StaticArray0(Class type, List values) { + super(type, 0, values); + } + + @SafeVarargs + public StaticArray0(Class type, T... values) { + super(type, 0, values); + } +} diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/primitive/Int.java b/abi/src/main/java/org/tron/trident/abi/datatypes/primitive/Int.java index 482894cd..0198ca20 100644 --- a/abi/src/main/java/org/tron/trident/abi/datatypes/primitive/Int.java +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/primitive/Int.java @@ -16,7 +16,7 @@ import org.tron.trident.abi.datatypes.NumericType; import org.tron.trident.abi.datatypes.generated.Int32; -public final class Int extends Number { +public final class Int extends Number { public Int(int value) { super(value); diff --git a/abi/src/main/java/org/tron/trident/abi/datatypes/reflection/Parameterized.java b/abi/src/main/java/org/tron/trident/abi/datatypes/reflection/Parameterized.java new file mode 100644 index 00000000..2a96b3bd --- /dev/null +++ b/abi/src/main/java/org/tron/trident/abi/datatypes/reflection/Parameterized.java @@ -0,0 +1,25 @@ +/* + * Copyright 2022 Web3 Labs Ltd. + * + * 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.tron.trident.abi.datatypes.reflection; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Parameterized { + Class type(); +} \ No newline at end of file diff --git a/abi/src/test/java/org/tron/trident/abi/AbiV2TestFixture.java b/abi/src/test/java/org/tron/trident/abi/AbiV2TestFixture.java index 3a51dcea..2d4747fa 100644 --- a/abi/src/test/java/org/tron/trident/abi/AbiV2TestFixture.java +++ b/abi/src/test/java/org/tron/trident/abi/AbiV2TestFixture.java @@ -14,14 +14,26 @@ package org.tron.trident.abi; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.tron.trident.abi.datatypes.Address; +import org.tron.trident.abi.datatypes.DynamicArray; +import org.tron.trident.abi.datatypes.DynamicBytes; import org.tron.trident.abi.datatypes.DynamicStruct; +import org.tron.trident.abi.datatypes.Event; import org.tron.trident.abi.datatypes.Function; import org.tron.trident.abi.datatypes.StaticStruct; import org.tron.trident.abi.datatypes.Type; import org.tron.trident.abi.datatypes.Utf8String; +import org.tron.trident.abi.datatypes.generated.StaticArray1; +import org.tron.trident.abi.datatypes.generated.StaticArray2; +import org.tron.trident.abi.datatypes.generated.StaticArray3; import org.tron.trident.abi.datatypes.generated.Uint256; +import org.tron.trident.abi.datatypes.generated.Uint32; +import org.tron.trident.abi.datatypes.reflection.Parameterized; public class AbiV2TestFixture { @@ -31,12 +43,60 @@ public class AbiV2TestFixture { public static final String FUNC_GETFOOBAR = "getFooBar"; + public static final String FUNC_GETFOOBARBAR = "getFooBarBar"; + + public static final String FUNC_GETFOOFOOBARBAR = "getFooFooBarBar"; + + public static final String FUNC_GETNARBARBARFUZZFOONARFUZZNUUFOOFUZZFUNCTION = + "getNarBarBarFuzzFooNarFuzzNuuFooFuzz"; + public static final String FUNC_GETFOOUINT = "getFooUint"; + public static final String FUNC_GETFOOSTATICARRAY1 = "getFooStaticArray1"; + + public static final String FUNC_GETFOOSTATICARRAY2 = "getFooStaticArray2"; + + public static final String FUNC_GETFOOSTATICARRAY3 = "getFooStaticArray3"; + + public static final String FUNC_GETFOODYNAMICARRAY = "getFooDynamicArray"; + + public static final String FUNC_GETNARBARFOONARFOODYNAMICARRAY = "getNarBarFooNarFooDynamicArrays"; + + public static final String FUNC_IDNARBARFOONARFOODYNAMICARRAY = "idNarBarFooNarFooDynamicArrays"; + + public static final String FUNC_GETBARDYNAMICARRAY = "getBarDynamicArray"; + + public static final String FUNC_GETBARSTATICARRAY = "getBarStaticArray"; + + public static final String FUNC_SETBARSTATICARRAY = "setBarStaticArray"; + + public static final String FUNC_SETBARDYNAMICARRAY = "setBarDynamicArray"; + + public static final String FUNC_GETNARDYNAMICARRAY = "getNarDynamicArray"; + + public static final String FUNC_GETNARSTATICARRAY = "getNarStaticArray"; + + public static final String FUNC_SETFOODYNAMICARRAY = "setFooDynamicArray"; + + public static final String FUNC_GETFOOMULTIPLESTATICARRAY = "getFooMultipleStaticArray"; + + public static final String FUNC_GETFOOMULTIPLEDYNAMICARRAY = "getFooMultipleDynamicArray"; + + public static final String FUNC_IDNARBARFOONARFOOARRAYS = "idNarBarFooNarFooArrays"; + + public static final String FUNC_IDBARNARFOONARFOOARRAYS = "idBarNarFooNarFooArrays"; + + public static final String FUNC_GETFOOMULTIPLEDYNAMICSTATICARRAY = + "getFooMultipleDynamicStaticArray"; + public static final String FUNC_GETFUZZ = "getFuzz"; + public static final String FUNC_GETFUZZFUZZ = "getFuzzFuzz"; + public static final String FUNC_GETNAZ = "getNaz"; + public static final String FUNC_GETNAR = "getNar"; + public static final String FUNC_SETBAR = "setBar"; public static final String FUNC_SETBAZ = "setBaz"; @@ -53,8 +113,23 @@ public class AbiV2TestFixture { public static final String FUNC_SETWIZ = "setWiz"; - public static class Foo extends DynamicStruct { + public static final String FUNC_SETQUX = "setQux"; + + public static final String FUNC_GETQUX = "getQux"; + + public static final String FUNC_addDynamicBytesArray = "addDynamicBytesArray"; + public static final String FUNC_setArrayOfStructWithArraysFunction = + "setArrayOfStructWithArraysFunction"; + + public static final String FUNC_SETGETMULTIDIMSTATICARRAY = "setGetMultiDimStaticArray"; + + public static final String FUNC_SETGETMULTIDIMDYNAMICARRAY = "setGetMultiDimDynamicArray"; + + public static final String FUNC_setMultiDimStaticArrayWithUtf8StringSubType + = "setMultiDimStaticArrayWithUtf8StringSubType"; + + public static class Foo extends DynamicStruct { public String id; public String name; @@ -72,32 +147,330 @@ public Foo(Utf8String id, Utf8String name) { this.id = id.getValue(); this.name = name.getValue(); } + + @Override + public String toString() { + return "Foo{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; + } } public static final Function setFooFunction = new Function( FUNC_SETFOO, Arrays.asList(new Foo("id", "name")), - Collections.>emptyList()); + Collections.emptyList()); public static final Function getFooFunction = new Function( - FUNC_GETFOO, - Arrays.asList(), - Arrays.>asList(new TypeReference() { - })); + FUNC_GETFOO, Arrays.asList(), Arrays.asList(new TypeReference() {})); public static final Function getFooUintFunction = new Function( FUNC_GETFOOUINT, Arrays.asList(), + Arrays.asList(new TypeReference() {}, new TypeReference() {})); + + public static final Function getFooStaticArray1Function = + new Function( + FUNC_GETFOOSTATICARRAY1, + Arrays.asList(), + Arrays.asList(new TypeReference>() {})); + + public static final Function getFooStaticArray2Function = + new Function( + FUNC_GETFOOSTATICARRAY2, + Arrays.asList(), + Arrays.asList(new TypeReference>() {})); + + public static final Function getFooStaticArray3Function = + new Function( + FUNC_GETFOOSTATICARRAY3, + Arrays.asList(), + Arrays.asList(new TypeReference>() {})); + + public static final Function getFooDynamicArrayFunction = + new Function( + FUNC_GETFOODYNAMICARRAY, + Arrays.asList(), + Arrays.asList(new TypeReference>() {})); + + public static final Function getNarBarFooNarFooDynamicArrayFunction = + new Function( + FUNC_GETNARBARFOONARFOODYNAMICARRAY, + Arrays.asList(), + Arrays.asList( + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {})); + + public static final Function idNarBarFooNarFooDynamicArrayFunction = + new Function( + FUNC_IDNARBARFOONARFOODYNAMICARRAY, + Arrays.asList( + new StaticArray3<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", ""))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo")))), + new StaticArray3<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO)), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name")), + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new StaticArray3<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"))), + Arrays.asList( + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {})); + + public static final Function getBarDynamicArrayFunction = + new Function( + FUNC_GETBARDYNAMICARRAY, + Arrays.asList(), + Arrays.asList(new TypeReference>() {})); + + public static final Function getBarStaticArrayFunction = + new Function( + FUNC_GETBARSTATICARRAY, + Arrays.asList(), + Arrays.asList(new TypeReference>() {})); + + @SuppressWarnings("unchecked") + public static final Function setBarStaticArrayFunction = + new Function( + FUNC_SETBARSTATICARRAY, + Arrays.asList( + new StaticArray3( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar( + BigInteger.valueOf(0), BigInteger.valueOf(0)), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(0), BigInteger.valueOf(0)))), + Arrays.asList()); + + @SuppressWarnings("unchecked") + public static final Function setBarDynamicArrayFunction = + new Function( + FUNC_SETBARDYNAMICARRAY, + Arrays.asList( + new DynamicArray( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar( + BigInteger.valueOf(0), BigInteger.valueOf(0)), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(0), BigInteger.valueOf(0)))), + Arrays.asList()); + + public static final Function getNarDynamicArrayFunction = + new Function( + FUNC_GETNARDYNAMICARRAY, + Arrays.asList(), + Arrays.asList(new TypeReference>() {})); + + public static final Function getNarStaticArrayFunction = + new Function( + FUNC_GETNARSTATICARRAY, + Arrays.asList(), + Arrays.asList(new TypeReference>() {})); + + public static final Function getFooMultipleStaticArrayFunction = + new Function( + FUNC_GETFOOMULTIPLESTATICARRAY, + Arrays.asList(), + Arrays.asList( + new TypeReference>() {}, + new TypeReference>() {})); + + public static final Function getFooMultipleDynamicArrayFunction = + new Function( + FUNC_GETFOOMULTIPLEDYNAMICARRAY, + Arrays.asList(), + Arrays.asList( + new TypeReference>() {}, + new TypeReference>() {})); + + public static final Function idNarBarFooNarFooArraysFunction = + new Function( + FUNC_IDNARBARFOONARFOOARRAYS, + Arrays.asList( + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new StaticArray3<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO)), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name")), + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name"))), Arrays.>asList( - new TypeReference() { - }, new TypeReference() { - })); + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {})); - public static class Bar extends StaticStruct { + public static final Function idNarBarFooNarFooArraysFunction2 = + new Function( + FUNC_IDNARBARFOONARFOOARRAYS, + Arrays.asList( + new StaticArray3<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", ""))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo")))), + new DynamicArray<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(12), BigInteger.valueOf(33)), + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO)), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name")), + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new StaticArray3<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"))), + Arrays.>asList( + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {})); + public static final Function idBarNarFooNarFooArraysFunction = + new Function( + FUNC_IDBARNARFOONARFOOARRAYS, + Arrays.asList( + new StaticArray3<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO)), + new StaticArray3<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", ""))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo")))), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name")), + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new StaticArray3<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"))), + Arrays.>asList( + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {}, + new TypeReference>() {})); + + public static final Function getFooMultipleDynamicStaticArrayFunction = + new Function( + FUNC_GETFOOMULTIPLEDYNAMICSTATICARRAY, + Arrays.asList(), + Arrays.>asList( + new TypeReference>() {}, + new TypeReference>() {})); + + public static class Bar extends StaticStruct { public BigInteger id; public BigInteger data; @@ -115,6 +488,11 @@ public Bar(Uint256 id, Uint256 data) { this.id = id.getValue(); this.data = data.getValue(); } + + @Override + public String toString() { + return "Bar{" + "id='" + id + '\'' + ", data='" + data + '\'' + '}'; + } } public static final Function setBarFunction = @@ -127,20 +505,52 @@ public Bar(Uint256 id, Uint256 data) { new Function( FUNC_GETBAR, Arrays.asList(), - Arrays.>asList(new TypeReference() { - })); + Arrays.>asList(new TypeReference() {})); public static final Function getFooBarFunction = new Function( FUNC_GETFOOBAR, Arrays.asList(), Arrays.>asList( - new TypeReference() { - }, new TypeReference() { - })); + new TypeReference() {}, new TypeReference() {})); - public static class Baz extends DynamicStruct { + public static final Function getFooBarBarFunction = + new Function( + FUNC_GETFOOBARBAR, + Arrays.asList(), + Arrays.asList( + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {})); + + public static final Function getFooFooBarBarFunction = + new Function( + FUNC_GETFOOFOOBARBAR, + Arrays.asList(), + Arrays.asList( + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {})); + + public static final Function + getNarBarBarFuzzFooNarFuzzNuuFooFuzzFunction = + new Function( + FUNC_GETNARBARBARFUZZFOONARFUZZNUUFOOFUZZFUNCTION, + Arrays.asList(), + Arrays.asList( + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {})); + public static class Baz extends DynamicStruct { public String id; public BigInteger data; @@ -167,7 +577,6 @@ public Baz(Utf8String id, Uint256 data) { Collections.>emptyList()); public static class Boz extends DynamicStruct { - public BigInteger data; public String id; @@ -197,11 +606,9 @@ public Boz(Uint256 data, Utf8String id) { new Function( FUNC_SETBOZ, Collections.emptyList(), - Arrays.>asList(new TypeReference() { - })); + Arrays.>asList(new TypeReference() {})); public static class Fuzz extends StaticStruct { - public Bar bar; public BigInteger data; @@ -219,22 +626,43 @@ public Fuzz(Bar bar, Uint256 data) { } } + public static final Function setFooDynamicArrayFunction = + new Function( + FUNC_SETFOODYNAMICARRAY, + Collections.singletonList( + new DynamicArray<>( + Foo.class, + new Foo("", ""), + new Foo("id", "name"), + new Foo("", ""))), + Collections.emptyList()); + + public static final Function setDoubleFooStaticArrayFunction = + new Function( + FUNC_SETFOODYNAMICARRAY, + Arrays.asList(new Foo("", ""), new Foo("id", "name"), new Foo("", "")), + Collections.emptyList()); + public static final Function setFuzzFunction = new Function( FUNC_SETFUZZ, Arrays.asList( new Fuzz(new Bar(BigInteger.ONE, BigInteger.TEN), BigInteger.ONE)), - Collections.>emptyList()); + Collections.emptyList()); public static final Function getFuzzFunction = new Function( FUNC_GETFUZZ, Arrays.asList(), - Arrays.>asList(new TypeReference() { - })); + Arrays.asList(new TypeReference() {})); - public static class Nuu extends DynamicStruct { + public static final Function getFuzzFuzzFunction = + new Function( + FUNC_GETFUZZFUZZ, + Arrays.asList(), + Arrays.asList(new TypeReference() {}, new TypeReference() {})); + public static class Nuu extends DynamicStruct { public Foo foo; public Nuu(Foo foo) { @@ -253,11 +681,9 @@ public Nuu(Foo foo) { new Function( FUNC_SETNUU, Collections.emptyList(), - Arrays.>asList(new TypeReference() { - })); + Arrays.>asList(new TypeReference() {})); public static class Nar extends DynamicStruct { - public Nuu nuu; public Nar(Nuu nuu) { @@ -267,7 +693,6 @@ public Nar(Nuu nuu) { } public static class Naz extends DynamicStruct { - public Nar nar; public BigInteger data; @@ -285,22 +710,96 @@ public Naz(Nar nar, Uint256 data) { } } + public static class Nazzy extends DynamicStruct { + public List foo; + + public Nazzy(List foo) { + super(new DynamicArray<>(Foo.class, foo)); + this.foo = foo; + } + + public Nazzy(@Parameterized(type = Foo.class) DynamicArray foo) { + super(foo); + this.foo = foo.getValue(); + } + + @Override + public String toString() { + return "Nazzy{" + "foo=" + foo + '}'; + } + } + + public static class Barr extends DynamicStruct { + public List bars; + + public BigInteger data; + + public Barr(List bars, BigInteger data) { + super( + new DynamicArray<>(Bar.class, bars), + new Uint256(data)); + this.bars = bars; + this.data = data; + } + + public Barr(@Parameterized(type = Bar.class) DynamicArray bars, Uint256 data) { + super(bars, data); + this.bars = bars.getValue(); + this.data = data.getValue(); + } + } + + public static class Nazz extends DynamicStruct { + public List nazzy; + + public BigInteger data; + + public Nazz(List nazzy, BigInteger data) { + super( + new DynamicArray<>(Nazzy.class, nazzy), + new Uint256(data)); + this.nazzy = nazzy; + this.data = data; + } + + public Nazz(@Parameterized(type = Nazzy.class) DynamicArray nazzy, Uint256 data) { + super(nazzy, data); + this.nazzy = nazzy.getValue(); + this.data = data.getValue(); + } + + @Override + public String toString() { + return "Nazz{" + "nazzy=" + nazzy + ", data=" + data + '}'; + } + } + public static final Function setNazFunction = new Function( FUNC_SETNAZ, Arrays.asList( new Naz(new Nar(new Nuu(new Foo("id", "name"))), BigInteger.ONE)), - Collections.>emptyList()); + Collections.emptyList()); - public static final Function getNazFunction = + public static final Function getNazzFunction = new Function( FUNC_GETNAZ, Arrays.asList(), - Arrays.>asList(new TypeReference() { - })); + Arrays.asList(new TypeReference() {})); - public static class Wiz extends DynamicStruct { + public static final Event nazzEvent = + new Event( + "nazzEvent", + Arrays.asList(new TypeReference() {}, new TypeReference() {})); + + public static final Event nazzEvent2 = + new Event("nazzEvent2", Arrays.asList(new TypeReference>() {})); + + public static final Function getNarFunction = + new Function( + FUNC_GETNAR, Arrays.asList(), Arrays.asList(new TypeReference() {})); + public static class Wiz extends DynamicStruct { public Foo foo; public String data; @@ -322,5 +821,178 @@ public Wiz(Foo foo, Utf8String data) { new Function( FUNC_SETWIZ, Arrays.asList(new Wiz(new Foo("id", "name"), "data")), + Collections.emptyList()); + + public static class Qux extends DynamicStruct { + public Bar bar; + + public String data; + + public Qux(Bar bar, String data) { + super(bar, new Utf8String(data)); + this.bar = bar; + this.data = data; + } + + public Qux(Bar bar, Utf8String data) { + super(bar, data); + this.bar = bar; + this.data = data.getValue(); + } + } + + public static final Function setQuxFunction = + new Function( + FUNC_SETQUX, + Arrays.asList(new Qux(new Bar(BigInteger.ONE, BigInteger.TEN), "data")), + Collections.emptyList()); + + public static final Function getQuxFunction = + new Function( + FUNC_GETQUX, Arrays.asList(), Arrays.asList(new TypeReference() {})); + + public static class BytesStruct extends DynamicStruct { + public byte[] pubkey; + + public BigInteger something; + + public byte[] metadata; + + public BytesStruct(byte[] pubkey, BigInteger something, byte[] metadata) { + super( + new DynamicBytes(pubkey), + new Uint32(something), + new DynamicBytes(metadata)); + this.pubkey = pubkey; + this.something = something; + this.metadata = metadata; + } + + public BytesStruct(DynamicBytes pubkey, Uint32 something, DynamicBytes metadata) { + super(pubkey, something, metadata); + this.pubkey = pubkey.getValue(); + this.something = something.getValue(); + this.metadata = metadata.getValue(); + } + } + + public static final Function addDynamicBytesArrayFunction = + new Function( + FUNC_addDynamicBytesArray, + Arrays.asList( + new BytesStruct( + "dynamic".getBytes(StandardCharsets.UTF_8), + BigInteger.ZERO, + "Bytes".getBytes(StandardCharsets.UTF_8))), Collections.>emptyList()); + + public static class ArrayStruct extends DynamicStruct { + public BigInteger id; + + public List addresses; + + public ArrayStruct(BigInteger id, List addresses) { + super( + new Uint256(id), + new DynamicArray

( + Address.class, + Utils.typeMap( + addresses, Address.class))); + this.id = id; + this.addresses = addresses; + } + + public ArrayStruct(Uint256 id, DynamicArray
addresses) { + super(id, addresses); + this.id = id.getValue(); + this.addresses = + addresses.getValue().stream() + .map(v -> v.getValue()) + .collect(Collectors.toList()); + } + } + + public static final Function setArrayOfStructWithArraysFunction = + new Function( + FUNC_setArrayOfStructWithArraysFunction, + Arrays.asList( + new DynamicArray( + ArrayStruct.class, + Arrays.asList( + new ArrayStruct( + BigInteger.ONE, + Arrays.asList( + "0x0000000000000000000000000000000000000000", + "0x1111111111111111111111111111111111111111")), + new ArrayStruct( + BigInteger.TEN, + Arrays.asList( + "0x2222222222222222222222222222222222222222", + "0x3333333333333333333333333333333333333333"))))), + Collections.>emptyList()); + + public static final Function setGetMultiDimStaticArrayFunction = + new Function(FUNC_SETGETMULTIDIMSTATICARRAY, + Collections.singletonList(new StaticArray3<>( + (Class)StaticArray2.class, + new StaticArray2<>(Uint256.class, new Uint256(1), new Uint256(2)), + new StaticArray2<>(Uint256.class, new Uint256(3), new Uint256(4)), + new StaticArray2<>(Uint256.class, new Uint256(5), new Uint256(6)))), + Collections.singletonList(new TypeReference>>() {}) + ); + + public static final Function setGetMultiDimDynamicArrayFunction = + new Function(FUNC_SETGETMULTIDIMDYNAMICARRAY, + Collections.singletonList(new DynamicArray<>( + (Class)DynamicArray.class, + new DynamicArray<>( + (Class)DynamicArray.class, + new DynamicArray<>( + Uint256.class, + new Uint256(1))), + new DynamicArray<>( + (Class)DynamicArray.class, + new DynamicArray<>( + Uint256.class, + new Uint256(2), new Uint256(3))), + new DynamicArray<>( + (Class)DynamicArray.class, + new DynamicArray<>( + Uint256.class, + new Uint256(4), new Uint256(5)), + new DynamicArray<>( + Uint256.class, + new Uint256(6), new Uint256(7), new Uint256(8))))), + Collections.singletonList(new TypeReference>>>() {}) + ); + + public static Function setMultiDimStaticArrayWithUtf8StringSubTypeFunction() { + //string[2][2] + StaticArray2 inner1 = new StaticArray2<>( + Utf8String.class, + new Utf8String("1234567890123456789012345678901234567890" + + "1234567890123456789012345678901234567890"), + new Utf8String("short1") + ); + + StaticArray2 inner2 = new StaticArray2<>( + Utf8String.class, + new Utf8String("short2"), + new Utf8String("short3") + ); + + @SuppressWarnings("unchecked") + StaticArray2> outer = new StaticArray2<>( + (Class>) (Class) StaticArray2.class, + inner1, + inner2 + ); + + return new Function( + FUNC_setMultiDimStaticArrayWithUtf8StringSubType, + Collections.singletonList(outer), + Collections.emptyList() + ); + } + } diff --git a/abi/src/test/java/org/tron/trident/abi/CustomErrorEncoderTest.java b/abi/src/test/java/org/tron/trident/abi/CustomErrorEncoderTest.java new file mode 100644 index 00000000..0565d3b1 --- /dev/null +++ b/abi/src/test/java/org/tron/trident/abi/CustomErrorEncoderTest.java @@ -0,0 +1,78 @@ +package org.tron.trident.abi; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.tron.trident.abi.Utils.convert; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.tron.trident.abi.AbiV2TestFixture.Nazz; +import org.tron.trident.abi.datatypes.Address; +import org.tron.trident.abi.datatypes.CustomError; +import org.tron.trident.abi.datatypes.DynamicArray; +import org.tron.trident.abi.datatypes.Utf8String; +import org.tron.trident.abi.datatypes.generated.Uint256; + +public class CustomErrorEncoderTest { + @Test + public void testCalculateSignatureHash() { + assertEquals( + CustomErrorEncoder.calculateSignatureHash("InvalidAccess(address,string,uint256)"), + ("cb5157bf1b439b9573ea7a95f7c00cc33f832ed728345c2bd29146ce58bbab57")); + + assertEquals( + CustomErrorEncoder.calculateSignatureHash("RandomError(address[],bytes)"), + ("bf37b77ddf0fbbf29ee6a3ebda3d177c2d438123b10571806c57958230d9f905")); + } + + @Test + public void testEncode() { + CustomError error = + new CustomError( + "InvalidAccess", + Arrays.>asList( + new TypeReference
() {}, + new TypeReference() {}, + new TypeReference() {})); + + assertEquals( + CustomErrorEncoder.encode(error), + "cb5157bf1b439b9573ea7a95f7c00cc33f832ed728345c2bd29146ce58bbab57"); + } + + @Test + public void testBuildErrorSignature() { + List> parameters = + Arrays.>asList( + new TypeReference
() {}, + new TypeReference() {}, + new TypeReference() {}); + + assertEquals( + "InvalidAccess(address,string,uint256)", + CustomErrorEncoder.buildErrorSignature("InvalidAccess", convert(parameters))); + } + + @Test + void testBuildErrorSignatureWithDynamicStructs() { + List> parameters = + Arrays.asList( + new TypeReference() {}, + new TypeReference() {}); + + assertEquals( + "DynamicStructError((((string,string)[])[],uint256),(string,string))", + CustomErrorEncoder.buildErrorSignature("DynamicStructError", convert(parameters))); + } + + @Test + void testBuildErrorSignatureWithDynamicArrays() { + List> parameters = + Arrays.asList(new TypeReference>() {}); + + assertEquals( + "DynamicArrayError((((string,string)[])[],uint256)[])", + CustomErrorEncoder.buildErrorSignature("DynamicArrayError", convert(parameters))); + } + +} diff --git a/abi/src/test/java/org/tron/trident/abi/DefaultFunctionEncoderTest.java b/abi/src/test/java/org/tron/trident/abi/DefaultFunctionEncoderTest.java index cd05613e..295c6bf3 100644 --- a/abi/src/test/java/org/tron/trident/abi/DefaultFunctionEncoderTest.java +++ b/abi/src/test/java/org/tron/trident/abi/DefaultFunctionEncoderTest.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Iterator; +import java.util.List; import org.junit.jupiter.api.Test; import org.tron.trident.abi.datatypes.Address; import org.tron.trident.abi.datatypes.Bool; @@ -34,6 +35,7 @@ import org.tron.trident.abi.datatypes.generated.Bytes10; import org.tron.trident.abi.datatypes.generated.Uint256; import org.tron.trident.abi.datatypes.generated.Uint32; +import org.tron.trident.utils.Numeric; public class DefaultFunctionEncoderTest { @@ -57,6 +59,53 @@ public void testBuildEmptyMethodSignature() { DefaultFunctionEncoder.buildMethodSignature("empty", Collections.emptyList())); } + @Test + public void testBuildMethodSignatureWithStructWithArray() { + assertEquals( + "structWithArray((uint256,address[]))", + DefaultFunctionEncoder.buildMethodSignature( + "structWithArray", + Arrays.asList( + new AbiV2TestFixture.ArrayStruct( + BigInteger.ONE, Collections.emptyList())))); + } + + @Test + public void testBuildMessageSignatureWithComplexTuple() { + AbiV2TestFixture.Nazz nazz = + new AbiV2TestFixture.Nazz( + Arrays.asList( + new AbiV2TestFixture.Nazzy( + Arrays.asList( + new AbiV2TestFixture.Foo("a", "b"), + new AbiV2TestFixture.Foo("c", "d"))), + new AbiV2TestFixture.Nazzy( + Arrays.asList( + new AbiV2TestFixture.Foo("e", "f"), + new AbiV2TestFixture.Foo("g", "key")))), + BigInteger.valueOf(100L)); + + assertEquals( + "someFunc((((string,string)[])[],uint256))", + FunctionEncoder.buildMethodSignature("someFunc", Arrays.asList(nazz))); + + // correct handling of empty list of dynamic struct + AbiV2TestFixture.Nazz nazz2 = + new AbiV2TestFixture.Nazz(Collections.emptyList(), BigInteger.ZERO); + + assertEquals( + "someFunc((((string,string)[])[],uint256))", + FunctionEncoder.buildMethodSignature("someFunc", Arrays.asList(nazz2))); + + // correct handling of empty list of static struct + AbiV2TestFixture.Barr barr = + new AbiV2TestFixture.Barr(Collections.emptyList(), BigInteger.ZERO); + + assertEquals( + "someFunc(((uint256,uint256)[],uint256))", + FunctionEncoder.buildMethodSignature("someFunc", Arrays.asList(barr))); + } + @Test public void testEncodeConstructorEmpty() { assertEquals("", FunctionEncoder.encodeConstructor(Collections.emptyList())); @@ -82,6 +131,42 @@ public void testEncodeConstructorUint() { new Uint(BigInteger.ONE), new Uint(BigInteger.valueOf(0x20))))); } + @Test + public void testEncodeConstructorPacked_multipleParameters() { + assertEquals( + "00000045014772656574696e677321", + FunctionEncoder.encodeConstructorPacked( + Arrays.asList( + new Uint32(BigInteger.valueOf(69)), + new Bool(true), + new Utf8String("Greetings!")))); + + assertEquals( + "783139457468657265756d205369676e6564204d6573736167653a" + + "663e27adc18d862da9a82f060310621d379e469a" + + "000000000000000000000000000000000000000000000000000000000000000a" + + "31323334353637383930", + FunctionEncoder.encodeConstructorPacked( + Arrays.asList( + new Utf8String("x19Ethereum Signed Message:"), + new Address("0x663e27AdC18d862dA9A82f060310621D379e469a"), + new Uint256(BigInteger.TEN), + new Bytes10("1234567890".getBytes())))); + assertEquals( + "0000004501000102030405", + FunctionEncoder.encodeConstructorPacked( + Arrays.asList( + new Uint32(BigInteger.valueOf(69)), + new Bool(true), + new DynamicBytes((new byte[] {0, 1, 2, 3, 4, 5}))))); + assertEquals( + "12000102030405", + FunctionEncoder.encodeConstructorPacked( + Arrays.asList( + new DynamicBytes(Numeric.hexStringToByteArray("0x12")), + new DynamicBytes((new byte[] {0, 1, 2, 3, 4, 5}))))); + } + @Test public void testFunctionSimpleEncode() { Function function = @@ -245,6 +330,393 @@ public void testDynamicStructNestedEncode2() { FunctionEncoder.encode(AbiV2TestFixture.setNazFunction)); } + @Test + public void testMultiReturnStaticDynamicArrayWithStaticDynamicStructs() { + String encodedInput = + "1f9218db" + + "0000000000000000000000000000000000000000000000000000000000000140" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000460" + + "0000000000000000000000000000000000000000000000000000000000000560" + + "00000000000000000000000000000000000000000000000000000000000008a0" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000220" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000120" + + "00000000000000000000000000000000000000000000000000000000000001e0" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + assertEquals( + encodedInput, + FunctionEncoder.encode(AbiV2TestFixture.idNarBarFooNarFooDynamicArrayFunction)); + } + + @Test + public void testEncodeStructMultipleDynamicStaticArray() { + String expected = + "7fcdab08" + + "0000000000000000000000000000000000000000000000000000000000000140" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000480" + + "0000000000000000000000000000000000000000000000000000000000000580" + + "00000000000000000000000000000000000000000000000000000000000008c0" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals( + expected, FunctionEncoder.encode(AbiV2TestFixture.idNarBarFooNarFooArraysFunction)); + } + + @Test + public void testEncodeStructMultipleDynamicStaticArray2() { + String expected = + "8a62c214" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000140" + + "0000000000000000000000000000000000000000000000000000000000000460" + + "0000000000000000000000000000000000000000000000000000000000000560" + + "00000000000000000000000000000000000000000000000000000000000008a0" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000220" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000120" + + "00000000000000000000000000000000000000000000000000000000000001e0" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals( + expected, FunctionEncoder.encode(AbiV2TestFixture.idBarNarFooNarFooArraysFunction)); + } + + @Test + public void testEncodeStructMultipleDynamicStaticArray3() { + String expected = + "1b9faea1" + + "00000000000000000000000000000000000000000000000000000000000000a0" + + "00000000000000000000000000000000000000000000000000000000000003c0" + + "00000000000000000000000000000000000000000000000000000000000004a0" + + "00000000000000000000000000000000000000000000000000000000000005a0" + + "00000000000000000000000000000000000000000000000000000000000008e0" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000220" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000000c" + + "0000000000000000000000000000000000000000000000000000000000000021" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000120" + + "00000000000000000000000000000000000000000000000000000000000001e0" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals( + expected, + FunctionEncoder.encode(AbiV2TestFixture.idNarBarFooNarFooArraysFunction2)); + } + @Test public void testDynamicStructNestedEncode3() { assertEquals( @@ -304,6 +776,32 @@ public Struct2(BigInteger id, BigInteger name) { new Struct2(BigInteger.valueOf(100), BigInteger.ONE)))); } + @Test + public void testABIv2DynamicArrayEncode() { + assertEquals( + "8907a08c" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "00000000000000000000000000000000000000000000000000000000000000e0" + + "00000000000000000000000000000000000000000000000000000000000001a0" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000", + FunctionEncoder.encode(AbiV2TestFixture.setFooDynamicArrayFunction)); + } + @Test public void testMakeFunction() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, @@ -336,4 +834,208 @@ public void testMakeFunction() assertEquals(actualOutput.getType(), expectedOutput.next().getType()); } } + + @Test + public void testEncodeStaticStructStaticArray() { + String expected = + "8efa5af7" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000"; + + assertEquals(expected, FunctionEncoder.encode(AbiV2TestFixture.setBarStaticArrayFunction)); + } + + @Test + public void testEncodeStaticStructDynamicArray() { + String expected = + "e1f84cb5" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000"; + + assertEquals(expected, FunctionEncoder.encode(AbiV2TestFixture.setBarDynamicArrayFunction)); + } + + @Test + public void testEncodeArrayOfStructWithArrays() { + String expected = + "fc3fdffb" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "00000000000000000000000000000000000000000000000000000000000000e0" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000001111111111111111111111111111111111111111" + + "000000000000000000000000000000000000000000000000000000000000000a" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000002222222222222222222222222222222222222222" + + "0000000000000000000000003333333333333333333333333333333333333333"; + + assertEquals( + expected, + FunctionEncoder.encode(AbiV2TestFixture.setArrayOfStructWithArraysFunction)); + } + + @Test + public void testDynamicStructWithStaticStruct() { + String expected = + "3db2b680" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "000000000000000000000000000000000000000000000000000000000000000a" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6461746100000000000000000000000000000000000000000000000000000000"; + + assertEquals(expected, FunctionEncoder.encode(AbiV2TestFixture.setQuxFunction)); + } + + @Test + public void testBuildMethodSignatureWithDynamicStruct_dynamicArray() { + List bytes = new java.util.ArrayList<>(); + + bytes.add(new DynamicBytes("1234".getBytes())); + bytes.add(new DynamicBytes("5678".getBytes())); + + List dynamicArray = Arrays.asList( + new Uint256(10), + new DynamicArray<>(DynamicBytes.class, bytes) + ); + + DynamicStruct dynamicStruct = + new DynamicStruct(dynamicArray); + + String functionDynamicArray = FunctionEncoder.buildMethodSignature( + "testBytes", dynamicArray); + + String functionDynamicStruct = FunctionEncoder.buildMethodSignature( + "testBytes", Collections.singletonList(dynamicStruct)); + + assertEquals("testBytes(uint256,bytes[])", functionDynamicArray); + + assertEquals("testBytes((uint256,bytes[]))", functionDynamicStruct); + + assertEquals("880e82a8", FunctionEncoder.buildMethodId(functionDynamicArray)); + + assertEquals("256b906f", FunctionEncoder.buildMethodId(functionDynamicStruct)); + + } + + @Test + public void testBuildMethodSignatureWithDynamicStruct_Uint() { + List uintArrayList = new java.util.ArrayList<>(); + + uintArrayList.add(new Uint256(new BigInteger("1234"))); + uintArrayList.add(new Uint256(new BigInteger("2345"))); + + List dynamicArray = Arrays.asList( + new Uint256(new BigInteger(String.valueOf(10))), + new DynamicArray<>(Uint256.class, uintArrayList) + ); + + DynamicStruct dynamicStruct = + new DynamicStruct(dynamicArray); + + String functionDynamicArray = FunctionEncoder.buildMethodSignature( + "testBytes", dynamicArray); + + String functionDynamicStruct = FunctionEncoder.buildMethodSignature( + "testBytes", Collections.singletonList(dynamicStruct)); + + assertEquals("testBytes(uint256,uint256[])", functionDynamicArray); + assertEquals("testBytes((uint256,uint256[]))", functionDynamicStruct); + + } + + @Test + public void testEncodeMultiDimStaticArray() { + //setGetMultiDimStaticArray(uint256[2][3]) + String expected = + "3cb0b894" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "0000000000000000000000000000000000000000000000000000000000000005" + + "0000000000000000000000000000000000000000000000000000000000000006"; + + assertEquals(expected, FunctionEncoder.encode(AbiV2TestFixture.setGetMultiDimStaticArrayFunction)); + } + + @Test + public void testEncodeMultiDimDynamicArrayFunction() { + // setGetMultiDimDynamicArrayFunction(uint256[][][]) + // [[[1]],[[2,3]],[[4,5],[6,7,8]]] + String expected = + "bec12ef4" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "00000000000000000000000000000000000000000000000000000000000000e0" + + "0000000000000000000000000000000000000000000000000000000000000180" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "00000000000000000000000000000000000000000000000000000000000000a0" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "0000000000000000000000000000000000000000000000000000000000000005" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000006" + + "0000000000000000000000000000000000000000000000000000000000000007" + + "0000000000000000000000000000000000000000000000000000000000000008"; + + assertEquals(expected, FunctionEncoder.encode(AbiV2TestFixture.setGetMultiDimDynamicArrayFunction)); + } + + @Test + void testEncodeMultiDimStaticArrayFunction() { + // testNestedLongString(string[2][2]) + // [["12345...","short1"],["short2","short3"]] + Function function = AbiV2TestFixture.setMultiDimStaticArrayWithUtf8StringSubTypeFunction(); + + String encoded = FunctionEncoder.encode(function); + String expected = + "a30bf9b2" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000140" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "00000000000000000000000000000000000000000000000000000000000000c0" + + "0000000000000000000000000000000000000000000000000000000000000050" + + "3132333435363738393031323334353637383930313233343536373839303132" + + "3334353637383930313233343536373839303132333435363738393031323334" + + "3536373839303132333435363738393000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000006" + + "73686f7274310000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000006" + + "73686f7274320000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000006" + + "73686f7274330000000000000000000000000000000000000000000000000000"; + assertEquals(expected, encoded); + } + } diff --git a/abi/src/test/java/org/tron/trident/abi/EventEncoderTest.java b/abi/src/test/java/org/tron/trident/abi/EventEncoderTest.java index b31351c0..380580db 100644 --- a/abi/src/test/java/org/tron/trident/abi/EventEncoderTest.java +++ b/abi/src/test/java/org/tron/trident/abi/EventEncoderTest.java @@ -18,7 +18,9 @@ import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Test; +import org.tron.trident.abi.datatypes.DynamicArray; import org.tron.trident.abi.datatypes.Event; +import org.tron.trident.abi.datatypes.generated.StaticArray2; import org.tron.trident.abi.datatypes.generated.Uint256; public class EventEncoderTest { @@ -26,12 +28,12 @@ public class EventEncoderTest { @Test public void testBuildEventSignature() { assertEquals( - EventEncoder.buildEventSignature("Deposit(address,hash256,uint256)"), - ("0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20")); + EventEncoder.buildEventSignatureWithOutPrefix("Deposit(address,hash256,uint256)"), + ("50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20")); assertEquals( - EventEncoder.buildEventSignature("Notify(uint256,uint256)"), - ("0x71e71a8458267085d5ab16980fd5f114d2d37f232479c245d523ce8d23ca40ed")); + EventEncoder.buildEventSignatureWithOutPrefix("Notify(uint256,uint256)"), + ("71e71a8458267085d5ab16980fd5f114d2d37f232479c245d523ce8d23ca40ed")); } @Test @@ -45,8 +47,8 @@ public void testEncode() { })); assertEquals( - EventEncoder.encode(event), - "0x71e71a8458267085d5ab16980fd5f114d2d37f232479c245d523ce8d23ca40ed"); + EventEncoder.encodeWithOutPrefix(event), + "71e71a8458267085d5ab16980fd5f114d2d37f232479c245d523ce8d23ca40ed"); } @Test @@ -61,4 +63,33 @@ public void testBuildMethodSignature() { EventEncoder.buildMethodSignature("Notify", Utils.convert(parameters)), "Notify(uint256,uint256)"); } + + @Test + void testBuildMethodSignatureWithDynamicStructs() { + assertEquals( + "nazzEvent((((string,string)[])[],uint256),(string,string))", + EventEncoder.buildMethodSignature( + AbiV2TestFixture.nazzEvent.getName(), + AbiV2TestFixture.nazzEvent.getParameters())); + } + + @Test + void testBuildMethodSignatureWithDynamicArrays() { + assertEquals( + "nazzEvent2((((string,string)[])[],uint256)[])", + EventEncoder.buildMethodSignature( + AbiV2TestFixture.nazzEvent2.getName(), + AbiV2TestFixture.nazzEvent2.getParameters())); + } + + @Test + void testBuildMethodSignatureWithNestedStaticArray() { + List> parameters = + Arrays.asList(new TypeReference>>() { + }); + + assertEquals( + "Nested(uint256[2][])", + EventEncoder.buildMethodSignature("Nested", Utils.convert(parameters))); + } } diff --git a/abi/src/test/java/org/tron/trident/abi/FunctionReturnDecoderTest.java b/abi/src/test/java/org/tron/trident/abi/FunctionReturnDecoderTest.java index 635b17f5..3451fb5c 100644 --- a/abi/src/test/java/org/tron/trident/abi/FunctionReturnDecoderTest.java +++ b/abi/src/test/java/org/tron/trident/abi/FunctionReturnDecoderTest.java @@ -13,8 +13,9 @@ package org.tron.trident.abi; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertNull; import java.math.BigInteger; import java.util.ArrayList; @@ -25,14 +26,18 @@ import org.junit.jupiter.api.Test; import org.tron.trident.abi.datatypes.DynamicArray; import org.tron.trident.abi.datatypes.DynamicBytes; +import org.tron.trident.abi.datatypes.DynamicStruct; import org.tron.trident.abi.datatypes.Function; import org.tron.trident.abi.datatypes.StaticArray; +import org.tron.trident.abi.datatypes.StaticStruct; import org.tron.trident.abi.datatypes.Type; import org.tron.trident.abi.datatypes.Uint; import org.tron.trident.abi.datatypes.Utf8String; import org.tron.trident.abi.datatypes.generated.Bytes16; import org.tron.trident.abi.datatypes.generated.Bytes32; import org.tron.trident.abi.datatypes.generated.StaticArray2; +import org.tron.trident.abi.datatypes.generated.StaticArray3; +import org.tron.trident.abi.datatypes.generated.StaticArray4; import org.tron.trident.abi.datatypes.generated.Uint256; import org.tron.trident.crypto.Hash; import org.tron.trident.utils.Numeric; @@ -50,7 +55,7 @@ public void testSimpleFunctionDecode() { assertEquals( FunctionReturnDecoder.decode( - "0x0000000000000000000000000000000000000000000000000000000000000037", + "0000000000000000000000000000000000000000000000000000000000000037", function.getOutputParameters()), (Collections.singletonList(new Uint(BigInteger.valueOf(55))))); } @@ -66,7 +71,7 @@ public void testSimpleFunctionStringResultDecode() { List utf8Strings = FunctionReturnDecoder.decode( - "0x0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "000000000000000000000000000000000000000000000000000000000000000d" + "6f6e65206d6f72652074696d6500000000000000000000000000000000000000", function.getOutputParameters()); @@ -85,7 +90,7 @@ public void testFunctionEmptyStringResultDecode() { List utf8Strings = FunctionReturnDecoder.decode( - "0x0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000000", function.getOutputParameters()); @@ -104,7 +109,7 @@ public void testMultipleResultFunctionDecode() { assertEquals( FunctionReturnDecoder.decode( - "0x0000000000000000000000000000000000000000000000000000000000000037" + "0000000000000000000000000000000000000000000000000000000000000037" + "0000000000000000000000000000000000000000000000000000000000000007", function.getOutputParameters()), (Arrays.asList(new Uint(BigInteger.valueOf(55)), new Uint(BigInteger.valueOf(7))))); @@ -128,7 +133,7 @@ public void testDecodeMultipleStringValues() { assertEquals( FunctionReturnDecoder.decode( - "0x0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000080" + "00000000000000000000000000000000000000000000000000000000000000c0" + "0000000000000000000000000000000000000000000000000000000000000100" + "0000000000000000000000000000000000000000000000000000000000000140" @@ -159,7 +164,7 @@ public void testDecodeStaticArrayValue() { List decoded = FunctionReturnDecoder.decode( - "0x0000000000000000000000000000000000000000000000000000000000000037" + "0000000000000000000000000000000000000000000000000000000000000037" + "0000000000000000000000000000000000000000000000000000000000000001" + "000000000000000000000000000000000000000000000000000000000000000a", outputParameters); @@ -220,7 +225,7 @@ public void testDecodeIndexedStringValue() { @Test public void testDecodeIndexedBytes32Value() { - String rawInput = "0x1234567890123456789012345678901234567890123456789012345678901234"; + String rawInput = "1234567890123456789012345678901234567890123456789012345678901234"; byte[] rawInputBytes = Numeric.hexStringToByteArray(rawInput); Assertions.assertEquals( @@ -271,7 +276,7 @@ public void testDecodeIndexedDynamicArrayValue() { @Test public void testDecodeStaticStruct() { String rawInput = - "0x0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000064"; assertEquals( @@ -284,7 +289,7 @@ public void testDecodeStaticStruct() { @Test public void testDecodeDynamicStruct() { String rawInput = - "0x0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000040" + "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000002" @@ -298,10 +303,28 @@ public void testDecodeDynamicStruct() { Collections.singletonList(new AbiV2TestFixture.Foo("id", "name"))); } + @Test + public void testDecodeDynamicStruct3() { + AbiV2TestFixture.Nazz nazz = + new AbiV2TestFixture.Nazz( + Collections.singletonList( + new AbiV2TestFixture.Nazzy( + Arrays.asList( + new AbiV2TestFixture.Foo("a", "b"), + new AbiV2TestFixture.Foo("c", "d")))), + new BigInteger("100")); + String rawInput = FunctionEncoder.encodeConstructor(Collections.singletonList(nazz)); + + List decoded = + FunctionReturnDecoder.decode( + rawInput, AbiV2TestFixture.getNazzFunction.getOutputParameters()); + assertEquals(Collections.singletonList(nazz).get(0).toString(), decoded.get(0).toString()); + } + @Test public void testDecodeDynamicStruct2() { String rawInput = - "0x0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000040" + "0000000000000000000000000000000000000000000000000000000000000002" @@ -316,7 +339,7 @@ public void testDecodeDynamicStruct2() { @Test public void testDecodeStaticStructNested() { String rawInput = - "0x0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000001" + "000000000000000000000000000000000000000000000000000000000000000a" + "0000000000000000000000000000000000000000000000000000000000000001"; @@ -332,7 +355,7 @@ public void testDecodeStaticStructNested() { @Test public void testDynamicStructNestedEncode() { String rawInput = - "0x0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000040" + "0000000000000000000000000000000000000000000000000000000000000080" @@ -351,9 +374,545 @@ public void testDynamicStructNestedEncode() { @Test public void testDecodeTupleDynamicStructNested() { String rawInput = - "0x0000000000000000000000000000000000000000000000000000000000000060" + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "000000000000000000000000000000000000000000000000000000000000000a" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals(FunctionReturnDecoder.decode( + rawInput, + AbiV2TestFixture.getFooBarFunction.getOutputParameters()), + Arrays.asList( + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.TEN))); + } + + @Test + public void testDecodeMultipleDynamicStruct() { + String rawInput = + "00000000000000000000000000000000000000000000000000000000000000a0" + "0000000000000000000000000000000000000000000000000000000000000001" + "000000000000000000000000000000000000000000000000000000000000000a" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "000000000000000000000000000000000000000000000000000000000000000b" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, AbiV2TestFixture.getFooBarBarFunction.getOutputParameters()), + Arrays.asList( + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.TEN), + new AbiV2TestFixture.Bar(BigInteger.valueOf(2), BigInteger.valueOf(11)))); + } + + @Test + public void testDecodeMultipleDynamicStruct2() { + String rawInput = + "00000000000000000000000000000000000000000000000000000000000000c0" + + "0000000000000000000000000000000000000000000000000000000000000180" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "000000000000000000000000000000000000000000000000000000000000000a" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "000000000000000000000000000000000000000000000000000000000000000b" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, AbiV2TestFixture.getFooFooBarBarFunction.getOutputParameters()), + Arrays.asList( + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.TEN), + new AbiV2TestFixture.Bar(BigInteger.valueOf(2), BigInteger.valueOf(11)))); + } + + @Test + public void testDecodeDynamicNested3() { + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, AbiV2TestFixture.getNarFunction.getOutputParameters()), + Arrays.asList( + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))))); + } + + @Test + public void testDecodeMultipleDynamicStaticNestedStructs() { + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000240" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "00000000000000000000000000000000000000000000000000000000000004d1" + + "0000000000000000000000000000000000000000000000000000000000000079" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000340" + + "0000000000000000000000000000000000000000000000000000000000000400" + + "00000000000000000000000000000000000000000000000000000000000004d1" + + "0000000000000000000000000000000000000000000000000000000000000079" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000500" + + "00000000000000000000000000000000000000000000000000000000000005a0" + + "00000000000000000000000000000000000000000000000000000000000004d1" + + "0000000000000000000000000000000000000000000000000000000000000079" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6861686100000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "686f686f00000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6861686100000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "686f686f00000000000000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, + AbiV2TestFixture.getNarBarBarFuzzFooNarFuzzNuuFooFuzzFunction + .getOutputParameters()), + Arrays.asList( + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Bar(BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar(BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Fuzz( + new AbiV2TestFixture.Bar( + BigInteger.valueOf(1233), BigInteger.valueOf(121)), + BigInteger.valueOf(2)), + new AbiV2TestFixture.Foo("haha", "hoho"), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Fuzz( + new AbiV2TestFixture.Bar( + BigInteger.valueOf(1233), BigInteger.valueOf(121)), + BigInteger.valueOf(2)), + new AbiV2TestFixture.Nuu(new AbiV2TestFixture.Foo("", "")), + new AbiV2TestFixture.Foo("haha", "hoho"), + new AbiV2TestFixture.Fuzz( + new AbiV2TestFixture.Bar( + BigInteger.valueOf(1233), BigInteger.valueOf(121)), + BigInteger.valueOf(2)))); + } + + @Test + @SuppressWarnings("unchecked") + public void testDecodeDynamicStructDynamicArray() { + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, + AbiV2TestFixture.getFooDynamicArrayFunction.getOutputParameters()), + Arrays.asList( + new DynamicArray( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name")))); + } + + @Test + @SuppressWarnings("unchecked") + public void testDecodeStaticStructStaticArray() { + String rawInput = + "0x0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, AbiV2TestFixture.getBarStaticArrayFunction.getOutputParameters()), + Arrays.asList( + new StaticArray3( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar( + BigInteger.valueOf(0), BigInteger.valueOf(0)), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(0), BigInteger.valueOf(0))))); + } + + @Test + @SuppressWarnings("unchecked") + public void testDecodeDynamicStructStaticArray() { + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000220" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, AbiV2TestFixture.getNarStaticArrayFunction.getOutputParameters()), + Arrays.asList( + new StaticArray3( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu(new AbiV2TestFixture.Foo("", ""))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo")))))); + } + + @Test + @SuppressWarnings("unchecked") + public void testDecodeDynamicStructDynamicArray2() { + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, + AbiV2TestFixture.getNarDynamicArrayFunction.getOutputParameters()), + Arrays.asList( + new DynamicArray( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))))); + } + + @Test + public void testDecodeMultipleDynamicStructStaticDynamicArrays() { + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000140" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000460" + + "0000000000000000000000000000000000000000000000000000000000000560" + + "00000000000000000000000000000000000000000000000000000000000008a0" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000220" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000120" + + "00000000000000000000000000000000000000000000000000000000000001e0" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, + AbiV2TestFixture.getNarBarFooNarFooDynamicArrayFunction + .getOutputParameters()), + Arrays.asList( + new StaticArray3<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu(new AbiV2TestFixture.Foo("", ""))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo")))), + new StaticArray3<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO)), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, new AbiV2TestFixture.Foo("id", "name")), + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new StaticArray3<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name")))); + } + + @Test + public void testDecodeStructMultipleDynamicStaticArray() { + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000140" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000480" + + "0000000000000000000000000000000000000000000000000000000000000580" + + "00000000000000000000000000000000000000000000000000000000000008c0" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000040" + "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000002" @@ -361,19 +920,789 @@ public void testDecodeTupleDynamicStructNested() { + "0000000000000000000000000000000000000000000000000000000000000004" + "6e616d6500000000000000000000000000000000000000000000000000000000"; - assertThrows( - UnsupportedOperationException.class, - () -> - FunctionReturnDecoder.decode( - rawInput, - AbiV2TestFixture.getFooBarFunction.getOutputParameters())); - - // assertEquals( - // FunctionReturnDecoder.decode( - // rawInput, - // AbiV2TestFixture.getFooBarFunction.getOutputParameters()), - // Arrays.asList( - // new AbiV2TestFixture.Foo("id", "name"), - // new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.TEN))); + assertEquals( + FunctionReturnDecoder.decode( + rawInput, + AbiV2TestFixture.idNarBarFooNarFooArraysFunction.getOutputParameters()), + Arrays.asList( + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new StaticArray3<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO)), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, new AbiV2TestFixture.Foo("id", "name")), + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name")))); } + + @Test + public void testDecodeStructMultipleDynamicStaticArray2() { + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000140" + + "0000000000000000000000000000000000000000000000000000000000000460" + + "0000000000000000000000000000000000000000000000000000000000000560" + + "00000000000000000000000000000000000000000000000000000000000008a0" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000220" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000120" + + "00000000000000000000000000000000000000000000000000000000000001e0" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, + AbiV2TestFixture.idBarNarFooNarFooArraysFunction.getOutputParameters()), + Arrays.asList( + new StaticArray3<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO)), + new StaticArray3<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu(new AbiV2TestFixture.Foo("", ""))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo")))), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, new AbiV2TestFixture.Foo("id", "name")), + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new StaticArray3<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name")))); + } + + @Test + public void testDecodeStructMultipleDynamicStaticArray3() { + String rawInput = + "00000000000000000000000000000000000000000000000000000000000000a0" + + "00000000000000000000000000000000000000000000000000000000000003c0" + + "00000000000000000000000000000000000000000000000000000000000004a0" + + "00000000000000000000000000000000000000000000000000000000000005a0" + + "00000000000000000000000000000000000000000000000000000000000008e0" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000220" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000000c" + + "0000000000000000000000000000000000000000000000000000000000000021" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000160" + + "0000000000000000000000000000000000000000000000000000000000000260" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "3400000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "6e6573746564466f6f0000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000120" + + "00000000000000000000000000000000000000000000000000000000000001e0" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, + AbiV2TestFixture.idNarBarFooNarFooArraysFunction2.getOutputParameters()), + Arrays.asList( + new StaticArray3<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu(new AbiV2TestFixture.Foo("", ""))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo")))), + new DynamicArray<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(12), BigInteger.valueOf(33)), + new AbiV2TestFixture.Bar(BigInteger.ZERO, BigInteger.ZERO)), + new DynamicArray<>( + AbiV2TestFixture.Foo.class, new AbiV2TestFixture.Foo("id", "name")), + new DynamicArray<>( + AbiV2TestFixture.Nar.class, + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("4", "nestedFoo"))), + new AbiV2TestFixture.Nar( + new AbiV2TestFixture.Nuu( + new AbiV2TestFixture.Foo("", "")))), + new StaticArray3<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("id", "name")))); + } + + @Test + @SuppressWarnings("unchecked") + public void testDecodeStaticStructDynamicArray() { + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b"; + + assertEquals( + FunctionReturnDecoder.decode( + rawInput, + AbiV2TestFixture.getBarDynamicArrayFunction.getOutputParameters()), + Arrays.asList( + new DynamicArray( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123)), + new AbiV2TestFixture.Bar( + BigInteger.valueOf(123), BigInteger.valueOf(123))))); + } + + @Test + public void testBuildDynamicArrayOfStaticStruct() throws ClassNotFoundException { + // This is a version of testDecodeStaticStructDynamicArray() that builds + // the decoding TypeReferences using inner types. + String rawInput = + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b" + + "000000000000000000000000000000000000000000000000000000000000007b"; + + // (uint256, uint256) static struct. + TypeReference staticStructTr = + new TypeReference( + false, + Arrays.asList( + TypeReference.makeTypeReference("uint256"), + TypeReference.makeTypeReference("uint256"))) { + }; + + // (uint256, uint256)[] dynamic array of static struct. + TypeReference dynamicArray = + new TypeReference(false) { + @Override + public TypeReference getSubTypeReference() { + return staticStructTr; + } + + @Override + public java.lang.reflect.Type getType() { + return new java.lang.reflect.ParameterizedType() { + @Override + public java.lang.reflect.Type[] getActualTypeArguments() { + return new java.lang.reflect.Type[] {staticStructTr.getType()}; + } + + @Override + public java.lang.reflect.Type getRawType() { + return DynamicArray.class; + } + + @Override + public java.lang.reflect.Type getOwnerType() { + return Class.class; + } + }; + } + }; + + List decodedData = + FunctionReturnDecoder.decode(rawInput, Utils.convert(Arrays.asList(dynamicArray))); + + List decodedArray = ((DynamicArray) decodedData.get(0)).getValue(); + + List decodedStaticStruct0 = ((StaticStruct) decodedArray.get(0)).getValue(); + assertEquals(decodedStaticStruct0.get(0).getValue(), BigInteger.valueOf(123)); + assertEquals(decodedStaticStruct0.get(1).getValue(), BigInteger.valueOf(123)); + + List decodedStaticStruct1 = ((StaticStruct) decodedArray.get(1)).getValue(); + assertEquals(decodedStaticStruct1.get(0).getValue(), BigInteger.valueOf(123)); + assertEquals(decodedStaticStruct1.get(1).getValue(), BigInteger.valueOf(123)); + } + + @Test + public void testDecodeTupleOfStaticArrays() { + List outputParameters = new ArrayList>(); + outputParameters.addAll( + Arrays.asList( + new TypeReference>() { + }, + new TypeReference>() { + })); + + // tuple of (strings string[4]{"", "", "", ""}, ints int[4]{0, 0, 0, 0}) + String rawInput = + "0x" + + "00000000000000000000000000000000000000000000000000000000000000a0" + // strings array offset + + "0000000000000000000000000000000000000000000000000000000000000000" // ints[0] + + "0000000000000000000000000000000000000000000000000000000000000000" // ints[1] + + "0000000000000000000000000000000000000000000000000000000000000000" // ints[2] + + "0000000000000000000000000000000000000000000000000000000000000000" // ints[3] + + "0000000000000000000000000000000000000000000000000000000000000080" + // offset strings[0] + + "00000000000000000000000000000000000000000000000000000000000000a0" + // offset strings[1] + + "00000000000000000000000000000000000000000000000000000000000000c0" + // offset strings[2] + + "00000000000000000000000000000000000000000000000000000000000000e0" + // offset strings[3] + + "0000000000000000000000000000000000000000000000000000000000000000" // strings[0] + + "0000000000000000000000000000000000000000000000000000000000000000" // strings[1] + + "0000000000000000000000000000000000000000000000000000000000000000" // strings[2] + + "0000000000000000000000000000000000000000000000000000000000000000"; // strings[3] + + assertEquals( + FunctionReturnDecoder.decode(rawInput, outputParameters), + Arrays.asList( + new StaticArray4( + Utf8String.class, + new Utf8String(""), + new Utf8String(""), + new Utf8String(""), + new Utf8String("")), + new StaticArray4( + Uint256.class, + new Uint256(0), + new Uint256(0), + new Uint256(0), + new Uint256(0)))); + } + + @Test + @SuppressWarnings("unchecked") + public void testDecodeDynamicStructWithStaticStruct() { + String rawInput = + "0x0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "000000000000000000000000000000000000000000000000000000000000000a" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6461746100000000000000000000000000000000000000000000000000000000"; + assertEquals( + FunctionReturnDecoder.decode( + rawInput, AbiV2TestFixture.getQuxFunction.getOutputParameters()), + Arrays.asList( + new AbiV2TestFixture.Qux( + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.TEN), "data"))); + } + + @Test + public void testBuildDynamicStructWithStaticStruct() throws ClassNotFoundException { + // This is a version of testDecodeDynamicStructWithStaticStruct() that builds + // the decoding TypeReferences using inner types. + String rawInput = + "0x0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "000000000000000000000000000000000000000000000000000000000000000a" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6461746100000000000000000000000000000000000000000000000000000000"; + // (uint256, uint256) static struct. + TypeReference staticStruct = + new TypeReference( + false, + Arrays.asList( + TypeReference.makeTypeReference("uint256"), + TypeReference.makeTypeReference("uint256"))) { + }; + + // ((uint256, uint256), string) dynamic struct containing static struct. + TypeReference dynamicStruct = + new TypeReference( + false, + Arrays.asList(staticStruct, TypeReference.makeTypeReference("string"))) { + }; + + List decodedData = + FunctionReturnDecoder.decode(rawInput, Utils.convert(Arrays.asList(dynamicStruct))); + List decodedDynamicStruct = ((DynamicStruct) decodedData.get(0)).getValue(); + + List decodedStaticStruct = ((StaticStruct) decodedDynamicStruct.get(0)).getValue(); + assertEquals(decodedStaticStruct.get(0).getValue(), BigInteger.ONE); + assertEquals(decodedStaticStruct.get(1).getValue(), BigInteger.TEN); + + assertEquals(decodedDynamicStruct.get(1).getValue(), "data"); + } + + @Test + public void testDynamicStructOfDynamicStructWithAdditionalParametersReturn() + throws ClassNotFoundException { + // Return data from 'testInputAndOutput' function of this contract + // https://sepolia.etherscan.io/address/0x009C10396226ECFE3E39b3f1AEFa072E37578e30#readContract + // struct MyStruct { + // uint256 value1; + // string value2; + // string value3; + // MyStruct2 value4; + // } + // + // struct MyStruct2 { + // string value1; + // string value2; + // } + // function testInputAndOutput(MyStruct memory struc) external pure + // returns(string memory valueBefore, MyStruct memory, string memory valueAfter) { + // + // return ("valuebefore", mystruc, "valueafter"); + // + // } + String returnedData = + "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000b76616c75656265666f72650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063078313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a76616c7565616674657200000000000000000000000000000000000000000000"; + + List> myStruct2Types = new ArrayList<>(); + List> myStructTypes = new ArrayList<>(); + List> myParameters = new ArrayList<>(); + + myStruct2Types.add(TypeReference.makeTypeReference("string")); + myStruct2Types.add(TypeReference.makeTypeReference("string")); + + myStructTypes.add(TypeReference.makeTypeReference("uint256")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(new TypeReference(false, myStruct2Types) { + }); + + myParameters.add(TypeReference.makeTypeReference("string")); + myParameters.add(new TypeReference(false, myStructTypes) { + }); + + myParameters.add(TypeReference.makeTypeReference("string")); + + List decodedData = + FunctionReturnDecoder.decode(returnedData, Utils.convert(myParameters)); + + assertEquals(decodedData.get(0).getValue(), "valuebefore"); + + List structData = ((DynamicStruct) decodedData.get(1)).getValue(); + + assertEquals(structData.get(0).getValue(), BigInteger.valueOf(1)); + assertEquals(structData.get(1).getValue(), "2"); + assertEquals(structData.get(2).getValue(), "3"); + + List innerStructData = ((DynamicStruct) structData.get(3)).getValue(); + + assertEquals(innerStructData.get(0).getValue(), "1234"); + assertEquals(innerStructData.get(1).getValue(), "0x1234"); + + assertEquals(decodedData.get(2).getValue(), "valueafter"); + } + + @Test + public void testDynamicStructOfStaticStructReturn() throws ClassNotFoundException { + String returnedData = + "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004746573740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047465737400000000000000000000000000000000000000000000000000000000"; + + List> myStruct2Types = new ArrayList<>(); + List> myStructTypes = new ArrayList<>(); + + myStruct2Types.add(TypeReference.makeTypeReference("uint256")); + + myStructTypes.add(TypeReference.makeTypeReference("uint256")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(new TypeReference(false, myStruct2Types) { + }); + + TypeReference dynamicStruct = + new TypeReference(false, myStructTypes) { + }; + + List decodedData = + FunctionReturnDecoder.decode( + returnedData, Utils.convert(Arrays.asList(dynamicStruct))); + + List decodedStruct = ((DynamicStruct) decodedData.get(0)).getValue(); + + assertEquals(decodedStruct.get(0).getValue(), BigInteger.valueOf(1)); + assertEquals(decodedStruct.get(1).getValue(), "test"); + assertEquals(decodedStruct.get(2).getValue(), "test"); + + List innerStructData = ((StaticStruct) decodedStruct.get(3)).getValue(); + + assertEquals(innerStructData.get(0).getValue(), BigInteger.valueOf(1)); + } + + @Test + public void testBuildEventOfArrayOfDynamicStruct() throws ClassNotFoundException { + // The full event signature is + // + // Stamp3(uint256 indexed stampId, address indexed caller, bool odd, + // (uint256,bool,string) topMessage, (uint256,bool,string)[] messages), + // + // but we are only decoding the non-indexed data portion of it represented by + // 'bool odd, (uint256,bool,string) topMessage, (uint256,bool,string)[] messages'. + // + // Transaction: + // https://testnet.treasurescan.io/tx/0x041e53e7571283d462df99a95b2c21324279657f26a3adef907095d2d9c5ed85?tab=logs + // Contract: + // https://testnet.treasurescan.io/address/0x5167E9A422aCEd95C2D0b62bF05a7847a9a942B2 + String data = + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d000000000000000000000000000000000000000000000000000000000000"; + + TypeReference tupleTr = + new TypeReference( + false, + Arrays.asList( + TypeReference.makeTypeReference("uint256"), + TypeReference.makeTypeReference("bool"), + TypeReference.makeTypeReference("string"))) { + }; + + // Decode data according to the above signature for the non-indexed parameters. + List decodedData = + FunctionReturnDecoder.decode( + data, + Utils.convert( + Arrays.asList( + // bool odd + TypeReference.makeTypeReference("bool"), + + // (uint256,bool,string) + tupleTr, + + // (uint256,bool,string)[] + new TypeReference(false) { + @Override + public TypeReference getSubTypeReference() { + return tupleTr; + } + + @Override + public java.lang.reflect.Type getType() { + return new java.lang.reflect.ParameterizedType() { + @Override + public java.lang.reflect.Type[] + getActualTypeArguments() { + return new java.lang.reflect.Type[] { + tupleTr.getType() + }; + } + + @Override + public java.lang.reflect.Type getRawType() { + return DynamicArray.class; + } + + @Override + public java.lang.reflect.Type getOwnerType() { + return Class.class; + } + }; + } + }))); + + assertEquals(decodedData.get(0).getValue(), false); + + List tupleData = ((DynamicStruct) decodedData.get(1)).getValue(); + + assertEquals(tupleData.get(0).getValue(), BigInteger.valueOf(20)); + assertEquals(tupleData.get(1).getValue(), false); + assertEquals(tupleData.get(2).getValue(), "hello"); + + List tupleArrayData = + ((DynamicArray) decodedData.get(2)).getValue(); + + List tupleArrayEntry0 = tupleArrayData.get(0).getValue(); + assertEquals(tupleArrayEntry0.get(0).getValue(), BigInteger.valueOf(21)); + assertEquals(tupleArrayEntry0.get(1).getValue(), true); + assertEquals(tupleArrayEntry0.get(2).getValue(), "gm"); + + List tupleArrayEntry1 = tupleArrayData.get(1).getValue(); + assertEquals(tupleArrayEntry1.get(0).getValue(), BigInteger.valueOf(22)); + assertEquals(tupleArrayEntry1.get(1).getValue(), false); + assertEquals(tupleArrayEntry1.get(2).getValue(), "gm"); + + List tupleArrayEntry2 = tupleArrayData.get(2).getValue(); + assertEquals(tupleArrayEntry2.get(0).getValue(), BigInteger.valueOf(23)); + assertEquals(tupleArrayEntry2.get(1).getValue(), true); + assertEquals(tupleArrayEntry2.get(2).getValue(), "gm"); + } + + @Test + public void testDecodeAddress() { + String address = FunctionReturnDecoder.decodeAddress("00000000000000000000000051231ac2bedf45d3ef980ed0a8422d86310b70ef"); + assertEquals("THNDnhjKW9QPCetsMmxq5ebX9Ufmyr1m1C", address); + } + + @Test + public void testDecodeDynamicBytes() { + byte[] bytes = FunctionReturnDecoder.decodeDynamicBytes(""); + assertNull( bytes); + + bytes = FunctionReturnDecoder.decodeDynamicBytes("0x"); + assertNull( bytes); + + String rawInput = FunctionEncoder.encodeConstructor(Collections.singletonList(new DynamicBytes("hello".getBytes()))); + bytes = FunctionReturnDecoder.decodeDynamicBytes(rawInput); + assertArrayEquals("hello".getBytes(), bytes); + } + + @Test + public void testDecodeMultiDimStaticArray() { + String data = "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "0000000000000000000000000000000000000000000000000000000000000005" + + "0000000000000000000000000000000000000000000000000000000000000006"; + List outPutArrays + = FunctionReturnDecoder.decode(data, AbiV2TestFixture.setGetMultiDimStaticArrayFunction.getOutputParameters()); + + StaticArray3> outputStaticArray = (StaticArray3>)outPutArrays.get(0); + + assertEquals(3, outputStaticArray.getValue().size()); + + @SuppressWarnings("unchecked") + StaticArray3> expected = + (StaticArray3>) AbiV2TestFixture.setGetMultiDimStaticArrayFunction + .getInputParameters() + .get(0); + + assertEquals(expected, outputStaticArray); + } + +// @Test +// public void testDecodeMultiDimDynamicArrayFunction() throws ClassNotFoundException { +// //setGetMultiDimDynamicArrayFunction(uint256[][][]) +// // [[[1]],[[2,3]],[[4,5],[6,7,8]]] +// String data = "0000000000000000000000000000000000000000000000000000000000000020" +// + "0000000000000000000000000000000000000000000000000000000000000003" +// + "0000000000000000000000000000000000000000000000000000000000000060" +// + "00000000000000000000000000000000000000000000000000000000000000e0" +// + "0000000000000000000000000000000000000000000000000000000000000180" +// + "0000000000000000000000000000000000000000000000000000000000000001" +// + "0000000000000000000000000000000000000000000000000000000000000020" +// + "0000000000000000000000000000000000000000000000000000000000000001" +// + "0000000000000000000000000000000000000000000000000000000000000001" +// + "0000000000000000000000000000000000000000000000000000000000000001" +// + "0000000000000000000000000000000000000000000000000000000000000020" +// + "0000000000000000000000000000000000000000000000000000000000000002" +// + "0000000000000000000000000000000000000000000000000000000000000002" +// + "0000000000000000000000000000000000000000000000000000000000000003" +// + "0000000000000000000000000000000000000000000000000000000000000002" +// + "0000000000000000000000000000000000000000000000000000000000000040" +// + "00000000000000000000000000000000000000000000000000000000000000a0" +// + "0000000000000000000000000000000000000000000000000000000000000002" +// + "0000000000000000000000000000000000000000000000000000000000000004" +// + "0000000000000000000000000000000000000000000000000000000000000005" +// + "0000000000000000000000000000000000000000000000000000000000000003" +// + "0000000000000000000000000000000000000000000000000000000000000006" +// + "0000000000000000000000000000000000000000000000000000000000000007" +// + "0000000000000000000000000000000000000000000000000000000000000008"; +// +// List outPutDynamicArrays +// = FunctionReturnDecoder.decode(data, AbiV2TestFixture.setGetMultiDimDynamicArrayFunction.getOutputParameters()); +// +// @SuppressWarnings("unchecked") +// DynamicArray>> outputDynamicArray = (DynamicArray>>)outPutDynamicArrays.get(0); +// +// @SuppressWarnings("unchecked") +// DynamicArray>> expected = +// (DynamicArray>> )AbiV2TestFixture.setGetMultiDimDynamicArrayFunction +// .getInputParameters() +// .get(0); +// assertEquals(expected, outPutDynamicArrays.get(0)); +// } } diff --git a/abi/src/test/java/org/tron/trident/abi/TridentAbiEncodeDecodeCompatibilityTest.java b/abi/src/test/java/org/tron/trident/abi/TridentAbiEncodeDecodeCompatibilityTest.java new file mode 100644 index 00000000..df1ca5c3 --- /dev/null +++ b/abi/src/test/java/org/tron/trident/abi/TridentAbiEncodeDecodeCompatibilityTest.java @@ -0,0 +1,551 @@ +package org.tron.trident.abi; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.ParameterizedType; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import java.util.zip.GZIPInputStream; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.tron.trident.abi.datatypes.DynamicArray; +import org.tron.trident.abi.datatypes.DynamicBytes; +import org.tron.trident.abi.datatypes.DynamicStruct; +import org.tron.trident.abi.datatypes.StaticArray; +import org.tron.trident.abi.datatypes.StaticStruct; +import org.tron.trident.abi.datatypes.Type; +import org.tron.trident.abi.datatypes.Utf8String; +import org.tron.trident.utils.Numeric; + +/** + * Verifies that Trident's ABI encoding and return decoding match the reference + * bytes recorded in fixtures of pre-generated random test cases. + * + *

Fixture flow: + *

    + *
  1. Load the gzipped JSON fixture(s) from the test classpath.
  2. + *
  3. Parse the JSON-encoded value tree into native Java values.
  4. + *
  5. Use {@link TypeDecoder#instantiateType} to build Trident {@code Type} instances + * for parameter encoding checks.
  6. + *
  7. Build {@link TypeReference}s from ABI outputs for return decoding checks.
  8. + *
  9. Compare both encoded parameters and decode/re-encode round trips to the + * expected hex string from the fixture.
  10. + *
+ */ +@DisplayName("Trident ABI Encode Decode Compatibility") +public class TridentAbiEncodeDecodeCompatibilityTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + /** + * Test-data fixtures loaded from the test classpath. Each entry is a gzipped JSON + * array of pre-generated ABI compatibility test cases. Multiple fixtures are merged so + * the parameterized test runs across all of them; case names are prefixed with the + * fixture file (without extension) to keep JUnit identifiers unique. + */ + private static final String[] TEST_DATA_RESOURCES = { + "contract-interface.json.gz", + "contract-interface-abi2.json.gz", + }; + + /** + * A single fixture entry parsed from the JSON test data. + */ + public static class TestCase { + public String name; // case name (e.g. "random-0") + public String types; // raw type list (e.g. "[\"address\",\"address\",\"address\"]") + public String values; // raw value list, JSON-encoded + public String result; // expected encoded hex output + public String abi; // raw ABI interface JSON + + public TestCase(JsonNode obj) { + this.name = obj.get("name").asText(); + this.types = obj.get("types").asText(); + this.values = obj.get("values").asText(); + this.result = obj.get("result").asText(); + this.abi = obj.get("interface").asText(); + } + + @Override + public String toString() { + return name + " - " + types; + } + } + + /** + * Converts ABI definitions + raw JSON values into Trident {@code Type} instances, + * including nested tuples (structs) and arrays. + */ + static class AbiTypeInstantiator { + + /** + * Converts a fixture-encoded value node into the plain Java argument shape + * that {@link TypeDecoder#instantiateType} expects (BigInteger, String, List, etc.). + */ + static class JsonValueConverter { + static Object convert(JsonNode valueObj) { + if (valueObj.isArray()) { + List list = new ArrayList<>(); + for (JsonNode item : valueObj) { + list.add(convert(item)); + } + return list; + } + if (!valueObj.isObject()) { + throw new IllegalArgumentException( + "Expected object or array, got: " + valueObj.getNodeType()); + } + String type = valueObj.get("type").asText(); + JsonNode rawValue = valueObj.get("value"); + + switch (type) { + case "number": + if (rawValue.isNumber() || rawValue.isTextual()) { + return new BigInteger(rawValue.asText()); + } + return BigInteger.ZERO; + case "string": + return rawValue.asText(); + case "boolean": + return rawValue.asBoolean(); + case "buffer": + String bufValue = rawValue.asText(); + return bufValue.startsWith("0x") ? bufValue : "0x" + bufValue; + case "tuple": + List list = new ArrayList<>(); + for (JsonNode item : rawValue) { + list.add(convert(item)); + } + return list; + default: + throw new IllegalArgumentException("Unknown type: " + type); + } + } + } + + Type instantiateType(String typeStr, JsonNode value, JsonNode abiComponentDef) + throws Exception { + if (typeStr.contains("tuple")) { + return instantiateTypeWithTupleSupport(typeStr, value, abiComponentDef); + } + Object convertedValue = JsonValueConverter.convert(value); + return TypeDecoder.instantiateType(typeStr, convertedValue); + } + + private Type instantiateTypeWithTupleSupport(String typeStr, JsonNode valueObj, + JsonNode abiDef) throws Exception { + int firstBracket = typeStr.indexOf('['); + if (firstBracket < 0) { + if (typeStr.contains("tuple")) { + if (valueObj.isArray()) { + throw new IllegalArgumentException( + "Unexpected JSON array for tuple base type: " + typeStr); + } + return instantiateTuple(abiDef, valueObj); + } + Object convertedValue = JsonValueConverter.convert(valueObj); + return TypeDecoder.instantiateType(typeStr, convertedValue); + } + + String baseType = typeStr.substring(0, firstBracket); + String arrayParts = typeStr.substring(firstBracket); + + return instantiateTupleArrayRightToLeft(baseType, arrayParts, valueObj, abiDef); + } + + private Type instantiateTuple(JsonNode input, JsonNode valueObj) throws Exception { + JsonNode componentsArray = input.get("components"); + JsonNode tupleValuesArray = valueObj.get("value"); + + List componentTypes = new ArrayList<>(); + boolean hasDynamicComponent = false; + + for (int i = 0; i < componentsArray.size(); i++) { + JsonNode component = componentsArray.get(i); + String componentType = component.get("type").asText(); + JsonNode componentValue = tupleValuesArray.get(i); + + Type componentTypeObj; + if (componentType.contains("tuple")) { + componentTypeObj = instantiateTypeWithTupleSupport(componentType, + componentValue, component); + } else { + Object convertedValue = JsonValueConverter.convert(componentValue); + componentTypeObj = TypeDecoder.instantiateType(componentType, convertedValue); + } + componentTypes.add(componentTypeObj); + + if (isDynamicType(componentTypeObj)) { + hasDynamicComponent = true; + } + } + + return hasDynamicComponent + ? new DynamicStruct(componentTypes) + : new StaticStruct(componentTypes); + } + + private Type instantiateTupleArrayRightToLeft(String baseType, String arrayParts, + JsonNode valueObj, JsonNode abiDef) throws Exception { + int lastBracket = arrayParts.lastIndexOf('['); + String lastArrayPart = arrayParts.substring(lastBracket); + String remainingArrayParts = arrayParts.substring(0, lastBracket); + + if (!valueObj.isArray()) { + throw new IllegalArgumentException( + "Expected JSON array for array type: " + baseType + arrayParts); + } + + if (!remainingArrayParts.isEmpty()) { + List elements = new ArrayList<>(); + for (JsonNode elementValue : valueObj) { + Type elementType = instantiateTupleArrayRightToLeft(baseType, + remainingArrayParts, elementValue, abiDef); + elements.add(elementType); + } + return wrapAsArray(elements, lastArrayPart); + } + + List tupleElements = new ArrayList<>(); + for (JsonNode elementValue : valueObj) { + Type tupleInstance = instantiateTuple(abiDef, elementValue); + tupleElements.add(tupleInstance); + } + return wrapAsArray(tupleElements, lastArrayPart); + } + + private static Type wrapAsArray(List elements, String arrayPart) { + if ("[]".equals(arrayPart)) { + return new DynamicArray(Type.class, elements) { }; + } + int arraySize = Integer.parseInt( + arrayPart.substring(1, arrayPart.length() - 1)); + return new StaticArray(Type.class, arraySize, elements) { }; + } + + private boolean isDynamicType(Type type) { + if (type instanceof DynamicBytes || type instanceof Utf8String + || type instanceof DynamicArray || type instanceof DynamicStruct) { + return true; + } + if (type instanceof StaticArray) { + StaticArray staticArray = (StaticArray) type; + if (!staticArray.getValue().isEmpty()) { + return isDynamicType((Type) staticArray.getValue().get(0)); + } + } + return false; + } + } + + /** + * Converts ABI output definitions into {@link TypeReference}s for return decoding, + * including tuple structs and tuple arrays. + */ + static class AbiTypeReferenceBuilder { + static List> buildOutputTypeReferences(JsonNode outputsArray) + throws Exception { + List> typeReferences = new ArrayList<>(); + for (JsonNode output : outputsArray) { + String type = output.get("type").asText(); + typeReferences.add(type.contains("tuple") + ? makeTupleTypeReference(type, output) + : TypeReference.makeTypeReference(type)); + } + return Utils.convert(typeReferences); + } + + private static TypeReference makeTupleTypeReference(String typeStr, + JsonNode componentDef) throws Exception { + String arrayParts = typeStr.substring("tuple".length()); + JsonNode components = componentDef.get("components"); + + boolean dynamic = false; + List> innerTypes = new ArrayList<>(); + for (JsonNode component : components) { + String componentType = component.get("type").asText(); + if (isAbiTypeDynamic(componentType, component)) { + dynamic = true; + } + innerTypes.add(componentType.contains("tuple") + ? makeTupleTypeReference(componentType, component) + : TypeReference.makeTypeReference(componentType)); + } + + final List> finalInnerTypes = innerTypes; + TypeReference tupleRef = dynamic + ? new TypeReference(false, finalInnerTypes) { } + : new TypeReference(false, finalInnerTypes) { }; + + return arrayParts.isEmpty() ? tupleRef : wrapInArrayTypeReference(tupleRef, arrayParts); + } + + private static boolean isAbiTypeDynamic(String typeStr, JsonNode componentDef) { + if ("string".equals(typeStr) || "bytes".equals(typeStr) || typeStr.endsWith("[]")) { + return true; + } + if (typeStr.startsWith("tuple")) { + JsonNode components = componentDef.get("components"); + if (components == null) { + return true; + } + for (JsonNode component : components) { + if (isAbiTypeDynamic(component.get("type").asText(), component)) { + return true; + } + } + return false; + } + if (typeStr.endsWith("]")) { + return isAbiTypeDynamic(typeStr.substring(0, typeStr.lastIndexOf('[')), + componentDef); + } + return false; + } + + private static TypeReference wrapInArrayTypeReference(TypeReference baseTypeRef, + String arrayParts) throws Exception { + String remaining = arrayParts; + while (!remaining.isEmpty()) { + int firstBracket = remaining.indexOf('['); + int closeBracket = remaining.indexOf(']', firstBracket); + String arrayPart = remaining.substring(firstBracket, closeBracket + 1); + remaining = remaining.substring(closeBracket + 1); + final TypeReference innerRef = baseTypeRef; + + if ("[]".equals(arrayPart)) { + baseTypeRef = new TypeReference>() { + @Override + TypeReference getSubTypeReference() { + return innerRef; + } + + @Override + public java.lang.reflect.Type getType() { + return parameterizedType(DynamicArray.class, innerRef); + } + }; + } else { + int size = Integer.parseInt(arrayPart.substring(1, arrayPart.length() - 1)); + final Class arrayClass = Class.forName( + "org.tron.trident.abi.datatypes.generated.StaticArray" + size); + baseTypeRef = + new TypeReference.StaticArrayTypeReference>(size) { + @Override + TypeReference getSubTypeReference() { + return innerRef; + } + + @Override + public java.lang.reflect.Type getType() { + return parameterizedType(arrayClass, innerRef); + } + }; + } + } + return baseTypeRef; + } + + private static ParameterizedType parameterizedType(final Class rawType, + final TypeReference innerRef) { + return new ParameterizedType() { + @Override + public java.lang.reflect.Type[] getActualTypeArguments() { + return new java.lang.reflect.Type[] {innerRef.getType()}; + } + + @Override + public java.lang.reflect.Type getRawType() { + return rawType; + } + + @Override + public java.lang.reflect.Type getOwnerType() { + return null; + } + }; + } + } + + /** + * Loads every gzipped fixture in {@link #TEST_DATA_RESOURCES} and merges them into + * one list, prefixing each case name with the fixture file stem so JUnit reports + * stay disambiguated when both fixtures contain cases like "random-0". + */ + static List loadTestCases() throws IOException { + List all = new ArrayList<>(); + for (String resource : TEST_DATA_RESOURCES) { + all.addAll(loadTestCasesFromResource(resource)); + } + return all; + } + + private static List loadTestCasesFromResource(String resourceName) + throws IOException { + try (InputStream gz = TridentAbiEncodeDecodeCompatibilityTest.class + .getClassLoader() + .getResourceAsStream(resourceName)) { + if (gz == null) { + throw new IOException( + "Test data resource not found on classpath: " + resourceName); + } + String content; + try (GZIPInputStream in = new GZIPInputStream(gz); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + byte[] buf = new byte[8192]; + int n; + while ((n = in.read(buf)) != -1) { + out.write(buf, 0, n); + } + content = out.toString(StandardCharsets.UTF_8.name()); + } + JsonNode array = MAPPER.readTree(content); + String stem = resourceName.replaceFirst("\\.json\\.gz$", ""); + return StreamSupport.stream(array.spliterator(), false) + .map(node -> { + TestCase tc = new TestCase(node); + tc.name = stem + "/" + tc.name; + return tc; + }) + .collect(Collectors.toList()); + } + } + + /** + * Parameter encoding test. + * + *

Re-encodes each fixture's outputs as the inputs of a constructor call, + * then asserts the resulting hex matches the expected encoding from the fixture. + * Type identification correctness gates selector correctness; byte-for-byte + * encoding correctness gates wire compatibility. + */ + @ParameterizedTest(name = "{0}") + @MethodSource("loadTestCases") + @DisplayName("Parameter Encoding Should Match Expected Result") + void testParameterEncoding(TestCase testCase) throws Exception { + // 1. Parse the ABI interface definition. + JsonNode interfaceArray = MAPPER.readTree(testCase.abi); + ObjectNode functionAbi = (ObjectNode) interfaceArray.get(0); + + // Copy outputs into inputs so we exercise the parameter encoder against + // the (known-good) output encoding from the fixture. + JsonNode outputsArray = functionAbi.get("outputs"); + functionAbi.set("inputs", outputsArray); + + // 2. Extract the type list from the (now-rewritten) inputs. + JsonNode inputsArray = functionAbi.get("inputs"); + List types = new ArrayList<>(); + for (JsonNode input : inputsArray) { + types.add(input.get("type").asText()); + } + + // 3. Instantiate Trident Type values. + AbiTypeInstantiator instantiator = new AbiTypeInstantiator(); + JsonNode valuesArray = MAPPER.readTree(testCase.values); + List values = new ArrayList<>(); + + for (int i = 0; i < types.size(); i++) { + String typeStr = types.get(i); + JsonNode valueElement = valuesArray.get(i); + JsonNode inputDef = inputsArray.get(i); + + // Some fixtures encode signed-int values as the unsigned representation + // of the wrapped negative number (e.g. int8=189 represents -67). Trident + // encodes the value literally, producing a zero-extended slot instead of + // a sign-extended one. This is a documented behavior difference; skip + // such cases rather than fail. + if (isSignedIntOutOfRange(typeStr, valueElement)) { + Assumptions.assumeTrue(false, + "Skipping " + typeStr + + ": fixture value outside signed range " + + "(reference auto-wraps, trident encodes literally)"); + } + + try { + values.add(instantiator.instantiateType(typeStr, valueElement, inputDef)); + } catch (Exception e) { + // Skip cases where the type isn't supported by the current Trident impl, + // or where the fixture value violates the declared bit-width. + // Constructor-thrown exceptions arrive wrapped in InvocationTargetException + // via reflection in TypeDecoder.instantiateAtomicType, so unwrap before checking. + Throwable cause = + e instanceof java.lang.reflect.InvocationTargetException + ? e.getCause() + : e; + if (cause instanceof UnsupportedOperationException + || cause instanceof IllegalArgumentException) { + Assumptions.assumeTrue(false, + "Skipping " + typeStr + ": " + + cause.getClass().getSimpleName() + + " - " + cause.getMessage()); + } + throw e; + } + } + + // 4. Encode and compare. + String encoded = FunctionEncoder.encodeConstructor(values); + String actual = Numeric.prependHexPrefix(encoded).toLowerCase(); + String expected = testCase.result.toLowerCase(); + + assertEquals(expected, actual, "Encoding mismatch for: " + testCase.name); + } + + /** + * Return decoding test. + * + *

Decodes each fixture's expected output bytes using the ABI output + * definitions, then re-encodes the decoded values. The final bytes must still + * match the fixture, which exercises return-offset handling for dynamic values + * and structs. + */ + @ParameterizedTest(name = "{0}") + @MethodSource("loadTestCases") + @DisplayName("Encode Decode Should Round Trip Expected Result") + void testEncodeDecodeRoundTrip(TestCase testCase) throws Exception { + JsonNode interfaceArray = MAPPER.readTree(testCase.abi); + JsonNode outputsArray = interfaceArray.get(0).get("outputs"); + List> outputReferences = + AbiTypeReferenceBuilder.buildOutputTypeReferences(outputsArray); + + List decodedValues = + FunctionReturnDecoder.decode(testCase.result, outputReferences); + + String encoded = FunctionEncoder.encodeConstructor(decodedValues); + String actual = Numeric.prependHexPrefix(encoded).toLowerCase(); + String expected = testCase.result.toLowerCase(); + + assertEquals(expected, actual, "Decode round-trip mismatch for: " + testCase.name); + } + + /** + * Whether the given fixture value falls outside the signed range of the declared + * {@code intN} type. Only matches scalar signed ints (not arrays/tuples, not uint). + * Treats bare {@code int} as its ABI alias {@code int256}. + */ + private static boolean isSignedIntOutOfRange(String typeStr, JsonNode valueNode) { + if (!typeStr.matches("int\\d*")) { + return false; + } + if (!valueNode.isObject() || !"number".equals(valueNode.path("type").asText())) { + return false; + } + String suffix = typeStr.substring(3); + int bitSize = suffix.isEmpty() ? 256 : Integer.parseInt(suffix); + BigInteger v = new BigInteger(valueNode.get("value").asText()); + BigInteger max = BigInteger.ONE.shiftLeft(bitSize - 1).subtract(BigInteger.ONE); + BigInteger min = BigInteger.ONE.shiftLeft(bitSize - 1).negate(); + return v.compareTo(max) > 0 || v.compareTo(min) < 0; + } +} diff --git a/abi/src/test/java/org/tron/trident/abi/TypeDecoderTest.java b/abi/src/test/java/org/tron/trident/abi/TypeDecoderTest.java index f71e4e99..7a0f6350 100644 --- a/abi/src/test/java/org/tron/trident/abi/TypeDecoderTest.java +++ b/abi/src/test/java/org/tron/trident/abi/TypeDecoderTest.java @@ -18,8 +18,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigInteger; +import java.util.Arrays; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.tron.trident.abi.AbiV2TestFixture.Bar; import org.tron.trident.abi.datatypes.Address; import org.tron.trident.abi.datatypes.Bool; import org.tron.trident.abi.datatypes.Bytes; @@ -66,6 +68,7 @@ import org.tron.trident.abi.datatypes.generated.Int80; import org.tron.trident.abi.datatypes.generated.Int88; import org.tron.trident.abi.datatypes.generated.Int96; +import org.tron.trident.abi.datatypes.generated.StaticArray1; import org.tron.trident.abi.datatypes.generated.StaticArray2; import org.tron.trident.abi.datatypes.generated.StaticArray3; import org.tron.trident.abi.datatypes.generated.Uint104; @@ -1067,7 +1070,9 @@ public void testStaticArray() throws Exception { assertEquals( TypeDecoder.decodeStaticArray( - "000000000000000000000000000000000000000000000000000000000000000d" + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "000000000000000000000000000000000000000000000000000000000000000d" + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000d" + "776f726c64212048656c6c6f2c00000000000000000000000000000000000000", @@ -1080,6 +1085,8 @@ public void testStaticArray() throws Exception { new Utf8String("Hello, world!"), new Utf8String("world! Hello,")))); + + Type arr = TypeDecoder.instantiateType("uint256[2]", new long[] {10, Long.MAX_VALUE}); assertTrue(arr instanceof StaticArray2); @@ -1106,13 +1113,6 @@ public void testEmptyStaticArray() { 0)); } - @Test - public void testEmptyStaticArrayInstantiateType() { - assertThrows( - ClassNotFoundException.class, - () -> TypeDecoder.instantiateType("uint256[0]", new long[] {})); - } - @Test public void testDynamicArray() throws Exception { assertEquals( @@ -1139,6 +1139,8 @@ public void testDynamicArray() throws Exception { assertEquals( TypeDecoder.decodeDynamicArray( "0000000000000000000000000000000000000000000000000000000000000002" // length + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + "000000000000000000000000000000000000000000000000000000000000000d" + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000d" @@ -1164,6 +1166,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>>() {}), + 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>>() {}), + 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 { @@ -1189,4 +1247,32 @@ public void multiDimArrays() throws Exception { row1 = staticArray3StaticArray3.getValue().get(1).getValue().get(1); assertEquals(row1.getValue().get(1), (new Uint256(2))); } + + @Test + public void testDecodeNestedStaticArray() { + // uint256[2][2] + // Element 1: uint256[2] = [64, 2] + // Element 2: uint256[2] = [3, 4] + String input = "0000000000000000000000000000000000000000000000000000000000000040" // 64 + + "0000000000000000000000000000000000000000000000000000000000000002" // 2 + + "0000000000000000000000000000000000000000000000000000000000000003" // 3 + + "0000000000000000000000000000000000000000000000000000000000000004"; // 4 + + // Using TypeReference to define uint256[2][2] + TypeReference.StaticArrayTypeReference>> typeRef = + new TypeReference.StaticArrayTypeReference>>(2) { + @Override + public TypeReference getSubTypeReference() { + return new TypeReference.StaticArrayTypeReference>(2) {}; + } + }; + + StaticArray2> result = TypeDecoder.decodeStaticArray(input, 0, typeRef, 2); + + assertEquals(2, result.getValue().size()); + assertEquals(java.math.BigInteger.valueOf(64), result.getValue().get(0).getValue().get(0).getValue()); + assertEquals(java.math.BigInteger.valueOf(2), result.getValue().get(0).getValue().get(1).getValue()); + assertEquals(java.math.BigInteger.valueOf(3), result.getValue().get(1).getValue().get(0).getValue()); + assertEquals(java.math.BigInteger.valueOf(4), result.getValue().get(1).getValue().get(1).getValue()); + } } diff --git a/abi/src/test/java/org/tron/trident/abi/TypeEncoderPackedTest.java b/abi/src/test/java/org/tron/trident/abi/TypeEncoderPackedTest.java new file mode 100644 index 00000000..103ec298 --- /dev/null +++ b/abi/src/test/java/org/tron/trident/abi/TypeEncoderPackedTest.java @@ -0,0 +1,1082 @@ +package org.tron.trident.abi; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.math.BigInteger; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.tron.trident.abi.datatypes.Address; +import org.tron.trident.abi.datatypes.Bool; +import org.tron.trident.abi.datatypes.Bytes; +import org.tron.trident.abi.datatypes.DynamicArray; +import org.tron.trident.abi.datatypes.DynamicBytes; +import org.tron.trident.abi.datatypes.Fixed; +import org.tron.trident.abi.datatypes.Int; +import org.tron.trident.abi.datatypes.StaticArray; +import org.tron.trident.abi.datatypes.Ufixed; +import org.tron.trident.abi.datatypes.Uint; +import org.tron.trident.abi.datatypes.Utf8String; +import org.tron.trident.abi.datatypes.generated.Bytes1; +import org.tron.trident.abi.datatypes.generated.Bytes4; +import org.tron.trident.abi.datatypes.generated.Bytes6; +import org.tron.trident.abi.datatypes.generated.Int104; +import org.tron.trident.abi.datatypes.generated.Int112; +import org.tron.trident.abi.datatypes.generated.Int120; +import org.tron.trident.abi.datatypes.generated.Int128; +import org.tron.trident.abi.datatypes.generated.Int136; +import org.tron.trident.abi.datatypes.generated.Int144; +import org.tron.trident.abi.datatypes.generated.Int152; +import org.tron.trident.abi.datatypes.generated.Int16; +import org.tron.trident.abi.datatypes.generated.Int160; +import org.tron.trident.abi.datatypes.generated.Int168; +import org.tron.trident.abi.datatypes.generated.Int176; +import org.tron.trident.abi.datatypes.generated.Int184; +import org.tron.trident.abi.datatypes.generated.Int192; +import org.tron.trident.abi.datatypes.generated.Int200; +import org.tron.trident.abi.datatypes.generated.Int208; +import org.tron.trident.abi.datatypes.generated.Int216; +import org.tron.trident.abi.datatypes.generated.Int224; +import org.tron.trident.abi.datatypes.generated.Int232; +import org.tron.trident.abi.datatypes.generated.Int24; +import org.tron.trident.abi.datatypes.generated.Int240; +import org.tron.trident.abi.datatypes.generated.Int248; +import org.tron.trident.abi.datatypes.generated.Int32; +import org.tron.trident.abi.datatypes.generated.Int40; +import org.tron.trident.abi.datatypes.generated.Int48; +import org.tron.trident.abi.datatypes.generated.Int56; +import org.tron.trident.abi.datatypes.generated.Int64; +import org.tron.trident.abi.datatypes.generated.Int72; +import org.tron.trident.abi.datatypes.generated.Int8; +import org.tron.trident.abi.datatypes.generated.Int80; +import org.tron.trident.abi.datatypes.generated.Int88; +import org.tron.trident.abi.datatypes.generated.Int96; +import org.tron.trident.abi.datatypes.generated.StaticArray0; +import org.tron.trident.abi.datatypes.generated.StaticArray2; +import org.tron.trident.abi.datatypes.generated.StaticArray3; +import org.tron.trident.abi.datatypes.generated.Uint104; +import org.tron.trident.abi.datatypes.generated.Uint112; +import org.tron.trident.abi.datatypes.generated.Uint120; +import org.tron.trident.abi.datatypes.generated.Uint128; +import org.tron.trident.abi.datatypes.generated.Uint136; +import org.tron.trident.abi.datatypes.generated.Uint144; +import org.tron.trident.abi.datatypes.generated.Uint152; +import org.tron.trident.abi.datatypes.generated.Uint16; +import org.tron.trident.abi.datatypes.generated.Uint160; +import org.tron.trident.abi.datatypes.generated.Uint168; +import org.tron.trident.abi.datatypes.generated.Uint176; +import org.tron.trident.abi.datatypes.generated.Uint184; +import org.tron.trident.abi.datatypes.generated.Uint192; +import org.tron.trident.abi.datatypes.generated.Uint200; +import org.tron.trident.abi.datatypes.generated.Uint208; +import org.tron.trident.abi.datatypes.generated.Uint216; +import org.tron.trident.abi.datatypes.generated.Uint224; +import org.tron.trident.abi.datatypes.generated.Uint232; +import org.tron.trident.abi.datatypes.generated.Uint24; +import org.tron.trident.abi.datatypes.generated.Uint240; +import org.tron.trident.abi.datatypes.generated.Uint248; +import org.tron.trident.abi.datatypes.generated.Uint32; +import org.tron.trident.abi.datatypes.generated.Uint40; +import org.tron.trident.abi.datatypes.generated.Uint48; +import org.tron.trident.abi.datatypes.generated.Uint56; +import org.tron.trident.abi.datatypes.generated.Uint64; +import org.tron.trident.abi.datatypes.generated.Uint72; +import org.tron.trident.abi.datatypes.generated.Uint8; +import org.tron.trident.abi.datatypes.generated.Uint80; +import org.tron.trident.abi.datatypes.generated.Uint88; +import org.tron.trident.abi.datatypes.generated.Uint96; +import org.tron.trident.abi.datatypes.primitive.Byte; +import org.tron.trident.abi.datatypes.primitive.Char; +import org.tron.trident.abi.datatypes.primitive.Long; +import org.tron.trident.abi.datatypes.primitive.Short; + + +public class TypeEncoderPackedTest { + + @Test + public void testAddressEncodePacked() { + Address address = new Address("0x663e27AdC18d862dA9A82f060310621D379e469a"); + + assertEquals("663e27adc18d862da9a82f060310621d379e469a", TypeEncoder.encodePacked(address)); + + Address addressLong = + new Address( + 256, "0xa04462684b510796c186d19abfa6929742f79394583d6efb1243bbb473f21d9f"); + assertEquals( + "a04462684b510796c186d19abfa6929742f79394583d6efb1243bbb473f21d9f", + TypeEncoder.encodePacked(addressLong)); + } + + @Test + public void testBoolEncodePacked() { + assertEquals(TypeEncoder.encodePacked(new Bool(false)), ("00")); + assertEquals(TypeEncoder.encodePacked(new Bool(true)), ("01")); + } + + @Test + public void testUintEncodePacked() { + Uint zero8 = new Uint8(BigInteger.ZERO); + assertEquals("00", TypeEncoder.encodePacked(zero8)); + + Uint max8 = new Uint8(255); + assertEquals("ff", TypeEncoder.encodePacked(max8)); + + Uint zero16 = new Uint16(BigInteger.ZERO); + assertEquals("0000", TypeEncoder.encodePacked(zero16)); + Uint max16 = new Uint16(65535); + assertEquals("ffff", TypeEncoder.encodePacked(max16)); + + Uint zero24 = new Uint24(BigInteger.ZERO); + assertEquals("000000", TypeEncoder.encodePacked(zero24)); + Uint max24 = new Uint24(16777215); + assertEquals("ffffff", TypeEncoder.encodePacked(max24)); + + Uint zero32 = new Uint32(BigInteger.ZERO); + assertEquals("00000000", TypeEncoder.encodePacked(zero32)); + Uint max32 = new Uint32(BigInteger.valueOf(4294967295L)); + assertEquals("ffffffff", TypeEncoder.encodePacked(max32)); + + Uint zero40 = new Uint40(BigInteger.ZERO); + assertEquals("0000000000", TypeEncoder.encodePacked(zero40)); + Uint max40 = new Uint40(BigInteger.valueOf(1099511627775L)); + assertEquals("ffffffffff", TypeEncoder.encodePacked(max40)); + + Uint zero48 = new Uint48(BigInteger.ZERO); + assertEquals("000000000000", TypeEncoder.encodePacked(zero48)); + Uint max48 = new Uint48(BigInteger.valueOf(281474976710655L)); + assertEquals("ffffffffffff", TypeEncoder.encodePacked(max48)); + + Uint zero56 = new Uint56(BigInteger.ZERO); + assertEquals("00000000000000", TypeEncoder.encodePacked(zero56)); + Uint max56 = new Uint56(BigInteger.valueOf(72057594037927935L)); + assertEquals("ffffffffffffff", TypeEncoder.encodePacked(max56)); + + Uint zero64 = new Uint64(BigInteger.ZERO); + assertEquals("0000000000000000", TypeEncoder.encodePacked(zero64)); + + Uint maxLong = new Uint64(BigInteger.valueOf(java.lang.Long.MAX_VALUE)); + assertEquals("7fffffffffffffff", TypeEncoder.encodePacked(maxLong)); + + Uint maxValue64 = + new Uint( + new BigInteger( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + 16)); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(maxValue64)); + + Uint largeValue = + new Uint( + new BigInteger( + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + 16)); + assertEquals( + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + TypeEncoder.encodePacked(largeValue)); + + Uint zero72 = new Uint72(BigInteger.ZERO); + assertEquals("000000000000000000", TypeEncoder.encodePacked(zero72)); + Uint max72 = new Uint72(new BigInteger("4722366482869645213695")); + assertEquals("ffffffffffffffffff", TypeEncoder.encodePacked(max72)); + + Uint zero80 = new Uint80(BigInteger.ZERO); + assertEquals("00000000000000000000", TypeEncoder.encodePacked(zero80)); + Uint max80 = new Uint80(new BigInteger("1208925819614629174706175")); + assertEquals("ffffffffffffffffffff", TypeEncoder.encodePacked(max80)); + + Uint zero88 = new Uint88(BigInteger.ZERO); + assertEquals("0000000000000000000000", TypeEncoder.encodePacked(zero88)); + Uint max88 = new Uint88(new BigInteger("309485009821345068724781055")); + assertEquals("ffffffffffffffffffffff", TypeEncoder.encodePacked(max88)); + + Uint zero96 = new Uint96(BigInteger.ZERO); + assertEquals("000000000000000000000000", TypeEncoder.encodePacked(zero96)); + Uint max96 = new Uint96(new BigInteger("79228162514264337593543950335")); + assertEquals("ffffffffffffffffffffffff", TypeEncoder.encodePacked(max96)); + + Uint zero104 = new Uint104(BigInteger.ZERO); + assertEquals("00000000000000000000000000", TypeEncoder.encodePacked(zero104)); + Uint max104 = new Uint104(new BigInteger("20282409603651670423947251286015")); + assertEquals("ffffffffffffffffffffffffff", TypeEncoder.encodePacked(max104)); + + Uint zero112 = new Uint112(BigInteger.ZERO); + assertEquals("0000000000000000000000000000", TypeEncoder.encodePacked(zero112)); + Uint max112 = new Uint112(new BigInteger("5192296858534827628530496329220095")); + assertEquals("ffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max112)); + + Uint zero120 = new Uint120(BigInteger.ZERO); + assertEquals("000000000000000000000000000000", TypeEncoder.encodePacked(zero120)); + Uint max120 = new Uint120(new BigInteger("1329227995784915872903807060280344575")); + assertEquals("ffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max120)); + + Uint zero128 = new Uint128(BigInteger.ZERO); + assertEquals("00000000000000000000000000000000", TypeEncoder.encodePacked(zero128)); + Uint max128 = new Uint128(new BigInteger("340282366920938463463374607431768211455")); + assertEquals("ffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max128)); + + Uint zero136 = new Uint136(BigInteger.ZERO); + assertEquals("0000000000000000000000000000000000", TypeEncoder.encodePacked(zero136)); + Uint max136 = new Uint136(new BigInteger("87112285931760246646623899502532662132735")); + assertEquals("ffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max136)); + + Uint zero144 = new Uint144(BigInteger.ZERO); + assertEquals("000000000000000000000000000000000000", TypeEncoder.encodePacked(zero144)); + Uint max144 = new Uint144(new BigInteger("22300745198530623141535718272648361505980415")); + assertEquals("ffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max144)); + + Uint zero152 = new Uint152(BigInteger.ZERO); + assertEquals("00000000000000000000000000000000000000", TypeEncoder.encodePacked(zero152)); + Uint max152 = new Uint152(new BigInteger("5708990770823839524233143877797980545530986495")); + assertEquals("ffffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max152)); + + Uint zero160 = new Uint160(BigInteger.ZERO); + assertEquals("0000000000000000000000000000000000000000", TypeEncoder.encodePacked(zero160)); + Uint max160 = + new Uint160(new BigInteger("1461501637330902918203684832716283019655932542975")); + assertEquals("ffffffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max160)); + + Uint zero168 = new Uint168(BigInteger.ZERO); + assertEquals( + "000000000000000000000000000000000000000000", TypeEncoder.encodePacked(zero168)); + Uint max168 = + new Uint168(new BigInteger("374144419156711147060143317175368453031918731001855")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max168)); + + Uint zero176 = new Uint176(BigInteger.ZERO); + assertEquals( + "00000000000000000000000000000000000000000000", TypeEncoder.encodePacked(zero176)); + Uint max176 = + new Uint176( + new BigInteger("95780971304118053647396689196894323976171195136475135")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max176)); + + Uint zero184 = new Uint184(BigInteger.ZERO); + assertEquals( + "0000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero184)); + Uint max184 = + new Uint184( + new BigInteger("24519928653854221733733552434404946937899825954937634815")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max184)); + + Uint zero192 = new Uint192(BigInteger.ZERO); + assertEquals( + "000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero192)); + Uint max192 = + new Uint192( + new BigInteger( + "6277101735386680763835789423207666416102355444464034512895")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max192)); + + Uint zero200 = new Uint200(BigInteger.ZERO); + assertEquals( + "00000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero200)); + Uint max200 = + new Uint200( + new BigInteger( + "1606938044258990275541962092341162602522202993782792835301375")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max200)); + + Uint zero208 = new Uint208(BigInteger.ZERO); + assertEquals( + "0000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero208)); + Uint max208 = + new Uint208( + new BigInteger( + "411376139330301510538742295639337626245683966408394965837152255")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max208)); + + Uint zero216 = new Uint216(BigInteger.ZERO); + assertEquals( + "000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero216)); + Uint max216 = + new Uint216( + new BigInteger( + "105312291668557186697918027683670432318895095400549111254310977535")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max216)); + + Uint zero224 = new Uint224(BigInteger.ZERO); + assertEquals( + "00000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero224)); + Uint max224 = + new Uint224( + new BigInteger( + "26959946667150639794667015087019630673637144422540572481103610249215")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max224)); + + Uint zero232 = new Uint232(BigInteger.ZERO); + assertEquals( + "0000000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero232)); + Uint max232 = + new Uint232( + new BigInteger( + "6901746346790563787434755862277025452451108972170386555162524223799295")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max232)); + + Uint zero240 = new Uint240(BigInteger.ZERO); + assertEquals( + "000000000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero240)); + Uint max240 = + new Uint240( + new BigInteger( + "1766847064778384329583297500742918515827483896875618958121606201292619775")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max240)); + + Uint zero248 = new Uint248(BigInteger.ZERO); + assertEquals( + "00000000000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero248)); + Uint max248 = + new Uint248( + new BigInteger( + "452312848583266388373324160190187140051835877600158453279131187530910662655")); + assertEquals( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max248)); + } + + @Test + public void testIntEncodePacked() { + Int zero8 = new Int8(BigInteger.ZERO); + assertEquals("00", TypeEncoder.encodePacked(zero8)); + + Int max8 = new Int8(BigInteger.valueOf(127)); + assertEquals("7f", TypeEncoder.encodePacked(max8)); + + Int min8 = new Int8(BigInteger.valueOf(-128)); + assertEquals("80", TypeEncoder.encodePacked(min8)); + + Int zero16 = new Int16(BigInteger.ZERO); + assertEquals("0000", TypeEncoder.encodePacked(zero16)); + + Int max16 = new Int16(BigInteger.valueOf(32767)); + assertEquals("7fff", TypeEncoder.encodePacked(max16)); + + Int min16 = new Int16(BigInteger.valueOf(-32768)); + assertEquals("8000", TypeEncoder.encodePacked(min16)); + + Int zero24 = new Int24(BigInteger.ZERO); + assertEquals("000000", TypeEncoder.encodePacked(zero24)); + + Int max24 = new Int24(BigInteger.valueOf(8388607)); + assertEquals("7fffff", TypeEncoder.encodePacked(max24)); + + Int min24 = new Int24(BigInteger.valueOf(-8388608)); + assertEquals("800000", TypeEncoder.encodePacked(min24)); + + Int zero32 = new Int32(BigInteger.ZERO); + assertEquals("00000000", TypeEncoder.encodePacked(zero32)); + + Int max32 = new Int32(BigInteger.valueOf(2147483647)); + assertEquals("7fffffff", TypeEncoder.encodePacked(max32)); + + Int min32 = new Int32(BigInteger.valueOf(-2147483648)); + assertEquals("80000000", TypeEncoder.encodePacked(min32)); + + Int zero40 = new Int40(BigInteger.ZERO); + assertEquals("0000000000", TypeEncoder.encodePacked(zero40)); + + Int max40 = new Int40(BigInteger.valueOf(549755813887L)); + assertEquals("7fffffffff", TypeEncoder.encodePacked(max40)); + + Int min40 = new Int40(BigInteger.valueOf(-549755813888L)); + assertEquals("8000000000", TypeEncoder.encodePacked(min40)); + + Int zero48 = new Int48(BigInteger.ZERO); + assertEquals("000000000000", TypeEncoder.encodePacked(zero48)); + + Int max48 = new Int48(BigInteger.valueOf(140737488355327L)); + assertEquals("7fffffffffff", TypeEncoder.encodePacked(max48)); + + Int min48 = new Int48(BigInteger.valueOf(-140737488355328L)); + assertEquals("800000000000", TypeEncoder.encodePacked(min48)); + + Int zero56 = new Int56(BigInteger.ZERO); + assertEquals("00000000000000", TypeEncoder.encodePacked(zero56)); + + Int max56 = new Int56(BigInteger.valueOf(36028797018963967L)); + assertEquals("7fffffffffffff", TypeEncoder.encodePacked(max56)); + + Int min56 = new Int56(BigInteger.valueOf(-36028797018963968L)); + assertEquals("80000000000000", TypeEncoder.encodePacked(min56)); + + Int zero64 = new Int64(BigInteger.ZERO); + assertEquals("0000000000000000", TypeEncoder.encodePacked(zero64)); + + Int max64 = new Int64(BigInteger.valueOf(java.lang.Long.MAX_VALUE)); + assertEquals("7fffffffffffffff", TypeEncoder.encodePacked(max64)); + + Int min64 = new Int64(BigInteger.valueOf(java.lang.Long.MIN_VALUE)); + assertEquals("8000000000000000", TypeEncoder.encodePacked(min64)); + + Int zero72 = new Int72(BigInteger.ZERO); + assertEquals("000000000000000000", TypeEncoder.encodePacked(zero72)); + + Int max72 = new Int72(new BigInteger("2361183241434822606847")); + assertEquals("7fffffffffffffffff", TypeEncoder.encodePacked(max72)); + + Int min72 = new Int72(new BigInteger("-2361183241434822606848")); + assertEquals("800000000000000000", TypeEncoder.encodePacked(min72)); + + Int zero80 = new Int80(BigInteger.ZERO); + assertEquals("00000000000000000000", TypeEncoder.encodePacked(zero80)); + + Int max80 = new Int80(new BigInteger("604462909807314587353087")); + assertEquals("7fffffffffffffffffff", TypeEncoder.encodePacked(max80)); + + Int min80 = new Int80(new BigInteger("-604462909807314587353088")); + assertEquals("80000000000000000000", TypeEncoder.encodePacked(min80)); + + Int zero88 = new Int88(BigInteger.ZERO); + assertEquals("0000000000000000000000", TypeEncoder.encodePacked(zero88)); + + Int max88 = new Int88(new BigInteger("154742504910672534362390527")); + assertEquals("7fffffffffffffffffffff", TypeEncoder.encodePacked(max88)); + + Int min88 = new Int88(new BigInteger("-154742504910672534362390528")); + assertEquals("8000000000000000000000", TypeEncoder.encodePacked(min88)); + + Int zero96 = new Int96(BigInteger.ZERO); + assertEquals("000000000000000000000000", TypeEncoder.encodePacked(zero96)); + + Int max96 = new Int96(new BigInteger("39614081257132168796771975167")); + assertEquals("7fffffffffffffffffffffff", TypeEncoder.encodePacked(max96)); + + Int min96 = new Int96(new BigInteger("-39614081257132168796771975168")); + assertEquals("800000000000000000000000", TypeEncoder.encodePacked(min96)); + + Int zero104 = new Int104(BigInteger.ZERO); + assertEquals("00000000000000000000000000", TypeEncoder.encodePacked(zero104)); + + Int max104 = new Int104(new BigInteger("10141204801825835211973625643007")); + assertEquals("7fffffffffffffffffffffffff", TypeEncoder.encodePacked(max104)); + + Int min104 = new Int104(new BigInteger("-10141204801825835211973625643008")); + assertEquals("80000000000000000000000000", TypeEncoder.encodePacked(min104)); + + Int zero112 = new Int112(BigInteger.ZERO); + + assertEquals("0000000000000000000000000000", TypeEncoder.encodePacked(zero112)); + + Int max112 = new Int112(new BigInteger("2596148429267413814265248164610047")); + assertEquals("7fffffffffffffffffffffffffff", TypeEncoder.encodePacked(max112)); + + Int min112 = new Int112(new BigInteger("-2596148429267413814265248164610048")); + assertEquals("8000000000000000000000000000", TypeEncoder.encodePacked(min112)); + + Int zero120 = new Int120(BigInteger.ZERO); + + assertEquals("000000000000000000000000000000", TypeEncoder.encodePacked(zero120)); + + Int max120 = new Int120(new BigInteger("664613997892457936451903530140172287")); + assertEquals("7fffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max120)); + + Int min120 = new Int120(new BigInteger("-664613997892457936451903530140172288")); + assertEquals("800000000000000000000000000000", TypeEncoder.encodePacked(min120)); + + Int zero128 = new Int128(BigInteger.ZERO); + + assertEquals("00000000000000000000000000000000", TypeEncoder.encodePacked(zero128)); + + Int max128 = new Int128(new BigInteger("170141183460469231731687303715884105727")); + assertEquals("7fffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max128)); + + Int min128 = new Int128(new BigInteger("-170141183460469231731687303715884105728")); + assertEquals("80000000000000000000000000000000", TypeEncoder.encodePacked(min128)); + + Int zero136 = new Int136(BigInteger.ZERO); + + assertEquals("0000000000000000000000000000000000", TypeEncoder.encodePacked(zero136)); + + Int max136 = new Int136(new BigInteger("43556142965880123323311949751266331066367")); + assertEquals("7fffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max136)); + + Int min136 = new Int136(new BigInteger("-43556142965880123323311949751266331066368")); + assertEquals("8000000000000000000000000000000000", TypeEncoder.encodePacked(min136)); + + Int zero144 = new Int144(BigInteger.ZERO); + + assertEquals("000000000000000000000000000000000000", TypeEncoder.encodePacked(zero144)); + + Int max144 = new Int144(new BigInteger("11150372599265311570767859136324180752990207")); + assertEquals("7fffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max144)); + + Int min144 = new Int144(new BigInteger("-11150372599265311570767859136324180752990208")); + assertEquals("800000000000000000000000000000000000", TypeEncoder.encodePacked(min144)); + + Int zero152 = new Int152(BigInteger.ZERO); + + assertEquals("00000000000000000000000000000000000000", TypeEncoder.encodePacked(zero152)); + + Int max152 = new Int152(new BigInteger("2854495385411919762116571938898990272765493247")); + assertEquals("7fffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max152)); + + Int min152 = new Int152(new BigInteger("-2854495385411919762116571938898990272765493248")); + assertEquals("80000000000000000000000000000000000000", TypeEncoder.encodePacked(min152)); + + Int zero160 = new Int160(BigInteger.ZERO); + + assertEquals("0000000000000000000000000000000000000000", TypeEncoder.encodePacked(zero160)); + + Int max160 = new Int160(new BigInteger("730750818665451459101842416358141509827966271487")); + assertEquals("7fffffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max160)); + + Int min160 = + new Int160(new BigInteger("-730750818665451459101842416358141509827966271488")); + assertEquals("8000000000000000000000000000000000000000", TypeEncoder.encodePacked(min160)); + + Int zero168 = new Int168(BigInteger.ZERO); + + assertEquals( + "000000000000000000000000000000000000000000", TypeEncoder.encodePacked(zero168)); + + Int max168 = + new Int168(new BigInteger("187072209578355573530071658587684226515959365500927")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max168)); + + Int min168 = + new Int168(new BigInteger("-187072209578355573530071658587684226515959365500928")); + assertEquals( + "800000000000000000000000000000000000000000", TypeEncoder.encodePacked(min168)); + + Int zero176 = new Int176(BigInteger.ZERO); + + assertEquals( + "00000000000000000000000000000000000000000000", TypeEncoder.encodePacked(zero176)); + + Int max176 = + new Int176(new BigInteger("47890485652059026823698344598447161988085597568237567")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max176)); + + Int min176 = + new Int176( + new BigInteger("-47890485652059026823698344598447161988085597568237568")); + assertEquals( + "80000000000000000000000000000000000000000000", TypeEncoder.encodePacked(min176)); + + Int zero184 = new Int184(BigInteger.ZERO); + + assertEquals( + "0000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero184)); + + Int max184 = + new Int184( + new BigInteger("12259964326927110866866776217202473468949912977468817407")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffffff", TypeEncoder.encodePacked(max184)); + + Int min184 = + new Int184( + new BigInteger( + "-12259964326927110866866776217202473468949912977468817408")); + assertEquals( + "8000000000000000000000000000000000000000000000", TypeEncoder.encodePacked(min184)); + + Int zero192 = new Int192(BigInteger.ZERO); + + assertEquals( + "000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero192)); + + Int max192 = + new Int192( + new BigInteger( + "3138550867693340381917894711603833208051177722232017256447")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max192)); + + Int min192 = + new Int192( + new BigInteger( + "-3138550867693340381917894711603833208051177722232017256448")); + assertEquals( + "800000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(min192)); + + Int zero200 = new Int200(BigInteger.ZERO); + + assertEquals( + "00000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero200)); + + Int max200 = + new Int200( + new BigInteger( + "803469022129495137770981046170581301261101496891396417650687")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max200)); + + Int min200 = + new Int200( + new BigInteger( + "-803469022129495137770981046170581301261101496891396417650688")); + assertEquals( + "80000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(min200)); + + Int zero208 = new Int208(BigInteger.ZERO); + + assertEquals( + "0000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero208)); + + Int max208 = + new Int208( + new BigInteger( + "205688069665150755269371147819668813122841983204197482918576127")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max208)); + + Int min208 = + new Int208( + new BigInteger( + "-205688069665150755269371147819668813122841983204197482918576128")); + assertEquals( + "8000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(min208)); + + Int zero216 = new Int216(BigInteger.ZERO); + + assertEquals( + "000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero216)); + + Int max216 = + new Int216( + new BigInteger( + "52656145834278593348959013841835216159447547700274555627155488767")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max216)); + + Int min216 = + new Int216( + new BigInteger( + "-52656145834278593348959013841835216159447547700274555627155488768")); + assertEquals( + "800000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(min216)); + + Int zero224 = new Int224(BigInteger.ZERO); + + assertEquals( + "00000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero224)); + + Int max224 = + new Int224( + new BigInteger( + "13479973333575319897333507543509815336818572211270286240551805124607")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max224)); + + Int min224 = + new Int224( + new BigInteger( + "-13479973333575319897333507543509815336818572211270286240551805124608")); + assertEquals( + "80000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(min224)); + + Int zero232 = new Int232(BigInteger.ZERO); + + assertEquals( + "0000000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero232)); + + Int max232 = + new Int232( + new BigInteger( + "3450873173395281893717377931138512726225554486085193277581262111899647")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max232)); + + Int min232 = + new Int232( + new BigInteger( + "-3450873173395281893717377931138512726225554486085193277581262111899648")); + assertEquals( + "8000000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(min232)); + + Int zero240 = new Int240(BigInteger.ZERO); + + assertEquals( + "000000000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero240)); + + Int max240 = + new Int240( + new BigInteger( + "883423532389192164791648750371459257913741948437809479060803100646309887")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max240)); + + Int min240 = + new Int240( + new BigInteger( + "-883423532389192164791648750371459257913741948437809479060803100646309888")); + assertEquals( + "800000000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(min240)); + + Int zero248 = new Int248(BigInteger.ZERO); + + assertEquals( + "00000000000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(zero248)); + + Int max248 = + new Int248( + new BigInteger( + "226156424291633194186662080095093570025917938800079226639565593765455331327")); + assertEquals( + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + TypeEncoder.encodePacked(max248)); + + Int min248 = + new Int248( + new BigInteger( + "-226156424291633194186662080095093570025917938800079226639565593765455331328")); + assertEquals( + "80000000000000000000000000000000000000000000000000000000000000", + TypeEncoder.encodePacked(min248)); + Int minusOne = new Int(BigInteger.valueOf(-1)); + assertEquals( + TypeEncoder.encodePacked(minusOne), + ("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + } + + @Test + public void testStaticBytesEncodePacked() { + Bytes staticBytes = new Bytes6(new byte[] {0, 1, 2, 3, 4, 5}); + assertEquals(TypeEncoder.encodePacked(staticBytes), ("000102030405")); + + Bytes empty = new Bytes1(new byte[] {0}); + assertEquals(TypeEncoder.encodePacked(empty), ("00")); + + Bytes ones = new Bytes1(new byte[] {127}); + assertEquals(TypeEncoder.encodePacked(ones), ("7f")); + + Bytes dave = new Bytes4("dave".getBytes()); + assertEquals(TypeEncoder.encodePacked(dave), ("64617665")); + } + + @Test + public void testUtf8StringEncodePacked() { + Utf8String string = new Utf8String("Hello, world!"); + assertEquals("48656c6c6f2c20776f726c6421", TypeEncoder.encodePacked(string)); + + Utf8String largeString = new Utf8String("Very long string value for test!"); + assertEquals( + "56657279206c6f6e6720737472696e672076616c756520666f72207465737421", + TypeEncoder.encodePacked(largeString)); + + Utf8String veryLargeString = + new Utf8String("Very long string value for test!Very long string value for test!"); + assertEquals( + "56657279206c6f6e6720737472696e672076616c756520666f722074657374" + + "2156657279206c6f6e6720737472696e672076616c756520666f72207465737421", + TypeEncoder.encodePacked(veryLargeString)); + } + + @Test + public void testStaticArrayEncodePacked() { + StaticArray0 empty = new StaticArray0<>(Uint16.class); + + assertEquals("", TypeEncoder.encodePacked(empty)); + + StaticArray3 array = + new StaticArray3<>( + Uint16.class, new Uint16(0x45), new Uint16(0x7), new Uint16(65535)); + + assertEquals( + "0000000000000000000000000000000000000000000000000000000000000045" + + "0000000000000000000000000000000000000000000000000000000000000007" + + "000000000000000000000000000000000000000000000000000000000000ffff", + TypeEncoder.encodePacked(array)); + + StaticArray3 strings = + new StaticArray3<>( + Utf8String.class, + new Utf8String("test"), + new Utf8String("test"), + new Utf8String("test")); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(strings)); + + StaticArray3 dynamicStuct = + new StaticArray3<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("", ""), + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("", "")); + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(dynamicStuct)); + + StaticArray3 staticStruct = + new StaticArray3<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.ZERO), + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.ZERO), + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.ZERO)); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(staticStruct)); + + StaticArray ufixed = + new StaticArray2<>( + Ufixed.class, + new Ufixed(BigInteger.valueOf(0x2), BigInteger.valueOf(0x2)), + new Ufixed(BigInteger.valueOf(0x8), BigInteger.valueOf(0x8))); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(ufixed)); + + StaticArray fixed = + new StaticArray2<>( + Fixed.class, + new Fixed(BigInteger.valueOf(0x2), BigInteger.valueOf(0x2)), + new Fixed(BigInteger.valueOf(0x8), BigInteger.valueOf(0x8))); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(fixed)); + + StaticArray arrayOfEmptyBytes = + new StaticArray2<>( + DynamicBytes.class, + new DynamicBytes(new byte[0]), + new DynamicBytes(new byte[0])); + + Assertions.assertThrows( + UnsupportedOperationException.class, + () -> TypeEncoder.encodePacked(arrayOfEmptyBytes)); + } + + @Test + public void testDynamicBytesEncodePacked() { + DynamicBytes dynamicBytes = new DynamicBytes(new byte[] {0, 1, 2, 3, 4, 5}); + assertEquals("000102030405", TypeEncoder.encodePacked(dynamicBytes)); + + DynamicBytes zero = new DynamicBytes(new byte[] {0}); + assertEquals("00", TypeEncoder.encodePacked(zero)); + + DynamicBytes empty = new DynamicBytes(new byte[] {}); + assertEquals("", TypeEncoder.encodePacked(empty)); + + DynamicBytes dave = new DynamicBytes("dave".getBytes()); + assertEquals("64617665", TypeEncoder.encodePacked(dave)); + + DynamicBytes loremIpsum = + new DynamicBytes( + ("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod " + + "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim " + + "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex " + + "ea commodo consequat. Duis aute irure dolor in reprehenderit in " + + "voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur " + + "sint occaecat cupidatat non proident, sunt in culpa qui officia " + + "deserunt mollit anim id est laborum.") + .getBytes()); + assertEquals( + ("4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73" + + "656374657475722061646970697363696e6720656c69742c2073656420646f20" + + "656975736d6f642074656d706f7220696e6369646964756e74207574206c6162" + + "6f726520657420646f6c6f7265206d61676e6120616c697175612e2055742065" + + "6e696d206164206d696e696d2076656e69616d2c2071756973206e6f73747275" + + "6420657865726369746174696f6e20756c6c616d636f206c61626f726973206e" + + "69736920757420616c697175697020657820656120636f6d6d6f646f20636f6e" + + "7365717561742e2044756973206175746520697275726520646f6c6f7220696e" + + "20726570726568656e646572697420696e20766f6c7570746174652076656c69" + + "7420657373652063696c6c756d20646f6c6f726520657520667567696174206e" + + "756c6c612070617269617475722e204578636570746575722073696e74206f63" + + "63616563617420637570696461746174206e6f6e2070726f6964656e742c2073" + + "756e7420696e2063756c706120717569206f666669636961206465736572756e" + + "74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e"), + TypeEncoder.encodePacked(loremIpsum)); + } + + @Test + public void testDynamicArrayEncodePacked() { + DynamicArray empty = new DynamicArray<>(Uint.class); + + assertEquals("", TypeEncoder.encodePacked(empty)); + + DynamicArray array = + new DynamicArray<>( + Uint.class, + new Uint(BigInteger.ONE), + new Uint(BigInteger.valueOf(2)), + new Uint(BigInteger.valueOf(3))); + + assertEquals( + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000003", + TypeEncoder.encodePacked(array)); + + DynamicArray uints = + new DynamicArray<>( + Uint32.class, + new Uint32(BigInteger.ONE), + new Uint32(BigInteger.valueOf(2))); + + assertEquals( + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000002", + TypeEncoder.encodePacked(uints)); + + DynamicArray strings = + new DynamicArray<>( + Utf8String.class, + new Utf8String("one"), + new Utf8String("two"), + new Utf8String("three")); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(strings)); + + DynamicArray dynamicStuct = + new DynamicArray<>( + AbiV2TestFixture.Foo.class, + new AbiV2TestFixture.Foo("", ""), + new AbiV2TestFixture.Foo("id", "name"), + new AbiV2TestFixture.Foo("", "")); + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(dynamicStuct)); + + DynamicArray staticStruct = + new DynamicArray<>( + AbiV2TestFixture.Bar.class, + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.ZERO), + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.ZERO), + new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.ZERO)); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(staticStruct)); + + DynamicArray ufixed = + new DynamicArray<>( + Ufixed.class, + new Ufixed(BigInteger.valueOf(0x2), BigInteger.valueOf(0x2)), + new Ufixed(BigInteger.valueOf(0x8), BigInteger.valueOf(0x8))); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(ufixed)); + + DynamicArray fixed = + new DynamicArray<>( + Fixed.class, + new Fixed(BigInteger.valueOf(0x2), BigInteger.valueOf(0x2)), + new Fixed(BigInteger.valueOf(0x8), BigInteger.valueOf(0x8))); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> TypeEncoder.encodePacked(fixed)); + + DynamicArray arrayOfEmptyBytes = + new DynamicArray<>( + DynamicBytes.class, + new DynamicBytes(new byte[0]), + new DynamicBytes(new byte[0])); + + Assertions.assertThrows( + UnsupportedOperationException.class, + () -> TypeEncoder.encodePacked(arrayOfEmptyBytes)); + } + + @Test + public void testPrimitiveByteEncodePacked() { + assertEquals("00", TypeEncoder.encodePacked(new Byte((byte) 0))); + assertEquals("7f", TypeEncoder.encodePacked(new Byte((byte) 127))); + } + + @Test + public void testPrimitiveCharEncodePacked() { + assertEquals("61", TypeEncoder.encodePacked(new Char('a'))); + assertEquals("20", TypeEncoder.encodePacked(new Char(' '))); + } + + @Test + public void testPrimitiveIntEncodePacked() { + assertEquals( + "00000000", TypeEncoder.encodePacked(new org.tron.trident.abi.datatypes.primitive.Int(0))); + + assertEquals( + "80000000", + TypeEncoder.encodePacked( + new org.tron.trident.abi.datatypes.primitive.Int(Integer.MIN_VALUE))); + + assertEquals( + "7fffffff", + TypeEncoder.encodePacked( + new org.tron.trident.abi.datatypes.primitive.Int(Integer.MAX_VALUE))); + } + + @Test + public void testPrimitiveShortEncodePacked() { + assertEquals("0000", TypeEncoder.encodePacked(new Short((short) 0))); + + assertEquals("8000", TypeEncoder.encodePacked(new Short(java.lang.Short.MIN_VALUE))); + + assertEquals("7fff", TypeEncoder.encodePacked(new Short(java.lang.Short.MAX_VALUE))); + } + + @Test + public void testPrimitiveLongEncodePacked() { + assertEquals("0000000000000000", TypeEncoder.encodePacked(new Long(0))); + + assertEquals( + "8000000000000000", TypeEncoder.encodePacked(new Long(java.lang.Long.MIN_VALUE))); + + assertEquals( + "7fffffffffffffff", TypeEncoder.encodePacked(new Long(java.lang.Long.MAX_VALUE))); + } + + @Test + public void testPrimitiveFloatEncodePacked() { + assertThrows( + UnsupportedOperationException.class, + () -> TypeEncoder.encodePacked(new org.tron.trident.abi.datatypes.primitive.Float(0))); + } + + @Test + public void testPrimitiveDouble() { + assertThrows( + UnsupportedOperationException.class, + () -> TypeEncoder.encodePacked(new org.tron.trident.abi.datatypes.primitive.Double(0))); + } +} diff --git a/abi/src/test/java/org/tron/trident/abi/TypeEncoderTest.java b/abi/src/test/java/org/tron/trident/abi/TypeEncoderTest.java index 49f43461..a661ba00 100644 --- a/abi/src/test/java/org/tron/trident/abi/TypeEncoderTest.java +++ b/abi/src/test/java/org/tron/trident/abi/TypeEncoderTest.java @@ -15,10 +15,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.tron.trident.abi.TypeEncoder.encode; import java.math.BigInteger; +import java.util.Arrays; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.tron.trident.abi.AbiV2TestFixture.Bar; +import org.tron.trident.abi.AbiV2TestFixture.Foo; import org.tron.trident.abi.datatypes.Address; import org.tron.trident.abi.datatypes.Bool; import org.tron.trident.abi.datatypes.Bytes; @@ -63,6 +68,7 @@ import org.tron.trident.abi.datatypes.generated.Int88; import org.tron.trident.abi.datatypes.generated.Int96; import org.tron.trident.abi.datatypes.generated.StaticArray2; +import org.tron.trident.abi.datatypes.generated.StaticArray3; import org.tron.trident.abi.datatypes.generated.Uint104; import org.tron.trident.abi.datatypes.generated.Uint112; import org.tron.trident.abi.datatypes.generated.Uint120; @@ -84,6 +90,7 @@ import org.tron.trident.abi.datatypes.generated.Uint24; import org.tron.trident.abi.datatypes.generated.Uint240; import org.tron.trident.abi.datatypes.generated.Uint248; +import org.tron.trident.abi.datatypes.generated.Uint256; import org.tron.trident.abi.datatypes.generated.Uint32; import org.tron.trident.abi.datatypes.generated.Uint40; import org.tron.trident.abi.datatypes.generated.Uint48; @@ -1215,6 +1222,137 @@ public void testDynamicArray() { + "0000000000000000000000000000000000000000000000000000000000000003")); } + @Test + public void testDynamicStringsArray() { + DynamicArray array = + new DynamicArray<>( + Utf8String.class, + new Utf8String("web3j"), + new Utf8String("arrays"), + new Utf8String("encoding")); + + assertEquals( + ("0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "00000000000000000000000000000000000000000000000000000000000000a0" + + "00000000000000000000000000000000000000000000000000000000000000e0" + + "0000000000000000000000000000000000000000000000000000000000000005" + + "776562336a000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000006" + + "6172726179730000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000008" + + "656e636f64696e67000000000000000000000000000000000000000000000000"), + TypeEncoder.encodeDynamicArray(array)); + } + + @Test + public void testDynamicArrayOfDynamicArraysOfStaticStructs() { + DynamicArray> 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 array = + new DynamicArray<>( + Foo.class, + new Foo("id", "name"), + new Foo("id", "name"), + new Foo("id", "name")); + + assertEquals( + TypeEncoder.encodeDynamicArray(array), + ("0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000120" + + "00000000000000000000000000000000000000000000000000000000000001e0" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000")); + } + + @Test + public void testDynamicStructStaticArray() { + StaticArray3 array = + new StaticArray3<>( + Foo.class, new Foo("", ""), new Foo("id", "name"), new Foo("", "")); + + assertEquals( + ("0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "6964000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "6e616d6500000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000"), + TypeEncoder.encodeArrayValues(array)); + } + + @Test + public void testStaticStructStaticArray() { + StaticArray3 array = + new StaticArray3<>( + Bar.class, + new Bar(BigInteger.ONE, BigInteger.ZERO), + new Bar(BigInteger.ONE, BigInteger.ZERO), + new Bar(BigInteger.ONE, BigInteger.ZERO)); + + assertEquals( + TypeEncoder.encodeArrayValues(array), + ("0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000000")); + } + + @SuppressWarnings("unchecked") @Test public void testEmptyArray() { @@ -1354,21 +1492,21 @@ public void testArrayOfStrings() { @Test public void testPrimitiveByte() { Assertions.assertEquals( - TypeEncoder.encode(new Byte((byte) 0)), + encode(new Byte((byte) 0)), ("0000000000000000000000000000000000000000000000000000000000000000")); Assertions.assertEquals( - TypeEncoder.encode(new Byte((byte) 127)), + encode(new Byte((byte) 127)), ("7f00000000000000000000000000000000000000000000000000000000000000")); } @Test public void testPrimitiveChar() { Assertions.assertEquals( - TypeEncoder.encode(new Char('a')), + encode(new Char('a')), ("0000000000000000000000000000000000000000000000000000000000000001" + "6100000000000000000000000000000000000000000000000000000000000000")); Assertions.assertEquals( - TypeEncoder.encode(new Char(' ')), + encode(new Char(' ')), ("0000000000000000000000000000000000000000000000000000000000000001" + "2000000000000000000000000000000000000000000000000000000000000000")); } @@ -1376,45 +1514,45 @@ public void testPrimitiveChar() { @Test public void testPrimitiveInt() { Assertions.assertEquals( - TypeEncoder.encode(new Int(0)), + encode(new Int(0)), ("0000000000000000000000000000000000000000000000000000000000000000")); Assertions.assertEquals( - TypeEncoder.encode(new Int(Integer.MIN_VALUE)), + encode(new Int(Integer.MIN_VALUE)), ("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000")); Assertions.assertEquals( - TypeEncoder.encode(new Int(Integer.MAX_VALUE)), + encode(new Int(Integer.MAX_VALUE)), ("000000000000000000000000000000000000000000000000000000007fffffff")); } @Test public void testPrimitiveShort() { Assertions.assertEquals( - TypeEncoder.encode(new Short((short) 0)), + encode(new Short((short) 0)), ("0000000000000000000000000000000000000000000000000000000000000000")); Assertions.assertEquals( - TypeEncoder.encode(new Short(java.lang.Short.MIN_VALUE)), + encode(new Short(java.lang.Short.MIN_VALUE)), ("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000")); Assertions.assertEquals( - TypeEncoder.encode(new Short(java.lang.Short.MAX_VALUE)), + encode(new Short(java.lang.Short.MAX_VALUE)), ("0000000000000000000000000000000000000000000000000000000000007fff")); } @Test public void testPrimitiveLong() { Assertions.assertEquals( - TypeEncoder.encode(new Long(0)), + encode(new Long(0)), ("0000000000000000000000000000000000000000000000000000000000000000")); Assertions.assertEquals( - TypeEncoder.encode(new Long(java.lang.Long.MIN_VALUE)), + encode(new Long(java.lang.Long.MIN_VALUE)), ("ffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000")); Assertions.assertEquals( - TypeEncoder.encode(new Long(java.lang.Long.MAX_VALUE)), + encode(new Long(java.lang.Long.MAX_VALUE)), ("0000000000000000000000000000000000000000000000007fffffffffffffff")); } @@ -1422,13 +1560,44 @@ public void testPrimitiveLong() { public void testPrimitiveFloat() { assertThrows( UnsupportedOperationException.class, - () -> TypeEncoder.encode(new org.tron.trident.abi.datatypes.primitive.Float(0))); + () -> encode(new org.tron.trident.abi.datatypes.primitive.Float(0))); } @Test public void testPrimitiveDouble() { assertThrows( UnsupportedOperationException.class, - () -> TypeEncoder.encode(new Double(0))); + () -> encode(new Double(0))); + } + + @Test + public void testStructContainingDynamicBytes() { + String expectedEncoding = + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000a0" + + "0000000000000000000000000000000000000000000000000000000000000007" + + "64796e616d696300000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000005" + + "4279746573000000000000000000000000000000000000000000000000000000"; + assertEquals( + expectedEncoding, + encode(AbiV2TestFixture.addDynamicBytesArrayFunction.getInputParameters().get(0))); + } + + @Test + public void testStaticArrayOfDynamicElements() { + StaticArray2 string2 = new StaticArray2<>( + Utf8String.class, + new Utf8String("hello"), + new Utf8String("world")); + + String encoded = TypeEncoder.encode(string2); + + // In ABI, string[2] is dynamic. Its head should contain offsets. + // Head size = 2 * 32 = 64 bytes (0x40 in hex) + // The first word should be the offset to the first string: 0x00...0040 + assertTrue(encoded.startsWith("0000000000000000000000000000000000000000000000000000000000000040"), + "StaticArray of Utf8String should be encoded with offsets. Found: " + encoded); } } diff --git a/abi/src/test/java/org/tron/trident/abi/UtilsTest.java b/abi/src/test/java/org/tron/trident/abi/UtilsTest.java index 28d1df66..f672de34 100644 --- a/abi/src/test/java/org/tron/trident/abi/UtilsTest.java +++ b/abi/src/test/java/org/tron/trident/abi/UtilsTest.java @@ -21,19 +21,24 @@ import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.tron.trident.abi.datatypes.Address; import org.tron.trident.abi.datatypes.Bool; import org.tron.trident.abi.datatypes.DynamicArray; import org.tron.trident.abi.datatypes.DynamicBytes; import org.tron.trident.abi.datatypes.Fixed; import org.tron.trident.abi.datatypes.Int; import org.tron.trident.abi.datatypes.StaticArray; +import org.tron.trident.abi.datatypes.StaticStruct; +import org.tron.trident.abi.datatypes.Type; import org.tron.trident.abi.datatypes.Ufixed; import org.tron.trident.abi.datatypes.Uint; import org.tron.trident.abi.datatypes.Utf8String; import org.tron.trident.abi.datatypes.generated.Int64; import org.tron.trident.abi.datatypes.generated.StaticArray2; +import org.tron.trident.abi.datatypes.generated.StaticArray3; import org.tron.trident.abi.datatypes.generated.Uint256; import org.tron.trident.abi.datatypes.generated.Uint64; +import org.tron.trident.abi.datatypes.reflection.Parameterized; public class UtilsTest { @@ -68,6 +73,21 @@ public void testGetTypeName() { }), ("uint256[]")); } + @Test + public void testGetTypeNameNestedArrayUsesCanonicalAbiName() throws Exception { + assertEquals( + "uint256[2][]", + Utils.getTypeName(new TypeReference>>() { + })); + assertEquals( + "uint256[2][]", + Utils.getTypeName(TypeReference.makeTypeReference("uint256[2][]"))); + assertEquals( + "uint256[2][2]", + Utils.getTypeName(new TypeReference>>() { + })); + } + @Test public void testTypeMap() { final List input = @@ -106,4 +126,100 @@ public void testTypeMapEmpty() { Assertions.assertEquals(Utils.typeMap(new ArrayList<>(), Uint256.class), (new ArrayList())); } + + @Test + public void testValidateTypeReferenceDepthAcceptsNull() { + // Null root is a no-op (defensive); should not throw. + Assertions.assertDoesNotThrow(() -> Utils.validateTypeReferenceDepth(null)); + } + + @Test + public void testValidateTypeReferenceDepthAcceptsRealisticNesting() { + // DynamicArray> — depth 3, well below the limit. + TypeReference ref = new TypeReference>>() {}; + Assertions.assertDoesNotThrow(() -> Utils.validateTypeReferenceDepth(ref)); + } + + @Test + public void testValidateTypeReferenceDepthRejectsExcessiveNesting() { + // Build a chain of innerTypes 15 levels deep — beyond MAX_TYPEREF_DEPTH (10). + TypeReference chain = TypeReference.create(Uint256.class); + for (int i = 0; i < 15; i++) { + final TypeReference child = chain; + chain = new TypeReference(false, Arrays.asList(child)) { }; + } + final TypeReference tooDeep = chain; + UnsupportedOperationException ex = Assertions.assertThrows( + UnsupportedOperationException.class, + () -> Utils.validateTypeReferenceDepth(tooDeep)); + Assertions.assertTrue(ex.getMessage().contains("depth"), + "expected depth-related message, got: " + ex.getMessage()); + } + + @Test + public void testValidateTypeReferenceDepthAcceptsSharedNodes() { + TypeReference shared = TypeReference.create(Uint256.class); + TypeReference left = new TypeReference(false, Arrays.asList(shared)) { }; + TypeReference right = new TypeReference(false, Arrays.asList(shared)) { }; + TypeReference root = new TypeReference(false, Arrays.asList(left, right)) { }; + + Assertions.assertDoesNotThrow(() -> Utils.validateTypeReferenceDepth(root)); + } + + @Test + public void testValidateTypeReferenceDepthRejectsCycle() { + List> rootChildren = new ArrayList<>(); + TypeReference root = new TypeReference(false, rootChildren) { }; + TypeReference child = new TypeReference(false, Arrays.asList(root)) { }; + rootChildren.add(child); + + UnsupportedOperationException ex = Assertions.assertThrows( + UnsupportedOperationException.class, + () -> Utils.validateTypeReferenceDepth(root)); + Assertions.assertTrue(ex.getMessage().contains("Cycle"), + "expected cycle-related message, got: " + ex.getMessage()); + } + + @Test + public void testGetStructTypeWithParameterizedStaticArray() { + // @Parameterized on a generated StaticArrayN field should emit the static + // array suffix (e.g. "uint256[3]"), not collapse to "uint256[]". + String result = Utils.getStructType(TestStructWithParameterizedStaticArray.class); + assertEquals("(address,uint256[3])", result); + } + + @Test + public void testGetStructTypeWithParameterizedDynamicArrayUnchanged() { + // Regression guard: @Parameterized on a DynamicArray field must still + // produce the dynamic-array suffix "[]" — the StaticArray fix must not + // accidentally rewrite this case. + String result = Utils.getStructType(TestStructWithParameterizedDynamicArray.class); + assertEquals("(address,uint256[])", result); + } + + @Test + public void testExtractStaticArraySizeRejectsBareStaticArrayClass() { + // The bare StaticArray base class has no size suffix; using it as a + // field type for @Parameterized must fail loudly rather than silently + // producing a nonsense type string. + IllegalArgumentException ex = Assertions.assertThrows( + IllegalArgumentException.class, + () -> Utils.getTypeReferenceForParameterizedField(StaticArray.class, Uint256.class)); + Assertions.assertTrue(ex.getMessage().contains("StaticArrayN"), + "expected guidance toward StaticArrayN, got: " + ex.getMessage()); + } +} + +class TestStructWithParameterizedStaticArray extends StaticStruct { + public TestStructWithParameterizedStaticArray( + Address address, @Parameterized(type = Uint256.class) StaticArray3 uint256Array) { + super(address, uint256Array); + } +} + +class TestStructWithParameterizedDynamicArray extends StaticStruct { + public TestStructWithParameterizedDynamicArray( + Address address, @Parameterized(type = Uint256.class) DynamicArray uint256Array) { + super(address, uint256Array); + } } diff --git a/abi/src/test/java/org/tron/trident/abi/datatypes/AbiTypesTest.java b/abi/src/test/java/org/tron/trident/abi/datatypes/AbiTypesTest.java new file mode 100644 index 00000000..09332dbf --- /dev/null +++ b/abi/src/test/java/org/tron/trident/abi/datatypes/AbiTypesTest.java @@ -0,0 +1,57 @@ +package org.tron.trident.abi.datatypes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.tron.trident.abi.AbiV2TestFixture; + +public class AbiTypesTest { + + @Test + public void testGetType_rejectsNonTypeClass() { + UnsupportedOperationException ex = + assertThrows( + UnsupportedOperationException.class, + () -> AbiTypes.getType("java.lang.System")); + // sanity: error message names the offending input + assert ex.getMessage().contains("java.lang.System"); + } + + @Test + public void testGetType_rejectsRuntimeClass() { + assertThrows( + UnsupportedOperationException.class, + () -> AbiTypes.getType("java.lang.Runtime")); + } + + @Test + public void testGetType_rejectsNonexistentClass() { + assertThrows( + UnsupportedOperationException.class, + () -> AbiTypes.getType("does.not.exist.Foo")); + } + + @Test + public void testGetType_rejectsEmptyString() { + assertThrows( + UnsupportedOperationException.class, + () -> AbiTypes.getType("")); + } + + @Test + public void testGetType_acceptsBuiltinPrimitive() { + // primitive type names still hit the switch, never the reflective fallback + assertEquals(Address.class, AbiTypes.getType("address")); + assertEquals(Bool.class, AbiTypes.getType("bool")); + assertEquals(Utf8String.class, AbiTypes.getType("string")); + } + + @Test + public void testGetType_acceptsStructSubclassFqcn() { + // AbiV2TestFixture.Foo extends DynamicStruct extends ... extends Type, + // so the reflective fallback must accept it. + Class resolved = AbiTypes.getType(AbiV2TestFixture.Foo.class.getName()); + assertEquals(AbiV2TestFixture.Foo.class, resolved); + } +} diff --git a/abi/src/test/java/org/tron/trident/abi/datatypes/CustomErrorTest.java b/abi/src/test/java/org/tron/trident/abi/datatypes/CustomErrorTest.java new file mode 100644 index 00000000..9362f1ab --- /dev/null +++ b/abi/src/test/java/org/tron/trident/abi/datatypes/CustomErrorTest.java @@ -0,0 +1,30 @@ +package org.tron.trident.abi.datatypes; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.tron.trident.abi.TypeReference; +import org.tron.trident.abi.datatypes.generated.Uint256; + +public class CustomErrorTest { + @Test + public void testCreation() { + + List> parameters = + Arrays.>asList( + new TypeReference

() {}, new TypeReference() {}); + CustomError event = new CustomError("MyError", parameters); + + assertEquals(event.getName(), "MyError"); + + List> actualParameters = event.getParameters(); + assertEquals(parameters.size(), actualParameters.size(), + "parameter count mismatch"); + for (int i = 0; i < parameters.size(); i++) { + assertEquals(parameters.get(i), actualParameters.get(i)); + } + } + +} diff --git a/abi/src/test/java/org/tron/trident/abi/datatypes/DynamicArrayTest.java b/abi/src/test/java/org/tron/trident/abi/datatypes/DynamicArrayTest.java new file mode 100644 index 00000000..39846111 --- /dev/null +++ b/abi/src/test/java/org/tron/trident/abi/datatypes/DynamicArrayTest.java @@ -0,0 +1,95 @@ +package org.tron.trident.abi.datatypes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; +import org.tron.trident.abi.AbiV2TestFixture; +import org.tron.trident.abi.datatypes.generated.Uint256; +import org.tron.trident.abi.datatypes.generated.Uint8; + +public class DynamicArrayTest { + + @Test + public void testEmptyDynamicArray() { + final DynamicArray
array = + new DynamicArray<>(Address.class, Collections.emptyList()); + + assertEquals(Address.TYPE_NAME + "[]", array.getTypeAsString()); + } + + @Test + public void testDynamicArrayWithDynamicStruct() { + final List list = Collections.singletonList(new DynamicStruct()); + final DynamicArray array = new DynamicArray<>(DynamicStruct.class, list); + + assertEquals("()[]", array.getTypeAsString()); + } + + @Test + public void testEmptyDynamicArrayOfConcreteStruct() { + final DynamicArray array = + new DynamicArray<>(AbiV2TestFixture.Nazzy.class, Collections.emptyList()); + + assertEquals("((string,string)[])[]", array.getTypeAsString()); + } + + @Test + public void testDynamicArrayWithAbiType() { + final DynamicArray array = new DynamicArray<>(Uint.class, arrayOfUints(1)); + + assertEquals(Uint.TYPE_NAME + "[]", array.getTypeAsString()); + } + + @Test + public void testMultidimensionalDynamicArray() { + DynamicArray array = + new DynamicArray<>( + DynamicArray.class, + Collections.singletonList( + new DynamicArray<>( + DynamicArray.class, + Collections.singletonList( + new DynamicArray<>( + Uint256.class, new ArrayList<>()))))); + assertEquals("uint256[][][]", array.getTypeAsString()); + } + + @Test + public void testEmptyDynamicArrayOfRawArrayTypeThrows() { + // Nested empty arrays cannot recover the inner element type via reflection + // (Java type erasure); componentType is the raw DynamicArray.class, so + // emitting an ABI type string like "dynamicarray[]" would be malformed. + // Must fail loudly, same fail-fast policy as the empty generic struct case. + final DynamicArray empty = + new DynamicArray<>(DynamicArray.class, Collections.emptyList()); + + UnsupportedOperationException ex = assertThrows( + UnsupportedOperationException.class, + empty::getTypeAsString); + assertTrue(ex.getMessage().contains("nested array"), + "expected nested-array guidance, got: " + ex.getMessage()); + } + + @Test + public void testEmptyDynamicArrayOfGenericStructTypeThrows() { + final DynamicArray empty = + new DynamicArray<>(DynamicStruct.class, Collections.emptyList()); + + UnsupportedOperationException ex = assertThrows( + UnsupportedOperationException.class, + empty::getTypeAsString); + assertTrue(ex.getMessage().contains("generic struct"), + "expected generic-struct guidance, got: " + ex.getMessage()); + } + + private Uint[] arrayOfUints(int length) { + return IntStream.rangeClosed(1, length).mapToObj(Uint8::new).toArray(Uint[]::new); + } + +} diff --git a/abi/src/test/java/org/tron/trident/abi/datatypes/StaticArrayTest.java b/abi/src/test/java/org/tron/trident/abi/datatypes/StaticArrayTest.java index 1c2b7e7b..6807b1d9 100644 --- a/abi/src/test/java/org/tron/trident/abi/datatypes/StaticArrayTest.java +++ b/abi/src/test/java/org/tron/trident/abi/datatypes/StaticArrayTest.java @@ -16,8 +16,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; +import java.util.Collections; import java.util.stream.IntStream; import org.junit.jupiter.api.Test; +import org.tron.trident.abi.datatypes.generated.StaticArray0; import org.tron.trident.abi.datatypes.generated.StaticArray3; import org.tron.trident.abi.datatypes.generated.StaticArray32; import org.tron.trident.abi.datatypes.generated.Uint8; @@ -62,6 +64,14 @@ public void throwsIfSizeIsAboveMaxOf32() { } } + @Test + public void testEmptyStaticArray() { + final StaticArray
array = + new StaticArray0<>(Address.class, Collections.emptyList()); + + assertEquals(Address.TYPE_NAME + "[0]", array.getTypeAsString()); + } + private Uint[] arrayOfUints(int length) { return IntStream.rangeClosed(1, length).mapToObj(Uint8::new).toArray(Uint[]::new); } diff --git a/abi/src/test/java/org/tron/trident/abi/datatypes/StaticStructTest.java b/abi/src/test/java/org/tron/trident/abi/datatypes/StaticStructTest.java new file mode 100644 index 00000000..11110651 --- /dev/null +++ b/abi/src/test/java/org/tron/trident/abi/datatypes/StaticStructTest.java @@ -0,0 +1,35 @@ +package org.tron.trident.abi.datatypes; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.tron.trident.abi.datatypes.generated.StaticArray4; +import org.tron.trident.abi.datatypes.generated.Uint256; + +public class StaticStructTest { + @Test + public void testStaticStruct() { + Address address1 = Address.DEFAULT; + Address address2 = Address.DEFAULT; + StaticArray4 array4 = + new StaticArray4<>( + Uint256.class, + Uint256.DEFAULT, + Uint256.DEFAULT, + Uint256.DEFAULT, + Uint256.DEFAULT); + StaticStruct struct = new StaticStruct(address1, address2, array4); + + // (address,address,uint256[4]) + String expected = + "(" + + address1.getTypeAsString() + + "," + + address2.getTypeAsString() + + "," + + array4.getTypeAsString() + + ")"; + assertEquals(expected, struct.getTypeAsString()); + } + +} diff --git a/abi/src/test/java/org/tron/trident/abi/datatypes/Utf8StringTest.java b/abi/src/test/java/org/tron/trident/abi/datatypes/Utf8StringTest.java index 1dec0910..c9d02fca 100644 --- a/abi/src/test/java/org/tron/trident/abi/datatypes/Utf8StringTest.java +++ b/abi/src/test/java/org/tron/trident/abi/datatypes/Utf8StringTest.java @@ -24,4 +24,21 @@ public void testToString() { assertEquals(new Utf8String("").toString(), ("")); assertEquals(new Utf8String("string").toString(), ("string")); } + @Test + public void testBytes32PaddedLength() { + String string32Bytes = "12345678901234567890123456789012"; + String string33Bytes = "123456789012345678901234567890123"; + assertEquals(32, new Utf8String("").bytes32PaddedLength()); + assertEquals(32, new Utf8String("string").bytes32PaddedLength()); + assertEquals(32, new Utf8String(string32Bytes).bytes32PaddedLength()); + assertEquals(64, new Utf8String(string33Bytes).bytes32PaddedLength()); + } + + @Test + public void testBytes32PaddedLengthWithNullValue() { + // equals()/hashCode() already tolerate null value; bytes32PaddedLength + // must too, otherwise reflective construction paths can NPE before + // the value ever gets validated. + assertEquals(32, new Utf8String(null).bytes32PaddedLength()); + } } diff --git a/abi/src/test/resources/contract-interface-abi2.json.gz b/abi/src/test/resources/contract-interface-abi2.json.gz new file mode 100644 index 00000000..047719ff Binary files /dev/null and b/abi/src/test/resources/contract-interface-abi2.json.gz differ diff --git a/abi/src/test/resources/contract-interface.json.gz b/abi/src/test/resources/contract-interface.json.gz new file mode 100644 index 00000000..e0a8216d Binary files /dev/null and b/abi/src/test/resources/contract-interface.json.gz differ diff --git a/core/src/main/java/org/tron/trident/core/ApiWrapperBuilder.java b/core/src/main/java/org/tron/trident/core/ApiWrapperBuilder.java index f09bb239..ba59f33e 100644 --- a/core/src/main/java/org/tron/trident/core/ApiWrapperBuilder.java +++ b/core/src/main/java/org/tron/trident/core/ApiWrapperBuilder.java @@ -55,7 +55,8 @@ public ApiWrapperBuilder(String grpcEndpoint) { */ public ApiWrapperBuilder withTLS(File certFile) { Preconditions.checkNotNull(certFile, "certFile is null"); - Preconditions.checkArgument(certFile.exists(), "cert file does not exist: " + certFile.getAbsolutePath()); + Preconditions.checkArgument(certFile.exists(), + "cert file does not exist: " + certFile.getAbsolutePath()); this.useTLS = true; this.trustCert = certFile; return this; diff --git a/core/src/test/java/org/tron/trident/core/AbiV2ContractTest.java b/core/src/test/java/org/tron/trident/core/AbiV2ContractTest.java new file mode 100644 index 00000000..53559591 --- /dev/null +++ b/core/src/test/java/org/tron/trident/core/AbiV2ContractTest.java @@ -0,0 +1,798 @@ +package org.tron.trident.core; + +import org.junit.jupiter.api.*; +import org.tron.trident.abi.FunctionEncoder; +import org.tron.trident.abi.FunctionReturnDecoder; +import org.tron.trident.abi.TypeEncoder; +import org.tron.trident.abi.TypeReference; +import org.tron.trident.abi.Utils; +import org.tron.trident.abi.datatypes.*; +import org.tron.trident.abi.datatypes.generated.StaticArray2; +import org.tron.trident.abi.datatypes.generated.StaticArray3; +import org.tron.trident.abi.datatypes.generated.Uint256; +import org.tron.trident.abi.datatypes.reflection.Parameterized; +import org.tron.trident.core.utils.ByteArray; +import org.tron.trident.proto.Chain.Transaction; +import org.tron.trident.proto.Response; +import org.tron.trident.proto.Response.TransactionExtention; +import org.tron.trident.utils.Base58Check; +import org.tron.trident.utils.Numeric; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + + +/** + * End-to-end test for ABIv2 and encodePacked features. + * This test deploys the AbiV2TestContract and interacts with it to verify + * complex type encoding/decoding and packed encoding. + */ +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class AbiV2ContractTest { + // 1. Compile AbiV2TestContract.sol to get its ABI and Bytecode. + // 2. Provide a private key for an account on the Nile testnet with some TRX. + private static final String CONTRACT_ABI = "[\n" + + "\t{\n" + + "\t\t\"inputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\t\t\"name\": \"topLevelId\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"staticId\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t\t\t\t\t},\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"address\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"staticAddress\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"address\"\n" + + "\t\t\t\t\t\t\t}\n" + + "\t\t\t\t\t\t],\n" + + "\t\t\t\t\t\t\"internalType\": \"struct AbiV2TestContract.StaticInfo\",\n" + + "\t\t\t\t\t\t\"name\": \"staticPart\",\n" + + "\t\t\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"string\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"name\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"string\"\n" + + "\t\t\t\t\t\t\t},\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"bytes\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"data\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n" + + "\t\t\t\t\t\t\t},\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"uint256[]\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"scores\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"uint256[]\"\n" + + "\t\t\t\t\t\t\t}\n" + + "\t\t\t\t\t\t],\n" + + "\t\t\t\t\t\t\"internalType\": \"struct AbiV2TestContract.DynamicInfo\",\n" + + "\t\t\t\t\t\t\"name\": \"dynamicPart\",\n" + + "\t\t\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.NestedInfo\",\n" + + "\t\t\t\t\"name\": \"_info\",\n" + + "\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"name\": \"setAndGetNestedStruct\",\n" + + "\t\t\"outputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\t\t\"name\": \"topLevelId\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"staticId\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t\t\t\t\t},\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"address\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"staticAddress\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"address\"\n" + + "\t\t\t\t\t\t\t}\n" + + "\t\t\t\t\t\t],\n" + + "\t\t\t\t\t\t\"internalType\": \"struct AbiV2TestContract.StaticInfo\",\n" + + "\t\t\t\t\t\t\"name\": \"staticPart\",\n" + + "\t\t\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"string\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"name\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"string\"\n" + + "\t\t\t\t\t\t\t},\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"bytes\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"data\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n" + + "\t\t\t\t\t\t\t},\n" + + "\t\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\t\"internalType\": \"uint256[]\",\n" + + "\t\t\t\t\t\t\t\t\"name\": \"scores\",\n" + + "\t\t\t\t\t\t\t\t\"type\": \"uint256[]\"\n" + + "\t\t\t\t\t\t\t}\n" + + "\t\t\t\t\t\t],\n" + + "\t\t\t\t\t\t\"internalType\": \"struct AbiV2TestContract.DynamicInfo\",\n" + + "\t\t\t\t\t\t\"name\": \"dynamicPart\",\n" + + "\t\t\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.NestedInfo\",\n" + + "\t\t\t\t\"name\": \"\",\n" + + "\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"stateMutability\": \"nonpayable\",\n" + + "\t\t\"type\": \"function\"\n" + + "\t},\n" + + "\t{\n" + + "\t\t\"inputs\": [],\n" + + "\t\t\"name\": \"lastNestedInfo\",\n" + + "\t\t\"outputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\"name\": \"topLevelId\",\n" + + "\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t},\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\t\t\"name\": \"staticId\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"address\",\n" + + "\t\t\t\t\t\t\"name\": \"staticAddress\",\n" + + "\t\t\t\t\t\t\"type\": \"address\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.StaticInfo\",\n" + + "\t\t\t\t\"name\": \"staticPart\",\n" + + "\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t},\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"string\",\n" + + "\t\t\t\t\t\t\"name\": \"name\",\n" + + "\t\t\t\t\t\t\"type\": \"string\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"bytes\",\n" + + "\t\t\t\t\t\t\"name\": \"data\",\n" + + "\t\t\t\t\t\t\"type\": \"bytes\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256[]\",\n" + + "\t\t\t\t\t\t\"name\": \"scores\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256[]\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.DynamicInfo\",\n" + + "\t\t\t\t\"name\": \"dynamicPart\",\n" + + "\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"stateMutability\": \"view\",\n" + + "\t\t\"type\": \"function\"\n" + + "\t},\n" + + "\t{\n" + + "\t\t\"inputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"string\",\n" + + "\t\t\t\t\t\t\"name\": \"name\",\n" + + "\t\t\t\t\t\t\"type\": \"string\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"bytes\",\n" + + "\t\t\t\t\t\t\"name\": \"data\",\n" + + "\t\t\t\t\t\t\"type\": \"bytes\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256[]\",\n" + + "\t\t\t\t\t\t\"name\": \"scores\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256[]\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.DynamicInfo[]\",\n" + + "\t\t\t\t\"name\": \"_infos\",\n" + + "\t\t\t\t\"type\": \"tuple[]\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"name\": \"setAndGetDynamicArrayOfStructs\",\n" + + "\t\t\"outputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"string\",\n" + + "\t\t\t\t\t\t\"name\": \"name\",\n" + + "\t\t\t\t\t\t\"type\": \"string\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"bytes\",\n" + + "\t\t\t\t\t\t\"name\": \"data\",\n" + + "\t\t\t\t\t\t\"type\": \"bytes\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256[]\",\n" + + "\t\t\t\t\t\t\"name\": \"scores\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256[]\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.DynamicInfo[]\",\n" + + "\t\t\t\t\"name\": \"\",\n" + + "\t\t\t\t\"type\": \"tuple[]\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"stateMutability\": \"pure\",\n" + + "\t\t\"type\": \"function\"\n" + + "\t},\n" + + "\t{\n" + + "\t\t\"inputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"string\",\n" + + "\t\t\t\t\t\t\"name\": \"name\",\n" + + "\t\t\t\t\t\t\"type\": \"string\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"bytes\",\n" + + "\t\t\t\t\t\t\"name\": \"data\",\n" + + "\t\t\t\t\t\t\"type\": \"bytes\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256[]\",\n" + + "\t\t\t\t\t\t\"name\": \"scores\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256[]\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.DynamicInfo\",\n" + + "\t\t\t\t\"name\": \"_info\",\n" + + "\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"name\": \"setAndGetDynamicStruct\",\n" + + "\t\t\"outputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"string\",\n" + + "\t\t\t\t\t\t\"name\": \"name\",\n" + + "\t\t\t\t\t\t\"type\": \"string\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"bytes\",\n" + + "\t\t\t\t\t\t\"name\": \"data\",\n" + + "\t\t\t\t\t\t\"type\": \"bytes\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256[]\",\n" + + "\t\t\t\t\t\t\"name\": \"scores\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256[]\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.DynamicInfo\",\n" + + "\t\t\t\t\"name\": \"\",\n" + + "\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"stateMutability\": \"pure\",\n" + + "\t\t\"type\": \"function\"\n" + + "\t},\n" + + "\t{\n" + + "\t\t\"inputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"internalType\": \"uint256[2][3]\",\n" + + "\t\t\t\t\"name\": \"_matrix\",\n" + + "\t\t\t\t\"type\": \"uint256[2][3]\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"name\": \"setAndGetMultiDimArray\",\n" + + "\t\t\"outputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"internalType\": \"uint256[2][3]\",\n" + + "\t\t\t\t\"name\": \"\",\n" + + "\t\t\t\t\"type\": \"uint256[2][3]\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"stateMutability\": \"pure\",\n" + + "\t\t\"type\": \"function\"\n" + + "\t},\n" + + "\t{\n" + + "\t\t\"inputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\t\t\"name\": \"staticId\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"address\",\n" + + "\t\t\t\t\t\t\"name\": \"staticAddress\",\n" + + "\t\t\t\t\t\t\"type\": \"address\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.StaticInfo[2]\",\n" + + "\t\t\t\t\"name\": \"_infos\",\n" + + "\t\t\t\t\"type\": \"tuple[2]\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"name\": \"setAndGetStaticArrayOfStructs\",\n" + + "\t\t\"outputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\t\t\"name\": \"staticId\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"address\",\n" + + "\t\t\t\t\t\t\"name\": \"staticAddress\",\n" + + "\t\t\t\t\t\t\"type\": \"address\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.StaticInfo[2]\",\n" + + "\t\t\t\t\"name\": \"\",\n" + + "\t\t\t\t\"type\": \"tuple[2]\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"stateMutability\": \"pure\",\n" + + "\t\t\"type\": \"function\"\n" + + "\t},\n" + + "\t{\n" + + "\t\t\"inputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\t\t\"name\": \"staticId\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"address\",\n" + + "\t\t\t\t\t\t\"name\": \"staticAddress\",\n" + + "\t\t\t\t\t\t\"type\": \"address\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.StaticInfo\",\n" + + "\t\t\t\t\"name\": \"_info\",\n" + + "\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"name\": \"setAndGetStaticStruct\",\n" + + "\t\t\"outputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"components\": [\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\t\t\"name\": \"staticId\",\n" + + "\t\t\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"internalType\": \"address\",\n" + + "\t\t\t\t\t\t\"name\": \"staticAddress\",\n" + + "\t\t\t\t\t\t\"type\": \"address\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t],\n" + + "\t\t\t\t\"internalType\": \"struct AbiV2TestContract.StaticInfo\",\n" + + "\t\t\t\t\"name\": \"\",\n" + + "\t\t\t\t\"type\": \"tuple\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"stateMutability\": \"pure\",\n" + + "\t\t\"type\": \"function\"\n" + + "\t},\n" + + "\t{\n" + + "\t\t\"inputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"internalType\": \"bytes\",\n" + + "\t\t\t\t\"name\": \"javaPacked\",\n" + + "\t\t\t\t\"type\": \"bytes\"\n" + + "\t\t\t},\n" + + "\t\t\t{\n" + + "\t\t\t\t\"internalType\": \"uint256\",\n" + + "\t\t\t\t\"name\": \"id\",\n" + + "\t\t\t\t\"type\": \"uint256\"\n" + + "\t\t\t},\n" + + "\t\t\t{\n" + + "\t\t\t\t\"internalType\": \"address\",\n" + + "\t\t\t\t\"name\": \"addr\",\n" + + "\t\t\t\t\"type\": \"address\"\n" + + "\t\t\t},\n" + + "\t\t\t{\n" + + "\t\t\t\t\"internalType\": \"string\",\n" + + "\t\t\t\t\"name\": \"name\",\n" + + "\t\t\t\t\"type\": \"string\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"name\": \"verifyPackedEncoding\",\n" + + "\t\t\"outputs\": [\n" + + "\t\t\t{\n" + + "\t\t\t\t\"internalType\": \"bool\",\n" + + "\t\t\t\t\"name\": \"\",\n" + + "\t\t\t\t\"type\": \"bool\"\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"stateMutability\": \"pure\",\n" + + "\t\t\"type\": \"function\"\n" + + "\t}\n" + + "]"; + private static final String CONTRACT_BYTECODE = "6080604052348015600e575f5ffd5b50611cbc8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610086575f3560e01c806373b8a93d1161005957806373b8a93d1461013a5780639007edcf1461016a578063b73b16011461019a578063f683b31e146101ca57610086565b806328c60ba01461008a57806330a4842d146100ba5780634fb4bedd146100ea5780635f2f84921461010a575b5f5ffd5b6100a4600480360381019061009f91906109dc565b6101fa565b6040516100b19190610be7565b60405180910390f35b6100d460048036038101906100cf9190610d5c565b61020a565b6040516100e19190610e59565b60405180910390f35b6100f261021a565b60405161010193929190610eae565b60405180910390f35b610124600480360381019061011f9190610eea565b610417565b6040516101319190610f15565b60405180910390f35b610154600480360381019061014f919061108a565b610427565b60405161016191906111ca565b60405180910390f35b610184600480360381019061017f9190611260565b610437565b604051610191919061134f565b60405180910390f35b6101b460048036038101906101af919061144d565b6104f8565b6040516101c1919061154f565b60405180910390f35b6101e460048036038101906101df919061156f565b610502565b6040516101f19190611625565b60405180910390f35b610202610546565b819050919050565b610212610567565b819050919050565b5f805f015490806001016040518060400160405290815f8201548152602001600182015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152505090806003016040518060600160405290815f820180546102ac9061166b565b80601f01602080910402602001604051908101604052809291908181526020018280546102d89061166b565b80156103235780601f106102fa57610100808354040283529160200191610323565b820191905f5260205f20905b81548152906001019060200180831161030657829003601f168201915b5050505050815260200160018201805461033c9061166b565b80601f01602080910402602001604051908101604052809291908181526020018280546103689061166b565b80156103b35780601f1061038a576101008083540402835291602001916103b3565b820191905f5260205f20905b81548152906001019060200180831161039657829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561040957602002820191905f5260205f20905b8154815260200190600101908083116103f5575b505050505081525050905083565b61041f610594565b819050919050565b61042f6105c2565b819050919050565b61043f6105ef565b815f5f820151815f01556020820151816001015f820151815f01556020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050506040820151816003015f820151815f0190816104be919061184c565b5060208201518160010190816104d49190611980565b5060408201518160020190816104ea9190611b2b565b505050905050819050919050565b6060819050919050565b5f5f84848460405160200161051993929190611c4e565b60405160208183030381529060405290508080519060200120868051906020012014915050949350505050565b60405180606001604052806060815260200160608152602001606081525090565b60405180604001604052806002905b61057e610594565b8152602001906001900390816105765790505090565b60405180604001604052805f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681525090565b60405180606001604052806003905b6105d961061b565b8152602001906001900390816105d15790505090565b60405180606001604052805f8152602001610608610594565b8152602001610615610546565b81525090565b6040518060400160405280600290602082028036833780820191505090505090565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61069882610652565b810181811067ffffffffffffffff821117156106b7576106b6610662565b5b80604052505050565b5f6106c961063d565b90506106d5828261068f565b919050565b5f5ffd5b5f5ffd5b5f5ffd5b5f67ffffffffffffffff821115610700576106ff610662565b5b61070982610652565b9050602081019050919050565b828183375f83830152505050565b5f610736610731846106e6565b6106c0565b905082815260208101848484011115610752576107516106e2565b5b61075d848285610716565b509392505050565b5f82601f830112610779576107786106de565b5b8135610789848260208601610724565b91505092915050565b5f67ffffffffffffffff8211156107ac576107ab610662565b5b6107b582610652565b9050602081019050919050565b5f6107d46107cf84610792565b6106c0565b9050828152602081018484840111156107f0576107ef6106e2565b5b6107fb848285610716565b509392505050565b5f82601f830112610817576108166106de565b5b81356108278482602086016107c2565b91505092915050565b5f67ffffffffffffffff82111561084a57610849610662565b5b602082029050602081019050919050565b5f5ffd5b5f819050919050565b6108718161085f565b811461087b575f5ffd5b50565b5f8135905061088c81610868565b92915050565b5f6108a461089f84610830565b6106c0565b905080838252602082019050602084028301858111156108c7576108c661085b565b5b835b818110156108f057806108dc888261087e565b8452602084019350506020810190506108c9565b5050509392505050565b5f82601f83011261090e5761090d6106de565b5b813561091e848260208601610892565b91505092915050565b5f6060828403121561093c5761093b61064e565b5b61094660606106c0565b90505f82013567ffffffffffffffff811115610965576109646106da565b5b61097184828501610765565b5f83015250602082013567ffffffffffffffff811115610994576109936106da565b5b6109a084828501610803565b602083015250604082013567ffffffffffffffff8111156109c4576109c36106da565b5b6109d0848285016108fa565b60408301525092915050565b5f602082840312156109f1576109f0610646565b5b5f82013567ffffffffffffffff811115610a0e57610a0d61064a565b5b610a1a84828501610927565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f610a5582610a23565b610a5f8185610a2d565b9350610a6f818560208601610a3d565b610a7881610652565b840191505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f610aa782610a83565b610ab18185610a8d565b9350610ac1818560208601610a3d565b610aca81610652565b840191505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610b078161085f565b82525050565b5f610b188383610afe565b60208301905092915050565b5f602082019050919050565b5f610b3a82610ad5565b610b448185610adf565b9350610b4f83610aef565b805f5b83811015610b7f578151610b668882610b0d565b9750610b7183610b24565b925050600181019050610b52565b5085935050505092915050565b5f606083015f8301518482035f860152610ba68282610a4b565b91505060208301518482036020860152610bc08282610a9d565b91505060408301518482036040860152610bda8282610b30565b9150508091505092915050565b5f6020820190508181035f830152610bff8184610b8c565b905092915050565b5f67ffffffffffffffff821115610c2157610c20610662565b5b602082029050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610c5582610c2c565b9050919050565b610c6581610c4b565b8114610c6f575f5ffd5b50565b5f81359050610c8081610c5c565b92915050565b5f60408284031215610c9b57610c9a61064e565b5b610ca560406106c0565b90505f610cb48482850161087e565b5f830152506020610cc784828501610c72565b60208301525092915050565b5f610ce5610ce084610c07565b6106c0565b90508060408402830185811115610cff57610cfe61085b565b5b835b81811015610d285780610d148882610c86565b845260208401935050604081019050610d01565b5050509392505050565b5f82601f830112610d4657610d456106de565b5b6002610d53848285610cd3565b91505092915050565b5f60808284031215610d7157610d70610646565b5b5f610d7e84828501610d32565b91505092915050565b5f60029050919050565b5f81905092915050565b5f819050919050565b610dad81610c4b565b82525050565b604082015f820151610dc75f850182610afe565b506020820151610dda6020850182610da4565b50505050565b5f610deb8383610db3565b60408301905092915050565b5f602082019050919050565b610e0c81610d87565b610e168184610d91565b9250610e2182610d9b565b805f5b83811015610e51578151610e388782610de0565b9650610e4383610df7565b925050600181019050610e24565b505050505050565b5f608082019050610e6c5f830184610e03565b92915050565b610e7b8161085f565b82525050565b604082015f820151610e955f850182610afe565b506020820151610ea86020850182610da4565b50505050565b5f608082019050610ec15f830186610e72565b610ece6020830185610e81565b8181036060830152610ee08184610b8c565b9050949350505050565b5f60408284031215610eff57610efe610646565b5b5f610f0c84828501610c86565b91505092915050565b5f604082019050610f285f830184610e81565b92915050565b5f67ffffffffffffffff821115610f4857610f47610662565b5b602082029050919050565b5f67ffffffffffffffff821115610f6d57610f6c610662565b5b602082029050919050565b5f610f8a610f8584610f53565b6106c0565b90508060208402830185811115610fa457610fa361085b565b5b835b81811015610fcd5780610fb9888261087e565b845260208401935050602081019050610fa6565b5050509392505050565b5f82601f830112610feb57610fea6106de565b5b6002610ff8848285610f78565b91505092915050565b5f61101361100e84610f2e565b6106c0565b9050806040840283018581111561102d5761102c61085b565b5b835b8181101561105657806110428882610fd7565b84526020840193505060408101905061102f565b5050509392505050565b5f82601f830112611074576110736106de565b5b6003611081848285611001565b91505092915050565b5f60c0828403121561109f5761109e610646565b5b5f6110ac84828501611060565b91505092915050565b5f60039050919050565b5f81905092915050565b5f819050919050565b5f60029050919050565b5f81905092915050565b5f819050919050565b5f602082019050919050565b611104816110d2565b61110e81846110dc565b9250611119826110e6565b805f5b838110156111495781516111308782610b0d565b965061113b836110ef565b92505060018101905061111c565b505050505050565b5f61115c83836110fb565b60408301905092915050565b5f602082019050919050565b61117d816110b5565b61118781846110bf565b9250611192826110c9565b805f5b838110156111c25781516111a98782611151565b96506111b483611168565b925050600181019050611195565b505050505050565b5f60c0820190506111dd5f830184611174565b92915050565b5f608082840312156111f8576111f761064e565b5b61120260606106c0565b90505f6112118482850161087e565b5f83015250602061122484828501610c86565b602083015250606082013567ffffffffffffffff811115611248576112476106da565b5b61125484828501610927565b60408301525092915050565b5f6020828403121561127557611274610646565b5b5f82013567ffffffffffffffff8111156112925761129161064a565b5b61129e848285016111e3565b91505092915050565b5f606083015f8301518482035f8601526112c18282610a4b565b915050602083015184820360208601526112db8282610a9d565b915050604083015184820360408601526112f58282610b30565b9150508091505092915050565b5f608083015f8301516113175f860182610afe565b50602083015161132a6020860182610db3565b506040830151848203606086015261134282826112a7565b9150508091505092915050565b5f6020820190508181035f8301526113678184611302565b905092915050565b5f67ffffffffffffffff82111561138957611388610662565b5b602082029050602081019050919050565b5f6113ac6113a78461136f565b6106c0565b905080838252602082019050602084028301858111156113cf576113ce61085b565b5b835b8181101561141657803567ffffffffffffffff8111156113f4576113f36106de565b5b8086016114018982610927565b855260208501945050506020810190506113d1565b5050509392505050565b5f82601f830112611434576114336106de565b5b813561144484826020860161139a565b91505092915050565b5f6020828403121561146257611461610646565b5b5f82013567ffffffffffffffff81111561147f5761147e61064a565b5b61148b84828501611420565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f6114c883836112a7565b905092915050565b5f602082019050919050565b5f6114e682611494565b6114f0818561149e565b935083602082028501611502856114ae565b805f5b8581101561153d578484038952815161151e85826114bd565b9450611529836114d0565b925060208a01995050600181019050611505565b50829750879550505050505092915050565b5f6020820190508181035f83015261156781846114dc565b905092915050565b5f5f5f5f6080858703121561158757611586610646565b5b5f85013567ffffffffffffffff8111156115a4576115a361064a565b5b6115b087828801610803565b94505060206115c18782880161087e565b93505060406115d287828801610c72565b925050606085013567ffffffffffffffff8111156115f3576115f261064a565b5b6115ff87828801610765565b91505092959194509250565b5f8115159050919050565b61161f8161160b565b82525050565b5f6020820190506116385f830184611616565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061168257607f821691505b6020821081036116955761169461163e565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026116f77fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826116bc565b61170186836116bc565b95508019841693508086168417925050509392505050565b5f819050919050565b5f61173c6117376117328461085f565b611719565b61085f565b9050919050565b5f819050919050565b61175583611722565b61176961176182611743565b8484546116c8565b825550505050565b5f5f905090565b611780611771565b61178b81848461174c565b505050565b5f5b828110156117b1576117a65f828401611778565b600181019050611792565b505050565b601f8211156118045782821115611803576117d08161169b565b6117d9836116ad565b6117e2856116ad565b60208610156117ef575f90505b8083016117fe82840382611790565b505050505b5b505050565b5f82821c905092915050565b5f6118245f1984600802611809565b1980831691505092915050565b5f61183c8383611815565b9150826002028217905092915050565b61185582610a23565b67ffffffffffffffff81111561186e5761186d610662565b5b611878825461166b565b6118838282856117b6565b5f60209050601f8311600181146118b4575f84156118a2578287015190505b6118ac8582611831565b865550611913565b601f1984166118c28661169b565b5f5b828110156118e9578489015182556001820191506020850194506020810190506118c4565b868310156119065784890151611902601f891682611815565b8355505b6001600288020188555050505b505050505050565b5f819050815f5260205f209050919050565b601f82111561197b578282111561197a576119478161191b565b611950836116ad565b611959856116ad565b6020861015611966575f90505b80830161197582840382611790565b505050505b5b505050565b61198982610a83565b67ffffffffffffffff8111156119a2576119a1610662565b5b6119ac825461166b565b6119b782828561192d565b5f60209050601f8311600181146119e8575f84156119d6578287015190505b6119e08582611831565b865550611a47565b601f1984166119f68661191b565b5f5b82811015611a1d578489015182556001820191506020850194506020810190506119f8565b86831015611a3a5784890151611a36601f891682611815565b8355505b6001600288020188555050505b505050505050565b5f81549050919050565b5f8190506001806001038301049050919050565b5f819050815f5260205f209050919050565b5f5b82811015611aa057611a955f828401611778565b600181019050611a81565b505050565b81831015611adc57611ab682611a59565b611abf84611a59565b611ac883611a6d565b818101611ad783850382611a7f565b505050505b505050565b68010000000000000000821115611afb57611afa610662565b5b611b0481611a4f565b828255611b12838284611aa5565b505050565b5f611b22825161085f565b80915050919050565b611b3482610ad5565b67ffffffffffffffff811115611b4d57611b4c610662565b5b611b578183611ae1565b611b6083610aef565b611b6983611a6d565b600183045f5b81811015611ba6575f611b8185611b17565b611b8a81611743565b8092506020870196505050808285015550600181019050611b6f565b50505050505050565b5f819050919050565b611bc9611bc48261085f565b611baf565b82525050565b5f8160601b9050919050565b5f611be582611bcf565b9050919050565b5f611bf682611bdb565b9050919050565b611c0e611c0982610c4b565b611bec565b82525050565b5f81905092915050565b5f611c2882610a23565b611c328185611c14565b9350611c42818560208601610a3d565b80840191505092915050565b5f611c598286611bb8565b602082019150611c698285611bfd565b601482019150611c798284611c1e565b915081905094935050505056fea2646970667358221220d4662cb1ae03e587216abbcef7d1fd771291a1d23ffdb75a265b7fedc2c78daf64736f6c63430008210033"; + //also deployed https://sepolia.arbiscan.io/address/0x51f2799edab0fb17c51bb1878f67d325220c858b#readContract + private String contractAddress = "TVvQP8CM7dkFtjtiomxRVMyrcr4kzsLcsj"; + + // ================= STRUCT DEFINITIONS (mirroring Solidity) ================= + + public static class StaticInfo extends StaticStruct { + public Uint256 staticId; + public Address staticAddress; + + public StaticInfo(Uint256 staticId, Address staticAddress) { + super(staticId, staticAddress); + this.staticId = staticId; + this.staticAddress = staticAddress; + } + public StaticInfo(List values) { + super(values); + this.staticId = (Uint256) values.get(0); + this.staticAddress = (Address) values.get(1); + } + public StaticInfo(Type staticId, Type staticAddress) { + this( + (Uint256) staticId, + (Address) staticAddress + ); + } + } + + public static class DynamicInfo extends DynamicStruct { + public Utf8String name; + public DynamicBytes data; + public DynamicArray scores; + + public DynamicInfo(Utf8String name, DynamicBytes data, + @Parameterized(type = Uint256.class) DynamicArray scores) { + super(name, data, scores); + this.name = name; + this.data = data; + this.scores = scores; + } + public DynamicInfo(List values) { + super(values); + this.name = (Utf8String) values.get(0); + this.data = (DynamicBytes) values.get(1); + this.scores = (DynamicArray) values.get(2); + } + public DynamicInfo(Type name, Type data, Type scores) { + this( + (Utf8String) name, + (DynamicBytes) data, + (DynamicArray) scores + ); + } + } + + public static class NestedInfo extends DynamicStruct{ + public Uint256 topLevelId; + public StaticInfo staticPart; + public DynamicInfo dynamicPart; + + public NestedInfo(Uint256 topLevelId, StaticInfo staticPart,@Parameterized(type = DynamicInfo.class) DynamicInfo dynamicPart) { + super(topLevelId, staticPart, dynamicPart); + this.topLevelId = topLevelId; + this.staticPart = staticPart; + this.dynamicPart = dynamicPart; + } + public NestedInfo(List values) { + super(values); + this.topLevelId = (Uint256) values.get(0); + this.staticPart = (StaticInfo) values.get(1); + this.dynamicPart = (DynamicInfo) values.get(2); + } + public NestedInfo(Type topLevelId, Type staticPart, Type dynamicPart) { + this( + (Uint256) topLevelId, + (StaticInfo) staticPart, + (DynamicInfo) dynamicPart + ); + } + } + + static ApiWrapper client; + static String testAddress; + + @BeforeAll + static void setUp() { + client = ApiWrapper.ofNile(ApiWrapper.generateAddress().toPrivateKey()); + testAddress = client.keyPair.toBase58CheckAddress(); + } + + @AfterAll + static void tearDown() { + if (client != null) { + client.close(); + } + } + + @Test + @Order(0) + @Disabled("has deployed") + public void deployContract() throws Exception { + + // Deploy the contract + Response.TransactionExtention transactionExtention = + client.deployContract("AbiV2TestContract", CONTRACT_ABI, CONTRACT_BYTECODE, null, 1000_000_000L, 100, 10_000_000L, 0, null, 0); + + Transaction signTransaction = client.signTransaction(transactionExtention.getTransaction()); + String txId = client.broadcastTransaction(signTransaction); +// System.out.println("Deploy transaction ID: " + txId); + + // Wait for deployment to be confirmed (adjust sleep time if needed for the network) + + Thread.sleep(5000); + + Response.TransactionInfo transactionInfo = client.getTransactionInfoById(txId); + String contractAddress = Base58Check.bytesToBase58(transactionInfo.getContractAddress().toByteArray()); + + assertNotNull(contractAddress, "Contract deployment failed or was not confirmed in time."); +// System.out.println("Contract deployed to address: " + contractAddress); + } + + + @Test + @Order(1) + @DisplayName("Test encoding and decoding of a static struct") + void testStaticStruct() { + StaticInfo inputStruct = new StaticInfo(new Uint256(101), new Address(testAddress)); + + Function function = new Function("setAndGetStaticStruct", + Collections.singletonList(inputStruct), + Collections.singletonList(new TypeReference() {}) + ); + + Response.TransactionExtention transactionExtention = client.triggerConstantContract(client.keyPair.toBase58CheckAddress(), contractAddress, function); + + assertNotNull(transactionExtention.getConstantResultList()); + assertFalse(transactionExtention.getConstantResultList().isEmpty()); + List result = FunctionReturnDecoder.decode( + ByteArray.toHexString(transactionExtention.getConstantResult(0).toByteArray()), + function.getOutputParameters()); + + assertNotNull(result); + assertFalse(result.isEmpty()); + assertEquals(1, result.size()); + + StaticInfo outputStruct = (StaticInfo) result.get(0); + assertEquals(inputStruct.staticId.getValue(), outputStruct.staticId.getValue()); + assertEquals(inputStruct.staticAddress.getValue(), outputStruct.staticAddress.getValue()); + assertEquals(new BigInteger("101"), outputStruct.staticId.getValue()); + assertEquals(testAddress, outputStruct.staticAddress.getValue()); + } + + @Test + @Order(2) + @DisplayName("Test encoding and decoding of a dynamic struct") + void testDynamicStruct() { + DynamicInfo inputStruct = new DynamicInfo( + new Utf8String("Trident"), + new DynamicBytes("TestData".getBytes()), + new DynamicArray<>(Uint256.class, new Uint256(98), new Uint256(99), new Uint256(100)) + ); + + Function function = new Function("setAndGetDynamicStruct", + Collections.singletonList(inputStruct), + Collections.singletonList(new TypeReference() {}) + ); + + Response.TransactionExtention result = client.triggerConstantContract(client.keyPair.toBase58CheckAddress(), contractAddress, function); + + assertEquals(1, result.getConstantResultCount()); + System.out.println(FunctionEncoder.encode(function)); + System.out.println(ByteArray.toHexString(result.getConstantResult(0).toByteArray())); + + List outputStructs + = FunctionReturnDecoder.decode(ByteArray.toHexString(result.getConstantResult(0).toByteArray()), function.getOutputParameters()); + + DynamicInfo outputStruct = (DynamicInfo)outputStructs.get(0); + + assertEquals(inputStruct.name.getValue(), outputStruct.name.getValue()); + assertArrayEquals(inputStruct.data.getValue(), outputStruct.data.getValue()); + assertEquals(inputStruct.scores.getValue().size(), outputStruct.scores.getValue().size()); + assertEquals(inputStruct.scores.getValue().get(0).getValue(), outputStruct.scores.getValue().get(0).getValue()); + assertEquals(inputStruct.scores.getValue().get(1).getValue(), outputStruct.scores.getValue().get(1).getValue()); + assertEquals(inputStruct.scores.getValue().get(2).getValue(), outputStruct.scores.getValue().get(2).getValue()); + } + + @Test + @Order(3) + @DisplayName("Test encoding and decoding of a nested struct") + @Disabled("add private key to enable this case") + void testNestedStruct() throws Exception { + StaticInfo staticPart = new StaticInfo(new Uint256(202), new Address(testAddress)); + DynamicInfo dynamicPart = new DynamicInfo( + new Utf8String("Nested"), + new DynamicBytes(new byte[]{0x1, 0x2, 0x3}), + new DynamicArray<>(Uint256.class, new Uint256(1)) + ); + NestedInfo inputStruct = new NestedInfo(new Uint256(303), staticPart, dynamicPart); + + Function function = new Function("setAndGetNestedStruct", + Collections.singletonList(inputStruct), + Collections.singletonList(new TypeReference() {}) + ); + + TransactionExtention transactionExtention + = client.triggerContract(client.keyPair.toBase58CheckAddress(), + contractAddress, + FunctionEncoder.encode(function), + 0, + 0, + null, + 300_000_000L); + + Transaction signTransaction = client.signTransaction(transactionExtention.getTransaction()); + + String txId = client.broadcastTransaction(signTransaction); + System.out.println("setAndGetNestedStruct transaction ID: " + txId); + assertNotNull(txId); + + + Function function2 = new Function("lastNestedInfo", + Collections.emptyList(), + Collections.singletonList(new TypeReference() { + }) + ); + Response.TransactionExtention result + = client.triggerConstantContract(client.keyPair.toBase58CheckAddress(), contractAddress, function2); + assertEquals(1, result.getConstantResultCount()); + + String inputRaw = "0000000000000000000000000000000000000000000000000000000000000020" + + ByteArray.toHexString(result.getConstantResult(0).toByteArray()); + + List outputStructs + = FunctionReturnDecoder.decode(inputRaw, Utils.convert(Collections.singletonList(new TypeReference() {}))); + + NestedInfo outputStruct = (NestedInfo)outputStructs.get(0); + + assertEquals(inputStruct.topLevelId.getValue(), outputStruct.topLevelId.getValue()); + assertEquals(staticPart.staticId.getValue(), outputStruct.staticPart.staticId.getValue()); + assertEquals(staticPart.staticAddress.getValue(), outputStruct.staticPart.staticAddress.getValue()); + assertEquals(dynamicPart.name.getValue(), outputStruct.dynamicPart.name.getValue()); + assertArrayEquals(dynamicPart.data.getValue(), outputStruct.dynamicPart.data.getValue()); + assertEquals(dynamicPart.scores.getValue().size(), outputStruct.dynamicPart.scores.getValue().size()); + assertEquals(dynamicPart.scores.getValue().get(0).getValue(), outputStruct.dynamicPart.scores.getValue().get(0).getValue()); + } + + + @Test + @Order(4) + @DisplayName("Test static array of static structs") + void testStaticArrayOfStructs() { + StaticArray inputArray = new StaticArray2<>( + new StaticInfo(new Uint256(1), new Address(testAddress)), + new StaticInfo(new Uint256(2), new Address(testAddress)) + ); + + Function function = new Function("setAndGetStaticArrayOfStructs", + Collections.singletonList(inputArray), + Collections.singletonList(new TypeReference>() {}) + ); + + Response.TransactionExtention transactionExtention = client.triggerConstantContract(client.keyPair.toBase58CheckAddress(), contractAddress, function); + + List outPutStructs + = FunctionReturnDecoder.decode(ByteArray.toHexString(transactionExtention.getConstantResult(0).toByteArray()), function.getOutputParameters()); + + StaticArray2 outputStaticArrayOfStructs = (StaticArray2)outPutStructs.get(0); + + assertEquals(2, outputStaticArrayOfStructs.getValue().size()); + assertEquals(BigInteger.valueOf(1), ((StaticInfo)outputStaticArrayOfStructs.getValue().get(0)).staticId.getValue()); + assertEquals(testAddress, ((StaticInfo)outputStaticArrayOfStructs.getValue().get(0)).staticAddress.getValue()); + assertEquals(BigInteger.valueOf(2), ((StaticInfo)outputStaticArrayOfStructs.getValue().get(1)).staticId.getValue()); + assertEquals(testAddress, ((StaticInfo)outputStaticArrayOfStructs.getValue().get(1)).staticAddress.getValue()); + } + + @Test + @Order(5) + @DisplayName("Test multi-dimensional static array") + @SuppressWarnings("unchecked") + void testMultiDimArray() { + // Represents uint256[2][3] + StaticArray3> inputArray = new StaticArray3<>( + (Class)StaticArray2.class, + new StaticArray2<>(Uint256.class, new Uint256(1), new Uint256(2)), + new StaticArray2<>(Uint256.class, new Uint256(3), new Uint256(4)), + new StaticArray2<>(Uint256.class, new Uint256(5), new Uint256(6)) + ); + + Function function = new Function("setAndGetMultiDimArray", + Collections.singletonList(inputArray), + Collections.singletonList(new TypeReference>>() {}) + ); + + Response.TransactionExtention transactionExtention = client.triggerConstantContract(testAddress, contractAddress, function); + assertNotNull(transactionExtention.getConstantResultList()); + assertFalse(transactionExtention.getConstantResultList().isEmpty()); + assertFalse(transactionExtention.getConstantResultList().get(0).isEmpty()); + + List outPutStructs + = FunctionReturnDecoder.decode(ByteArray.toHexString(transactionExtention.getConstantResult(0).toByteArray()), function.getOutputParameters()); + + StaticArray3> outputStaticArrayOfStructs = (StaticArray3>)outPutStructs.get(0); + + assertEquals(3, outputStaticArrayOfStructs.getValue().size()); + + StaticArray2 outputArray1 = (StaticArray2) outputStaticArrayOfStructs.getValue().get(0); + StaticArray2 outputArray2 = (StaticArray2) outputStaticArrayOfStructs.getValue().get(1); + StaticArray2 outputArray3 = (StaticArray2) outputStaticArrayOfStructs.getValue().get(2); + + assertEquals(2, outputArray1.getValue().size()); + assertEquals(2, outputArray2.getValue().size()); + assertEquals(2, outputArray3.getValue().size()); + assertEquals(1, outputArray1.getValue().get(0).getValue().intValue()); + assertEquals(2, outputArray1.getValue().get(1).getValue().intValue()); + assertEquals(3, outputArray2.getValue().get(0).getValue().intValue()); + assertEquals(4, outputArray2.getValue().get(1).getValue().intValue()); + assertEquals(5, outputArray3.getValue().get(0).getValue().intValue()); + assertEquals(6, outputArray3.getValue().get(1).getValue().intValue()); + } + + @Test + @Order(6) + @DisplayName("Test abi.encodePacked verification") + void testEncodePacked() { + // 1. Define inputs + Uint256 id = new Uint256(12345); + Address addr = new Address(client.keyPair.toBase58CheckAddress()); + Utf8String name = new Utf8String("hello packed"); + + // 2. Generate packed bytes in Java + String javaPackedHex = TypeEncoder.encodePacked(id) + TypeEncoder.encodePacked(addr) + TypeEncoder.encodePacked(name); + byte[] javaPackedBytes = Numeric.hexStringToByteArray(javaPackedHex); + + // 3. Prepare function call to Solidity for verification + Function function = new Function("verifyPackedEncoding", + Arrays.asList(new DynamicBytes(javaPackedBytes), id, addr, name), + Collections.singletonList(new TypeReference() {}) + ); + + // 4. Call the contract and get the boolean result + TransactionExtention transactionExtention = client.triggerConstantContract(client.keyPair.toBase58CheckAddress(), + contractAddress, function); + + List result + = FunctionReturnDecoder.decode(ByteArray.toHexString(transactionExtention.getConstantResult(0).toByteArray()), function.getOutputParameters()); + + Bool isMatch = (Bool) result.get(0); + + assertTrue(isMatch.getValue(), "Java's abi.encodePacked result did NOT match Solidity's."); + } +} diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index e1e7357d..bd4f3561 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -41,11 +41,21 @@ + + + + + + + + + + @@ -61,6 +71,11 @@ + + + + + @@ -76,6 +91,19 @@ + + + + + + + + + + + + + @@ -84,6 +112,22 @@ + + + + + + + + + + + + + + + +