Skip to content

Commit f38d0fa

Browse files
authored
fix: make sure to serialize governance operations as part of embedded operations inside of CBOR. (#381)
1 parent 7f8163c commit f38d0fa

File tree

4 files changed

+77
-13
lines changed

4 files changed

+77
-13
lines changed

api/src/main/java/org/cardanofoundation/rosetta/common/util/CborEncodeUtil.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.List;
44

5+
import lombok.experimental.UtilityClass;
6+
57
import co.nstant.in.cbor.CborException;
68
import co.nstant.in.cbor.model.Array;
79
import co.nstant.in.cbor.model.UnicodeString;
@@ -11,28 +13,29 @@
1113

1214
import org.cardanofoundation.rosetta.common.mapper.OperationToCborMap;
1315

16+
@UtilityClass
1417
public class CborEncodeUtil {
1518

16-
private CborEncodeUtil() {
17-
18-
}
19-
2019
/**
2120
* Serialized extra Operations of type coin_spent, staking, pool, vote
21+
*
2222
* @param transaction serialized transaction
2323
* @param operations full list of operations
2424
* @param transactionMetadataHex transaction metadata in hex
2525
* @return serialized extra Operations of type coin_spent, staking, pool, vote
2626
* @throws CborException if serialization fails
2727
*/
28-
public static String encodeExtraData(String transaction, List<Operation> operations, String transactionMetadataHex)
29-
throws CborException {
28+
public String encodeExtraData(String transaction,
29+
List<Operation> operations,
30+
String transactionMetadataHex) throws CborException {
3031

3132
List<Operation> filteredExtraOperations = getTxExtraData(operations);
3233

3334
co.nstant.in.cbor.model.Map transactionExtraDataMap = new co.nstant.in.cbor.model.Map();
3435
Array operationArray = new Array();
36+
3537
filteredExtraOperations.forEach(operation -> operationArray.add(OperationToCborMap.convertToCborMap(operation)));
38+
3639
transactionExtraDataMap.put(new UnicodeString(Constants.OPERATIONS), operationArray);
3740
if (transactionMetadataHex != null) {
3841
transactionExtraDataMap.put(new UnicodeString(Constants.TRANSACTIONMETADATAHEX),
@@ -41,28 +44,33 @@ public static String encodeExtraData(String transaction, List<Operation> operati
4144
Array outputArray = new Array();
4245
outputArray.add(new UnicodeString(transaction));
4346
outputArray.add(transactionExtraDataMap);
47+
4448
return HexUtil.encodeHexString(
4549
com.bloxbean.cardano.client.common.cbor.CborSerializationUtil.serialize(outputArray,
4650
false));
4751
}
4852

4953
/**
5054
* Get all Operations which are of the type coin_spent, staking, pool, vote
51-
* @param operations Operations to be filtered
52-
* @return List of Operations of type coin_spent, staking, pool, vote
55+
* @param includeOperations operations to be accepted
56+
* @return List of Operations of type coin_spent, staking, pool, vote, governance
5357
*/
54-
private static List<Operation> getTxExtraData(List<Operation> operations){
55-
return operations.stream()
58+
/** friendly */ List<Operation> getTxExtraData(List<Operation> includeOperations) {
59+
return includeOperations.stream()
5660
.filter(operation -> {
5761
String coinAction = ObjectUtils.isEmpty(operation.getCoinChange()) ? null
5862
: operation.getCoinChange().getCoinAction().toString();
63+
5964
boolean coinActionStatement =
6065
!ObjectUtils.isEmpty(coinAction) && coinAction.equals(Constants.COIN_SPENT_ACTION);
66+
6167
return coinActionStatement ||
6268
Constants.STAKING_OPERATIONS.contains(operation.getType()) ||
6369
Constants.POOL_OPERATIONS.contains(operation.getType()) ||
64-
Constants.VOTE_OPERATIONS.contains(operation.getType());
70+
Constants.VOTE_OPERATIONS.contains(operation.getType()) ||
71+
Constants.GOVERNANCE_OPERATIONS.contains(operation.getType());
6572
}
73+
6674
).toList();
6775
}
6876

api/src/main/java/org/cardanofoundation/rosetta/common/util/Constants.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,20 @@ private Constants() {
100100
OperationType.STAKE_KEY_REGISTRATION.getValue(),
101101
OperationType.STAKE_KEY_DEREGISTRATION.getValue(),
102102
OperationType.WITHDRAWAL.getValue());
103+
103104
public static final List<String> POOL_OPERATIONS =
104105
List.of(OperationType.POOL_RETIREMENT.getValue(),
105106
OperationType.POOL_REGISTRATION.getValue(),
106107
OperationType.POOL_REGISTRATION_WITH_CERT.getValue());
108+
107109
public static final List<String> STAKE_POOL_OPERATIONS =
108110
List.of(OperationType.STAKE_DELEGATION.getValue(),
109111
OperationType.STAKE_KEY_REGISTRATION.getValue(),
110112
OperationType.STAKE_KEY_DEREGISTRATION.getValue(),
111113
OperationType.POOL_RETIREMENT.getValue(),
112114
OperationType.POOL_REGISTRATION.getValue(),
113115
OperationType.POOL_REGISTRATION_WITH_CERT.getValue());
116+
114117
public static final List<String> VOTE_OPERATIONS =
115118
List.of(OperationType.VOTE_REGISTRATION.getValue());
116119

@@ -125,6 +128,10 @@ private Constants() {
125128
public static final String OPERATION_TYPE_POOL_RETIREMENT = "poolRetirement";
126129
public static final String OPERATION_TYPE_VOTE_REGISTRATION = "voteRegistration";
127130

131+
public static final List<String> GOVERNANCE_OPERATIONS = List.of(
132+
OperationType.VOTE_DREP_DELEGATION.getValue()
133+
);
134+
128135
// Plomin hard fork governance related
129136
public static final String OPERATION_TYPE_DREP_VOTE_DELEGATION = "dRepVoteDelegation";
130137

api/src/test/java/org/cardanofoundation/rosetta/api/construction/service/PayloadsApiTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ void stakeKeyDeregistrationTest() throws Exception {
8181

8282
@Test
8383
void dRepDelegationTest() throws Exception {
84-
String expectedUnsignedTransaction = "8279018e6135303064393031303238313832353832303266323366643863636138333561663231663361633337356261633630316639376561643735663265373931343362646637316665326334626530343365386630313031383238323538316436316262343066316136343762633838633162643662373338646238656236363335376439323634373465613566666436626161373663396662313932373130383235383164363162623430663161363437626338386331626436623733386462386562363633353764393236343734656135666664366261613736633966623139396334303032316230303030303031346634366134306230303331393033653830346439303130323831383330393832303035383163626234306631613634376263383863316264366237333864623865623636333537643932363437346561356666643662616137366339666238323030353831633734393834666165346361313731356661316638373539663964383731303135616338376634343961383564656136636639393536646131a16a6f7065726174696f6e7381a6746f7065726174696f6e5f6964656e746966696572a265696e646578006d6e6574776f726b5f696e64657800676163636f756e74a16761646472657373783a616464723176786135707564786737376733736461646465636d773874766336686d796e79776e34396c6c747434666d766e3763706e6b63707866616d6f756e74a26863757272656e6379a26673796d626f6c6341444168646563696d616c73066576616c75656c2d39303030303030303030306b636f696e5f6368616e6765a26f636f696e5f6964656e746966696572a16a6964656e7469666965727842326632336664386363613833356166323166336163333735626163363031663937656164373566326537393134336264663731666532633462653034336538663a316b636f696e5f616374696f6e6a636f696e5f7370656e74667374617475736773756363657373647479706565696e707574";
84+
String expectedUnsignedTransaction = "8279018e6135303064393031303238313832353832303266323366643863636138333561663231663361633337356261633630316639376561643735663265373931343362646637316665326334626530343365386630313031383238323538316436316262343066316136343762633838633162643662373338646238656236363335376439323634373465613566666436626161373663396662313932373130383235383164363162623430663161363437626338386331626436623733386462386562363633353764393236343734656135666664366261613736633966623139396334303032316230303030303031346634366134306230303331393033653830346439303130323831383330393832303035383163626234306631613634376263383863316264366237333864623865623636333537643932363437346561356666643662616137366339666238323030353831633734393834666165346361313731356661316638373539663964383731303135616338376634343961383564656136636639393536646131a16a6f7065726174696f6e7382a6746f7065726174696f6e5f6964656e746966696572a265696e646578006d6e6574776f726b5f696e64657800676163636f756e74a16761646472657373783a616464723176786135707564786737376733736461646465636d773874766336686d796e79776e34396c6c747434666d766e3763706e6b63707866616d6f756e74a26863757272656e6379a26673796d626f6c6341444168646563696d616c73066576616c75656c2d39303030303030303030306b636f696e5f6368616e6765a26f636f696e5f6964656e746966696572a16a6964656e7469666965727842326632336664386363613833356166323166336163333735626163363031663937656164373566326537393134336264663731666532633462653034336538663a316b636f696e5f616374696f6e6a636f696e5f7370656e74667374617475736773756363657373647479706565696e707574a5746f7065726174696f6e5f6964656e746966696572a165696e64657803676163636f756e74a16761646472657373783b7374616b653175786135707564786737376733736461646465636d773874766336686d796e79776e34396c6c747434666d766e376361656b376135686d65746164617461a1727374616b696e675f63726564656e7469616ca2696865785f62797465737840314234303044363041414633344541463644434241423942424134363030314132333439373838364346313130363646373834363933334433304535414433466a63757276655f747970656c65647761726473323535313966737461747573677375636365737364747970657264526570566f746544656c65676174696f6e";
8585

8686
assertConstructionPayloads("testdata/construction/payloads/dRep_delegation.json",
8787
expectedUnsignedTransaction);

api/src/test/java/org/cardanofoundation/rosetta/common/util/CborEncodeUtilTest.java

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import com.bloxbean.cardano.client.exception.AddressExcepion;
1111
import com.bloxbean.cardano.client.exception.CborSerializationException;
1212
import com.fasterxml.jackson.databind.ObjectMapper;
13+
import org.mockito.Mockito;
14+
import org.openapitools.client.model.CoinAction;
15+
import org.openapitools.client.model.CoinChange;
1316
import org.openapitools.client.model.ConstructionPayloadsRequest;
1417
import org.openapitools.client.model.Operation;
1518

@@ -21,7 +24,8 @@
2124
import org.cardanofoundation.rosetta.common.model.cardano.transaction.UnsignedTransaction;
2225

2326
import static org.cardanofoundation.rosetta.common.util.Constants.MIN_DUMMY_FEE;
24-
import static org.junit.jupiter.api.Assertions.assertEquals;
27+
import static org.junit.jupiter.api.Assertions.*;
28+
import static org.mockito.Mockito.when;
2529

2630
class CborEncodeUtilTest {
2731

@@ -102,6 +106,51 @@ private String getEncodedTransaction(String requestPayloadFilename)
102106
);
103107
}
104108

109+
@Test
110+
void testGetTxExtraData() {
111+
Operation coinSpentOperation = createOperation(CoinAction.SPENT, "someType");
112+
Operation stakingOperation = createOperation(null, Constants.OPERATION_TYPE_STAKE_KEY_REGISTRATION);
113+
Operation poolOperation = createOperation(null, Constants.OPERATION_TYPE_POOL_REGISTRATION);
114+
Operation voteOperation = createOperation(null, Constants.OPERATION_TYPE_VOTE_REGISTRATION);
115+
Operation drepVoteDelegation = createOperation(null, Constants.OPERATION_TYPE_DREP_VOTE_DELEGATION);
116+
117+
Operation unrelatedOperation = createOperation(null, "unrelatedType");
118+
119+
List<Operation> inputOperations = List.of(
120+
coinSpentOperation,
121+
stakingOperation,
122+
poolOperation,
123+
voteOperation,
124+
drepVoteDelegation,
125+
unrelatedOperation
126+
);
127+
128+
List<Operation> result = CborEncodeUtil.getTxExtraData(inputOperations);
129+
130+
assertEquals(5, result.size());
131+
assertTrue(result.contains(coinSpentOperation));
132+
assertTrue(result.contains(stakingOperation));
133+
assertTrue(result.contains(poolOperation));
134+
assertTrue(result.contains(voteOperation));
135+
assertTrue(result.contains(drepVoteDelegation));
136+
137+
assertFalse(result.contains(unrelatedOperation));
138+
}
139+
140+
private Operation createOperation(CoinAction coinAction, String type) {
141+
Operation operation = Mockito.mock(Operation.class);
142+
CoinChange coinChange = Mockito.mock(CoinChange.class);
143+
144+
if (coinAction != null) {
145+
when(coinChange.getCoinAction()).thenReturn(coinAction);
146+
when(operation.getCoinChange()).thenReturn(coinChange);
147+
}
148+
149+
when(operation.getType()).thenReturn(type);
150+
151+
return operation;
152+
}
153+
105154
private ConstructionPayloadsRequest getRequest(String fileName) throws IOException {
106155
File file = new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource(fileName)).getFile());
107156

0 commit comments

Comments
 (0)