Skip to content

Commit 82293b5

Browse files
authored
feat: create ChainAccount, ChainCategory and ChainContribution from raw data (#162)
1 parent 8d0de6f commit 82293b5

File tree

13 files changed

+231
-85
lines changed

13 files changed

+231
-85
lines changed
Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 IEXEC BLOCKCHAIN TECH
2+
* Copyright 2020-2025 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.
@@ -16,27 +16,29 @@
1616

1717
package com.iexec.commons.poco.chain;
1818

19+
import com.iexec.commons.poco.encoding.PoCoDataDecoder;
1920
import lombok.Builder;
2021
import lombok.Value;
21-
import org.web3j.tuples.generated.Tuple2;
22+
import lombok.extern.slf4j.Slf4j;
2223

23-
import java.math.BigInteger;
24+
import static com.iexec.commons.poco.chain.Web3jAbstractService.toBigInt;
2425

26+
@Slf4j
2527
@Value
2628
@Builder
2729
public class ChainAccount {
28-
2930
long deposit;
3031
long locked;
3132

32-
public static ChainAccount tuple2Account(Tuple2<BigInteger, BigInteger> account) {
33-
if (account != null) {
33+
public static ChainAccount fromRawData(final String rawData) {
34+
log.debug("ChainAccount.fromRawData");
35+
final String[] parts = PoCoDataDecoder.toParts(rawData);
36+
if (parts.length == 2) {
3437
return ChainAccount.builder()
35-
.deposit(account.component1().longValue())
36-
.locked(account.component2().longValue())
38+
.deposit(toBigInt(parts[0]).longValue())
39+
.locked(toBigInt(parts[1]).longValue())
3740
.build();
3841
}
3942
return null;
4043
}
41-
4244
}

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 IEXEC BLOCKCHAIN TECH
2+
* Copyright 2020-2025 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.
@@ -16,26 +16,33 @@
1616

1717
package com.iexec.commons.poco.chain;
1818

19+
import com.iexec.commons.poco.encoding.PoCoDataDecoder;
1920
import lombok.Builder;
2021
import lombok.Value;
22+
import lombok.extern.slf4j.Slf4j;
2123

22-
import java.math.BigInteger;
24+
import static com.iexec.commons.poco.chain.Web3jAbstractService.toBigInt;
2325

26+
@Slf4j
2427
@Value
2528
@Builder
2629
public class ChainCategory {
27-
2830
long id;
2931
String name;
3032
String description;
3133
long maxExecutionTime;
3234

33-
public static ChainCategory tuple2ChainCategory(long id, String name, String description, BigInteger maxTime) {
35+
public static ChainCategory fromRawData(final long id, final String rawData) {
36+
log.debug("ChainCategory.fromRawData");
37+
final String[] parts = PoCoDataDecoder.toParts(rawData);
38+
final int offset = toBigInt(parts[0]).intValue() / 32;
39+
final int nameContribOffset = toBigInt(parts[offset]).intValue() / 32;
40+
final int descriptionContribOffset = toBigInt(parts[offset + 1]).intValue() / 32;
3441
return ChainCategory.builder()
3542
.id(id)
36-
.name(name)
37-
.description(description)
38-
.maxExecutionTime(maxTime.longValue() * 1000)
43+
.name(PoCoDataDecoder.decodeToAsciiString(parts, offset + nameContribOffset))
44+
.description(PoCoDataDecoder.decodeToAsciiString(parts, offset + descriptionContribOffset))
45+
.maxExecutionTime(toBigInt(parts[offset + 2]).longValue() * 1000)
3946
.build();
4047
}
4148
}
Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 IEXEC BLOCKCHAIN TECH
2+
* Copyright 2020-2025 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.
@@ -16,32 +16,37 @@
1616

1717
package com.iexec.commons.poco.chain;
1818

19-
import com.iexec.commons.poco.utils.BytesUtils;
19+
import com.iexec.commons.poco.encoding.PoCoDataDecoder;
2020
import lombok.Builder;
2121
import lombok.Value;
22-
import org.web3j.tuples.generated.Tuple4;
22+
import lombok.extern.slf4j.Slf4j;
2323

2424
import java.math.BigInteger;
2525

26+
import static com.iexec.commons.poco.chain.Web3jAbstractService.toBigInt;
27+
28+
@Slf4j
2629
@Value
2730
@Builder
2831
public class ChainContribution {
29-
3032
ChainContributionStatus status;
3133
String resultHash;
3234
String resultSeal;
3335
String enclaveChallenge;
36+
BigInteger weight;
3437

35-
public static ChainContribution tuple2Contribution(Tuple4<BigInteger, byte[], byte[], String> contribution) {
36-
if (contribution != null) {
38+
public static ChainContribution fromRawData(final String rawData) {
39+
log.debug("ChainContribution.fromRawData");
40+
final String[] parts = PoCoDataDecoder.toParts(rawData);
41+
if (parts.length == 5) {
3742
return ChainContribution.builder()
38-
.status(ChainContributionStatus.getValue(contribution.component1()))
39-
.resultHash(BytesUtils.bytesToString(contribution.component2()))
40-
.resultSeal(BytesUtils.bytesToString(contribution.component3()))
41-
.enclaveChallenge(contribution.component4())
43+
.status(ChainContributionStatus.values()[toBigInt(parts[0]).intValue()])
44+
.resultHash("0x" + parts[1])
45+
.resultSeal("0x" + parts[2])
46+
.enclaveChallenge("0x" + parts[3])
47+
.weight(toBigInt(parts[4]))
4248
.build();
4349
}
4450
return null;
4551
}
46-
4752
}

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

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import lombok.extern.slf4j.Slf4j;
2828
import org.web3j.crypto.Credentials;
2929
import org.web3j.ens.EnsResolutionException;
30-
import org.web3j.tuples.generated.Tuple3;
3130
import org.web3j.tx.RawTransactionManager;
3231
import org.web3j.tx.gas.ContractGasProvider;
3332
import org.web3j.tx.response.PollingTransactionReceiptProcessor;
@@ -286,25 +285,29 @@ public Optional<ChainTask> getChainTask(String chainTaskId) {
286285
return Optional.empty();
287286
}
288287

289-
public Optional<ChainAccount> getChainAccount(String walletAddress) {
288+
public Optional<ChainAccount> getChainAccount(final String walletAddress) {
290289
try {
291-
return Optional.of(ChainAccount.tuple2Account(
292-
iexecHubContract.viewAccountABILegacy(walletAddress).send()));
290+
final String txData = VIEW_ACCOUNT_SELECTOR +
291+
Numeric.toHexStringNoPrefixZeroPadded(Numeric.toBigInt(walletAddress), 64);
292+
return Optional.ofNullable(ChainAccount.fromRawData(
293+
web3jAbstractService.sendCall(credentials.getAddress(), iexecHubAddress, txData)));
293294
} catch (Exception e) {
294295
log.error("Failed to get ChainAccount [walletAddress:{}]", walletAddress, e);
295296
}
296297
return Optional.empty();
297298
}
298299

299-
public Optional<ChainContribution> getChainContribution(String chainTaskId,
300-
String workerAddress) {
300+
public Optional<ChainContribution> getChainContribution(final String chainTaskId,
301+
final String workerAddress) {
301302
try {
302-
return Optional.of(ChainContribution.tuple2Contribution(
303-
iexecHubContract.viewContributionABILegacy(
304-
BytesUtils.stringToBytes(chainTaskId), workerAddress).send()));
303+
final String txData = VIEW_CONTRIBUTION_SELECTOR +
304+
Numeric.toHexStringNoPrefixZeroPadded(Numeric.toBigInt(chainTaskId), 64) +
305+
Numeric.toHexStringNoPrefixZeroPadded(Numeric.toBigInt(workerAddress), 64);
306+
final String rawData = web3jAbstractService.sendCall(credentials.getAddress(), iexecHubAddress, txData);
307+
return Optional.ofNullable(ChainContribution.fromRawData(rawData));
305308
} catch (Exception e) {
306-
log.error("Failed to get ChainContribution [chainTaskId:{}" +
307-
", workerAddress:{}]", chainTaskId, workerAddress, e);
309+
log.error("Failed to get ChainContribution [chainTaskId:{}, workerAddress:{}]",
310+
chainTaskId, workerAddress, e);
308311
}
309312
return Optional.empty();
310313
}
@@ -337,13 +340,9 @@ public Optional<ChainCategory> getChainCategory(final long id) {
337340
*/
338341
private void retrieveCategory(final long id) {
339342
try {
340-
Tuple3<String, String, BigInteger> category = iexecHubContract
341-
.viewCategoryABILegacy(BigInteger.valueOf(id)).send();
342-
ChainCategory chainCategory = ChainCategory.tuple2ChainCategory(id,
343-
category.component1(),
344-
category.component2(),
345-
category.component3()
346-
);
343+
final String txData = VIEW_CATEGORY_SELECTOR + Numeric.toHexStringNoPrefixZeroPadded(BigInteger.valueOf(id), 64);
344+
final ChainCategory chainCategory = ChainCategory.fromRawData(
345+
id, web3jAbstractService.sendCall(credentials.getAddress(), iexecHubAddress, txData));
347346
if (chainCategory.getMaxExecutionTime() <= 0) {
348347
log.error("Category max execution time should be greater than zero " +
349348
"(likely a blockchain issue) [categoryId:{}, maxExecutionTime:{}]",
@@ -386,10 +385,21 @@ public Optional<ChainDataset> getChainDataset(final String datasetAddress) {
386385
return Optional.empty();
387386
}
388387

389-
public Optional<Integer> getWorkerScore(String address) {
388+
/**
389+
* Read worker score.
390+
* <p>
391+
* The score only changes in replicated deals when an actual replication occurs.
392+
*
393+
* @param address Worker address
394+
* @return The worker score
395+
* @see <a href="https://github.com/iExecBlockchainComputing/PoCo/blob/v6.1.0-contracts/contracts/facets/IexecPoco2Facet.sol#L462">distributeRewards</a>
396+
*/
397+
public Optional<Integer> getWorkerScore(final String address) {
390398
if (address != null && !address.isEmpty()) {
391399
try {
392-
BigInteger workerScore = iexecHubContract.viewScore(address).send();
400+
final String txData = VIEW_SCORE_SELECTOR +
401+
Numeric.toHexStringNoPrefixZeroPadded(Numeric.toBigInt(address), 64);
402+
final BigInteger workerScore = toBigInt(web3jAbstractService.sendCall(credentials.getAddress(), iexecHubAddress, txData));
393403
return Optional.of(workerScore.intValue());
394404
} catch (Exception e) {
395405
log.error("Failed to getWorkerScore [address:{}]", address, e);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ public class AccessorsEncoder {
5050
*/
5151
public static final String OWNER_SELECTOR = "0x8da5cb5b";
5252

53+
public static final String VIEW_ACCOUNT_SELECTOR = "0x6b55f4a5";
54+
public static final String VIEW_CATEGORY_SELECTOR = "0x4f5f44ec";
5355
public static final String VIEW_CONSUMED_SELECTOR = "0x4b2bec8c";
56+
public static final String VIEW_CONTRIBUTION_SELECTOR = "0xe741363b";
57+
public static final String VIEW_SCORE_SELECTOR = "0xdb230b52";
5458

5559
// app
5660
/**

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

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,34 +39,21 @@ public class LogTopic {
3939

4040

4141
public static String decode(String topic) {
42-
switch (topic) {
43-
case TRANSFER_EVENT:
44-
return "Transfer";
45-
case REWARD_EVENT:
46-
return "Reward";
47-
case SEIZE_EVENT:
48-
return "Seize";
49-
case LOCK_EVENT:
50-
return "Lock";
51-
case UNLOCK_EVENT:
52-
return "Unlock";
53-
case ORDERS_MATCHED_EVENT:
54-
return "OrdersMatched";
55-
case SCHEDULER_NOTICE_EVENT:
56-
return "SchedulerNotice";
57-
case TASK_INITIALIZE_EVENT:
58-
return "TaskInitialize";
59-
case TASK_CONTRIBUTE_EVENT:
60-
return "TaskContribute";
61-
case TASK_CONSENSUS_EVENT:
62-
return "TaskConsensus";
63-
case TASK_REVEAL_EVENT:
64-
return "TaskReveal";
65-
case TASK_FINALIZE_EVENT:
66-
return "TaskFinalize";
67-
default:
68-
return topic;
69-
}
42+
return switch (topic) {
43+
case TRANSFER_EVENT -> "Transfer";
44+
case REWARD_EVENT -> "Reward";
45+
case SEIZE_EVENT -> "Seize";
46+
case LOCK_EVENT -> "Lock";
47+
case UNLOCK_EVENT -> "Unlock";
48+
case ORDERS_MATCHED_EVENT -> "OrdersMatched";
49+
case SCHEDULER_NOTICE_EVENT -> "SchedulerNotice";
50+
case TASK_INITIALIZE_EVENT -> "TaskInitialize";
51+
case TASK_CONTRIBUTE_EVENT -> "TaskContribute";
52+
case TASK_CONSENSUS_EVENT -> "TaskConsensus";
53+
case TASK_REVEAL_EVENT -> "TaskReveal";
54+
case TASK_FINALIZE_EVENT -> "TaskFinalize";
55+
default -> topic;
56+
};
7057
}
7158

7259
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,24 @@
2929
@Slf4j
3030
@NoArgsConstructor(access = AccessLevel.PRIVATE)
3131
public class PoCoDataDecoder {
32+
/**
33+
* Extracts 32 bytes hex strings from a raw data and converts them back to ASCII characters.
34+
*
35+
* @param parts Arrays of 32 bytes hex strings
36+
* @param offset Where to find the first data which is the string length
37+
* @return The decoded string
38+
*/
3239
public static String decodeToAsciiString(final String[] parts, final int offset) {
3340
return BytesUtils.hexStringToAscii(decodeToHexString(parts, offset));
3441
}
3542

43+
/**
44+
* Extracts 32 bytes hex strings from a raw data.
45+
*
46+
* @param parts Arrays of 32 bytes hex strings
47+
* @param offset Where to find the first data which is the string length
48+
* @return The extracted hex string
49+
*/
3650
public static String decodeToHexString(final String[] parts, final int offset) {
3751
final int size = toBigInt(parts[offset]).intValue();
3852
log.debug("Size {}", size);
@@ -50,6 +64,12 @@ public static String decodeToHexString(final String[] parts, final int offset) {
5064
return sb.toString();
5165
}
5266

67+
/**
68+
* Remove hex prefix and split every 32 bytes, i.e. every 64 hex characters.
69+
*
70+
* @param rawData Raw data to parse
71+
* @return array containing 32 bytes length hex strings
72+
*/
5373
public static String[] toParts(final String rawData) {
5474
final String[] parts = Numeric.cleanHexPrefix(rawData).split("(?<=\\G.{64})");
5575
if (log.isTraceEnabled()) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2025 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.encoding;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.web3j.crypto.Hash;
21+
22+
import static com.iexec.commons.poco.encoding.AccessorsEncoder.*;
23+
import static org.assertj.core.api.Assertions.assertThat;
24+
25+
class AccessorsEncoderTests {
26+
private String getSelector(final String methodSignature) {
27+
return Hash.sha3String(methodSignature).substring(0, 10);
28+
}
29+
30+
@Test
31+
void checkSelectorValues() {
32+
assertThat(getSelector("callbackgas()")).isEqualTo(CALLBACKGAS_SELECTOR);
33+
assertThat(getSelector("contribution_deadline_ratio()")).isEqualTo(CONTRIBUTION_DEADLINE_RATIO_SELECTOR);
34+
assertThat(getSelector("final_deadline_ratio()")).isEqualTo(FINAL_DEADLINE_RATIO_SELECTOR);
35+
assertThat(getSelector("owner()")).isEqualTo(OWNER_SELECTOR);
36+
assertThat(getSelector("viewAccount(address)")).isEqualTo(VIEW_ACCOUNT_SELECTOR);
37+
assertThat(getSelector("viewCategory(uint256)")).isEqualTo(VIEW_CATEGORY_SELECTOR);
38+
assertThat(getSelector("viewConsumed(bytes32)")).isEqualTo(VIEW_CONSUMED_SELECTOR);
39+
assertThat(getSelector("viewContribution(bytes32,address)")).isEqualTo(VIEW_CONTRIBUTION_SELECTOR);
40+
assertThat(getSelector("viewScore(address)")).isEqualTo(VIEW_SCORE_SELECTOR);
41+
assertThat(getSelector("viewApp(address)")).isEqualTo(VIEW_APP_SELECTOR);
42+
assertThat(getSelector("viewDataset(address)")).isEqualTo(VIEW_DATASET_SELECTOR);
43+
}
44+
}

0 commit comments

Comments
 (0)