diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b020eb..efc9b8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ All notable changes to this project will be documented in this file. +## [[4.2.0]](https://github.com/iExecBlockchainComputing/iexec-commons-poco/releases/tag/v4.2.0) 2024-12-18 + +### New Features + +- Add `getUserGasPrice` method to `Web3jAbstractService`. (#104) +- Make **TEE tasks** with callback eligible to `contributeAndFinalize` flow. (#109) +- Add accessors to read on-chain deployed PoCo Smart Contracts configurations: + `callbackgas`, `contribution_deadline_ratio` and `final_deadline_ratio`. (#111) + +### Bug Fixes + +- Prefer methods with ECKEyPair parameter when signing prefixed messages. (#107) + +### Quality + +- Use `poco-chain` with `poco v5.5.0` and `voucher v1.0.0` in tests. (#106) +- Manage deal parameters in a single field and add assets owner and assets price fields in `TaskDescription`. (#108) + +### Dependency Upgrades + +- Upgrade to Gradle 8.10.2. (#105) +- Upgrade to `testcontainers` 1.20.4. (#110) + ## [[4.1.0]](https://github.com/iExecBlockchainComputing/iexec-commons-poco/releases/tag/v4.1.0) 2024-06-17 ### New Features diff --git a/build.gradle b/build.gradle index f408fce..a72f7a3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,15 @@ plugins { id 'java-library' - id 'io.freefair.lombok' version '8.6' + id 'io.freefair.lombok' version '8.10.2' id 'jacoco' - id 'org.sonarqube' version '5.0.0.4638' + id 'org.sonarqube' version '5.1.0.4882' id 'maven-publish' } group = 'com.iexec.commons' ext { - testContainersVersion = '1.19.3' + testContainersVersion = '1.20.4' } if (!project.hasProperty('gitBranch')) { diff --git a/docker-compose.yml b/docker-compose.yml index 7c4ea27..44aead5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ services: poco-chain: - image: docker-regis.iex.ec/poco-chain:native-v5.4.2-5s + image: docker-regis.iex.ec/poco-chain:1.0.0-poco-v5.5.0-voucher-v1.0.0-nethermind expose: - "8545" diff --git a/gradle.properties b/gradle.properties index c5fecb3..a381b0e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -version=4.1.0 +version=4.2.0 nexusUser nexusPassword diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e644113..a4b76b9 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23..df97d72 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4..f5feea6 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 7101f8e..9b42019 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## diff --git a/src/main/java/com/iexec/commons/poco/chain/IexecHubAbstractService.java b/src/main/java/com/iexec/commons/poco/chain/IexecHubAbstractService.java index c5d6b0b..f087431 100644 --- a/src/main/java/com/iexec/commons/poco/chain/IexecHubAbstractService.java +++ b/src/main/java/com/iexec/commons/poco/chain/IexecHubAbstractService.java @@ -26,13 +26,16 @@ import org.awaitility.core.ConditionTimeoutException; import org.web3j.crypto.Credentials; import org.web3j.ens.EnsResolutionException; +import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.RemoteCall; import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.tuples.generated.Tuple3; import org.web3j.tx.RawTransactionManager; import org.web3j.tx.gas.ContractGasProvider; import org.web3j.tx.gas.DefaultGasProvider; +import org.web3j.utils.Numeric; +import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -41,6 +44,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; +import static com.iexec.commons.poco.encoding.AccessorsEncoder.*; import static com.iexec.commons.poco.encoding.AssetDataEncoder.getAssetAddressFromReceipt; import static com.iexec.commons.poco.tee.TeeEnclaveConfiguration.buildEnclaveConfigurationFromJsonString; import static com.iexec.commons.poco.utils.BytesUtils.isNonZeroedBytes32; @@ -889,7 +893,7 @@ public long getMaxNbOfPeriodsForConsensus() { private void setMaxNbOfPeriodsForConsensus() { try { - maxNbOfPeriodsForConsensus = iexecHubContract.contribution_deadline_ratio().send().longValue(); + maxNbOfPeriodsForConsensus = getContributionDeadlineRatio().longValue(); } catch (Exception e) { log.error("Failed to get maxNbOfPeriodsForConsensus from the chain", e); maxNbOfPeriodsForConsensus = -1; @@ -951,6 +955,45 @@ public boolean isTeeTask(String chainTaskId) { return taskDescription.isTeeTask(); } + // region accessors + + /** + * Send call to callbackgas() PoCo method. + * + * @return callbackgas value + * @throws IOException if communication fails + */ + public BigInteger getCallbackGas() throws IOException { + return sendCallWithFunctionSelector(CALLBACKGAS_SELECTOR); + } + + /** + * Send call to contribution_deadline_ratio() PoCo method. + * + * @return contribution_deadline_ratio value + * @throws IOException if communication fails + */ + public BigInteger getContributionDeadlineRatio() throws IOException { + return sendCallWithFunctionSelector(CONTRIBUTION_DEADLINE_RATIO_SELECTOR); + } + + /** + * Send call to final_deadline_ratio() PoCo method. + * + * @return final_deadline_ratio value + * @throws IOException if communication fails + */ + public BigInteger getFinalDeadlineRatio() throws IOException { + return sendCallWithFunctionSelector(FINAL_DEADLINE_RATIO_SELECTOR); + } + + private BigInteger sendCallWithFunctionSelector(final String functionSelector) throws IOException { + return Numeric.toBigInt( + txManager.sendCall(iexecHubAddress, functionSelector, DefaultBlockParameterName.LATEST)); + } + + // endregion + // region Purge /** diff --git a/src/main/java/com/iexec/commons/poco/chain/SignerService.java b/src/main/java/com/iexec/commons/poco/chain/SignerService.java index bfe1fc1..560011b 100644 --- a/src/main/java/com/iexec/commons/poco/chain/SignerService.java +++ b/src/main/java/com/iexec/commons/poco/chain/SignerService.java @@ -34,7 +34,6 @@ import org.web3j.protocol.core.methods.response.Transaction; import org.web3j.protocol.exceptions.JsonRpcError; import org.web3j.tx.RawTransactionManager; -import org.web3j.utils.Numeric; import java.io.IOException; import java.math.BigInteger; @@ -108,8 +107,7 @@ public BigInteger getNonce() { * Signs messages with Ethereum prefix */ public Signature signMessageHash(String messageHash) { - String hexPrivateKey = Numeric.toHexStringWithPrefix(credentials.getEcKeyPair().getPrivateKey()); - return SignatureUtils.signMessageHashAndGetSignature(messageHash, hexPrivateKey); + return SignatureUtils.signMessageHashAndGetSignature(messageHash, credentials.getEcKeyPair()); } public String signEIP712Entity(EIP712Entity eip712Entity) { diff --git a/src/main/java/com/iexec/commons/poco/chain/Web3jAbstractService.java b/src/main/java/com/iexec/commons/poco/chain/Web3jAbstractService.java index f135cc6..f341266 100644 --- a/src/main/java/com/iexec/commons/poco/chain/Web3jAbstractService.java +++ b/src/main/java/com/iexec/commons/poco/chain/Web3jAbstractService.java @@ -265,7 +265,7 @@ public Optional getBalance(String address) { */ public Optional getNetworkGasPrice() { try { - BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice(); + final BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice(); if (gasPrice != null && gasPrice.signum() > 0) { return Optional.of(gasPrice); } @@ -276,17 +276,28 @@ public Optional getNetworkGasPrice() { } public BigInteger getUserGasPrice(float gasPriceMultiplier, long gasPriceCap) { - Optional networkGasPrice = getNetworkGasPrice(); + final Optional networkGasPrice = getNetworkGasPrice(); if (networkGasPrice.isEmpty()) { log.warn("Undefined network gas price (will use default) " + "[userGasPriceCap:{}]", gasPriceCap); return BigInteger.valueOf(gasPriceCap); } - long wishedGasPrice = (long) (networkGasPrice.get().floatValue() * gasPriceMultiplier); + final long wishedGasPrice = (long) (networkGasPrice.get().floatValue() * gasPriceMultiplier); return BigInteger.valueOf(Math.min(wishedGasPrice, gasPriceCap)); } + /** + * Returns gas price following current user parameters defined in the {@code Web3jAbstractService} instance. + *

