Skip to content

Commit

Permalink
Merge pull request #137 from /issues/136
Browse files Browse the repository at this point in the history
Issues/136 - Incorrect byte serialisation of CLMaps that contain byte array values
  • Loading branch information
cnorburn authored Sep 15, 2022
2 parents aa46436 + b344771 commit ac8a252
Show file tree
Hide file tree
Showing 10 changed files with 447 additions and 10 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
apply plugin: 'java'

group = 'network.casper'
version='0.3.12'
version='0.3.13'
sourceCompatibility = 1.8
targetCompatibility = 1.8

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ byte[] toBytesForCLTypeInfo(final CLTypeInfo typeInfo) {
private byte[] getMapType(final CLMapTypeInfo typeInfo) {
return new ByteArrayBuilder()
.append(getTypeBytes(typeInfo))
.append(getTypeBytes(typeInfo.getKeyType()))
.append(getTypeBytes(typeInfo.getValueType()))
.append(toBytesForCLTypeInfo(typeInfo.getKeyType()))
.append(toBytesForCLTypeInfo(typeInfo.getValueType()))
.toByteArray();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

import com.casper.sdk.service.serialization.cltypes.TypesFactory;
import com.casper.sdk.service.serialization.util.ByteUtils;
import com.casper.sdk.types.CLByteArrayInfo;
import com.casper.sdk.types.CLValue;
import org.apache.commons.lang3.ArrayUtils;

import java.math.BigInteger;

/**
* Converts a CLValue to a byte array
Expand All @@ -16,7 +20,7 @@ public CLValueByteSerializer(final TypesFactory typesFactory) {
@Override
public byte[] toBytes(final CLValue source) {

final byte[] lengthBytes = getU32Serializer().serialize(source.getBytes().length);
final byte[] lengthBytes = getLengthBytes(source);
final byte[] sourceBytes = source.getBytes();
final byte[] typeInfoBytes = toBytesForCLTypeInfo(source.getCLTypeInfo());

Expand All @@ -31,4 +35,45 @@ public byte[] toBytes(final CLValue source) {
public Class<CLValue> getType() {
return CLValue.class;
}

/**
* Obtains the length of the values bytes as a U32 4 byte array.
*
* @param source the value whose byte length is to be obtained
* @return the length of the values bytes as a U32 4 byte array
*/
private byte[] getLengthBytes(final CLValue source) {

if (containsLengthBytes(source)) {
// Don't supply a length as it is already present in the byte array
return new byte[0];
} else {
return getU32Serializer().serialize(source.getBytes().length);
}
}

/**
* Indicates if the CLValues bytes are prefixed with th e length
*
* @param source the value to test if bytes are prefixed
* @return true if the bytes are prefixed with a U32 4 byte length otherwise null
*/
private boolean containsLengthBytes(final CLValue source) {
if (source.getCLTypeInfo() instanceof CLByteArrayInfo) {
// we already know the length so use existing length
final int size = ((CLByteArrayInfo) source.getCLTypeInfo()).getSize();
if (size >= 4) {
final byte[] lb = new byte[4];
// Obtain the length from the 1st four bytes U32 representation
System.arraycopy(source.getBytes(), 0, lb, 0, 4);
// Change from BE to LE (Java network order)
ArrayUtils.reverse(lb);

final int biLen = new BigInteger(lb).intValue();
// Don't supply a length as it is already present in the byte array
return biLen == size && source.getBytes().length - 4 == biLen;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ public class DeployNamedArgBuilder {
private final List<DeployNamedArg> argList = new ArrayList<>();

public DeployNamedArgBuilder add(final String name, final CLValue value) {
argList.add(new DeployNamedArg(name, value));
return add(new DeployNamedArg(name, value));
}

public DeployNamedArgBuilder add(final DeployNamedArg deployNamedArg) {
argList.add(deployNamedArg);
return this;
}

Expand Down
134 changes: 131 additions & 3 deletions src/test/java/com/casper/sdk/CasperSdkIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.casper.sdk.how_to.HowToUtils;
import com.casper.sdk.service.hash.HashService;
import com.casper.sdk.service.serialization.cltypes.CLValueBuilder;
import com.casper.sdk.service.serialization.types.ByteSerializerFactory;
import com.casper.sdk.service.serialization.util.ByteUtils;
import com.casper.sdk.service.serialization.util.CollectionUtils;
import com.casper.sdk.types.*;
import com.fasterxml.jackson.core.JsonProcessingException;
Expand All @@ -15,20 +17,21 @@
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PublicKey;
import java.time.Instant;
import java.util.List;

import static com.casper.sdk.how_to.HowToUtils.getUserKeyPairStreams;
import static com.casper.sdk.how_to.HowToUtils.*;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

/**
* Casper SDK integration tests. The NCTL test nodes must be running for these tests to execute.
*
* <p>
* Path the nctl folder can be overridden with -Dnctl.home=some-path
*/
@Disabled // Remove this comment to test against a network
Expand Down Expand Up @@ -62,6 +65,8 @@ class CasperSdkIntegrationTest {
*/
private CasperSdk casperSdk;

private final ByteSerializerFactory serializerFactory = new ByteSerializerFactory();

@BeforeEach
void setUp() {
casperSdk = new CasperSdk("http://localhost", 11101);
Expand Down Expand Up @@ -208,6 +213,129 @@ void getBlockTransfers() throws JsonProcessingException {
assertThat(blockTransfersByHash, hasJsonPath("$.transfers"));
}

@Test
@Disabled
void testIssue2() throws IOException {


final InputStream erc20wasmIn = getWasmIn("/com/casper/sdk/how_to/erc20.wasm");
final String chainName = "casper-net-1";
final Number payment = 50e9;
final int tokenDecimals = 11;
final String tokenName = "Acme Token";
final Number tokenTotalSupply = 1e15;
final String tokenSymbol = "ACME";

// Get contract operator.
final KeyPairStreams faucetKeyPair = getFaucetKeyPair();
final KeyPair operatorKeyPair = casperSdk.loadKeyPair(faucetKeyPair.getPublicKeyIn(), faucetKeyPair.getPrivateKeyIn());

// Set deploy.
final Deploy installContractDeploy = casperSdk.makeInstallContract(
new DeployParams(
operatorKeyPair.getPublic(),
chainName,
null,
null,
null,
null
),
payment,
erc20wasmIn,
tokenDecimals,
tokenName,
tokenSymbol,
tokenTotalSupply
);

// Approve deploy.
casperSdk.signDeploy(installContractDeploy, operatorKeyPair);

// Dispatch deploy to a node.
Digest contractHash = casperSdk.putDeploy(installContractDeploy);
assertThat(contractHash, is(notNullValue()));

contractHash = new ContractHash("6b6f1b4a38d94956154d20089842ca69f891ea44322df9d20921015ce711dc34");

System.out.println("ContractHash: " + contractHash);

byte[] k1Bytes = ByteUtils.decodeHex("e07cA98F1b5C15bC9ce75e8adB8a3b4D334A1B1Fa14DD16CfD3320bf77Cc3aAb");
final CLValue key1 = CLValueBuilder.byteArray(k1Bytes);



byte[] k2Bytes = ByteUtils.decodeHex("e3D394334Ce46C6043BCd33E4686D2B7a369C606BfCce4C26ca14d2C73Fac824");
final CLValue key2 = CLValueBuilder.byteArray(k2Bytes);
final CLValue value = CLValueBuilder.u256(0.4e6);



final KeyPair platformKeyPair = getNodeKeyPair(1);

CLMap map1 = CLValueBuilder.map(CollectionUtils.Map.of(key1, value));
CLMap map2 = CLValueBuilder.map(CollectionUtils.Map.of(key2, value));

byte[] map1Bytes = map1.getBytes();
byte[] map2Byte = map2.getBytes();

final DeployNamedArg assetHolders = new DeployNamedArg("asset_holders", map1);
final DeployNamedArg liabilityHolders = new DeployNamedArg("liability_holders", map2);

// Test the bytes from both CLMap named args
byte[] assertHoldersBytes = serializerFactory.getByteSerializer(assetHolders).toBytes(assetHolders);

// Assert assertHoldersBytes match expected
// TODO

byte[] liabilityHoldersBytes = serializerFactory.getByteSerializer(liabilityHolders).toBytes(liabilityHolders);

// Assert liabilityHoldersBytes match expected
// TODO

final List<DeployNamedArg> namedArgs = new DeployNamedArgBuilder()
.add("token_id", CLValueBuilder.string("token-id"))
.add("instrument_id", CLValueBuilder.string("c9536033-386a-4bed-9b57-fd67c3d49dc1"))
.add("asset_decimals", CLValueBuilder.u256(1))
.add("asset_units", CLValueBuilder.u256(50000))
.add(assetHolders)
.add("liability_decimals", CLValueBuilder.u256(1))
.add("liability_units", CLValueBuilder.u256(40000))
.add(liabilityHolders)
.build();

byte[] namedArgsBytes = serializerFactory.getByteSerializer(namedArgs).toBytes(namedArgs);

// Assert namedArgsBytes match expected
// TODO


final Deploy deploy = casperSdk.makeDeploy(
new DeployParams(
platformKeyPair.getPublic(), "casper-net-1",
1,
Instant.now().toEpochMilli(),
DeployParams.DEFAULT_TTL,
null
),
new StoredContractByHash(
new ContractHash(contractHash.getHash()),
"set_state",
namedArgs),
casperSdk.standardPayment(new BigInteger("10000000000"))
);

assertThat(deploy, is(notNullValue()));

casperSdk.signDeploy(deploy, operatorKeyPair);

Digest digest = casperSdk.putDeploy(deploy);
assertThat(digest, is(notNullValue()));

// Assert hash matches expected
byte [] expectedIssue2Hash = {};
assertThat(digest.getHash(), is(expectedIssue2Hash));
}

private KeyPair geUserKeyPair(int userNumber) throws IOException {
final KeyPairStreams streams = getUserKeyPairStreams(userNumber);
return casperSdk.loadKeyPair(streams.getPublicKeyIn(), streams.getPrivateKeyIn());
Expand Down
Loading

0 comments on commit ac8a252

Please sign in to comment.