Skip to content

Commit c07cf70

Browse files
authored
feat: refactor EIP-712 implementation with a new interface and applies it to iExec orders (#145)
1 parent adc4f2c commit c07cf70

File tree

14 files changed

+294
-48
lines changed

14 files changed

+294
-48
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

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

19+
import com.iexec.commons.poco.eip712.EIP712Domain;
1920
import com.iexec.commons.poco.eip712.EIP712Entity;
21+
import com.iexec.commons.poco.eip712.EIP712TypedData;
22+
import com.iexec.commons.poco.order.Order;
2023
import com.iexec.commons.poco.security.Signature;
2124
import com.iexec.commons.poco.utils.BytesUtils;
2225
import com.iexec.commons.poco.utils.EthAddress;
@@ -111,6 +114,10 @@ public Signature signMessageHash(String messageHash) {
111114
return SignatureUtils.signMessageHashAndGetSignature(messageHash, credentials.getEcKeyPair());
112115
}
113116

117+
/**
118+
* @deprecated use signTypedDataForDomain instead
119+
*/
120+
@Deprecated(forRemoval = true)
114121
public String signEIP712Entity(EIP712Entity<?> eip712Entity) {
115122
final String signature = eip712Entity.signMessage(credentials.getEcKeyPair());
116123
if (StringUtils.isEmpty(signature)) {
@@ -120,6 +127,23 @@ public String signEIP712Entity(EIP712Entity<?> eip712Entity) {
120127
return signature;
121128
}
122129

130+
/**
131+
* Hashes and signs structured type data following EIP-712
132+
*
133+
* @param typedData structured data implementing {@link EIP712TypedData} to hash and sign
134+
* @param domain EIP712 domain describing the target for which the data is hashed and signed
135+
* @return a valid signature
136+
* @see <a href="https://eips.ethereum.org/EIPS/eip-712">EIP-712</a>
137+
*/
138+
public String signTypedDataForDomain(final EIP712TypedData typedData, final EIP712Domain domain) {
139+
return typedData.sign(credentials.getEcKeyPair(), domain);
140+
}
141+
142+
public Order signOrderForDomain(final Order order, final EIP712Domain domain) {
143+
final String sig = signTypedDataForDomain(order, domain);
144+
return order.withSignature(sig);
145+
}
146+
123147
/**
124148
* Builds an authorization token for given {@link EIP712Entity}.
125149
* <p>

src/main/java/com/iexec/commons/poco/eip712/EIP712Entity.java

Lines changed: 5 additions & 1 deletion
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.
@@ -27,6 +27,10 @@
2727
import java.util.stream.Collectors;
2828
import java.util.stream.Stream;
2929

30+
/**
31+
* @deprecated implements {@code EIP712TypedData} interface instead
32+
*/
33+
@Deprecated(forRemoval = true)
3034
@Slf4j
3135
@NoArgsConstructor
3236
public abstract class EIP712Entity<M> implements EIP712<M> {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.eip712;
18+
19+
import com.iexec.commons.poco.utils.HashUtils;
20+
import com.iexec.commons.poco.utils.SignatureUtils;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import org.web3j.crypto.ECKeyPair;
24+
25+
public interface EIP712TypedData {
26+
Logger log = LoggerFactory.getLogger(EIP712TypedData.class);
27+
28+
default String computeHash(final EIP712Domain domain) {
29+
final String domainSeparator = domain.getDomainSeparator();
30+
final String messageHash = computeMessageHash();
31+
final String hash = HashUtils.concatenateAndHash("0x1901", domainSeparator, messageHash);
32+
if (log.isDebugEnabled()) {
33+
log.debug("domainSeparator {}", domainSeparator);
34+
log.debug("messageHash {}", messageHash);
35+
log.debug("hash {}", hash);
36+
}
37+
return hash;
38+
}
39+
40+
String computeMessageHash();
41+
42+
default String sign(final ECKeyPair ecKeyPair, final EIP712Domain domain) {
43+
return SignatureUtils.signAsString(computeHash(domain), ecKeyPair);
44+
}
45+
}

src/main/java/com/iexec/commons/poco/order/AppOrder.java

Lines changed: 27 additions & 1 deletion
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.
@@ -19,18 +19,25 @@
1919
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2020
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
2121
import com.iexec.commons.poco.contract.generated.IexecHubContract;
22+
import com.iexec.commons.poco.eip712.EIP712Utils;
23+
import com.iexec.commons.poco.utils.HashUtils;
2224
import lombok.Builder;
2325
import lombok.EqualsAndHashCode;
2426
import lombok.Value;
27+
import lombok.extern.slf4j.Slf4j;
2528
import org.web3j.utils.Numeric;
2629

2730
import java.math.BigInteger;
31+
import java.util.stream.Stream;
2832

33+
@Slf4j
2934
@Value
3035
@EqualsAndHashCode(callSuper = true)
3136
@JsonDeserialize(builder = AppOrder.AppOrderBuilder.class)
3237
public class AppOrder extends Order {
3338

39+
private static final String EIP712_TYPE = "AppOrder(address app,uint256 appprice,uint256 volume,bytes32 tag,address datasetrestrict,address workerpoolrestrict,address requesterrestrict,bytes32 salt)";
40+
3441
String app;
3542
BigInteger appprice;
3643
String datasetrestrict;
@@ -70,6 +77,25 @@ public AppOrder withSignature(String signature) {
7077
);
7178
}
7279

80+
// region EIP-712
81+
public String computeMessageHash() {
82+
final String[] encodedValues = Stream.of(EIP712_TYPE, app, appprice, volume, tag, datasetrestrict, workerpoolrestrict, requesterrestrict, salt)
83+
.map(EIP712Utils::encodeData)
84+
.toArray(String[]::new);
85+
if (log.isDebugEnabled()) {
86+
log.debug("{}", EIP712_TYPE);
87+
for (String value : encodedValues) {
88+
log.debug("{}", value);
89+
}
90+
}
91+
return HashUtils.concatenateAndHash(encodedValues);
92+
}
93+
// endregion
94+
95+
/**
96+
* @deprecated no more used
97+
*/
98+
@Deprecated(forRemoval = true)
7399
public IexecHubContract.AppOrder toHubContract() {
74100
return new IexecHubContract.AppOrder(
75101
this.app,

src/main/java/com/iexec/commons/poco/order/DatasetOrder.java

Lines changed: 27 additions & 1 deletion
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.
@@ -19,18 +19,25 @@
1919
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2020
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
2121
import com.iexec.commons.poco.contract.generated.IexecHubContract;
22+
import com.iexec.commons.poco.eip712.EIP712Utils;
23+
import com.iexec.commons.poco.utils.HashUtils;
2224
import lombok.Builder;
2325
import lombok.EqualsAndHashCode;
2426
import lombok.Value;
27+
import lombok.extern.slf4j.Slf4j;
2528
import org.web3j.utils.Numeric;
2629

2730
import java.math.BigInteger;
31+
import java.util.stream.Stream;
2832

33+
@Slf4j
2934
@Value
3035
@EqualsAndHashCode(callSuper = true)
3136
@JsonDeserialize(builder = DatasetOrder.DatasetOrderBuilder.class)
3237
public class DatasetOrder extends Order {
3338

39+
private static final String EIP712_TYPE = "DatasetOrder(address dataset,uint256 datasetprice,uint256 volume,bytes32 tag,address apprestrict,address workerpoolrestrict,address requesterrestrict,bytes32 salt)";
40+
3441
String dataset;
3542
BigInteger datasetprice;
3643
String apprestrict;
@@ -69,6 +76,25 @@ public DatasetOrder withSignature(String signature) {
6976
);
7077
}
7178

79+
// region EIP-712
80+
public String computeMessageHash() {
81+
final String[] encodedValues = Stream.of(EIP712_TYPE, dataset, datasetprice, volume, tag, apprestrict, workerpoolrestrict, requesterrestrict, salt)
82+
.map(EIP712Utils::encodeData)
83+
.toArray(String[]::new);
84+
if (log.isDebugEnabled()) {
85+
log.debug("{}", EIP712_TYPE);
86+
for (String value : encodedValues) {
87+
log.debug("{}", value);
88+
}
89+
}
90+
return HashUtils.concatenateAndHash(encodedValues);
91+
}
92+
// endregion
93+
94+
/**
95+
* @deprecated no more used
96+
*/
97+
@Deprecated(forRemoval = true)
7298
public IexecHubContract.DatasetOrder toHubContract() {
7399
return new IexecHubContract.DatasetOrder(
74100
this.dataset,

src/main/java/com/iexec/commons/poco/order/Order.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020 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.
@@ -17,15 +17,18 @@
1717
package com.iexec.commons.poco.order;
1818

1919
import com.fasterxml.jackson.annotation.JsonIgnore;
20+
import com.iexec.commons.poco.eip712.EIP712TypedData;
2021
import lombok.AccessLevel;
2122
import lombok.AllArgsConstructor;
2223
import lombok.Getter;
24+
import lombok.extern.slf4j.Slf4j;
2325

2426
import java.math.BigInteger;
2527

28+
@Slf4j
2629
@Getter
2730
@AllArgsConstructor(access = AccessLevel.PROTECTED)
28-
public abstract class Order {
31+
public abstract class Order implements EIP712TypedData {
2932

3033
protected final BigInteger volume;
3134
protected final String tag;

src/main/java/com/iexec/commons/poco/order/RequestOrder.java

Lines changed: 27 additions & 1 deletion
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.
@@ -19,18 +19,25 @@
1919
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2020
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
2121
import com.iexec.commons.poco.contract.generated.IexecHubContract;
22+
import com.iexec.commons.poco.eip712.EIP712Utils;
23+
import com.iexec.commons.poco.utils.HashUtils;
2224
import lombok.Builder;
2325
import lombok.EqualsAndHashCode;
2426
import lombok.Value;
27+
import lombok.extern.slf4j.Slf4j;
2528
import org.web3j.utils.Numeric;
2629

2730
import java.math.BigInteger;
31+
import java.util.stream.Stream;
2832

33+
@Slf4j
2934
@Value
3035
@EqualsAndHashCode(callSuper = true)
3136
@JsonDeserialize(builder = RequestOrder.RequestOrderBuilder.class)
3237
public class RequestOrder extends Order {
3338

39+
private static final String EIP712_TYPE = "RequestOrder(address app,uint256 appmaxprice,address dataset,uint256 datasetmaxprice,address workerpool,uint256 workerpoolmaxprice,address requester,uint256 volume,bytes32 tag,uint256 category,uint256 trust,address beneficiary,address callback,string params,bytes32 salt)";
40+
3441
String app;
3542
BigInteger appmaxprice;
3643
String dataset;
@@ -92,6 +99,25 @@ public RequestOrder withSignature(String signature) {
9299
);
93100
}
94101

102+
// region EIP-712
103+
public String computeMessageHash() {
104+
final String[] encodedValues = Stream.of(EIP712_TYPE, app, appmaxprice, dataset, datasetmaxprice, workerpool, workerpoolmaxprice, requester, volume, tag, category, trust, beneficiary, callback, params, salt)
105+
.map(EIP712Utils::encodeData)
106+
.toArray(String[]::new);
107+
if (log.isDebugEnabled()) {
108+
log.debug("{}", EIP712_TYPE);
109+
for (String value : encodedValues) {
110+
log.debug("{}", value);
111+
}
112+
}
113+
return HashUtils.concatenateAndHash(encodedValues);
114+
}
115+
// endregion
116+
117+
/**
118+
* @deprecated no more used
119+
*/
120+
@Deprecated(forRemoval = true)
95121
public IexecHubContract.RequestOrder toHubContract() {
96122
return new IexecHubContract.RequestOrder(
97123
this.app,

src/main/java/com/iexec/commons/poco/order/WorkerpoolOrder.java

Lines changed: 27 additions & 3 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.
@@ -19,20 +19,25 @@
1919
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2020
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
2121
import com.iexec.commons.poco.contract.generated.IexecHubContract;
22+
import com.iexec.commons.poco.eip712.EIP712Utils;
23+
import com.iexec.commons.poco.utils.HashUtils;
2224
import lombok.Builder;
2325
import lombok.EqualsAndHashCode;
24-
import lombok.ToString;
2526
import lombok.Value;
27+
import lombok.extern.slf4j.Slf4j;
2628
import org.web3j.utils.Numeric;
2729

2830
import java.math.BigInteger;
31+
import java.util.stream.Stream;
2932

33+
@Slf4j
3034
@Value
3135
@EqualsAndHashCode(callSuper = true)
32-
@ToString(callSuper = true)
3336
@JsonDeserialize(builder = WorkerpoolOrder.WorkerpoolOrderBuilder.class)
3437
public class WorkerpoolOrder extends Order {
3538

39+
private static final String EIP712_TYPE = "WorkerpoolOrder(address workerpool,uint256 workerpoolprice,uint256 volume,bytes32 tag,uint256 category,uint256 trust,address apprestrict,address datasetrestrict,address requesterrestrict,bytes32 salt)";
40+
3641
String workerpool;
3742
BigInteger workerpoolprice;
3843
BigInteger trust;
@@ -78,6 +83,25 @@ public WorkerpoolOrder withSignature(String signature) {
7883
);
7984
}
8085

86+
// region EIP-712
87+
public String computeMessageHash() {
88+
final String[] encodedValues = Stream.of(EIP712_TYPE, workerpool, workerpoolprice, volume, tag, category, trust, apprestrict, datasetrestrict, requesterrestrict, salt)
89+
.map(EIP712Utils::encodeData)
90+
.toArray(String[]::new);
91+
if (log.isDebugEnabled()) {
92+
log.debug("{}", EIP712_TYPE);
93+
for (String value : encodedValues) {
94+
log.debug("{}", value);
95+
}
96+
}
97+
return HashUtils.concatenateAndHash(encodedValues);
98+
}
99+
// endregion
100+
101+
/**
102+
* @deprecated no more used
103+
*/
104+
@Deprecated(forRemoval = true)
81105
public IexecHubContract.WorkerpoolOrder toHubContract() {
82106
return new IexecHubContract.WorkerpoolOrder(
83107
this.workerpool,

0 commit comments

Comments
 (0)