Skip to content

Commit 928a480

Browse files
authored
Add encodings to call PoCo assets address prediction functions (#101)
1 parent ab0ded0 commit 928a480

File tree

7 files changed

+229
-49
lines changed

7 files changed

+229
-49
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
99
- Add `AbstractAssetDeploymentService` and move `getNonce` method. (#92)
1010
- Estimate gas and submit a transaction in a single method. (#97)
1111
- Add method to fetch on-chain deal without app or dataset details. (#98)
12+
- Add encodings to call PoCo assets address prediction functions. (#101)
1213

1314
### Quality
1415

src/main/java/com/iexec/commons/poco/chain/AbstractAssetDeploymentService.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,6 @@ public boolean isAssetDeployed(String address) throws IOException {
5656
return Numeric.toBigInt(signerService.sendCall(assetRegistryAddress, isRegisteredTxData)).equals(BigInteger.ONE);
5757
}
5858

59-
public BigInteger estimateCreateAsset(String assetTxData) throws IOException {
60-
Objects.requireNonNull(assetRegistryAddress, ASSET_REGISTRY_ADDRESS_INITIALIZATION_NEEDED);
61-
return signerService.estimateGas(assetRegistryAddress, assetTxData);
62-
}
63-
6459
public String submitAssetTxData(BigInteger nonce, BigInteger gasPrice, String assetTxData) throws IOException {
6560
Objects.requireNonNull(assetRegistryAddress, ASSET_REGISTRY_ADDRESS_INITIALIZATION_NEEDED);
6661
return signerService.signAndSendTransaction(nonce, gasPrice, assetRegistryAddress, assetTxData);

src/main/java/com/iexec/commons/poco/encoding/AssetDataEncoder.java

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@
3434
public class AssetDataEncoder {
3535

3636
private static final String CREATE_APP_SELECTOR = "0x3f7868ff";
37+
private static final String PREDICT_APP_SELECTOR = "0xe92118ed";
3738
private static final String CREATE_DATASET_SELECTOR = "0x3354bcdb";
39+
private static final String PREDICT_DATASET_SELECTOR = "0xfe17fc7a";
3840
private static final String CREATE_WORKERPOOL_SELECTOR = "0xe40238f4";
41+
private static final String PREDICT_WORKERPOOL_SELECTOR = "0x064a6c2a";
3942
private static final String IS_REGISTERED_SELECTOR = "0xc3c5a547";
4043
private static final String NFT_TRANSFER_EVENT = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
4144

@@ -51,8 +54,27 @@ public class AssetDataEncoder {
5154
* @return encoded data
5255
*/
5356
public static String encodeApp(String owner, String name, String type, String multiaddr, String checksum, String mrenclave) {
57+
return encodeAppTxData(CREATE_APP_SELECTOR, owner, name, type, multiaddr, checksum, mrenclave);
58+
}
59+
60+
/**
61+
* Encodes data for {@code predictApp} transaction.
62+
*
63+
* @param owner Owner Ethereum address
64+
* @param name App name
65+
* @param type App type, unique supported value should be DOCKER
66+
* @param multiaddr Docker registry download address
67+
* @param checksum Docker image SHA-256 checksum
68+
* @param mrenclave TEE configuration if applicable, should be a valid JSON string of {@link com.iexec.commons.poco.tee.TeeEnclaveConfiguration}
69+
* @return encoded data
70+
*/
71+
public static String encodePredictApp(String owner, String name, String type, String multiaddr, String checksum, String mrenclave) {
72+
return encodeAppTxData(PREDICT_APP_SELECTOR, owner, name, type, multiaddr, checksum, mrenclave);
73+
}
74+
75+
private static String encodeAppTxData(String selector, String owner, String name, String type, String multiaddr, String checksum, String mrenclave) {
5476
long offset = 6;
55-
StringBuilder sb = new StringBuilder(CREATE_APP_SELECTOR);
77+
StringBuilder sb = new StringBuilder(selector);
5678
sb.append(toHexString(owner));
5779

5880
sb.append(toHexString(BigInteger.valueOf(offset * 32)));
@@ -90,8 +112,25 @@ public static String encodeApp(String owner, String name, String type, String mu
90112
* @return encoded data
91113
*/
92114
public static String encodeDataset(String owner, String name, String multiaddr, String checksum) {
115+
return encodeDatasetTxData(CREATE_DATASET_SELECTOR, owner, name, multiaddr, checksum);
116+
}
117+
118+
/**
119+
* Encodes data for {@code predictDataset} transaction.
120+
*
121+
* @param owner Owner Ethereum address
122+
* @param name Dataset name, can be empty
123+
* @param multiaddr Download address, can be a http link or an IPFS multiaddr
124+
* @param checksum SHA-256 checksum of the dataset
125+
* @return encoded data
126+
*/
127+
public static String encodePredictDataset(String owner, String name, String multiaddr, String checksum) {
128+
return encodeDatasetTxData(PREDICT_DATASET_SELECTOR, owner, name, multiaddr, checksum);
129+
}
130+
131+
private static String encodeDatasetTxData(String selector, String owner, String name, String multiaddr, String checksum) {
93132
long offset = 4;
94-
StringBuilder sb = new StringBuilder(CREATE_DATASET_SELECTOR);
133+
StringBuilder sb = new StringBuilder(selector);
95134

96135
String nameOffset = toHexString(BigInteger.valueOf(offset * 32));
97136
String nameContrib = TypeEncoder.encode(new DynamicBytes(name.getBytes(StandardCharsets.UTF_8)));
@@ -118,8 +157,23 @@ public static String encodeDataset(String owner, String name, String multiaddr,
118157
* @return encoded data
119158
*/
120159
public static String encodeWorkerpool(String owner, String description) {
160+
return encodeWorkerpoolTxData(CREATE_WORKERPOOL_SELECTOR, owner, description);
161+
}
162+
163+
/**
164+
* Encodes data for {@code predictWorkerpool} transaction.
165+
*
166+
* @param owner Owner Ethereum address
167+
* @param description Workerpool description
168+
* @return encoded data
169+
*/
170+
public static String encodePredictWorkerpool(String owner, String description) {
171+
return encodeWorkerpoolTxData(PREDICT_WORKERPOOL_SELECTOR, owner, description);
172+
}
173+
174+
private static String encodeWorkerpoolTxData(String selector, String owner, String description) {
121175
long offset = 2;
122-
StringBuilder sb = new StringBuilder(CREATE_WORKERPOOL_SELECTOR);
176+
StringBuilder sb = new StringBuilder(selector);
123177
sb.append(toHexString(owner));
124178
sb.append(toHexString(BigInteger.valueOf(offset * 32)));
125179
String descriptionContrib = TypeEncoder.encode(new DynamicBytes(description.getBytes(StandardCharsets.UTF_8)));
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2024 IEXEC BLOCKCHAIN TECH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.iexec.commons.poco.itest;
18+
19+
import com.iexec.commons.poco.chain.SignerService;
20+
import org.apache.commons.lang3.RandomStringUtils;
21+
import org.junit.jupiter.api.BeforeEach;
22+
import org.junit.jupiter.api.Tag;
23+
import org.junit.jupiter.api.Test;
24+
import org.testcontainers.containers.ComposeContainer;
25+
import org.testcontainers.junit.jupiter.Container;
26+
import org.testcontainers.junit.jupiter.Testcontainers;
27+
import org.web3j.crypto.CipherException;
28+
import org.web3j.crypto.Credentials;
29+
import org.web3j.crypto.WalletUtils;
30+
import org.web3j.tx.exceptions.ContractCallException;
31+
32+
import java.io.File;
33+
import java.io.IOException;
34+
import java.math.BigInteger;
35+
import java.util.concurrent.TimeUnit;
36+
37+
import static com.iexec.commons.poco.itest.ChainTests.SERVICE_NAME;
38+
import static com.iexec.commons.poco.itest.ChainTests.SERVICE_PORT;
39+
import static com.iexec.commons.poco.itest.Web3jTestService.BLOCK_TIME;
40+
import static org.assertj.core.api.Assertions.assertThat;
41+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
42+
import static org.awaitility.Awaitility.await;
43+
import static org.junit.jupiter.api.Assertions.assertAll;
44+
45+
@Tag("itest")
46+
@Testcontainers
47+
class AssetRegistriesTests {
48+
49+
private SignerService signerService;
50+
private IexecHubTestService iexecHubService;
51+
private Web3jTestService web3jService;
52+
53+
@Container
54+
static ComposeContainer environment = new ComposeContainer(new File("docker-compose.yml"))
55+
.withExposedService("poco-chain", 8545);
56+
57+
@BeforeEach
58+
void init() throws CipherException, IOException {
59+
final Credentials credentials = WalletUtils.loadCredentials("whatever", "src/test/resources/wallet.json");
60+
String chainNodeAddress = "http://" + environment.getServiceHost(SERVICE_NAME, SERVICE_PORT) + ":" +
61+
environment.getServicePort(SERVICE_NAME, SERVICE_PORT);
62+
web3jService = new Web3jTestService(chainNodeAddress);
63+
iexecHubService = new IexecHubTestService(credentials, web3jService);
64+
signerService = new SignerService(web3jService.getWeb3j(), web3jService.getChainId(), credentials);
65+
}
66+
67+
@Test
68+
void shouldCreateAndPredictCallsBeEqualWhenAssetNotDeployed() {
69+
final String appName = RandomStringUtils.randomAlphanumeric(16);
70+
final String datasetName = RandomStringUtils.randomAlphanumeric(16);
71+
final String workerpoolName = RandomStringUtils.randomAlphanumeric(16);
72+
73+
assertAll(
74+
() -> assertThat(iexecHubService.callCreateApp(appName))
75+
.isEqualTo(iexecHubService.callPredictApp(appName)),
76+
() -> assertThat(iexecHubService.callCreateDataset(datasetName))
77+
.isEqualTo(iexecHubService.callPredictDataset(datasetName)),
78+
() -> assertThat(iexecHubService.callCreateWorkerpool(workerpoolName))
79+
.isEqualTo(iexecHubService.callPredictWorkerpool(workerpoolName))
80+
);
81+
}
82+
83+
@Test
84+
void shouldCreateCallRevertWhenAssetDeployed() throws IOException {
85+
final String appName = RandomStringUtils.randomAlphanumeric(16);
86+
final String datasetName = RandomStringUtils.randomAlphanumeric(16);
87+
final String workerpoolName = RandomStringUtils.randomAlphanumeric(16);
88+
BigInteger nonce = signerService.getNonce();
89+
final String appTxHash = iexecHubService.submitCreateAppTx(nonce, appName);
90+
nonce = nonce.add(BigInteger.ONE);
91+
final String datasetTxHash = iexecHubService.submitCreateDatasetTx(nonce, datasetName);
92+
nonce = nonce.add(BigInteger.ONE);
93+
final String workerpoolTxHash = iexecHubService.submitCreateWorkerpoolTx(nonce, workerpoolName);
94+
95+
await().atMost(BLOCK_TIME, TimeUnit.SECONDS)
96+
.until(() -> web3jService.areTxMined(appTxHash, datasetTxHash, workerpoolTxHash));
97+
assertThat(web3jService.areTxStatusOK(appTxHash, datasetTxHash, workerpoolTxHash)).isTrue();
98+
99+
// fetch asset addresses from call on predict assets
100+
final String predictedAppAddress = iexecHubService.callPredictApp(appName);
101+
final String predictedDatasetAddress = iexecHubService.callPredictDataset(datasetName);
102+
final String predictedWorkerpoolAddress = iexecHubService.callPredictWorkerpool(workerpoolName);
103+
104+
// check assets are deployed
105+
assertAll(
106+
() -> assertThat(iexecHubService.isAppPresent(predictedAppAddress)).isTrue(),
107+
() -> assertThat(iexecHubService.isAppPresent(predictedDatasetAddress)).isFalse(),
108+
() -> assertThat(iexecHubService.isAppPresent(predictedWorkerpoolAddress)).isFalse(),
109+
() -> assertThat(iexecHubService.isDatasetPresent(predictedAppAddress)).isFalse(),
110+
() -> assertThat(iexecHubService.isDatasetPresent(predictedDatasetAddress)).isTrue(),
111+
() -> assertThat(iexecHubService.isDatasetPresent(predictedWorkerpoolAddress)).isFalse(),
112+
() -> assertThat(iexecHubService.isWorkerpoolPresent(predictedAppAddress)).isFalse(),
113+
() -> assertThat(iexecHubService.isWorkerpoolPresent(predictedDatasetAddress)).isFalse(),
114+
() -> assertThat(iexecHubService.isWorkerpoolPresent(predictedWorkerpoolAddress)).isTrue()
115+
);
116+
117+
// call on create assets should revert
118+
final String errorMessage = "Contract Call has been reverted by the EVM with the reason: 'VM execution error.'.";
119+
120+
assertAll(
121+
() -> assertThatThrownBy(() -> iexecHubService.callCreateApp(appName), "Should have failed to call createApp")
122+
.isInstanceOf(ContractCallException.class)
123+
.hasMessage(errorMessage),
124+
() -> assertThatThrownBy(() -> iexecHubService.callCreateDataset(datasetName), "Should have failed to call createDataset")
125+
.isInstanceOf(ContractCallException.class)
126+
.hasMessage(errorMessage),
127+
() -> assertThatThrownBy(() -> iexecHubService.callCreateWorkerpool(workerpoolName), "Should have failed to call createWorkerpool")
128+
.isInstanceOf(ContractCallException.class)
129+
.hasMessage(errorMessage)
130+
);
131+
}
132+
}

src/test/java/com/iexec/commons/poco/itest/ChainTests.java

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023 IEXEC BLOCKCHAIN TECH
2+
* Copyright 2023-2024 IEXEC BLOCKCHAIN TECH
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,7 +36,6 @@
3636
import java.io.File;
3737
import java.io.IOException;
3838
import java.math.BigInteger;
39-
import java.util.Optional;
4039
import java.util.stream.Stream;
4140

4241
import static org.assertj.core.api.Assertions.assertThat;
@@ -60,32 +59,30 @@ class ChainTests {
6059
@BeforeEach
6160
void init() throws CipherException, IOException {
6261
credentials = WalletUtils.loadCredentials("whatever", "src/test/resources/wallet.json");
63-
String chainNodeAddress = "http://" + environment.getServiceHost(SERVICE_NAME, SERVICE_PORT) + ":" +
62+
final String chainNodeAddress = "http://" + environment.getServiceHost(SERVICE_NAME, SERVICE_PORT) + ":" +
6463
environment.getServicePort(SERVICE_NAME, SERVICE_PORT);
6564
web3jService = new Web3jTestService(chainNodeAddress);
6665
iexecHubService = new IexecHubTestService(credentials, web3jService);
6766
}
6867

6968
@Test
7069
void shouldGetAccount() {
71-
Optional<ChainAccount> oChainAccount = iexecHubService.getChainAccount(credentials.getAddress());
72-
assertThat(oChainAccount).isPresent();
73-
assertThat(oChainAccount.get().getDeposit()).isEqualTo(10_000_000L);
74-
assertThat(oChainAccount.get().getLocked()).isZero();
70+
final ChainAccount chainAccount = iexecHubService.getChainAccount(credentials.getAddress()).orElse(null);
71+
assertThat(chainAccount).isNotNull();
72+
assertThat(chainAccount.getDeposit()).isEqualTo(10_000_000L);
73+
assertThat(chainAccount.getLocked()).isZero();
7574
}
7675

7776
@Test
7877
void shouldGetBalance() {
79-
Optional<BigInteger> oBalance = web3jService.getBalance(credentials.getAddress());
80-
assertThat(oBalance)
81-
.isPresent()
82-
.contains(new BigInteger("1000000000000000000000000000000000000000000"));
78+
final BigInteger balance = web3jService.getBalance(credentials.getAddress()).orElse(null);
79+
assertThat(balance).isEqualTo(new BigInteger("1000000000000000000000000000000000000000000"));
8380
}
8481

8582
@Test
8683
void shouldNotGetBalance() {
87-
Web3jTestService web3jService = new Web3jTestService("http://localhost:8545");
88-
assertThat(web3jService.getBalance(credentials.getAddress())).isEmpty();
84+
final Web3jTestService badWeb3jService = new Web3jTestService("http://localhost:8545");
85+
assertThat(badWeb3jService.getBalance(credentials.getAddress())).isEmpty();
8986
}
9087

9188
@Test
@@ -99,17 +96,15 @@ void shouldGetBlockNumber() throws IOException {
9996

10097
@Test
10198
void shouldNotGetBlockNumber() {
102-
Web3jTestService web3jService = new Web3jTestService("http://localhost:8545");
103-
assertThat(web3jService.getLatestBlockNumber()).isZero();
99+
final Web3jTestService badWeb3jService = new Web3jTestService("http://localhost:8545");
100+
assertThat(badWeb3jService.getLatestBlockNumber()).isZero();
104101
}
105102

106103
@ParameterizedTest
107104
@MethodSource("categoryProvider")
108105
void shouldGetCategory(long id, ChainCategory expectedCategory) {
109-
Optional<ChainCategory> oChainCategory = iexecHubService.getChainCategory(id);
110-
assertThat(oChainCategory)
111-
.isPresent()
112-
.contains(expectedCategory);
106+
final ChainCategory chainCategory = iexecHubService.getChainCategory(id).orElse(null);
107+
assertThat(chainCategory).isEqualTo(expectedCategory);
113108
}
114109

115110
private static Stream<Arguments> categoryProvider() {
@@ -124,15 +119,14 @@ private static Stream<Arguments> categoryProvider() {
124119

125120
@Test
126121
void shouldGetMaxNbOfPeriodsForConsensus() {
127-
long maxNbOfPeriodsForConsensus = iexecHubService.getMaxNbOfPeriodsForConsensus();
122+
final long maxNbOfPeriodsForConsensus = iexecHubService.getMaxNbOfPeriodsForConsensus();
128123
assertThat(maxNbOfPeriodsForConsensus).isEqualTo(7L);
129124
}
130125

131126
@Test
132127
void shouldGetWorkerScore() {
133-
Optional<Integer> oScore = iexecHubService.getWorkerScore(credentials.getAddress());
134-
assertThat(oScore).isPresent();
135-
assertThat(oScore.get()).isZero();
128+
final Integer score = iexecHubService.getWorkerScore(credentials.getAddress()).orElse(null);
129+
assertThat(score).isZero();
136130
}
137131

138132
@Test

0 commit comments

Comments
 (0)