From 3132b7029bf539d490fd1cfe43fbae70e274dba8 Mon Sep 17 00:00:00 2001 From: Jeremy Bernard Date: Wed, 29 Apr 2026 13:32:44 +0200 Subject: [PATCH] fix: remove BrokerService only used for IntegrationTests --- .../iexec/blockchain/IntegrationTests.java | 58 +- .../blockchain/broker/BrokerService.java | 222 ------- .../blockchain/broker/BrokerServiceTests.java | 544 ------------------ 3 files changed, 32 insertions(+), 792 deletions(-) delete mode 100644 src/main/java/com/iexec/blockchain/broker/BrokerService.java delete mode 100644 src/test/java/com/iexec/blockchain/broker/BrokerServiceTests.java diff --git a/src/itest/java/com/iexec/blockchain/IntegrationTests.java b/src/itest/java/com/iexec/blockchain/IntegrationTests.java index e43b096..12fc687 100644 --- a/src/itest/java/com/iexec/blockchain/IntegrationTests.java +++ b/src/itest/java/com/iexec/blockchain/IntegrationTests.java @@ -18,14 +18,15 @@ import com.iexec.blockchain.api.BlockchainAdapterApiClient; import com.iexec.blockchain.api.BlockchainAdapterApiClientBuilder; -import com.iexec.blockchain.broker.BrokerService; import com.iexec.blockchain.chain.ChainConfig; import com.iexec.blockchain.chain.IexecHubService; import com.iexec.blockchain.chain.Web3jService; +import com.iexec.blockchain.command.generic.SubmittedTx; import com.iexec.common.chain.adapter.args.TaskFinalizeArgs; -import com.iexec.common.sdk.broker.BrokerOrder; import com.iexec.commons.poco.chain.*; import com.iexec.commons.poco.eip712.EIP712Domain; +import com.iexec.commons.poco.encoding.LogTopic; +import com.iexec.commons.poco.encoding.MatchOrdersDataEncoder; import com.iexec.commons.poco.order.AppOrder; import com.iexec.commons.poco.order.DatasetOrder; import com.iexec.commons.poco.order.RequestOrder; @@ -44,7 +45,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.ComposeContainer; @@ -52,6 +52,8 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.web3j.crypto.Hash; +import org.web3j.protocol.core.methods.response.Log; +import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.protocol.exceptions.TransactionException; import org.web3j.utils.Numeric; @@ -76,7 +78,6 @@ @Slf4j @Testcontainers -@ActiveProfiles("itest") @SpringBootTest(properties = {"chain.max-allowed-tx-per-block=2"}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class IntegrationTests { @@ -107,8 +108,8 @@ class IntegrationTests { @DynamicPropertySource static void registerProperties(DynamicPropertyRegistry registry) { registry.add("chain.id", () -> "65535"); - registry.add("chain.hubAddress", () -> IEXEC_HUB_ADDRESS); - registry.add("chain.nodeAddress", () -> getServiceUrl( + registry.add("chain.hub-address", () -> IEXEC_HUB_ADDRESS); + registry.add("chain.node-address", () -> getServiceUrl( environment.getServiceHost(CHAIN_SVC_NAME, CHAIN_SVC_PORT), environment.getServicePort(CHAIN_SVC_NAME, CHAIN_SVC_PORT))); registry.add("spring.data.mongodb.host", () -> environment.getServiceHost(MONGO_SVC_NAME, MONGO_SVC_PORT)); @@ -121,7 +122,6 @@ static void registerProperties(DynamicPropertyRegistry registry) { private final IexecHubService iexecHubService; private final Web3jService web3jService; private final SignerService signerService; - private final BrokerService brokerService; private final EIP712Domain domain; private BlockchainAdapterApiClient appClient; @@ -133,12 +133,10 @@ static void registerProperties(DynamicPropertyRegistry registry) { IntegrationTests(final IexecHubService iexecHubService, final Web3jService web3jService, final SignerService signerService, - final BrokerService brokerService, final ChainConfig chainConfig) throws IOException { this.iexecHubService = iexecHubService; this.web3jService = web3jService; this.signerService = signerService; - this.brokerService = brokerService; this.domain = new EIP712Domain(chainConfig.getId(), chainConfig.getHubAddress()); this.appRegistryAddress = toEthereumAddress(signerService.sendCall(IEXEC_HUB_ADDRESS, APP_REGISTRY_SELECTOR)); this.datasetRegistryAddress = toEthereumAddress(signerService.sendCall(IEXEC_HUB_ADDRESS, DATASET_REGISTRY_SELECTOR)); @@ -193,7 +191,7 @@ void shouldBeFinalized() throws IOException, TransactionException { } @Test - void shouldBurstTransactionsWithAverageOfOneTxPerBlock() throws IOException { + void shouldBurstTransactionsWithAverageOfOneTxPerBlock() throws IOException, TransactionException { int taskVolume = 10;//small volume ensures reasonable execution time on CI/CD final String dealId = triggerDeal(taskVolume); final List> txCompletionWatchers = new ArrayList<>(); @@ -223,7 +221,7 @@ void shouldBurstTransactionsWithAverageOfOneTxPerBlock() throws IOException { txCompletionWatchers.forEach(CompletableFuture::join); } - private String triggerDeal(int taskVolume) throws IOException { + private String triggerDeal(int taskVolume) throws IOException, TransactionException { BigInteger nonce = signerService.getNonce(); final String appTxData = encodeApp( signerService.getAddress(), @@ -271,14 +269,30 @@ private String triggerDeal(int taskVolume) throws IOException { .iexecResultStorageProvider("ipfs") .iexecResultStorageProxy("https://v6.result.goerli.iex.ec") .build()); - final BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(signedAppOrder) - .workerpoolOrder(signedWorkerpoolOrder) - .requestOrder(signedRequestOrder) - .datasetOrder(signedDatasetOrder) - .build(); - final String dealId = brokerService.matchOrders(brokerOrder); + nonce = nonce.add(BigInteger.ONE); + final String matchOrdersTxData = MatchOrdersDataEncoder.encode( + signedAppOrder, signedDatasetOrder, signedWorkerpoolOrder, signedRequestOrder); + final BigInteger gasLimit = signerService.estimateGas(IEXEC_HUB_ADDRESS, matchOrdersTxData); + final String matchOrdersTxHash = signerService.signAndSendTransaction( + nonce, BigInteger.ZERO, gasLimit, IEXEC_HUB_ADDRESS, matchOrdersTxData); + final TransactionReceipt receipt = iexecHubService.waitForTxMined( + new SubmittedTx(nonce, gasLimit, matchOrdersTxData, matchOrdersTxHash)); + log.info("block {}, hash {}, status {}", receipt.getBlockNumber(), receipt.getTransactionHash(), receipt.getStatus()); + log.info("logs count {}", receipt.getLogs().size()); + + final String workerpoolAddress = Numeric.toHexStringWithPrefixZeroPadded( + Numeric.toBigInt(signedWorkerpoolOrder.getWorkerpool()), 64); + final List expectedTopics = List.of(LogTopic.SCHEDULER_NOTICE_EVENT, workerpoolAddress); + List events = receipt.getLogs().stream() + .filter(log -> expectedTopics.equals(log.getTopics())) + .map(Log::getData) + .toList(); + log.info("logs {}", events); + if (events.size() != 1) { + throw new IllegalStateException("A single deal should have been created, not " + events.size()); + } + final String dealId = events.get(0); assertThat(dealId).isNotEmpty(); log.info("Created deal: {}", dealId); // no need to wait since broker is synchronous, just checking deal existence @@ -349,13 +363,6 @@ private RequestOrder buildRequestOrder( DatasetOrder datasetOrder, String requesterAddress, DealParams dealParams) { - boolean isCompatibleVolume = - appOrder.getVolume().equals(workerpoolOrder.getVolume()) - && appOrder.getVolume().equals(datasetOrder.getVolume()); - if (!isCompatibleVolume) { - log.info("Volumes are not compatible"); - return null; - } final RequestOrder requestOrder = RequestOrder.builder() .app(appOrder.getApp()) .appmaxprice(appOrder.getAppprice()) @@ -370,7 +377,6 @@ private RequestOrder buildRequestOrder( .beneficiary(BytesUtils.EMPTY_ADDRESS) .requester(requesterAddress) .callback(BytesUtils.EMPTY_ADDRESS) - //.params("{\"iexec_result_storage_provider\":\"ipfs\",\"iexec_result_storage_proxy\":\"https://v6.result.goerli.iex.ec\",\"iexec_args\":\"abc\"}") .params(dealParams.toJsonString()) .salt(Hash.sha3String(RandomStringUtils.randomAlphanumeric(20))) .build(); diff --git a/src/main/java/com/iexec/blockchain/broker/BrokerService.java b/src/main/java/com/iexec/blockchain/broker/BrokerService.java deleted file mode 100644 index 2c40ca9..0000000 --- a/src/main/java/com/iexec/blockchain/broker/BrokerService.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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. - * 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.blockchain.broker; - -import com.iexec.blockchain.chain.ChainConfig; -import com.iexec.blockchain.chain.IexecHubService; -import com.iexec.blockchain.command.generic.SubmittedTx; -import com.iexec.common.sdk.broker.BrokerOrder; -import com.iexec.commons.poco.chain.ChainAccount; -import com.iexec.commons.poco.chain.SignerService; -import com.iexec.commons.poco.encoding.MatchOrdersDataEncoder; -import com.iexec.commons.poco.order.AppOrder; -import com.iexec.commons.poco.order.DatasetOrder; -import com.iexec.commons.poco.order.RequestOrder; -import com.iexec.commons.poco.order.WorkerpoolOrder; -import com.iexec.commons.poco.utils.BytesUtils; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; -import org.web3j.crypto.Hash; -import org.web3j.protocol.core.methods.response.Log; -import org.web3j.protocol.core.methods.response.TransactionReceipt; -import org.web3j.utils.Numeric; - -import java.math.BigInteger; -import java.text.MessageFormat; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -@Slf4j -@Service -@Profile("itest") -public class BrokerService { - - static final String SCHEDULER_NOTICE = Hash.sha3String("SchedulerNotice(address,bytes32)"); - - private final ChainConfig chainConfig; - private final IexecHubService iexecHubService; - private final SignerService signerService; - - public BrokerService(final ChainConfig chainConfig, - final IexecHubService iexecHubService, - final SignerService signerService) { - this.chainConfig = chainConfig; - this.iexecHubService = iexecHubService; - this.signerService = signerService; - } - - void checkBrokerOrder(BrokerOrder brokerOrder) { - Objects.requireNonNull(brokerOrder, "Broker order cannot be null"); - AppOrder appOrder = brokerOrder.getAppOrder(); - DatasetOrder datasetOrder = brokerOrder.getDatasetOrder(); - RequestOrder requestOrder = brokerOrder.getRequestOrder(); - WorkerpoolOrder workerpoolOrder = brokerOrder.getWorkerpoolOrder(); - Objects.requireNonNull(appOrder, "App order cannot be null"); - Objects.requireNonNull(workerpoolOrder, "Workerpool order cannot be null"); - - checkRequestOrder(requestOrder); - checkAssetOrder(requestOrder.getApp(), appOrder.getApp(), appOrder.getAppprice(), "App"); - checkAssetOrder(requestOrder.getWorkerpool(), workerpoolOrder.getWorkerpool(), workerpoolOrder.getWorkerpoolprice(), "Workerpool"); - if (withDataset(requestOrder.getDataset())) { - Objects.requireNonNull(datasetOrder, "Dataset order cannot be null"); - checkAssetOrder(requestOrder.getDataset(), datasetOrder.getDataset(), datasetOrder.getDatasetprice(), "Dataset"); - } - } - - void checkRequestOrder(RequestOrder requestOrder) { - Objects.requireNonNull(requestOrder, "Request order cannot be null"); - Objects.requireNonNull(requestOrder.getAppmaxprice(), "Requester application max price cannot be null"); - Objects.requireNonNull(requestOrder.getWorkerpoolmaxprice(), "Requester workerpool max price cannot be null"); - if (withDataset(requestOrder.getDataset())) { - Objects.requireNonNull(requestOrder.getDatasetmaxprice(), "Requester dataset max price cannot be null"); - } - } - - void checkAssetOrder(String requestOrderAddress, String assetAddress, BigInteger assetPrice, String assetType) { - Objects.requireNonNull(requestOrderAddress, assetType + " address cannot be null in request order"); - Objects.requireNonNull(assetAddress, assetType + " address cannot be null"); - Objects.requireNonNull(assetPrice, assetType + " price cannot be null"); - if (!requestOrderAddress.equalsIgnoreCase(assetAddress)) { - throw new IllegalStateException("Ethereum address is not the same in " + assetType.toLowerCase() + " order and request order"); - } - } - - // TODO return status to controller and API requester, errors are only written in logs - public String matchOrders(BrokerOrder brokerOrder) { - checkBrokerOrder(brokerOrder); - AppOrder appOrder = brokerOrder.getAppOrder(); - WorkerpoolOrder workerpoolOrder = brokerOrder.getWorkerpoolOrder(); - DatasetOrder datasetOrder = brokerOrder.getDatasetOrder(); - RequestOrder requestOrder = brokerOrder.getRequestOrder(); - final boolean withDataset = withDataset(requestOrder.getDataset()); - BigInteger datasetPrice = withDataset ? datasetOrder.getDatasetprice() : BigInteger.ZERO; - if (!hasRequesterAcceptedPrices( - requestOrder, - appOrder.getAppprice(), - workerpoolOrder.getWorkerpoolprice(), - datasetPrice, - withDataset)) { - throw new IllegalStateException("Incompatible prices"); - } - //TODO check workerpool stake - long deposit = iexecHubService.getChainAccount(requestOrder.getRequester()) - .map(ChainAccount::getDeposit) - .orElse(-1L); - if (!hasRequesterDepositedEnough(deposit, - appOrder.getAppprice().longValue(), - workerpoolOrder.getWorkerpoolprice().longValue(), - datasetPrice.longValue())) { - throw new IllegalStateException("Deposit too low"); - } - String beneficiary = requestOrder.getBeneficiary(); - String messageDetails = MessageFormat.format("requester:{0}, beneficiary:{1}, pool:{2}, app:{3}", - requestOrder.getRequester(), beneficiary, workerpoolOrder.getWorkerpool(), appOrder.getApp()); - if (withDataset) { - messageDetails += ", dataset:" + datasetOrder.getDataset(); - } - log.info("Matching valid orders on-chain [{}]", messageDetails); - return fireMatchOrders(appOrder, datasetOrder, workerpoolOrder, requestOrder) - .orElse(""); - } - - Optional fireMatchOrders( - AppOrder appOrder, - DatasetOrder datasetOrder, - WorkerpoolOrder workerpoolOrder, - RequestOrder requestOrder) { - try { - final BigInteger nonce = signerService.getNonce(); - final String matchOrdersTxData = MatchOrdersDataEncoder.encode(appOrder, datasetOrder, workerpoolOrder, requestOrder); - final BigInteger gasLimit = signerService.estimateGas(chainConfig.getHubAddress(), matchOrdersTxData); - final String matchOrdersTxHash = signerService.signAndSendTransaction( - nonce, BigInteger.ZERO, gasLimit, chainConfig.getHubAddress(), matchOrdersTxData); - final TransactionReceipt receipt = iexecHubService.waitForTxMined( - new SubmittedTx(nonce, gasLimit, matchOrdersTxData, matchOrdersTxHash)); - log.info("block {}, hash {}, status {}", receipt.getBlockNumber(), receipt.getTransactionHash(), receipt.getStatus()); - log.info("logs count {}", receipt.getLogs().size()); - - final String workerpoolAddress = Numeric.toHexStringWithPrefixZeroPadded( - Numeric.toBigInt(workerpoolOrder.getWorkerpool()), 64); - final List expectedTopics = List.of(SCHEDULER_NOTICE, workerpoolAddress); - List events = receipt.getLogs().stream() - .filter(log -> expectedTopics.equals(log.getTopics())) - .map(Log::getData) - .toList(); - log.info("logs {}", events); - if (events.size() != 1) { - throw new IllegalStateException("A single deal should have been created, not " + events.size()); - } - final String dealId = events.get(0); - log.info("Matched orders [chainDealId:{}, tx:{}]", dealId, receipt.getTransactionHash()); - return Optional.of(dealId); - } catch (Exception e) { - log.error("Failed to request match order [requester:{}, app:{}, workerpool:{}, dataset:{}]", - requestOrder.getRequester(), requestOrder.getApp(), - requestOrder.getWorkerpool(), requestOrder.getDataset(), e); - } - return Optional.empty(); - } - - boolean hasRequesterAcceptedPrices( - RequestOrder requestOrder, - BigInteger appPrice, - BigInteger workerpoolPrice, - BigInteger datasetPrice, - boolean withDataset - ) { - boolean isAppPriceAccepted = requestOrder.getAppmaxprice().longValue() >= appPrice.longValue(); - boolean isPoolPriceAccepted = requestOrder.getWorkerpoolmaxprice().longValue() >= workerpoolPrice.longValue(); - boolean isAccepted = isAppPriceAccepted && isPoolPriceAccepted; - String messageDetails = MessageFormat.format("[isAppPriceAccepted:{0}, isPoolPriceAccepted:{1}]", - isAppPriceAccepted, isPoolPriceAccepted); - if (withDataset) { - boolean isDatasetPriceAccepted = requestOrder.getDatasetmaxprice().longValue() >= datasetPrice.longValue(); - isAccepted = isAccepted && isDatasetPriceAccepted; - messageDetails = MessageFormat.format("[isAppPriceAccepted:{0}, isPoolPriceAccepted:{1}, isDatasetPriceAccepted:{2}]", - isAppPriceAccepted, isPoolPriceAccepted, isDatasetPriceAccepted - ); - } - if (!isAccepted) { - log.error("Prices not accepted (too expensive) {}", messageDetails); - } - return isAccepted; - } - - boolean hasRequesterDepositedEnough(long deposit, long appPrice, long workerpoolPrice, long datasetPrice) { - long price = appPrice + workerpoolPrice + datasetPrice; - if (price > deposit) { - log.error("Deposit too low [price:{}, deposit:{}]", price, deposit); - return false; - } - return true; - } - - /** - * Checks if a dataset is part of the order. - * A valid request requires a dataset address which is not empty and not the empty address. - * - * @param datasetAddress Dataset address to check - * @return true if a dataset is part of the request, false otherwise. - */ - boolean withDataset(String datasetAddress) { - return StringUtils.isNotEmpty(datasetAddress) && !datasetAddress.equals(BytesUtils.EMPTY_ADDRESS); - } - -} diff --git a/src/test/java/com/iexec/blockchain/broker/BrokerServiceTests.java b/src/test/java/com/iexec/blockchain/broker/BrokerServiceTests.java deleted file mode 100644 index ff53e8e..0000000 --- a/src/test/java/com/iexec/blockchain/broker/BrokerServiceTests.java +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Copyright 2022-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.blockchain.broker; - -import com.iexec.blockchain.chain.ChainConfig; -import com.iexec.blockchain.chain.IexecHubService; -import com.iexec.blockchain.command.generic.SubmittedTx; -import com.iexec.common.sdk.broker.BrokerOrder; -import com.iexec.commons.poco.chain.ChainAccount; -import com.iexec.commons.poco.chain.SignerService; -import com.iexec.commons.poco.order.*; -import com.iexec.commons.poco.utils.BytesUtils; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.boot.test.system.OutputCaptureExtension; -import org.web3j.crypto.Credentials; -import org.web3j.crypto.ECKeyPair; -import org.web3j.crypto.Keys; -import org.web3j.protocol.core.RemoteFunctionCall; -import org.web3j.protocol.core.methods.response.Log; -import org.web3j.protocol.core.methods.response.TransactionReceipt; -import org.web3j.utils.Numeric; - -import java.io.IOException; -import java.math.BigInteger; -import java.text.MessageFormat; -import java.util.List; -import java.util.Optional; - -import static com.iexec.blockchain.broker.BrokerService.SCHEDULER_NOTICE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -@ExtendWith(OutputCaptureExtension.class) -class BrokerServiceTests { - - @Mock - private ChainConfig chainConfig; - @Mock - private IexecHubService iexecHubService; - @Mock - private SignerService signerService; - @InjectMocks - private BrokerService brokerService; - - @Mock - RemoteFunctionCall remoteCall; - - private static final ChainAccount deposit = ChainAccount.builder().deposit(100L).build(); - private static final ChainAccount emptyDeposit = ChainAccount.builder().deposit(0L).build(); - - AppOrder generateAppOrder() { - return AppOrder.builder() - .app(generateEthereumAddress()) - .appprice(BigInteger.ONE) - .volume(BigInteger.ONE) - .tag(OrderTag.TEE_SCONE.getValue()) - .datasetrestrict(BytesUtils.EMPTY_ADDRESS) - .workerpoolrestrict(BytesUtils.EMPTY_ADDRESS) - .requesterrestrict(BytesUtils.EMPTY_ADDRESS) - .salt(BytesUtils.toByte32HexString(1)) - .sign("sign") - .build(); - } - - DatasetOrder generateDatasetOrder(boolean withDataset) { - String address = withDataset ? generateEthereumAddress() : BytesUtils.EMPTY_ADDRESS; - return DatasetOrder.builder() - .dataset(address) - .datasetprice(BigInteger.ONE) - .volume(BigInteger.ONE) - .tag(OrderTag.TEE_SCONE.getValue()) - .apprestrict(BytesUtils.EMPTY_ADDRESS) - .workerpoolrestrict(BytesUtils.EMPTY_ADDRESS) - .requesterrestrict(BytesUtils.EMPTY_ADDRESS) - .salt(BytesUtils.toByte32HexString(1)) - .sign("sign") - .build(); - } - - WorkerpoolOrder generateWorkerpoolOrder() { - return WorkerpoolOrder.builder() - .workerpool(generateEthereumAddress()) - .workerpoolprice(BigInteger.ONE) - .volume(BigInteger.ONE) - .tag(OrderTag.TEE_SCONE.getValue()) - .trust(BigInteger.ZERO) - .category(BigInteger.ZERO) - .apprestrict(BytesUtils.EMPTY_ADDRESS) - .datasetrestrict(BytesUtils.EMPTY_ADDRESS) - .requesterrestrict(BytesUtils.EMPTY_ADDRESS) - .salt(BytesUtils.toByte32HexString(1)) - .sign("sign") - .build(); - } - - RequestOrder generateRequestOrder( - AppOrder appOrder, DatasetOrder datasetOrder, WorkerpoolOrder workerpoolOrder) { - String requestAddress = generateEthereumAddress(); - return RequestOrder.builder() - .app(appOrder.getApp()) - .appmaxprice(BigInteger.ONE) - .dataset(datasetOrder.getDataset()) - .datasetmaxprice(BigInteger.ONE) - .workerpool(workerpoolOrder.getWorkerpool()) - .workerpoolmaxprice(BigInteger.ONE) - .requester(requestAddress) - .volume(BigInteger.ONE) - .tag(OrderTag.TEE_SCONE.getValue()) - .category(BigInteger.ZERO) - .trust(BigInteger.ONE) - .beneficiary(requestAddress) - .callback(BytesUtils.EMPTY_ADDRESS) - .params("") - .salt(BytesUtils.toByte32HexString(1)) - .sign("sign") - .build(); - } - - BrokerOrder generateBrokerOrder(boolean withDataset) { - AppOrder appOrder = generateAppOrder(); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - DatasetOrder datasetOrder = generateDatasetOrder(withDataset); - RequestOrder requestOrder = generateRequestOrder( - appOrder, datasetOrder, workerpoolOrder); - return BrokerOrder.builder() - .appOrder(appOrder) - .datasetOrder(datasetOrder) - .workerpoolOrder(workerpoolOrder) - .requestOrder(requestOrder) - .build(); - } - - String generateEthereumAddress() { - try { - ECKeyPair ecKeypair = Keys.createEcKeyPair(); - return Credentials.create(ecKeypair).getAddress(); - } catch (Exception e) { - throw new RuntimeException("Cannot generate ethereum address", e); - } - } - - //region matchOrders - @Test - void shouldNotMatchOrderWhenBrokerOrderIsNull() { - assertThatThrownBy(() -> brokerService.matchOrders(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("Broker order cannot be null"); - } - - @Test - void shouldNotMatchOrderWhenAppOrderIsNull() { - BrokerOrder brokerOrder = BrokerOrder.builder() - .requestOrder(RequestOrder.builder().build()) - .workerpoolOrder(WorkerpoolOrder.builder().build()) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(NullPointerException.class) - .hasMessage("App order cannot be null"); - } - - @Test - void shouldNotMatchOrderWhenRequestOrderIsNull() { - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(AppOrder.builder().build()) - .workerpoolOrder(WorkerpoolOrder.builder().build()) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(NullPointerException.class) - .hasMessage("Request order cannot be null"); - } - - @Test - void shouldNotMatchOrderWhenWorkerpoolOrderIsNull() { - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(AppOrder.builder().build()) - .requestOrder(RequestOrder.builder().build()) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(NullPointerException.class) - .hasMessage("Workerpool order cannot be null"); - } - - @Test - void shouldNotMatchOrderWhenAppAddressDoesNotMatch() { - AppOrder appOrder = generateAppOrder(); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - RequestOrder requestOrder = RequestOrder.builder() - .app("") - .appmaxprice(BigInteger.ZERO) - .workerpool(workerpoolOrder.getWorkerpool()) - .workerpoolmaxprice(BigInteger.ZERO) - .build(); - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(appOrder) - .workerpoolOrder(workerpoolOrder) - .requestOrder(requestOrder) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(IllegalStateException.class) - .hasMessage("Ethereum address is not the same in app order and request order"); - } - - @Test - void shouldNotMatchOrderWhenWorkerpoolAddressDoesNotMatch() { - AppOrder appOrder = generateAppOrder(); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - RequestOrder requestOrder = RequestOrder.builder() - .app(appOrder.getApp()) - .appmaxprice(BigInteger.ZERO) - .workerpool("") - .workerpoolmaxprice(BigInteger.ZERO) - .build(); - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(appOrder) - .workerpoolOrder(workerpoolOrder) - .requestOrder(requestOrder) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(IllegalStateException.class) - .hasMessage("Ethereum address is not the same in workerpool order and request order"); - } - - @Test - void shouldNotMatchOrderWhenAppPriceIsNull() { - AppOrder appOrder = AppOrder.builder().app(generateEthereumAddress()).build(); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - RequestOrder requestOrder = RequestOrder.builder() - .app(appOrder.getApp()) - .appmaxprice(BigInteger.ONE) - .workerpool(workerpoolOrder.getWorkerpool()) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(appOrder) - .requestOrder(requestOrder) - .workerpoolOrder(workerpoolOrder) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(NullPointerException.class) - .hasMessage("App price cannot be null"); - } - - @Test - void shouldNotMatchOrderWhenWorkerpoolPriceIsNull() { - AppOrder appOrder = generateAppOrder(); - WorkerpoolOrder workerpoolOrder = WorkerpoolOrder.builder().workerpool(generateEthereumAddress()).build(); - RequestOrder requestOrder = RequestOrder.builder() - .app(appOrder.getApp()) - .appmaxprice(BigInteger.ONE) - .workerpool(workerpoolOrder.getWorkerpool()) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(appOrder) - .requestOrder(requestOrder) - .workerpoolOrder(workerpoolOrder) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(NullPointerException.class) - .hasMessage("Workerpool price cannot be null"); - } - - @Test - void shouldNotMatchOrderWhenRequestOrderNeedsDatasetAndDatasetOrderIsNull() { - AppOrder appOrder = generateAppOrder(); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - RequestOrder requestOrder = RequestOrder.builder() - .app(appOrder.getApp()) - .appmaxprice(BigInteger.ONE) - .dataset(generateEthereumAddress()) - .datasetmaxprice(BigInteger.ONE) - .workerpool(workerpoolOrder.getWorkerpool()) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(appOrder) - .requestOrder(requestOrder) - .workerpoolOrder(workerpoolOrder) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(NullPointerException.class) - .hasMessage("Dataset order cannot be null"); - } - - @Test - void shouldNotMatchOrderWhenRequestOrderNeedsDatasetAndDatasetAddressDoesNotMatch() { - AppOrder appOrder = generateAppOrder(); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - DatasetOrder datasetOrder = generateDatasetOrder(true); - RequestOrder requestOrder = RequestOrder.builder() - .app(appOrder.getApp()) - .appmaxprice(BigInteger.ONE) - .dataset(generateEthereumAddress()) - .datasetmaxprice(BigInteger.ONE) - .workerpool(workerpoolOrder.getWorkerpool()) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(appOrder) - .datasetOrder(datasetOrder) - .requestOrder(requestOrder) - .workerpoolOrder(workerpoolOrder) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(IllegalStateException.class) - .hasMessage("Ethereum address is not the same in dataset order and request order"); - } - - @Test - void shouldNotMatchOrderWhenRequestOrderNeedsDatasetAndDatasetPriceIsNull() { - AppOrder appOrder = generateAppOrder(); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - DatasetOrder datasetOrder = DatasetOrder.builder().dataset(generateEthereumAddress()).build(); - RequestOrder requestOrder = RequestOrder.builder() - .app(appOrder.getApp()) - .appmaxprice(BigInteger.ONE) - .dataset(datasetOrder.getDataset()) - .datasetmaxprice(BigInteger.ONE) - .workerpool(workerpoolOrder.getWorkerpool()) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(appOrder) - .datasetOrder(datasetOrder) - .requestOrder(requestOrder) - .workerpoolOrder(workerpoolOrder) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(NullPointerException.class) - .hasMessage("Dataset price cannot be null"); - } - - @Test - void shouldNotMatchOrderWhenPricesNotAccepted() { - AppOrder appOrder = generateAppOrder(); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - RequestOrder requestOrder = RequestOrder.builder() - .app(appOrder.getApp()) - .appmaxprice(BigInteger.ZERO) - .workerpool(workerpoolOrder.getWorkerpool()) - .workerpoolmaxprice(BigInteger.ZERO) - .build(); - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(appOrder) - .requestOrder(requestOrder) - .workerpoolOrder(workerpoolOrder) - .build(); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(IllegalStateException.class) - .hasMessage("Incompatible prices"); - } - - @Test - void shouldNotMatchOrderWhenDepositIsToLow() { - AppOrder appOrder = generateAppOrder(); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - RequestOrder requestOrder = RequestOrder.builder() - .app(appOrder.getApp()) - .appmaxprice(BigInteger.ONE) - .workerpool(workerpoolOrder.getWorkerpool()) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - BrokerOrder brokerOrder = BrokerOrder.builder() - .appOrder(appOrder) - .requestOrder(requestOrder) - .workerpoolOrder(workerpoolOrder) - .build(); - when(iexecHubService.getChainAccount(anyString())).thenReturn(Optional.of(emptyDeposit)); - assertThatThrownBy(() -> brokerService.matchOrders(brokerOrder)) - .isInstanceOf(IllegalStateException.class) - .hasMessage("Deposit too low"); - } - - @Test - void shouldNotMatchOrderAndReturnEmptyStringWithDataset(CapturedOutput output) { - BrokerOrder brokerOrder = generateBrokerOrder(true); - RequestOrder requestOrder = brokerOrder.getRequestOrder(); - when(iexecHubService.getChainAccount(anyString())).thenReturn(Optional.of(deposit)); - assertThat(brokerService.matchOrders(brokerOrder)).isEmpty(); - String expectedMessage = MessageFormat.format("Matching valid orders on-chain [requester:{0}, beneficiary:{1}, pool:{2}, app:{3}, dataset:{4}]", - requestOrder.getRequester(), requestOrder.getBeneficiary(), requestOrder.getWorkerpool(), requestOrder.getApp(), requestOrder.getDataset()); - assertThat(output.getOut()).contains(expectedMessage); - } - - @Test - void shouldNotMatchOrderAndReturnEmptyStringWithoutDataset(CapturedOutput output) { - BrokerOrder brokerOrder = generateBrokerOrder(false); - RequestOrder requestOrder = brokerOrder.getRequestOrder(); - when(iexecHubService.getChainAccount(anyString())).thenReturn(Optional.of(deposit)); - assertThat(brokerService.matchOrders(brokerOrder)).isEmpty(); - String expectedMessage = MessageFormat.format("Matching valid orders on-chain [requester:{0}, beneficiary:{1}, pool:{2}, app:{3}]", - requestOrder.getRequester(), requestOrder.getBeneficiary(), requestOrder.getWorkerpool(), requestOrder.getApp()); - assertThat(output.getOut()).contains(expectedMessage); - } - //endregion - - //region fireMatchOrders - @Test - void shouldFailToMatchOrdersWithDataset() throws Exception { - AppOrder appOrder = generateAppOrder(); - DatasetOrder datasetOrder = generateDatasetOrder(true); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - RequestOrder requestOrder = generateRequestOrder( - appOrder, datasetOrder, workerpoolOrder); - when(signerService.signAndSendTransaction(any(), any(), any(), any(), any())).thenThrow(IOException.class); - assertThat(brokerService.fireMatchOrders(appOrder, datasetOrder, workerpoolOrder, requestOrder)) - .isEmpty(); - } - - @Test - void shouldFailToMatchOrdersWithoutDataset() throws Exception { - AppOrder appOrder = generateAppOrder(); - DatasetOrder datasetOrder = generateDatasetOrder(false); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - RequestOrder requestOrder = generateRequestOrder( - appOrder, datasetOrder, workerpoolOrder); - when(signerService.signAndSendTransaction(any(), any(), any(), any(), any())).thenThrow(IOException.class); - assertThat(brokerService.fireMatchOrders(appOrder, datasetOrder, workerpoolOrder, requestOrder)) - .isEmpty(); - } - - @Test - void shouldMatchOrdersWithDataset() throws Exception { - String dealId = "dealId"; - AppOrder appOrder = generateAppOrder(); - DatasetOrder datasetOrder = generateDatasetOrder(true); - WorkerpoolOrder workerpoolOrder = generateWorkerpoolOrder(); - RequestOrder requestOrder = generateRequestOrder( - appOrder, datasetOrder, workerpoolOrder); - when(signerService.signAndSendTransaction(any(), any(), any(), any(), any())).thenReturn("txHash"); - String workerpoolAddress = Numeric.toHexStringWithPrefixZeroPadded( - Numeric.toBigInt(workerpoolOrder.getWorkerpool()), 64); - Log web3Log = new Log(); - web3Log.setData(dealId); - web3Log.setTopics(List.of(SCHEDULER_NOTICE, workerpoolAddress)); - TransactionReceipt receipt = new TransactionReceipt(); - receipt.setTransactionHash("txHash"); - receipt.setBlockNumber("0x1"); - receipt.setStatus("1"); - receipt.setLogs(List.of(web3Log)); - when(iexecHubService.waitForTxMined(any(SubmittedTx.class))).thenReturn(receipt); - assertThat(brokerService.fireMatchOrders(appOrder, datasetOrder, workerpoolOrder, requestOrder)) - .isNotEmpty() - .contains(dealId); - } - //endregion - - //region hasRequesterAcceptedPrices - @Test - void shouldFailWhenPricesUnderThreshold() { - RequestOrder requestOrder = RequestOrder.builder() - .appmaxprice(BigInteger.ZERO) - .datasetmaxprice(BigInteger.ZERO) - .workerpoolmaxprice(BigInteger.ZERO) - .build(); - assertThat(brokerService.hasRequesterAcceptedPrices(requestOrder, BigInteger.ONE, BigInteger.ONE, BigInteger.ONE, true)) - .isFalse(); - } - - @Test - void shouldFailForBigAppPrice() { - RequestOrder requestOrder = RequestOrder.builder() - .appmaxprice(BigInteger.ONE) - .datasetmaxprice(BigInteger.ONE) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - assertThat(brokerService.hasRequesterAcceptedPrices(requestOrder, BigInteger.TEN, BigInteger.ONE, BigInteger.ONE, true)) - .isFalse(); - } - - @Test - void shouldFailForBigWorkerpoolPrice() { - RequestOrder requestOrder = RequestOrder.builder() - .appmaxprice(BigInteger.ONE) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - assertThat(brokerService.hasRequesterAcceptedPrices(requestOrder, BigInteger.ONE, BigInteger.TEN, BigInteger.ZERO, false)) - .isFalse(); - } - - @Test - void shouldFailForBigDatasetPrice() { - RequestOrder requestOrder = RequestOrder.builder() - .appmaxprice(BigInteger.ONE) - .datasetmaxprice(BigInteger.ONE) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - assertThat(brokerService.hasRequesterAcceptedPrices(requestOrder, BigInteger.ONE, BigInteger.ONE, BigInteger.TEN, true)) - .isFalse(); - } - - @Test - void shouldSucceedWhenPricesAboveThreshold() { - RequestOrder requestOrder = RequestOrder.builder() - .appmaxprice(BigInteger.ONE) - .workerpoolmaxprice(BigInteger.ONE) - .build(); - assertThat(brokerService.hasRequesterAcceptedPrices(requestOrder, BigInteger.ONE, BigInteger.ONE, BigInteger.ZERO, false)) - .isTrue(); - } - //endregion - - //region hasRequesterDepositedEnough - @Test - void shouldCheckDepositAgainstRequiredPrices() { - assertThat(brokerService.hasRequesterDepositedEnough(5L, 1L, 1L, 1L)).isTrue(); - assertThat(brokerService.hasRequesterDepositedEnough(0L, 1L, 1L, 1L)).isFalse(); - } - //endregion - - //region withDataset - @Test - void testWithDataset() { - assertThat(brokerService.withDataset(null)).isFalse(); - assertThat(brokerService.withDataset("")).isFalse(); - assertThat(brokerService.withDataset("0x1")).isTrue(); - assertThat(brokerService.withDataset(BytesUtils.EMPTY_ADDRESS)).isFalse(); - } - //endregion - -}