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
22 changes: 22 additions & 0 deletions src/main/java/com/iexec/commons/poco/chain/ChainApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@

package com.iexec.commons.poco.chain;

import com.iexec.commons.poco.encoding.PoCoDataDecoder;
import com.iexec.commons.poco.tee.TeeEnclaveConfiguration;
import lombok.Builder;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;

import static com.iexec.commons.poco.chain.Web3jAbstractService.toBigInt;

@Slf4j
@Value
@Builder
public class ChainApp {
Expand All @@ -28,4 +33,21 @@ public class ChainApp {
String multiaddr;
String checksum;
TeeEnclaveConfiguration enclaveConfiguration;

public static ChainApp fromRawData(final String address, final String rawData) {
log.debug("ChainApp.fromRawData [address:{}]", address);
final String[] parts = PoCoDataDecoder.toParts(rawData);
final int offset = toBigInt(parts[0]).intValue() / 32;
final int typeOffset = toBigInt(parts[offset + 2]).intValue() / 32;
final int multiaddrOffest = toBigInt(parts[offset + 3]).intValue() / 32;
final int enclaveOffset = toBigInt(parts[offset + 5]).intValue() / 32;
final String enclaveContrib = PoCoDataDecoder.decodeToAsciiString(parts, offset + enclaveOffset);
return ChainApp.builder()
.chainAppId(address)
.type(PoCoDataDecoder.decodeToAsciiString(parts, offset + typeOffset))
.multiaddr(PoCoDataDecoder.decodeToAsciiString(parts, offset + multiaddrOffest))
.checksum("0x" + parts[offset + 4])
.enclaveConfiguration(TeeEnclaveConfiguration.fromJsonString(enclaveContrib))
.build();
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/iexec/commons/poco/chain/ChainDataset.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,32 @@

package com.iexec.commons.poco.chain;

import com.iexec.commons.poco.encoding.PoCoDataDecoder;
import com.iexec.commons.poco.utils.MultiAddressHelper;
import lombok.Builder;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;

import static com.iexec.commons.poco.chain.Web3jAbstractService.toBigInt;

@Slf4j
@Value
@Builder
public class ChainDataset {
String chainDatasetId;
String multiaddr;
String checksum;

public static ChainDataset fromRawData(final String address, final String rawData) {
log.debug("ChainDataset.fromRawData [address:{}]", address);
final String[] parts = PoCoDataDecoder.toParts(rawData);
final int offset = toBigInt(parts[0]).intValue() / 32;
final int multiaddrOffset = toBigInt(parts[offset + 2]).intValue() / 32;
final String multiaddr = PoCoDataDecoder.decodeToHexString(parts, offset + multiaddrOffset);
return ChainDataset.builder()
.chainDatasetId(address)
.multiaddr(MultiAddressHelper.convertToURI(multiaddr))
.checksum("0x" + parts[offset + 3])
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,9 @@
import com.iexec.commons.poco.order.DatasetOrder;
import com.iexec.commons.poco.task.TaskDescription;
import com.iexec.commons.poco.utils.BytesUtils;
import com.iexec.commons.poco.utils.MultiAddressHelper;
import com.iexec.commons.poco.utils.Retryer;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.crypto.Credentials;
import org.web3j.ens.EnsResolutionException;
import org.web3j.tuples.generated.Tuple3;
Expand All @@ -45,7 +42,6 @@
import static com.iexec.commons.poco.chain.Web3jAbstractService.toBigInt;
import static com.iexec.commons.poco.chain.Web3jAbstractService.toEthereumAddress;
import static com.iexec.commons.poco.encoding.AccessorsEncoder.*;
import static com.iexec.commons.poco.tee.TeeEnclaveConfiguration.buildEnclaveConfigurationFromJsonString;
import static com.iexec.commons.poco.utils.BytesUtils.isNonZeroedBytes32;

/*
Expand Down Expand Up @@ -363,49 +359,25 @@ public Optional<ChainApp> getChainApp(final String appAddress) {
if (appAddress == null || appAddress.equals(BytesUtils.EMPTY_ADDRESS)) {
return Optional.empty();
}
final ChainApp.ChainAppBuilder chainAppBuilder = ChainApp.builder();
try {
chainAppBuilder
.chainAppId(appAddress)
.type(sendCallAndDecodeDynamicBytes(appAddress, M_APPTYPE_SELECTOR))
.multiaddr(sendCallAndDecodeDynamicBytes(appAddress, M_APPMULTIADDR_SELECTOR))
.checksum(sendCallAndGetRawResult(appAddress, M_APPCHECKSUM_SELECTOR));
final String txData = VIEW_APP_SELECTOR +
Numeric.toHexStringNoPrefixZeroPadded(Numeric.toBigInt(appAddress), 64);
final String rawData = web3jAbstractService.sendCall(credentials.getAddress(), iexecHubAddress, txData);
return Optional.of(ChainApp.fromRawData(appAddress, rawData));
} catch (Exception e) {
log.error("Failed to get chain app [chainAppId:{}]",
appAddress, e);
return Optional.empty();
}
String mrEnclave;
try {
mrEnclave = sendCallAndDecodeDynamicBytes(appAddress, M_APPMRENCLAVE_SELECTOR);
} catch (Exception e) {
log.error("Failed to get chain app mrenclave [chainAppId:{}]",
appAddress, e);
return Optional.empty();
}
if (StringUtils.isEmpty(mrEnclave)) {
// Standard application
return Optional.of(chainAppBuilder.build());
}
try {
chainAppBuilder.enclaveConfiguration(
buildEnclaveConfigurationFromJsonString(mrEnclave));
} catch (Exception e) {
log.error("Failed to get tee chain app enclave configuration [chainAppId:{}, mrEnclave:{}]",
appAddress, mrEnclave, e);
return Optional.empty();
}
return Optional.of(chainAppBuilder.build());
}

public Optional<ChainDataset> getChainDataset(final String datasetAddress) {
if (datasetAddress != null && !datasetAddress.equals(BytesUtils.EMPTY_ADDRESS)) {
try {
return Optional.of(ChainDataset.builder()
.chainDatasetId(datasetAddress)
.multiaddr(sendCallAndDecodeDynamicBytes(datasetAddress, M_DATASETMULTIADDR_SELECTOR))
.checksum(sendCallAndGetRawResult(datasetAddress, M_DATASETCHECKSUM_SELECTOR))
.build());
final String txData = VIEW_DATASET_SELECTOR +
Numeric.toHexStringNoPrefixZeroPadded(Numeric.toBigInt(datasetAddress), 64);
final String rawData = web3jAbstractService.sendCall(credentials.getAddress(), iexecHubAddress, txData);
return Optional.of(ChainDataset.fromRawData(datasetAddress, rawData));
} catch (Exception e) {
log.error("Failed to get ChainDataset [chainDatasetId:{}]",
datasetAddress, e);
Expand All @@ -414,31 +386,6 @@ public Optional<ChainDataset> getChainDataset(final String datasetAddress) {
return Optional.empty();
}

/**
* Send a call to a Smart contract to retrieve a single value corresponding to a dynamic type and decode it.
*
* @param address Smart Contract address (can be an App or a Dataset in PoCo)
* @param selector Function selector
* @return The decoded String result returned by the call
* @throws IOException on communication error
*/
private String sendCallAndDecodeDynamicBytes(final String address, final String selector) throws IOException {
return MultiAddressHelper.convertToURI(
FunctionReturnDecoder.decodeDynamicBytes(sendCallAndGetRawResult(address, selector)));
}

/**
* Send a call to a Smart contract to retrieve a single value.
*
* @param address Smart Contract address (can be an App or a Dataset in PoCo)
* @param selector Function selector
* @return The hexadecimal representation of retrieved bytes, may need further decoding
* @throws IOException on communication error
*/
private String sendCallAndGetRawResult(final String address, final String selector) throws IOException {
return web3jAbstractService.sendCall(credentials.getAddress(), address, selector);
}

public Optional<Integer> getWorkerScore(String address) {
if (address != null && !address.isEmpty()) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,41 @@ public class AccessorsEncoder {
public static final String VIEW_CONSUMED_SELECTOR = "0x4b2bec8c";

// app
/**
* @deprecated no more used, replaced with viewApp new call
*/
@Deprecated(forRemoval = true)
public static final String M_APPCHECKSUM_SELECTOR = "0x84aaf12e";
/**
* @deprecated no more used, replaced with viewApp new call
*/
@Deprecated(forRemoval = true)
public static final String M_APPMRENCLAVE_SELECTOR = "0xe30d26a8";
/**
* @deprecated no more used, replaced with viewApp new call
*/
@Deprecated(forRemoval = true)
public static final String M_APPMULTIADDR_SELECTOR = "0x39e75d45";
/**
* @deprecated no more used, replaced with viewApp new call
*/
@Deprecated(forRemoval = true)
public static final String M_APPTYPE_SELECTOR = "0xf8c2ceb3";

// dataset
/**
* @deprecated no more used, replaced with viewDataset new call
*/
@Deprecated(forRemoval = true)
public static final String M_DATASETCHECKSUM_SELECTOR = "0x1ba99d7e";
/**
* @deprecated no more used, replaced with viewDataset new call
*/
@Deprecated(forRemoval = true)
public static final String M_DATASETMULTIADDR_SELECTOR = "0xa61ca6c5";

// assets
public static final String VIEW_APP_SELECTOR = "0xe1523fb4";
public static final String VIEW_DATASET_SELECTOR = "0x1e143ef7";

}
61 changes: 61 additions & 0 deletions src/main/java/com/iexec/commons/poco/encoding/PoCoDataDecoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2025 IEXEC BLOCKCHAIN TECH
*
* 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 com.iexec.commons.poco.encoding;

import com.iexec.commons.poco.utils.BytesUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.web3j.utils.Numeric;

import java.util.Arrays;

import static com.iexec.commons.poco.chain.Web3jAbstractService.toBigInt;

@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PoCoDataDecoder {
public static String decodeToAsciiString(final String[] parts, final int offset) {
return BytesUtils.hexStringToAscii(decodeToHexString(parts, offset));
}

public static String decodeToHexString(final String[] parts, final int offset) {
final int size = toBigInt(parts[offset]).intValue();
log.debug("Size {}", size);
final StringBuilder sb = new StringBuilder();
int remainingSize = size;
int chunk = 1;
while (remainingSize >= 32) {
sb.append(parts[offset + chunk]);
remainingSize -= 32;
chunk++;
}
if (remainingSize != 0) {
sb.append(parts[offset + chunk], 0, 2 * remainingSize);
}
return sb.toString();
}

public static String[] toParts(final String rawData) {
final String[] parts = Numeric.cleanHexPrefix(rawData).split("(?<=\\G.{64})");
if (log.isTraceEnabled()) {
log.trace("parts size {}", parts.length);
Arrays.stream(parts).forEach(log::trace);
}
return parts;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 IEXEC BLOCKCHAIN TECH
* Copyright 2020-2025 IEXEC BLOCKCHAIN TECH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -41,6 +41,14 @@ public static TeeEnclaveConfiguration buildEnclaveConfigurationFromJsonString(St
.readValue(jsonString, TeeEnclaveConfiguration.class);
}

public static TeeEnclaveConfiguration fromJsonString(final String jsonString) {
try {
return buildEnclaveConfigurationFromJsonString(jsonString);
} catch (Exception e) {
return null;
}
}

public String toJsonString() throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@

import static com.iexec.commons.poco.itest.ChainTests.SERVICE_NAME;
import static com.iexec.commons.poco.itest.ChainTests.SERVICE_PORT;
import static com.iexec.commons.poco.itest.IexecHubTestService.ASSET_CHECKSUM;
import static com.iexec.commons.poco.itest.IexecHubTestService.ASSET_MULTI_ADDRESS;
import static com.iexec.commons.poco.itest.Web3jTestService.MINING_TIMEOUT;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -74,14 +73,16 @@ void init() throws CipherException, IOException {
@Test
void shouldCreateAndPredictCallsBeEqualWhenAssetNotDeployed() {
final String appName = RandomStringUtils.randomAlphanumeric(16);
final String appChecksum = iexecHubService.generateChecksum();
final String datasetName = RandomStringUtils.randomAlphanumeric(16);
final String datasetChecksum = iexecHubService.generateChecksum();
final String workerpoolName = RandomStringUtils.randomAlphanumeric(16);

assertAll(
() -> assertThat(iexecHubService.callCreateApp(appName))
.isEqualTo(iexecHubService.callPredictApp(appName)),
() -> assertThat(iexecHubService.callCreateDataset(datasetName))
.isEqualTo(iexecHubService.callPredictDataset(datasetName)),
() -> assertThat(iexecHubService.callCreateApp(appName, appChecksum))
.isEqualTo(iexecHubService.callPredictApp(appName, appChecksum)),
() -> assertThat(iexecHubService.callCreateDataset(datasetName, datasetChecksum))
.isEqualTo(iexecHubService.callPredictDataset(datasetName, datasetChecksum)),
() -> assertThat(iexecHubService.callCreateWorkerpool(workerpoolName))
.isEqualTo(iexecHubService.callPredictWorkerpool(workerpoolName))
);
Expand All @@ -90,12 +91,14 @@ void shouldCreateAndPredictCallsBeEqualWhenAssetNotDeployed() {
@Test
void shouldCreateCallRevertWhenAssetDeployed() throws IOException {
final String appName = RandomStringUtils.randomAlphanumeric(16);
final String appChecksum = iexecHubService.generateChecksum();
final String datasetName = RandomStringUtils.randomAlphanumeric(16);
final String datasetChecksum = iexecHubService.generateChecksum();
final String workerpoolName = RandomStringUtils.randomAlphanumeric(16);
BigInteger nonce = signerService.getNonce();
final String appTxHash = iexecHubService.submitCreateAppTx(nonce, appName);
final String appTxHash = iexecHubService.submitCreateAppTx(nonce, appName, appChecksum);
nonce = nonce.add(BigInteger.ONE);
final String datasetTxHash = iexecHubService.submitCreateDatasetTx(nonce, datasetName);
final String datasetTxHash = iexecHubService.submitCreateDatasetTx(nonce, datasetName, datasetChecksum);
nonce = nonce.add(BigInteger.ONE);
final String workerpoolTxHash = iexecHubService.submitCreateWorkerpoolTx(nonce, workerpoolName);

Expand All @@ -104,8 +107,8 @@ void shouldCreateCallRevertWhenAssetDeployed() throws IOException {
assertThat(web3jService.areTxStatusOK(appTxHash, datasetTxHash, workerpoolTxHash)).isTrue();

// fetch asset addresses from call on predict assets
final String predictedAppAddress = iexecHubService.callPredictApp(appName);
final String predictedDatasetAddress = iexecHubService.callPredictDataset(datasetName);
final String predictedAppAddress = iexecHubService.callPredictApp(appName, appChecksum);
final String predictedDatasetAddress = iexecHubService.callPredictDataset(datasetName, datasetChecksum);
final String predictedWorkerpoolAddress = iexecHubService.callPredictWorkerpool(workerpoolName);

// check assets are deployed
Expand All @@ -125,10 +128,10 @@ void shouldCreateCallRevertWhenAssetDeployed() throws IOException {
final String errorMessage = "Create2: Failed on deploy";

assertAll(
() -> assertThatThrownBy(() -> iexecHubService.callCreateApp(appName), "Should have failed to call createApp")
() -> assertThatThrownBy(() -> iexecHubService.callCreateApp(appName, appChecksum), "Should have failed to call createApp")
.isInstanceOf(JsonRpcError.class)
.hasMessage(errorMessage),
() -> assertThatThrownBy(() -> iexecHubService.callCreateDataset(datasetName), "Should have failed to call createDataset")
() -> assertThatThrownBy(() -> iexecHubService.callCreateDataset(datasetName, datasetChecksum), "Should have failed to call createDataset")
.isInstanceOf(JsonRpcError.class)
.hasMessage(errorMessage),
() -> assertThatThrownBy(() -> iexecHubService.callCreateWorkerpool(workerpoolName), "Should have failed to call createWorkerpool")
Expand All @@ -140,7 +143,7 @@ void shouldCreateCallRevertWhenAssetDeployed() throws IOException {
assertThat(chainApp).contains(
ChainApp.builder()
.chainAppId(predictedAppAddress)
.checksum("0x" + ASSET_CHECKSUM)
.checksum("0x" + appChecksum)
.multiaddr(ASSET_MULTI_ADDRESS)
.enclaveConfiguration(TeeEnclaveConfiguration.buildEnclaveConfigurationFromJsonString("{}"))
.type("DOCKER")
Expand All @@ -150,7 +153,7 @@ void shouldCreateCallRevertWhenAssetDeployed() throws IOException {
assertThat(chainDataset).contains(
ChainDataset.builder()
.chainDatasetId(predictedDatasetAddress)
.checksum("0x" + ASSET_CHECKSUM)
.checksum("0x" + datasetChecksum)
.multiaddr(ASSET_MULTI_ADDRESS)
.build()
);
Expand Down
Loading