Skip to content

Commit

Permalink
feat(ContractFunctionParameters): added addBytes4 and addBytes4Array
Browse files Browse the repository at this point in the history
Signed-off-by: Jeffery Orazulike <[email protected]>
  • Loading branch information
logickoder committed Oct 5, 2024
1 parent 905c187 commit 267b6af
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import com.google.errorprone.annotations.Var;
import com.google.protobuf.ByteString;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -29,6 +30,7 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.annotation.Nullable;

import org.bouncycastle.util.encoders.DecoderException;
import org.bouncycastle.util.encoders.Hex;

Expand Down Expand Up @@ -86,6 +88,14 @@ private static ByteString encodeBytes(byte[] bytes) {
.concat(rightPad32(ByteString.copyFrom(bytes)));
}

private static ByteString encodeBytes4(byte[] bytes) {
if (bytes.length > 4) {
throw new IllegalArgumentException("bytes4 encoding forbids byte array length greater than 4");
}
return rightPad32(ByteString.copyFrom(bytes));
}


private static ByteString encodeBytes32(byte[] bytes) {
if (bytes.length > 32) {
throw new IllegalArgumentException("byte32 encoding forbids byte array length greater than 32");
Expand Down Expand Up @@ -187,7 +197,7 @@ static ByteString leftPad32(ByteString input, boolean negative) {
return rem == 32
? input
: (negative ? negativePadding : padding).substring(0, rem)
.concat(input);
.concat(input);
}

static ByteString leftPad32(byte[] input, boolean negative) {
Expand Down Expand Up @@ -275,6 +285,35 @@ public ContractFunctionParameters addBytesArray(byte[][] param) {
return this;
}

/**
* Add a parameter of type {@code bytes4}, a 4-byte fixed-length byte-string.
*
* @param param The 4-byte array to be added
* @return {@code this}
* @throws IllegalArgumentException if the length of the byte array is not 4.
*/
public ContractFunctionParameters addBytes4(byte[] param) {
args.add(new Argument("bytes4", encodeBytes4(param), false));

return this;
}

/**
* Add a parameter of type {@code bytes4[]}, an array of 4-byte fixed-length byte-strings.
*
* @param param The array of 4-byte arrays to be added
* @return {@code this}
* @throws IllegalArgumentException if the length of any byte array is not 4.
*/
public ContractFunctionParameters addBytes4Array(byte[][] param) {
Stream<ByteString> byteArrays = Arrays.stream(param)
.map(ContractFunctionParameters::encodeBytes4);

args.add(new Argument("bytes4[]", encodeArray(byteArrays), true));

return this;
}

/**
* Add a parameter of type {@code bytes32}, a 32-byte byte-string.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,44 @@ void functionsError() {
});
}

@Test
@DisplayName("encodes bytes4 correctly")
void bytes4Encoding() {
var params = new ContractFunctionParameters()
.addBytes4(new byte[]{1, 2, 3, 4});
assertThat(
"580526ee" +
"0102030400000000000000000000000000000000000000000000000000000000"
).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray()));
}

@Test
@DisplayName("fails to encode bytes4 if length too long")
void bytes4EncodingError() {
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> {
new ContractFunctionParameters().addBytes4(new byte[]{1, 2, 3, 4, 5});
});
}

@Test
@DisplayName("encodes UTF-8 string as bytes4 correctly")
void bytes4UTF8Encoding() {
var params = new ContractFunctionParameters()
.addBytes4("ABCD".getBytes(StandardCharsets.UTF_8));
assertThat(
"580526ee" +
"4142434400000000000000000000000000000000000000000000000000000000"
).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray()));
}

@Test
@DisplayName("fails to encode UTF-8 string as bytes4 if length is bigger than 4 bytes")
void bytes4UTF8EncodingError() {
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> {
new ContractFunctionParameters().addBytes4("ABCDE".getBytes(StandardCharsets.UTF_8));
});
}

@Test
@DisplayName("encodes bytes32 correctly")
void bytes() {
Expand Down Expand Up @@ -410,6 +448,24 @@ void arrayTypesEncoding() {
).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray()));
}

@Test
@DisplayName("bytes4[] encodes correctly")
void fixedBytes4ArrayEncoding() {
ContractFunctionParameters params = new ContractFunctionParameters()
.addBytes4Array(new byte[][]{
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
});
assertThat(
"0000000000000000000000000000000000000000000000000000000000000020" + // offset of array
"0000000000000000000000000000000000000000000000000000000000000003" + // length of array
"0102030400000000000000000000000000000000000000000000000000000000" + // first bytes4
"0506070800000000000000000000000000000000000000000000000000000000" + // second bytes4
"090a0b0c00000000000000000000000000000000000000000000000000000000" // third bytes4
).isEqualTo(Hex.toHexString(params.toBytes(null).toByteArray()));
}

@Test
@DisplayName("bytes32[] encodes correctly")
void fixedBytesArrayEncoding() {
Expand Down Expand Up @@ -544,10 +600,10 @@ void uInt256EncodesCorrectly(long val, String hexString, int bitWidth) {
@Test
void intSizesEncodeCorrectly() throws Exception {
List<String> snapshotStrings = new ArrayList<>();
for (int n = 8; n <= 256; n+= 8) {
for (int n = 8; n <= 256; n += 8) {
var bitWidth = n;

var argType = ((Supplier<Class<?>>)() -> {
var argType = ((Supplier<Class<?>>) () -> {
if (bitWidth == 8) {
return byte.class;
} else if (bitWidth <= 32) {
Expand All @@ -559,7 +615,7 @@ void intSizesEncodeCorrectly() throws Exception {
}
}).get();

var argVal = ((Supplier<Object>)() -> {
var argVal = ((Supplier<Object>) () -> {
if (bitWidth == 8) {
return (byte) (1 << (bitWidth - 1));
} else if (bitWidth <= 32) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2788,6 +2788,39 @@ void canCallContractFunctionBytesArrayType() throws Exception {
assertThat(responseResult).isEqualTo(testBytes);
}


@Test
@DisplayName("Can receive bytes4 value from contract call")
void canCallContractFunctionBytes4Type() throws Exception {
byte[] testBytes = "Test".getBytes();
byte[] testBytesLen4 = new byte[4];
System.arraycopy(testBytes, 0, testBytesLen4, 0, testBytes.length);

var response = new ContractCallQuery().setContractId(contractId).setGas(1500000)
.setFunction("returnBytes4", new ContractFunctionParameters().addBytes4(testBytesLen4))
.setQueryPayment(new Hbar(10)).execute(testEnv.client);

assertThat(response.getBytes(0)).isEqualTo(testBytesLen4);
}

@Test
@DisplayName("Can receive bytes4 array value from contract call")
void canCallContractFunctionBytes4ArrayType() throws Exception {
byte[] testBytes = "Test".getBytes();
byte[] testBytes2 = "Test2".getBytes();
byte[][] testBytesLen4 = new byte[2][4];
System.arraycopy(testBytes, 0, testBytesLen4[0], 0, testBytes.length);
System.arraycopy(testBytes2, 0, testBytesLen4[1], 0, testBytes2.length);

var response = new ContractCallQuery().setContractId(contractId).setGas(1500000)
.setFunction("returnBytes4Arr", new ContractFunctionParameters().addBytes4Array(testBytesLen4))
.setQueryPayment(new Hbar(10)).execute(testEnv.client);

var responseResult = (byte[][]) response.getResult("(bytes4[])").get(0);

assertThat(responseResult).isEqualTo(testBytesLen4);
}

@Test
@DisplayName("Can receive bytes32 value from contract call")
void canCallContractFunctionBytes32Type() throws Exception {
Expand Down

0 comments on commit 267b6af

Please sign in to comment.