Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/main/java/org/fisco/bcos/sdk/abi/Constant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.fisco.bcos.sdk.abi;

import java.math.BigInteger;

public class Constant {
public static final BigInteger MAX_UINT256 =
new BigInteger("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
public static final BigInteger MAX_INT256 =
new BigInteger("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
public static final BigInteger MIN_INT256 =
new BigInteger("-7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
}
47 changes: 47 additions & 0 deletions src/main/java/org/fisco/bcos/sdk/abi/EventEncoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.fisco.bcos.sdk.abi;

import java.util.List;
import java.util.stream.Collectors;
import org.fisco.bcos.sdk.abi.datatypes.Event;
import org.fisco.bcos.sdk.abi.datatypes.Type;
import org.fisco.bcos.sdk.crypto.CryptoInterface;
import org.fisco.bcos.sdk.utils.Numeric;

/**
* Ethereum filter encoding. Further limited details are available <a
* href="https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#events">here</a>.
*/
public class EventEncoder {

private CryptoInterface cryptoInterface;

public EventEncoder(CryptoInterface cryptoInterface) {
this.cryptoInterface = cryptoInterface;
}

public String encode(Event event) {

String methodSignature = buildMethodSignature(event.getName(), event.getParameters());

return buildEventSignature(methodSignature);
}

private <T extends Type> String buildMethodSignature(
String methodName, List<TypeReference<T>> parameters) {

StringBuilder result = new StringBuilder();
result.append(methodName);
result.append("(");
String params =
parameters.stream().map(p -> Utils.getTypeName(p)).collect(Collectors.joining(","));
result.append(params);
result.append(")");
return result.toString();
}

public String buildEventSignature(String methodSignature) {
byte[] input = methodSignature.getBytes();
byte[] hash = cryptoInterface.hash(input);
return Numeric.toHexString(hash);
}
}
23 changes: 23 additions & 0 deletions src/main/java/org/fisco/bcos/sdk/abi/EventValues.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.fisco.bcos.sdk.abi;

import java.util.List;
import org.fisco.bcos.sdk.abi.datatypes.Type;

/** Persisted solidity event parameters. */
public class EventValues {
private final List<Type> indexedValues;
private final List<Type> nonIndexedValues;

public EventValues(List<Type> indexedValues, List<Type> nonIndexedValues) {
this.indexedValues = indexedValues;
this.nonIndexedValues = nonIndexedValues;
}

public List<Type> getIndexedValues() {
return indexedValues;
}

public List<Type> getNonIndexedValues() {
return nonIndexedValues;
}
}
78 changes: 78 additions & 0 deletions src/main/java/org/fisco/bcos/sdk/abi/FunctionEncoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.fisco.bcos.sdk.abi;

import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;
import org.fisco.bcos.sdk.abi.datatypes.Function;
import org.fisco.bcos.sdk.abi.datatypes.Type;
import org.fisco.bcos.sdk.abi.datatypes.Uint;
import org.fisco.bcos.sdk.crypto.CryptoInterface;
import org.fisco.bcos.sdk.utils.Numeric;

/**
* Ethereum Contract Application Binary Interface (ABI) encoding for functions. Further details are
* available <a href="https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI">here</a>.
*/
public class FunctionEncoder {

private CryptoInterface cryptoInterface;

public FunctionEncoder(CryptoInterface cryptoInterface) {
this.cryptoInterface = cryptoInterface;
}

public String encode(Function function) {
List<Type> parameters = function.getInputParameters();

String methodSignature = buildMethodSignature(function.getName(), parameters);
String methodId = buildMethodId(methodSignature);

StringBuilder result = new StringBuilder();
result.append(methodId);

return encodeParameters(parameters, result);
}

public String encodeConstructor(List<Type> parameters) {
return encodeParameters(parameters, new StringBuilder());
}

public String encodeParameters(List<Type> parameters, StringBuilder result) {
int dynamicDataOffset = Utils.getLength(parameters) * Type.MAX_BYTE_LENGTH;
StringBuilder dynamicData = new StringBuilder();

for (Type parameter : parameters) {
String encodedValue = TypeEncoder.encode(parameter);

if (parameter.dynamicType()) {
String encodedDataOffset =
TypeEncoder.encodeNumeric(new Uint(BigInteger.valueOf(dynamicDataOffset)));
result.append(encodedDataOffset);
dynamicData.append(encodedValue);
dynamicDataOffset += (encodedValue.length() >> 1);
} else {
result.append(encodedValue);
}
}
result.append(dynamicData);

return result.toString();
}

private String buildMethodSignature(String methodName, List<Type> parameters) {
StringBuilder result = new StringBuilder();
result.append(methodName);
result.append("(");
String params =
parameters.stream().map(Type::getTypeAsString).collect(Collectors.joining(","));
result.append(params);
result.append(")");
return result.toString();
}

public String buildMethodId(String methodSignature) {
byte[] input = methodSignature.getBytes();
byte[] hash = cryptoInterface.hash(input);
return Numeric.toHexString(hash).substring(0, 10);
}
}
133 changes: 133 additions & 0 deletions src/main/java/org/fisco/bcos/sdk/abi/FunctionReturnDecoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.fisco.bcos.sdk.abi;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.fisco.bcos.sdk.abi.datatypes.Array;
import org.fisco.bcos.sdk.abi.datatypes.Bytes;
import org.fisco.bcos.sdk.abi.datatypes.BytesType;
import org.fisco.bcos.sdk.abi.datatypes.DynamicArray;
import org.fisco.bcos.sdk.abi.datatypes.StaticArray;
import org.fisco.bcos.sdk.abi.datatypes.Type;
import org.fisco.bcos.sdk.abi.datatypes.Utf8String;
import org.fisco.bcos.sdk.abi.datatypes.generated.Bytes32;
import org.fisco.bcos.sdk.utils.Numeric;
import org.fisco.bcos.sdk.utils.StringUtils;

/** Decodes values returned by function or event calls. */
public class FunctionReturnDecoder {

private FunctionReturnDecoder() {}

/**
* Decode ABI encoded return values from smart contract function call.
*
* @param rawInput ABI encoded input
* @param outputParameters list of return types as {@link TypeReference}
* @return {@link List} of values returned by function, {@link Collections#emptyList()} if
* invalid response
*/
public static List<Type> decode(String rawInput, List<TypeReference<Type>> outputParameters) {
String input = Numeric.cleanHexPrefix(rawInput);

if (StringUtils.isEmpty(input)) {
return Collections.emptyList();
} else {
return build(input, outputParameters);
}
}

/**
* 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
* parameters and return values.
*
* <p>If any of the following types are indexed, the Keccak-256 hashes of the values are
* returned instead. These are returned as a bytes32 value.
*
* <ul>
* <li>Arrays
* <li>Strings
* <li>Bytes
* </ul>
*
* <p>See the <a href="http://solidity.readthedocs.io/en/latest/contracts.html#events">Solidity
* documentation</a> for further information.
*
* @param rawInput ABI encoded input
* @param typeReference of expected result type
* @param <T> type of TypeReference
* @return the decode value
*/
@SuppressWarnings("unchecked")
public static <T extends Type> Type decodeIndexedValue(
String rawInput, TypeReference<T> typeReference) {
String input = Numeric.cleanHexPrefix(rawInput);

try {
Class<T> type = typeReference.getClassType();

if (Bytes.class.isAssignableFrom(type)) {
return TypeDecoder.decodeBytes(input, (Class<Bytes>) Class.forName(type.getName()));
} else if (Array.class.isAssignableFrom(type)
|| BytesType.class.isAssignableFrom(type)
|| Utf8String.class.isAssignableFrom(type)) {
return TypeDecoder.decodeBytes(input, Bytes32.class);
} else {
return TypeDecoder.decode(input, 0, type);
}
} catch (ClassNotFoundException e) {
throw new UnsupportedOperationException("Invalid class reference provided", e);
}
}

private static List<Type> build(String input, List<TypeReference<Type>> outputParameters) {
List<Type> results = new ArrayList<>(outputParameters.size());

int offset = 0;
for (TypeReference<?> typeReference : outputParameters) {
try {
@SuppressWarnings("unchecked")
Class<Type> cls = (Class<Type>) typeReference.getClassType();

int hexStringDataOffset = getDataOffset(input, offset, typeReference.getType());

Type result;
if (DynamicArray.class.isAssignableFrom(cls)) {
result =
TypeDecoder.decodeDynamicArray(
input, hexStringDataOffset, typeReference.getType());
} else if (StaticArray.class.isAssignableFrom(cls)) {
int length =
Integer.parseInt(
cls.getSimpleName()
.substring(StaticArray.class.getSimpleName().length()));
result =
TypeDecoder.decodeStaticArray(
input, hexStringDataOffset, typeReference.getType(), length);
} else {
result = TypeDecoder.decode(input, hexStringDataOffset, cls);
}

results.add(result);

offset +=
Utils.getOffset(typeReference.getType())
* TypeDecoder.MAX_BYTE_LENGTH_FOR_HEX_STRING;

} catch (ClassNotFoundException e) {
throw new UnsupportedOperationException("Invalid class reference provided", e);
}
}
return results;
}

private static <T extends Type> int getDataOffset(
String input, int offset, java.lang.reflect.Type type) throws ClassNotFoundException {
if (Utils.dynamicType(type)) {
return TypeDecoder.decodeUintAsInt(input, offset) << 1;
} else {
return offset;
}
}
}
Loading