+ * The gas price will be Min(gasPriceCap, networkGasPrice * gasPriceMultiplier) + * + * @return The gas Price as a {@code BigInteger} + */ + public BigInteger getUserGasPrice() { + return getUserGasPrice(gasPriceMultiplier, gasPriceCap); + } + private ContractGasProvider getWritingContractGasProvider() { return new ContractGasProvider() { diff --git a/src/main/java/com/iexec/commons/poco/encoding/AccessorsEncoder.java b/src/main/java/com/iexec/commons/poco/encoding/AccessorsEncoder.java new file mode 100644 index 0000000..22205de --- /dev/null +++ b/src/main/java/com/iexec/commons/poco/encoding/AccessorsEncoder.java @@ -0,0 +1,48 @@ +/* + * Copyright 2024 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 lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * Class containing Ethereum function selectors to read PoCo Smart Contracts configurations. + *

+ * Current accessors allow to read callback gas, contribution deadline ratio and final deadline ratio. + * + * @see PoCo accessors + * @see Ethereum Contract ABI Specification + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class AccessorsEncoder { + + /** + * keccak256(callbackgas()) + */ + public static final String CALLBACKGAS_SELECTOR = "0xe63ec07d"; + + /** + * keccak256(contribution_deadline_ratio()) + */ + public static final String CONTRIBUTION_DEADLINE_RATIO_SELECTOR = "0x74ed5244"; + + /** + * keccak256(final_deadline_ratio()) + */ + public static final String FINAL_DEADLINE_RATIO_SELECTOR = "0xdb8aaa26"; + +} diff --git a/src/main/java/com/iexec/commons/poco/task/TaskDescription.java b/src/main/java/com/iexec/commons/poco/task/TaskDescription.java index 5c1b296..a0028ec 100644 --- a/src/main/java/com/iexec/commons/poco/task/TaskDescription.java +++ b/src/main/java/com/iexec/commons/poco/task/TaskDescription.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 IEXEC BLOCKCHAIN TECH + * Copyright 2020-2024 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. @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.iexec.commons.poco.chain.ChainDeal; import com.iexec.commons.poco.chain.ChainTask; +import com.iexec.commons.poco.chain.DealParams; import com.iexec.commons.poco.dapp.DappType; import com.iexec.commons.poco.tee.TeeEnclaveConfiguration; import com.iexec.commons.poco.tee.TeeFramework; @@ -38,7 +39,17 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class TaskDescription { + // computed data, not available on-chain String chainTaskId; + + // assets + String appOwner; + BigInteger appPrice; + String datasetOwner; + BigInteger datasetPrice; + String workerpoolOwner; + BigInteger workerpoolPrice; + String requester; String beneficiary; String callback; @@ -46,6 +57,10 @@ public class TaskDescription { String appUri; String appAddress; TeeEnclaveConfiguration appEnclaveConfiguration; + /** + * @deprecated Use dealParams instead + */ + @Deprecated(forRemoval = true) String cmd; boolean isTeeTask; TeeFramework teeFramework; @@ -55,12 +70,34 @@ public class TaskDescription { String datasetUri; String datasetName; String datasetChecksum; + /** + * @deprecated Use dealParams instead + */ + @Deprecated(forRemoval = true) List inputFiles; + /** + * @deprecated Use dealParams instead + */ + @Deprecated(forRemoval = true) boolean isResultEncryption; + /** + * @deprecated Use dealParams instead + */ + @Deprecated(forRemoval = true) String resultStorageProvider; + /** + * @deprecated Use dealParams instead + */ + @Deprecated(forRemoval = true) String resultStorageProxy; String smsUrl; + /** + * @deprecated Use dealParams instead + */ + @Deprecated(forRemoval = true) Map secrets; + @Builder.Default + DealParams dealParams = DealParams.builder().build(); BigInteger trust; // from task int botIndex; @@ -101,16 +138,15 @@ public boolean containsCallback() { * @return true if at least one input file is present, false otherwise */ public boolean containsInputFiles() { - return inputFiles != null && !inputFiles.isEmpty(); + return (dealParams != null && dealParams.getIexecInputFiles() != null && !dealParams.getIexecInputFiles().isEmpty()) + || (inputFiles != null && !inputFiles.isEmpty()); } public String getAppCommand() { - String appArgs = appEnclaveConfiguration.getEntrypoint(); - //TODO: Add unit test - if (!StringUtils.isEmpty(cmd)) { - appArgs = appArgs + " " + cmd; - } - return appArgs; + final String args = (dealParams != null && !StringUtils.isEmpty(dealParams.getIexecArgs())) ? + dealParams.getIexecArgs() : cmd; + return StringUtils.isEmpty(args) ? appEnclaveConfiguration.getEntrypoint() : + appEnclaveConfiguration.getEntrypoint() + " " + args; } /** @@ -125,9 +161,7 @@ public String getAppCommand() { * @return {@literal true} if eligible, {@literal false} otherwise. */ public boolean isEligibleToContributeAndFinalize() { - return isTeeTask - && BigInteger.ONE.equals(trust) - && !containsCallback(); + return isTeeTask && BigInteger.ONE.equals(trust); } /** @@ -138,7 +172,7 @@ public boolean isEligibleToContributeAndFinalize() { * @param chainTask On-chain task from PoCo smart contracts * @return the created taskDescription */ - public static TaskDescription toTaskDescription(ChainDeal chainDeal, ChainTask chainTask) { + public static TaskDescription toTaskDescription(final ChainDeal chainDeal, final ChainTask chainTask) { if (chainDeal == null || chainTask == null) { return null; } @@ -155,6 +189,12 @@ public static TaskDescription toTaskDescription(ChainDeal chainDeal, ChainTask c final String tag = chainDeal.getTag(); return TaskDescription.builder() .chainTaskId(chainTask.getChainTaskId()) + .appOwner(chainDeal.getDappOwner()) + .appPrice(chainDeal.getDappPrice()) + .datasetOwner(chainDeal.getDataOwner()) + .datasetPrice(chainDeal.getDataPrice()) + .workerpoolOwner(chainDeal.getPoolOwner()) + .workerpoolPrice(chainDeal.getPoolPrice()) .requester(chainDeal .getRequester()) .beneficiary(chainDeal @@ -183,6 +223,7 @@ public static TaskDescription toTaskDescription(ChainDeal chainDeal, ChainTask c .getIexecResultStorageProxy()) .secrets(chainDeal.getParams() .getIexecSecrets()) + .dealParams(chainDeal.getParams()) .datasetAddress(datasetAddress) .datasetUri(datasetUri) .datasetName(datasetName) diff --git a/src/main/java/com/iexec/commons/poco/utils/SignatureUtils.java b/src/main/java/com/iexec/commons/poco/utils/SignatureUtils.java index f9d015e..574aad6 100644 --- a/src/main/java/com/iexec/commons/poco/utils/SignatureUtils.java +++ b/src/main/java/com/iexec/commons/poco/utils/SignatureUtils.java @@ -108,27 +108,39 @@ private static String createStringFromSignature(Sign.SignatureData sign) { return String.join("", r, Numeric.cleanHexPrefix(s), Numeric.cleanHexPrefix(v)); } - /* - * web3j signMessageHash(..) base method [built on EthereumMessageHash] - * */ + /** + * Sign a message prefixed with {@code "\x19Ethereum Signed Message:\n" + len(message)}. + * + * @param messageHash Hashed data to sign + * @param ecKeyPair Key pair to sign the message + * @return The signature data + * @see ERC-191 specification. + */ private static Sign.SignatureData signMessageHash(String messageHash, ECKeyPair ecKeyPair) { return Sign.signPrefixedMessage(stringToBytes(messageHash), ecKeyPair); } - /* - * web3j signMessageHash(..) returns Sign.SignatureData [built on EthereumMessageHash] - * */ - public static Sign.SignatureData signMessageHashAndGetSignatureData(String messageHash, String privateKey) { - ECKeyPair ecKeyPair = ECKeyPair.create(BytesUtils.hexStringToBytes32(privateKey)); - return signMessageHash(messageHash, ecKeyPair); + /** + * Sign a message prefixed with {@code "\x19Ethereum Signed Message:\n" + len(message)}. + * + * @param messageHash Hashed data to sign + * @param ecKeyPair Key pair to sign the message + * @return The signature data wrapped in a {@link Signature} instance + */ + public static Signature signMessageHashAndGetSignature(String messageHash, ECKeyPair ecKeyPair) { + return new Signature(signMessageHash(messageHash, ecKeyPair)); } - /* - * iExec signMessageHash(..) returns Signature [built on EthereumMessageHash] - * */ + /** + * Sign a message prefixed with {@code "\x19Ethereum Signed Message:\n" + len(message)}. + * + * @param messageHash Hashed data to sign + * @param privateKey Private key to use, an {@code ECKeyPair} will be created from this key + * @return The signature data wrapper in a {@link Signature} instance + */ public static Signature signMessageHashAndGetSignature(String messageHash, String privateKey) { - Sign.SignatureData signatureData = signMessageHashAndGetSignatureData(messageHash, privateKey); - return new Signature(signatureData); + final ECKeyPair ecKeyPair = ECKeyPair.create(Numeric.hexStringToByteArray(privateKey)); + return signMessageHashAndGetSignature(messageHash, ecKeyPair); } /* diff --git a/src/test/java/com/iexec/commons/poco/chain/Web3jAbstractServiceTest.java b/src/test/java/com/iexec/commons/poco/chain/Web3jAbstractServiceTest.java index 610508a..a81e633 100644 --- a/src/test/java/com/iexec/commons/poco/chain/Web3jAbstractServiceTest.java +++ b/src/test/java/com/iexec/commons/poco/chain/Web3jAbstractServiceTest.java @@ -56,9 +56,9 @@ void shouldNotCreateInstanceWhenNullBlockTime() { @Test void shouldNotCreateInstanceWhenNegativeBlockTime() { - Duration blockTime = Duration.ofSeconds(-1); + final Duration negativeBlockTime = Duration.ofSeconds(-1); assertThrows(IllegalArgumentException.class, - () -> new Web3jAbstractService(chainId, nodeAddress, blockTime, gasPriceMultiplier, gasPriceCap, isSidechain) { + () -> new Web3jAbstractService(chainId, nodeAddress, negativeBlockTime, gasPriceMultiplier, gasPriceCap, isSidechain) { }); } diff --git a/src/test/java/com/iexec/commons/poco/itest/AssetRegistriesTests.java b/src/test/java/com/iexec/commons/poco/itest/AssetRegistriesTests.java index 4c2e5b9..b312de0 100644 --- a/src/test/java/com/iexec/commons/poco/itest/AssetRegistriesTests.java +++ b/src/test/java/com/iexec/commons/poco/itest/AssetRegistriesTests.java @@ -52,7 +52,8 @@ class AssetRegistriesTests { @Container static ComposeContainer environment = new ComposeContainer(new File("docker-compose.yml")) - .withExposedService("poco-chain", 8545); + .withPull(true) + .withExposedService(SERVICE_NAME, SERVICE_PORT); @BeforeEach void init() throws CipherException, IOException { diff --git a/src/test/java/com/iexec/commons/poco/itest/ChainTests.java b/src/test/java/com/iexec/commons/poco/itest/ChainTests.java index 0e5c102..dd709d7 100644 --- a/src/test/java/com/iexec/commons/poco/itest/ChainTests.java +++ b/src/test/java/com/iexec/commons/poco/itest/ChainTests.java @@ -47,41 +47,45 @@ class ChainTests { static final String SERVICE_NAME = "poco-chain"; static final int SERVICE_PORT = 8545; + private final String badBlockchainAddress = "http://localhost:5458"; + private Credentials credentials; private IexecHubTestService iexecHubService; private Web3jTestService web3jService; + private String chainNodeAddress; @Container static ComposeContainer environment = new ComposeContainer(new File("docker-compose.yml")) - .withExposedService("poco-chain", 8545); + .withPull(true) + .withExposedService(SERVICE_NAME, SERVICE_PORT); @BeforeEach void init() throws CipherException, IOException { - credentials = WalletUtils.loadCredentials("whatever", "src/test/resources/wallet.json"); - final String chainNodeAddress = "http://" + environment.getServiceHost(SERVICE_NAME, SERVICE_PORT) + ":" + + this.credentials = WalletUtils.loadCredentials("whatever", "src/test/resources/wallet.json"); + this.chainNodeAddress = "http://" + environment.getServiceHost(SERVICE_NAME, SERVICE_PORT) + ":" + environment.getServicePort(SERVICE_NAME, SERVICE_PORT); - web3jService = new Web3jTestService(chainNodeAddress); - iexecHubService = new IexecHubTestService(credentials, web3jService); + this.web3jService = new Web3jTestService(chainNodeAddress); + this.iexecHubService = new IexecHubTestService(credentials, web3jService); } @Test void shouldGetAccount() { final ChainAccount chainAccount = iexecHubService.getChainAccount(credentials.getAddress()).orElse(null); assertThat(chainAccount).isNotNull(); - assertThat(chainAccount.getDeposit()).isEqualTo(10_000_000L); + assertThat(chainAccount.getDeposit()).isEqualTo(40_178L); assertThat(chainAccount.getLocked()).isZero(); } @Test void shouldGetBalance() { final BigInteger balance = web3jService.getBalance(credentials.getAddress()).orElse(null); - assertThat(balance).isEqualTo(new BigInteger("1000000000000000000000000000000000000000000")); + assertThat(balance).isEqualTo(new BigInteger("3188369135434504514964210500676909925639291603846501657344")); } @Test void shouldNotGetBalance() { - final Web3jTestService badWeb3jService = new Web3jTestService("http://localhost:8545"); + final Web3jTestService badWeb3jService = new Web3jTestService(badBlockchainAddress); assertThat(badWeb3jService.getBalance(credentials.getAddress())).isEmpty(); } @@ -96,10 +100,28 @@ void shouldGetBlockNumber() throws IOException { @Test void shouldNotGetBlockNumber() { - final Web3jTestService badWeb3jService = new Web3jTestService("http://localhost:8545"); + final Web3jTestService badWeb3jService = new Web3jTestService(badBlockchainAddress); assertThat(badWeb3jService.getLatestBlockNumber()).isZero(); } + @Test + void shouldGetCallbackGas() throws IOException { + final BigInteger callbackGas = iexecHubService.getCallbackGas(); + assertThat(callbackGas).isEqualTo(BigInteger.valueOf(200_000)); + } + + @Test + void shouldGetContributionDeadlineRatio() throws IOException { + final BigInteger contributionDeadlineRatio = iexecHubService.getContributionDeadlineRatio(); + assertThat(contributionDeadlineRatio).isEqualTo(BigInteger.valueOf(7)); + } + + @Test + void shouldGetFinalDeadlineRatio() throws IOException { + final BigInteger finalDeadlineRatio = iexecHubService.getFinalDeadlineRatio(); + assertThat(finalDeadlineRatio).isEqualTo(BigInteger.valueOf(10)); + } + @ParameterizedTest @MethodSource("categoryProvider") void shouldGetCategory(long id, ChainCategory expectedCategory) { @@ -150,4 +172,30 @@ void shouldValidateSmartContract() { assertThat(IexecHubSmartContractValidator.validate(iexecHubService.getHubContract())).isTrue(); } + // region gas price + @Test + void shouldGetNetworkGasPrice() { + assertThat(web3jService.getNetworkGasPrice()).isEmpty(); + } + + @Test + void shouldReturnNetworkPriceWhenBelowGasPriceCap() { + assertThat(web3jService.getUserGasPrice()).isEqualTo(22_000_000_000L); + } + + @Test + void shouldReturnUserPriceCapWhenBelowNetworkPrice() { + final float gasPriceMultiplier = 1.2f; + final long gasPriceCap = 5_000_000_000L; + final Web3jTestService newService = new Web3jTestService(chainNodeAddress, gasPriceMultiplier, gasPriceCap); + assertThat(newService.getUserGasPrice()).isEqualTo(gasPriceCap); + } + + @Test + void shouldReturnGasPriceCapOnBlockchainCommunicationError() { + final Web3jTestService badWeb3jService = new Web3jTestService(badBlockchainAddress); + assertThat(badWeb3jService.getUserGasPrice()).isEqualTo(22_000_000_000L); + } + // endregion + } diff --git a/src/test/java/com/iexec/commons/poco/itest/IexecHubTestService.java b/src/test/java/com/iexec/commons/poco/itest/IexecHubTestService.java index 9200718..a72023d 100644 --- a/src/test/java/com/iexec/commons/poco/itest/IexecHubTestService.java +++ b/src/test/java/com/iexec/commons/poco/itest/IexecHubTestService.java @@ -33,7 +33,7 @@ public class IexecHubTestService extends IexecHubAbstractService { static final BigInteger GAS_PRICE = BigInteger.valueOf(22_000_000_000L); static final BigInteger GAS_LIMIT = BigInteger.valueOf(1_000_000L); - static final String IEXEC_HUB_ADDRESS = "0xC129e7917b7c7DeDfAa5Fff1FB18d5D7050fE8ca"; + static final String IEXEC_HUB_ADDRESS = "0xc4b11f41746D3Ad8504da5B383E1aB9aa969AbC7"; private static final String ASSET_MULTI_ADDRESS = "multiAddress"; private static final String ASSET_CHECKSUM = Numeric.toHexStringNoPrefix(new byte[32]); diff --git a/src/test/java/com/iexec/commons/poco/itest/MatchOrdersTests.java b/src/test/java/com/iexec/commons/poco/itest/MatchOrdersTests.java index 57da3de..73d7cb6 100644 --- a/src/test/java/com/iexec/commons/poco/itest/MatchOrdersTests.java +++ b/src/test/java/com/iexec/commons/poco/itest/MatchOrdersTests.java @@ -61,6 +61,7 @@ class MatchOrdersTests { @Container static ComposeContainer environment = new ComposeContainer(new File("docker-compose.yml")) + .withPull(true) .withExposedService(SERVICE_NAME, SERVICE_PORT); @BeforeEach @@ -88,34 +89,34 @@ void shouldMatchOrdersWithIexecHubService() throws Exception { BytesUtils.EMPTY_HEX_STRING_32, "{}" ); - assertThat(appAddress).isEqualTo("0x564ef252a271ff68a74266e8f574ed827297caf4"); + assertThat(appAddress).isEqualTo("0x909375d8bc4e9c26afa996bad7571b88b6cefa72"); String datasetAddress = iexecHubService.createDataset( "my-dataset-1", "multiAddress", BytesUtils.EMPTY_HEX_STRING_32 ); - assertThat(datasetAddress).isEqualTo("0x8e0c3441b788fe2bafb29a729c59d9e9a9ca5483"); + assertThat(datasetAddress).isEqualTo("0x049d8b40e50282ae8b34780240ff49235b87bf59"); String workerpoolAddress = iexecHubService.createWorkerpool("my-workerpool-1"); - assertThat(workerpoolAddress).isEqualTo("0x1d9340e5c33c6d289250df3b1ce31bc3ac6b16c0"); + assertThat(workerpoolAddress).isEqualTo("0x4f1e8ec6d1fb6ce8309d816ecacde77da4f7de9c"); final AppOrder signedAppOrder = ordersService.buildSignedAppOrder(appAddress); assertThat(signedAppOrder.getSign()) - .isEqualTo("0xbcb64941a9e7162f6f3a2b9e636596eec1bee8d420556469e11641905b45c7785691c310e4cce295311ebf6439acf2a6a943fb8d5db87a1971f979a83e6ea34d1b"); + .isEqualTo("0x95bc07e7a64415d7529ded8c4730c17870d519e569857533e273f350235b4ebf17d46ffff80b37452cca1b6540db127cd991ce91c6cb7ba3d2a1bc9409d70ad51b"); final DatasetOrder signedDatasetOrder = ordersService.buildSignedDatasetOrder(datasetAddress); assertThat(signedDatasetOrder.getSign()) - .isEqualTo("0x014011344b5684761605fd34ecdcebd34b1efc249658a1f975d8c2a26a7adc5b68dcda7bd298cc578aea99877b5ae4e7b0f3cacd307cd4fdfb382d87a0fdc5e11c"); + .isEqualTo("0x08195ca0a92aea60ff6e08c7b8d089d50b650a4ac214d1233d6857cc58349a8f710fdc16121bf028e035b26774c2a341c67e556119bec42532c4e0586256f5481b"); final WorkerpoolOrder signedWorkerpoolOrder = ordersService.buildSignedWorkerpoolOrder(workerpoolAddress); assertThat(signedWorkerpoolOrder.getSign()) - .isEqualTo("0xb3b4cc51859bf106562baecf6261bfa8e168a11e06dcc82b91d0ccfbe16be14c414526c4a2e0908ef61eae6a91ec96a8d977fda4b204ac06d1b7e477abe8ea8d1b"); + .isEqualTo("0x012dd3697776b6ce1a8ea8dc8541155dff59422bc9f21c28e61009817934ec943ff274d3831432b9ee6b2206b8b0a7e669e38cf8fa9c73bd339dd6a6e1514c8b1b"); final RequestOrder signedRequestOrder = ordersService.buildSignedRequestOrder( signedAppOrder, signedDatasetOrder, signedWorkerpoolOrder); assertThat(signedRequestOrder.getSign()) - .isEqualTo("0xa6a6eea2c49c7df388d8a265926f19bf4cac049f73875f1dbbe813ce088a7e833a2552cb33a14927b350c858349d3bcedbe23286edf912ad585dc102a1249e751c"); + .isEqualTo("0x66d7c636055ff2d03e5250065cc24481ed7bfa80cc0b92f2437feb9690c9ad30077351549030ab65eaf647de8b2b9e7ac758d6b6cb53550d4771b213b5f206e11b"); BigInteger nonce = signerService.getNonce(); String matchOrdersTxData = MatchOrdersDataEncoder.encode(signedAppOrder, signedDatasetOrder, signedWorkerpoolOrder, signedRequestOrder); @@ -156,20 +157,20 @@ void shouldMatchOrdersWithSignerService() throws IOException { final AppOrder signedAppOrder = ordersService.buildSignedAppOrder(predictedAppAddress); assertThat(signedAppOrder.getSign()) - .isEqualTo("0xc200bf4ac4170d3949831d0049a184b7ee612d74a54f4d12af62cfa734330b3523c1db3a57a4f91d03932313aed3c95200e7950d52cc2f98ee0e08cb0648cbf31c"); + .isEqualTo("0x8a3d3283f11e22318ed65fc22dcf1101a5905c1fbfb2dee67981668de7130647175ca7b728f066d764006b9e95cc4f8097157de7ee6b6d917479f6598d055ff81b"); final DatasetOrder signedDatasetOrder = ordersService.buildSignedDatasetOrder(predictedDatasetAddress); assertThat(signedDatasetOrder.getSign()) - .isEqualTo("0xfa2bf8bbfde7db118970aae8781dc78210bd9433111f1332f549aa003f7f28cc6c68c038aa35b5ddfc98242a8ede07ea8fa073f2b0e7f1247b8b97fadc4837c91c"); + .isEqualTo("0x936734f59674cb89ba77616b4a00f91b9b5ec938723b4465b15bca74e3e8274a72db4ad60cc5097dbecd5f721f7d241efe5905fe6d49968dffe6a14ca33c82951b"); final WorkerpoolOrder signedWorkerpoolOrder = ordersService.buildSignedWorkerpoolOrder(predictedWorkerpoolAddress); assertThat(signedWorkerpoolOrder.getSign()) - .isEqualTo("0xf6474336a10e3ec12ff65e721fee6ba53763f937a1b54659332f61cf1770b4b26947df22ad89e8623aa985763e917e9f66670e51befc7195b16b0e8db365c13c1c"); + .isEqualTo("0x0d204f203e07e8250bb587fbcd64adf8da95accf2f3cc686e9585794f3d1aad076a996d5d2fe6ff4b4cabd8f127fc7543468d784c74420a8440d9742a72bd0ee1b"); final RequestOrder signedRequestOrder = ordersService.buildSignedRequestOrder( signedAppOrder, signedDatasetOrder, signedWorkerpoolOrder); assertThat(signedRequestOrder.getSign()) - .isEqualTo("0x17d53276b2f125953d147af94381388ee20e6be80a6c76cf5359c8a1f04c0f0b670a3bd890fdca9307a11ef52927634b65399fa3a03fc6e89f17d5e3c383b4c31c"); + .isEqualTo("0xf6117dcc9a2feac58fcf3bb9b4cfebac9cd2f538c6611dce8ff2c3a7ea1d5c464c4e46e8622f5faeb2ec88dc6e5231fb97163494acbccf54bf8650477b11c4781b"); nonce = nonce.add(BigInteger.ONE); final String matchOrdersTxData = MatchOrdersDataEncoder.encode(signedAppOrder, signedDatasetOrder, signedWorkerpoolOrder, signedRequestOrder); diff --git a/src/test/java/com/iexec/commons/poco/itest/Web3jTestService.java b/src/test/java/com/iexec/commons/poco/itest/Web3jTestService.java index 82ba456..f5f0b66 100644 --- a/src/test/java/com/iexec/commons/poco/itest/Web3jTestService.java +++ b/src/test/java/com/iexec/commons/poco/itest/Web3jTestService.java @@ -29,10 +29,14 @@ @Slf4j public class Web3jTestService extends Web3jAbstractService { - static long BLOCK_TIME = 5; + static final long BLOCK_TIME = 5; public Web3jTestService(String chainNodeAddress) { - super(65535, chainNodeAddress, Duration.ofSeconds(BLOCK_TIME), 1.0f, 22_000_000_000L, true); + this(chainNodeAddress, 1.0f, 22_000_000_000L); + } + + public Web3jTestService(String chainNodeAddress, float gasPriceMultiplier, long gasPriceCap) { + super(65535, chainNodeAddress, Duration.ofSeconds(BLOCK_TIME), gasPriceMultiplier, gasPriceCap, true); } public boolean areTxMined(String... txHashes) { diff --git a/src/test/java/com/iexec/commons/poco/task/TaskDescriptionTests.java b/src/test/java/com/iexec/commons/poco/task/TaskDescriptionTests.java index 9b60845..f9c0e34 100644 --- a/src/test/java/com/iexec/commons/poco/task/TaskDescriptionTests.java +++ b/src/test/java/com/iexec/commons/poco/task/TaskDescriptionTests.java @@ -1,18 +1,15 @@ /* - * Copyright 2020-2023 IEXEC BLOCKCHAIN TECH + * Copyright 2020-2024 IEXEC BLOCKCHAIN TECH * - * Licensed under the Apache License, -Version 2.0 (the "License"); + * 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 + * 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. + * 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. */ @@ -25,54 +22,132 @@ import com.iexec.commons.poco.tee.TeeFramework; import com.iexec.commons.poco.tee.TeeUtils; import com.iexec.commons.poco.utils.BytesUtils; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; +import java.util.stream.Stream; + +import static com.iexec.commons.poco.utils.BytesUtils.EMPTY_ADDRESS; +import static org.junit.jupiter.api.Assertions.*; class TaskDescriptionTests { - public static final String CHAIN_TASK_ID = "chainTaskId"; - public static final String REQUESTER = "requester"; - public static final String BENEFICIARY = "beneficiary"; - public static final String CALLBACK = "callback"; - public static final DappType APP_TYPE = DappType.DOCKER; - public static final String APP_URI = "https://uri"; - public static final String APP_ADDRESS = "appAddress"; - public static final TeeEnclaveConfiguration enclaveConfig = TeeEnclaveConfiguration.builder().build(); - public static final String CMD = "cmd"; - public static final int MAX_EXECUTION_TIME = 1; - public static final boolean IS_TEE_TASK = true; - public static final TeeFramework TEE_FRAMEWORK = TeeFramework.SCONE; - public static final int BOT_SIZE = 1; - public static final int BOT_FIRST = 2; - public static final int TASK_IDX = 3; - public static final String DATASET_ADDRESS = "datasetAddress"; - public static final String DATASET_URI = "https://datasetUri"; - public static final String DATASET_NAME = "datasetName"; - public static final String DATASET_CHECKSUM = "datasetChecksum"; - public static final List INPUT_FILES = Collections.singletonList("inputFiles"); - public static final boolean IS_CALLBACK_REQUESTED = true; - public static final boolean IS_RESULT_ENCRYPTION = true; - public static final String RESULT_STORAGE_PROVIDER = "resultStorageProvider"; - public static final String RESULT_STORAGE_PROXY = "resultStorageProxy"; - public static final BigInteger TRUST = BigInteger.ONE; + private static final String APP_OWNER = "0x1"; + private static final BigInteger APP_PRICE = BigInteger.ZERO; + private static final String DATA_OWNER = "0x2"; + private static final BigInteger DATA_PRICE = BigInteger.ONE; + private static final String WORKERPOOL_OWNER = "0x3"; + private static final BigInteger WORKERPOOL_PRICE = BigInteger.TEN; + + private static final String CHAIN_TASK_ID = "chainTaskId"; + private static final String REQUESTER = "requester"; + private static final String BENEFICIARY = "beneficiary"; + private static final String CALLBACK = "callback"; + private static final DappType APP_TYPE = DappType.DOCKER; + private static final String APP_URI = "https://uri"; + private static final String APP_ADDRESS = "appAddress"; + private static final String ENTRYPOINT = "entrypoint"; + private static final String CMD = "cmd"; + private static final int MAX_EXECUTION_TIME = 1; + private static final boolean IS_TEE_TASK = true; + private static final TeeFramework TEE_FRAMEWORK = TeeFramework.SCONE; + private static final int BOT_SIZE = 1; + private static final int BOT_FIRST = 2; + private static final int TASK_IDX = 3; + private static final String DATASET_ADDRESS = "datasetAddress"; + private static final String DATASET_URI = "https://datasetUri"; + private static final String DATASET_NAME = "datasetName"; + private static final String DATASET_CHECKSUM = "datasetChecksum"; + private static final List INPUT_FILES = Collections.singletonList("inputFiles"); + private static final boolean IS_RESULT_ENCRYPTION = true; + private static final String RESULT_STORAGE_PROVIDER = "resultStorageProvider"; + private static final String RESULT_STORAGE_PROXY = "resultStorageProxy"; + private static final BigInteger TRUST = BigInteger.ONE; @Test - void shouldBuildAndGetTaskDescription() { - TaskDescription task = TaskDescription.builder() + void toTaskDescriptionWithNullDeal() { + assertNull(TaskDescription.toTaskDescription(null, null)); + assertNull(TaskDescription.toTaskDescription(ChainDeal.builder().build(), null)); + assertNull(TaskDescription.toTaskDescription(null, ChainTask.builder().build())); + } + + @Test + void toTaskDescription() { + final ChainCategory chainCategory = ChainCategory.builder() + .maxExecutionTime(MAX_EXECUTION_TIME) + .build(); + final TeeEnclaveConfiguration enclaveConfiguration = TeeEnclaveConfiguration.builder() + .entrypoint(ENTRYPOINT) + .build(); + final ChainApp chainApp = ChainApp.builder() + .chainAppId(APP_ADDRESS) + .type(APP_TYPE.toString()) + .uri(BytesUtils.bytesToString(APP_URI.getBytes(StandardCharsets.UTF_8))) + .enclaveConfiguration(enclaveConfiguration) + .build(); + final ChainDataset chainDataset = ChainDataset.builder() + .chainDatasetId(DATASET_ADDRESS) + .name(DATASET_NAME) + .uri(BytesUtils.bytesToString(DATASET_URI.getBytes(StandardCharsets.UTF_8))) + .checksum(DATASET_CHECKSUM) + .build(); + final DealParams dealParams = DealParams.builder() + .iexecArgs(CMD) + .iexecInputFiles(INPUT_FILES) + .iexecResultStorageProvider(RESULT_STORAGE_PROVIDER) + .iexecResultStorageProxy(RESULT_STORAGE_PROXY) + .iexecResultEncryption(IS_RESULT_ENCRYPTION) + .build(); + final ChainDeal chainDeal = ChainDeal.builder() + .dappOwner(APP_OWNER) + .dappPrice(APP_PRICE) + .dataOwner(DATA_OWNER) + .dataPrice(DATA_PRICE) + .poolOwner(WORKERPOOL_OWNER) + .poolPrice(WORKERPOOL_PRICE) + .requester(REQUESTER) + .beneficiary(BENEFICIARY) + .callback(CALLBACK) + .chainApp(chainApp) + .params(dealParams) + .chainDataset(chainDataset) + .tag(TeeUtils.TEE_SCONE_ONLY_TAG) // any supported TEE tag + .chainCategory(chainCategory) + .botFirst(BigInteger.valueOf(BOT_FIRST)) + .botSize(BigInteger.valueOf(BOT_SIZE)) + .trust(TRUST) + .build(); + final ChainTask chainTask = ChainTask.builder() + .dealid(chainDeal.getChainDealId()) + .chainTaskId(CHAIN_TASK_ID) + .idx(TASK_IDX) + .build(); + + final TaskDescription task = TaskDescription.toTaskDescription(chainDeal, chainTask); + + final TaskDescription expectedTaskDescription = TaskDescription.builder() .chainTaskId(CHAIN_TASK_ID) + .appOwner(APP_OWNER) + .appPrice(APP_PRICE) + .datasetOwner(DATA_OWNER) + .datasetPrice(DATA_PRICE) + .workerpoolOwner(WORKERPOOL_OWNER) + .workerpoolPrice(WORKERPOOL_PRICE) .requester(REQUESTER) .beneficiary(BENEFICIARY) .callback(CALLBACK) .appType(APP_TYPE) .appUri(APP_URI) .appAddress(APP_ADDRESS) - .appEnclaveConfiguration(enclaveConfig) - .cmd(CMD) + .appEnclaveConfiguration(enclaveConfiguration) .maxExecutionTime(MAX_EXECUTION_TIME) .isTeeTask(IS_TEE_TASK) .teeFramework(TEE_FRAMEWORK) @@ -83,160 +158,24 @@ void shouldBuildAndGetTaskDescription() { .datasetUri(DATASET_URI) .datasetName(DATASET_NAME) .datasetChecksum(DATASET_CHECKSUM) + .cmd(CMD) .inputFiles(INPUT_FILES) .isResultEncryption(IS_RESULT_ENCRYPTION) .resultStorageProvider(RESULT_STORAGE_PROVIDER) .resultStorageProxy(RESULT_STORAGE_PROXY) - .trust(TRUST) - .build(); - Assertions.assertEquals(CHAIN_TASK_ID, - task.getChainTaskId()); - Assertions.assertEquals(REQUESTER, - task.getRequester()); - Assertions.assertEquals(BENEFICIARY, - task.getBeneficiary()); - Assertions.assertEquals(CALLBACK, - task.getCallback()); - Assertions.assertEquals(APP_TYPE, - task.getAppType()); - Assertions.assertEquals(APP_URI, - task.getAppUri()); - Assertions.assertEquals(APP_ADDRESS, - task.getAppAddress()); - Assertions.assertEquals(enclaveConfig, - task.getAppEnclaveConfiguration()); - Assertions.assertEquals(CMD, - task.getCmd()); - Assertions.assertEquals(MAX_EXECUTION_TIME, - task.getMaxExecutionTime()); - Assertions.assertEquals(IS_TEE_TASK, - task.isTeeTask()); - Assertions.assertEquals(TEE_FRAMEWORK, - task.getTeeFramework()); - Assertions.assertEquals(TASK_IDX, - task.getBotIndex()); - Assertions.assertEquals(BOT_SIZE, - task.getBotSize()); - Assertions.assertEquals(BOT_FIRST, - task.getBotFirstIndex()); - Assertions.assertEquals(DATASET_URI, - task.getDatasetUri()); - Assertions.assertEquals(DATASET_NAME, - task.getDatasetName()); - Assertions.assertEquals(DATASET_CHECKSUM, - task.getDatasetChecksum()); - Assertions.assertEquals(INPUT_FILES, - task.getInputFiles()); - Assertions.assertEquals(IS_CALLBACK_REQUESTED, - task.containsCallback()); - Assertions.assertEquals(IS_RESULT_ENCRYPTION, - task.isResultEncryption()); - Assertions.assertEquals(RESULT_STORAGE_PROVIDER, - task.getResultStorageProvider()); - Assertions.assertEquals(RESULT_STORAGE_PROXY, - task.getResultStorageProxy()); - Assertions.assertEquals(TRUST, - task.getTrust()); - Assertions.assertTrue(task.containsDataset()); - } - - @Test - void toTaskDescriptionWithNullDeal() { - Assertions.assertNull(TaskDescription.toTaskDescription(null, null)); - } - - @Test - void toTaskDescription() { - ChainDeal chainDeal = ChainDeal.builder() - .requester(REQUESTER) - .beneficiary(BENEFICIARY) - .callback(CALLBACK) - .chainApp(ChainApp.builder() - .chainAppId(APP_ADDRESS) - .type(APP_TYPE.toString()) - .uri(BytesUtils.bytesToString(APP_URI.getBytes(StandardCharsets.UTF_8))) - .build()) - .params(DealParams.builder() - .iexecArgs(CMD) - .iexecInputFiles(INPUT_FILES) - .iexecResultStorageProvider(RESULT_STORAGE_PROVIDER) - .iexecResultStorageProxy(RESULT_STORAGE_PROXY) - .iexecResultEncryption(IS_RESULT_ENCRYPTION) - .build()) - .chainDataset(ChainDataset.builder() - .chainDatasetId(DATASET_ADDRESS) - .name(DATASET_NAME) - .uri(BytesUtils.bytesToString(DATASET_URI.getBytes(StandardCharsets.UTF_8))) - .checksum(DATASET_CHECKSUM).build()) - .tag(TeeUtils.TEE_SCONE_ONLY_TAG) // any supported TEE tag - .chainCategory(ChainCategory.builder() - .maxExecutionTime(MAX_EXECUTION_TIME) - .build()) - .botFirst(BigInteger.valueOf(BOT_FIRST)) - .botSize(BigInteger.valueOf(BOT_SIZE)) + .secrets(Collections.emptyMap()) + .dealParams(dealParams) .trust(TRUST) .build(); - ChainTask chainTask = ChainTask.builder() - .dealid(chainDeal.getChainDealId()) - .chainTaskId(CHAIN_TASK_ID) - .idx(TASK_IDX) - .build(); - - TaskDescription task = - TaskDescription.toTaskDescription(chainDeal, chainTask); - - Assertions.assertEquals(CHAIN_TASK_ID, - task.getChainTaskId()); - Assertions.assertEquals(REQUESTER, - task.getRequester()); - Assertions.assertEquals(BENEFICIARY, - task.getBeneficiary()); - Assertions.assertEquals(CALLBACK, - task.getCallback()); - Assertions.assertEquals(APP_TYPE, - task.getAppType()); - Assertions.assertEquals(APP_URI, - task.getAppUri()); - Assertions.assertEquals(APP_ADDRESS, - task.getAppAddress()); - Assertions.assertEquals(CMD, - task.getCmd()); - Assertions.assertEquals(MAX_EXECUTION_TIME, - task.getMaxExecutionTime()); - Assertions.assertEquals(IS_TEE_TASK, - task.isTeeTask()); - Assertions.assertEquals(TEE_FRAMEWORK, - task.getTeeFramework()); - Assertions.assertEquals(TASK_IDX, - task.getBotIndex()); - Assertions.assertEquals(BOT_SIZE, - task.getBotSize()); - Assertions.assertEquals(BOT_FIRST, - task.getBotFirstIndex()); - Assertions.assertEquals(DATASET_URI, - task.getDatasetUri()); - Assertions.assertEquals(DATASET_NAME, - task.getDatasetName()); - Assertions.assertEquals(DATASET_CHECKSUM, - task.getDatasetChecksum()); - Assertions.assertEquals(INPUT_FILES, - task.getInputFiles()); - Assertions.assertEquals(IS_CALLBACK_REQUESTED, - task.containsCallback()); - Assertions.assertEquals(IS_RESULT_ENCRYPTION, - task.isResultEncryption()); - Assertions.assertEquals(RESULT_STORAGE_PROVIDER, - task.getResultStorageProvider()); - Assertions.assertEquals(RESULT_STORAGE_PROXY, - task.getResultStorageProxy()); - Assertions.assertEquals(TRUST, - task.getTrust()); + assertEquals(expectedTaskDescription, task); + assertTrue(task.containsCallback()); } + // region containsDataset @Test void shouldContainDataset() { - Assertions.assertTrue(TaskDescription.builder() + assertTrue(TaskDescription.builder() .datasetAddress(DATASET_ADDRESS) .datasetUri(DATASET_URI) .datasetName(DATASET_NAME) @@ -244,7 +183,7 @@ void shouldContainDataset() { .build() .containsDataset()); - Assertions.assertTrue(TaskDescription.builder() + assertTrue(TaskDescription.builder() .datasetAddress(DATASET_ADDRESS) .datasetUri(DATASET_URI) // .datasetName(DATASET_NAME) @@ -255,7 +194,7 @@ void shouldContainDataset() { @Test void shouldNotContainDataset() { - Assertions.assertFalse(TaskDescription.builder() + assertFalse(TaskDescription.builder() // .datasetAddress(DATASET_ADDRESS) .datasetUri(DATASET_URI) .datasetName(DATASET_NAME) @@ -263,7 +202,15 @@ void shouldNotContainDataset() { .build() .containsDataset()); - Assertions.assertFalse(TaskDescription.builder() + assertFalse(TaskDescription.builder() + .datasetAddress(EMPTY_ADDRESS) + .datasetUri(DATASET_URI) + .datasetName(DATASET_NAME) + .datasetChecksum(DATASET_CHECKSUM) + .build() + .containsDataset()); + + assertFalse(TaskDescription.builder() .datasetAddress(DATASET_ADDRESS) // .datasetUri(DATASET_URI) .datasetName(DATASET_NAME) @@ -271,7 +218,7 @@ void shouldNotContainDataset() { .build() .containsDataset()); - Assertions.assertFalse(TaskDescription.builder() + assertFalse(TaskDescription.builder() .datasetAddress(DATASET_ADDRESS) .datasetUri(DATASET_URI) .datasetName(DATASET_NAME) @@ -279,10 +226,12 @@ void shouldNotContainDataset() { .build() .containsDataset()); } + // endregion + // region containsCallback @Test void shouldContainCallback() { - Assertions.assertTrue(TaskDescription.builder() + assertTrue(TaskDescription.builder() .callback(CALLBACK) .build() .containsCallback()); @@ -290,19 +239,21 @@ void shouldContainCallback() { @Test void shouldNotContainCallback() { - Assertions.assertFalse(TaskDescription.builder() - .callback(BytesUtils.EMPTY_ADDRESS) + assertFalse(TaskDescription.builder() + .callback(EMPTY_ADDRESS) .build() .containsCallback()); - Assertions.assertFalse(TaskDescription.builder() + assertFalse(TaskDescription.builder() // .callback(CALLBACK) .build() .containsCallback()); } + // endregion + // region containsInputFiles @Test void shouldContainInputFiles() { - Assertions.assertTrue(TaskDescription.builder() + assertTrue(TaskDescription.builder() .chainTaskId(CHAIN_TASK_ID) .inputFiles(List.of("http://file1", "http://file2")) .build() @@ -310,24 +261,97 @@ void shouldContainInputFiles() { } @Test - void shouldNotContainInputFiles() { - Assertions.assertFalse(TaskDescription.builder() + void shouldContainsInputFilesFromDealParams() { + assertTrue(TaskDescription.builder() .chainTaskId(CHAIN_TASK_ID) - // .inputFiles(List.of("http://file1", "http://file2")) + .dealParams(DealParams.builder().iexecInputFiles(List.of("http://file1", "http://file2")).build()) .build() .containsInputFiles()); } - // region isEligibleToContributeAndFinalize + @ParameterizedTest + @NullSource + @MethodSource("provideTaskDescriptionWithoutInputFiles") + void shouldNotContainInputFilesWhenNullDealParams(final DealParams dealParams) { + assertFalse(TaskDescription.builder() + .chainTaskId(CHAIN_TASK_ID) + .dealParams(dealParams) + .build() + .containsInputFiles()); + } + + private static Stream provideTaskDescriptionWithoutInputFiles() { + return Stream.of( + Arguments.of(DealParams.builder().build()), + Arguments.of(DealParams.builder().iexecInputFiles(null).build()) + ); + } + @Test - void shouldBeEligibleToContributeAndFinalize() { + void shouldNotContainInputFilesWhenEmptyInputFiles() { + assertFalse(TaskDescription.builder() + .chainTaskId(CHAIN_TASK_ID) + .inputFiles(List.of()) + .build() + .containsInputFiles()); + } + + @Test + void shouldNotContainInputFilesWhenNullInputFiles() { + assertFalse(TaskDescription.builder() + .chainTaskId(CHAIN_TASK_ID) + .inputFiles(null) + .build() + .containsInputFiles()); + } + // endregion + + // region getAppCommand + @Test + void shouldGenerateAppCommandWithEntrypointWhenEmptyDealParams() { + final TaskDescription taskDescription = TaskDescription.builder() + .appEnclaveConfiguration(TeeEnclaveConfiguration.builder().entrypoint(ENTRYPOINT).build()) + .dealParams(DealParams.builder().build()) + .build(); + assertEquals(ENTRYPOINT, taskDescription.getAppCommand()); + } + + @Test + void shouldGenerateAppCommandWithEntrypointWhenNullDealParams() { + final TaskDescription taskDescription = TaskDescription.builder() + .appEnclaveConfiguration(TeeEnclaveConfiguration.builder().entrypoint(ENTRYPOINT).build()) + .dealParams(null) + .build(); + assertEquals(ENTRYPOINT, taskDescription.getAppCommand()); + } + + @Test + void shouldGenerateAppCommandWithEntrypointAndArgs() { + assertEquals(ENTRYPOINT + " " + CMD, TaskDescription.builder() + .appEnclaveConfiguration(TeeEnclaveConfiguration.builder().entrypoint(ENTRYPOINT).build()) + .dealParams(DealParams.builder().iexecArgs(CMD).build()) + .build() + .getAppCommand()); + assertEquals(ENTRYPOINT + " " + CMD, TaskDescription.builder() + .appEnclaveConfiguration(TeeEnclaveConfiguration.builder().entrypoint(ENTRYPOINT).build()) + .dealParams(DealParams.builder().build()) + .cmd(CMD) + .build() + .getAppCommand()); + } + // endregion + + // region isEligibleToContributeAndFinalize + @ParameterizedTest + @ValueSource(strings = {"", CALLBACK}) + void shouldBeEligibleToContributeAndFinalize(final String callback) { final TaskDescription taskDescription = TaskDescription.builder() .isTeeTask(true) .trust(BigInteger.ONE) - .callback("") + .callback(callback) .build(); - Assertions.assertTrue(taskDescription.isEligibleToContributeAndFinalize()); + assertTrue(taskDescription.isEligibleToContributeAndFinalize()); } @Test @@ -338,7 +362,7 @@ void shouldNotBeEligibleToContributeAndFinalizeSinceNotTee() { .callback("") .build(); - Assertions.assertFalse(taskDescription.isEligibleToContributeAndFinalize()); + assertFalse(taskDescription.isEligibleToContributeAndFinalize()); } @Test @@ -349,18 +373,7 @@ void shouldNotBeEligibleToContributeAndFinalizeSinceWrongTrust() { .callback("") .build(); - Assertions.assertFalse(taskDescription.isEligibleToContributeAndFinalize()); - } - - @Test - void shouldNotBeEligibleToContributeAndFinalizeSinceCallback() { - final TaskDescription taskDescription = TaskDescription.builder() - .isTeeTask(true) - .trust(BigInteger.ONE) - .callback(CALLBACK) - .build(); - - Assertions.assertFalse(taskDescription.isEligibleToContributeAndFinalize()); + assertFalse(taskDescription.isEligibleToContributeAndFinalize()); } // endregion }