Skip to content

Commit 921e976

Browse files
author
Mateusz Czeladka
committed
feat: SPO / dReps governance actions voting support
1 parent f8b11e0 commit 921e976

32 files changed

+2421
-1499
lines changed

api/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@
271271
<sourceFolder>src/gen/java/main</sourceFolder>
272272
<useJakartaEe>true</useJakartaEe>
273273
<skipValidateSpec>true</skipValidateSpec>
274+
<useEnumCaseInsensitive>true</useEnumCaseInsensitive>
274275
<interfaceOnly>true</interfaceOnly>
275276
<!--suppress UnresolvedMavenProperty -->
276277
<additionalModelTypeAnnotations>@lombok.Builder @lombok.NoArgsConstructor

api/src/main/java/org/cardanofoundation/rosetta/api/block/mapper/TransactionMapper.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ public interface TransactionMapper {
5858
@Mapping(target = "metadata.drep", source = "model.drep", qualifiedByName = "convertDRepFromRosetta")
5959
Operation mapDRepDelegationToOperation(DRepDelegation model, OperationStatus status, int index);
6060

61+
@Mapping(target = "status", source = "status.status")
62+
@Mapping(target = "type", constant = Constants.OPERATION_TYPE_GOVERNANCE_CAST_VOTE)
63+
@Mapping(target = "operationIdentifier", source = "index", qualifiedByName = "OperationIdentifier")
64+
@Mapping(target = "metadata.govCastVoteParams.actionId", source = "governanceVote.govActionId", qualifiedByName = "convertGovActionIdFromRosetta")
65+
@Mapping(target = "metadata.govCastVoteParams.voter", source = "governanceVote.voter", qualifiedByName = "convertGovVoterFromRosetta")
66+
@Mapping(target = "metadata.govCastVoteParams.vote", source = "governanceVote.vote", qualifiedByName = "convertGovVoteFromRosetta")
67+
@Mapping(target = "metadata.govCastVoteParams.anchor", source = "governanceVote.anchor", qualifiedByName = "convertGovAnchorFromRosetta")
68+
Operation mapGovernanceVoteToOperation(GovernanceVote governanceVote, OperationStatus status, int index);
69+
6170
@Mapping(target = "type", constant = Constants.INPUT)
6271
@Mapping(target = "coinChange.coinAction", source = "model", qualifiedByName = "getCoinSpentAction")
6372
@Mapping(target = "metadata", source = "model.amounts", qualifiedByName = "mapAmountsToOperationMetadataInput")

api/src/main/java/org/cardanofoundation/rosetta/api/block/mapper/TransactionMapperUtils.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,18 @@
1010
import lombok.RequiredArgsConstructor;
1111

1212
import org.springframework.stereotype.Component;
13+
import com.bloxbean.cardano.client.transaction.spec.governance.Anchor;
14+
import com.bloxbean.cardano.client.transaction.spec.governance.Vote;
15+
import com.bloxbean.cardano.client.transaction.spec.governance.Voter;
16+
import com.bloxbean.cardano.client.transaction.spec.governance.actions.GovActionId;
1317
import com.bloxbean.cardano.yaci.core.model.certs.CertificateType;
1418
import org.mapstruct.Named;
1519
import org.openapitools.client.model.*;
1620

1721
import org.cardanofoundation.rosetta.api.account.model.domain.Amt;
1822
import org.cardanofoundation.rosetta.api.account.model.domain.Utxo;
1923
import org.cardanofoundation.rosetta.api.block.model.domain.DRepDelegation;
24+
import org.cardanofoundation.rosetta.api.block.model.domain.GovernanceVote;
2025
import org.cardanofoundation.rosetta.api.block.model.domain.StakeRegistration;
2126
import org.cardanofoundation.rosetta.common.enumeration.OperationType;
2227
import org.cardanofoundation.rosetta.common.mapper.DataMapper;
@@ -29,6 +34,47 @@ public class TransactionMapperUtils {
2934

3035
final ProtocolParamService protocolParamService;
3136

37+
@Named("convertGovAnchorFromRosetta")
38+
public GovAnchorParams convertGovAnchorFromRosetta(Anchor anchor) {
39+
return GovernanceVote.convertFromRosetta(anchor);
40+
}
41+
42+
@Named("convertGovAnchorToRosetta")
43+
public Anchor convertGovAnchorFromRosetta(GovAnchorParams params) {
44+
return GovernanceVote.convertToRosetta(params);
45+
}
46+
47+
@Named("convertGovVoteFromRosetta")
48+
public GovVoteParams convertGovVoteFromRosetta(Vote vote) {
49+
return GovernanceVote.convertFromRosetta(vote);
50+
}
51+
52+
@Named("convertGovVoteToRosetta")
53+
public Vote convertGovVoteToRosetta(GovVoteParams voteParams) {
54+
return GovernanceVote.convertToRosetta(voteParams);
55+
}
56+
57+
@Named("convertGovActionIdFromRosetta")
58+
public GovActionIdParams convertGovActionIdFromRosetta(GovActionId govActionId) {
59+
return GovernanceVote.convertFromRosetta(govActionId);
60+
}
61+
62+
@Named("convertGovActionIdToRosetta")
63+
public GovActionId convertGovActionIdToRosetta(GovActionIdParams govActionIdParams) {
64+
return GovernanceVote.convertToRosetta(govActionIdParams);
65+
}
66+
67+
@Named("convertGovVoterFromRosetta")
68+
public GovVoterParams convertGovVoterFromRosetta(Voter voter) {
69+
return GovernanceVote.convertFromRosetta(voter);
70+
}
71+
72+
@Named("convertGovVoterToRosetta")
73+
public Voter convertGovVoterToRosetta(GovVoterParams voterParams) {
74+
return GovernanceVote.convertToRosetta(voterParams);
75+
}
76+
77+
3278
@Named("convertDRepToRosetta")
3379
public DRepDelegation.DRep convertDRepToRosetta(DRepParams dRepParams) {
3480
return DRepDelegation.DRep.convertDRepToRosetta(dRepParams);
@@ -72,6 +118,7 @@ public OperationMetadata mapToOperationMetaData(boolean spent, List<Amt> amounts
72118
.build()
73119
)
74120
);
121+
75122
return Objects.isNull(operationMetadata.getTokenBundle()) ? null : operationMetadata;
76123
}
77124

api/src/main/java/org/cardanofoundation/rosetta/api/block/model/domain/BlockTx.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class BlockTx {
3030
protected List<StakeRegistration> stakeRegistrations;
3131
protected List<StakePoolDelegation> stakePoolDelegations;
3232
protected List<DRepDelegation> dRepDelegations;
33+
protected List<GovernanceVote> governanceVotes;
3334
protected List<PoolRegistration> poolRegistrations;
3435
protected List<PoolRetirement> poolRetirements;
3536
protected List<Withdrawal> withdrawals;
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package org.cardanofoundation.rosetta.api.block.model.domain;
2+
3+
import java.util.Optional;
4+
import javax.annotation.Nullable;
5+
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Data;
9+
import lombok.NoArgsConstructor;
10+
11+
import com.bloxbean.cardano.client.address.Credential;
12+
import com.bloxbean.cardano.client.transaction.spec.governance.Anchor;
13+
import com.bloxbean.cardano.client.transaction.spec.governance.Vote;
14+
import com.bloxbean.cardano.client.transaction.spec.governance.Voter;
15+
import com.bloxbean.cardano.client.transaction.spec.governance.VoterType;
16+
import com.bloxbean.cardano.client.transaction.spec.governance.actions.GovActionId;
17+
import com.bloxbean.cardano.client.util.HexUtil;
18+
import org.openapitools.client.model.*;
19+
20+
import org.cardanofoundation.rosetta.common.exception.ExceptionFactory;
21+
22+
@Data
23+
@Builder
24+
@NoArgsConstructor
25+
@AllArgsConstructor
26+
public class GovernanceVote {
27+
28+
private GovActionId govActionId;
29+
private Vote vote;
30+
private Voter voter;
31+
32+
@Nullable
33+
private Anchor anchor;
34+
35+
public static GovernanceVote convertToRosetta(GovCastVoteParams voteParams) {
36+
GovernanceVoteBuilder governanceVoteBuilder = GovernanceVote.builder()
37+
.govActionId(convertToRosetta(voteParams.getActionId()))
38+
.voter(convertToRosetta(voteParams.getVoter()))
39+
.vote(convertToRosetta(voteParams.getVote()));
40+
41+
Optional.ofNullable(voteParams.getAnchor()).ifPresent(govAnchorParams -> {
42+
governanceVoteBuilder.anchor(convertToRosetta(govAnchorParams));
43+
});
44+
45+
return governanceVoteBuilder.build();
46+
}
47+
48+
public static GovCastVoteParams convertFromRosetta(GovernanceVote governanceVote) {
49+
GovCastVoteParams.GovCastVoteParamsBuilder builder = GovCastVoteParams.builder();
50+
51+
builder.actionId(convertFromRosetta(governanceVote.getGovActionId()));
52+
builder.voter(convertFromRosetta(governanceVote.getVoter()));
53+
builder.vote(convertFromRosetta(governanceVote.getVote()));
54+
Optional.ofNullable(governanceVote.getAnchor()).ifPresent(govAnchorParams -> {
55+
builder.anchor(convertFromRosetta(govAnchorParams));
56+
});
57+
58+
return builder.build();
59+
}
60+
61+
public static GovAnchorParams convertFromRosetta(Anchor anchor) {
62+
return GovAnchorParams.builder()
63+
.url(anchor.getAnchorUrl())
64+
.dataHash(HexUtil.encodeHexString(anchor.getAnchorDataHash()))
65+
.build();
66+
}
67+
68+
public static Anchor convertToRosetta(GovAnchorParams govAnchorParams) {
69+
return Anchor.builder()
70+
.anchorUrl(govAnchorParams.getUrl())
71+
.anchorDataHash(HexUtil.decodeHexString(govAnchorParams.getDataHash()))
72+
.build();
73+
}
74+
75+
public static GovVoteParams convertFromRosetta(Vote vote) {
76+
return switch (vote) {
77+
case YES -> GovVoteParams.YES;
78+
case NO -> GovVoteParams.NO;
79+
case ABSTAIN -> GovVoteParams.ABSTAIN;
80+
};
81+
}
82+
83+
public static GovActionId convertToRosetta(GovActionIdParams govActionIdParams) {
84+
return GovActionId.builder()
85+
.govActionIndex(govActionIdParams.getIndex())
86+
.transactionId(govActionIdParams.getTxId())
87+
.build();
88+
}
89+
90+
public static GovActionIdParams convertFromRosetta(GovActionId govActionId) {
91+
return GovActionIdParams.builder()
92+
.index(govActionId.getGovActionIndex())
93+
.txId(govActionId.getTransactionId())
94+
.build();
95+
}
96+
97+
public static Voter convertToRosetta(GovVoterParams voterParams) {
98+
String voterId = voterParams.getId();
99+
100+
VoterType voteType = switch (voterParams.getType()) {
101+
case GovVoterTypeParams.DREP_KEY_HASH -> VoterType.DREP_KEY_HASH;
102+
case GovVoterTypeParams.STAKING_POOL_KEY_HASH -> VoterType.STAKING_POOL_KEY_HASH;
103+
case GovVoterTypeParams.DREP_SCRIPT_HASH -> VoterType.DREP_SCRIPT_HASH;
104+
};
105+
106+
return new Voter(voteType, credentialToRosetta(voterId, voteType));
107+
}
108+
109+
public static GovVoterParams convertFromRosetta(Voter voter) {
110+
GovVoterTypeParams govVoterTypeParams = switch (voter.getType()) {
111+
case VoterType.DREP_KEY_HASH -> GovVoterTypeParams.DREP_KEY_HASH;
112+
case VoterType.STAKING_POOL_KEY_HASH -> GovVoterTypeParams.STAKING_POOL_KEY_HASH;
113+
case VoterType.DREP_SCRIPT_HASH -> GovVoterTypeParams.DREP_SCRIPT_HASH;
114+
default -> throw ExceptionFactory.unsupportedVoterType();
115+
};
116+
117+
return GovVoterParams.builder()
118+
.id(convertFromRosetta(voter.getCredential()))
119+
.type(govVoterTypeParams)
120+
.build();
121+
}
122+
123+
private static Credential credentialToRosetta(String voterId, VoterType voterType) {
124+
return switch (voterType) {
125+
case DREP_KEY_HASH, STAKING_POOL_KEY_HASH -> Credential.fromKey(voterId);
126+
case DREP_SCRIPT_HASH -> Credential.fromScript(voterId);
127+
default -> throw ExceptionFactory.unsupportedVoterType();
128+
};
129+
}
130+
131+
private static String convertFromRosetta(Credential credential) {
132+
return HexUtil.encodeHexString(credential.getBytes());
133+
}
134+
135+
public static Vote convertToRosetta(GovVoteParams voteParams) {
136+
return switch (voteParams) {
137+
case GovVoteParams.YES -> Vote.YES;
138+
case GovVoteParams.NO -> Vote.NO;
139+
case GovVoteParams.ABSTAIN -> Vote.ABSTAIN;
140+
};
141+
}
142+
143+
}

api/src/main/java/org/cardanofoundation/rosetta/api/block/model/domain/ProcessOperations.java

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

33
import java.math.BigInteger;
44
import java.util.ArrayList;
5+
import java.util.List;
56

67
import lombok.AllArgsConstructor;
78
import lombok.Getter;
@@ -20,14 +21,16 @@
2021
@NoArgsConstructor
2122
public class ProcessOperations {
2223

23-
private ArrayList<TransactionInput> transactionInputs = new ArrayList<>();
24-
private ArrayList<TransactionOutput> transactionOutputs = new ArrayList<>();
25-
private ArrayList<Certificate> certificates = new ArrayList<>();
26-
private ArrayList<Withdrawal> withdrawals = new ArrayList<>();
27-
private ArrayList<String> addresses = new ArrayList<>();
28-
private ArrayList<BigInteger> inputAmounts = new ArrayList<>();
29-
private ArrayList<BigInteger> outputAmounts = new ArrayList<>();
30-
private ArrayList<BigInteger> withdrawalAmounts = new ArrayList<>();
24+
private List<TransactionInput> transactionInputs = new ArrayList<>();
25+
private List<TransactionOutput> transactionOutputs = new ArrayList<>();
26+
private List<Certificate> certificates = new ArrayList<>();
27+
private List<Withdrawal> withdrawals = new ArrayList<>();
28+
private List<String> addresses = new ArrayList<>();
29+
private List<BigInteger> inputAmounts = new ArrayList<>();
30+
private List<BigInteger> outputAmounts = new ArrayList<>();
31+
private List<BigInteger> withdrawalAmounts = new ArrayList<>();
32+
private List<GovernanceVote> governanceVotes = new ArrayList<>();
33+
3134
private double stakeKeyRegistrationsCount = 0.0;
3235
private double stakeKeyDeRegistrationsCount = 0.0;
3336
private double poolRegistrationsCount = 0.0;

api/src/main/java/org/cardanofoundation/rosetta/api/block/model/domain/ProcessOperationsReturn.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.cardanofoundation.rosetta.api.block.model.domain;
22

3-
import java.util.ArrayList;
3+
import java.util.List;
44
import java.util.Set;
55

66
import lombok.AllArgsConstructor;
@@ -20,10 +20,11 @@
2020
@Setter
2121
public class ProcessOperationsReturn {
2222

23-
private ArrayList<TransactionInput> transactionInputs;
24-
private ArrayList<TransactionOutput> transactionOutputs;
25-
private ArrayList<Certificate> certificates;
26-
private ArrayList<Withdrawal> withdrawals;
23+
private List<TransactionInput> transactionInputs;
24+
private List<TransactionOutput> transactionOutputs;
25+
private List<Certificate> certificates;
26+
private List<GovernanceVote> governanceVotes;
27+
private List<Withdrawal> withdrawals;
2728
private Set<String> addresses;
2829
private Long fee;
2930
private AuxiliaryData voteRegistrationMetadata;

api/src/main/java/org/cardanofoundation/rosetta/api/block/service/LedgerBlockServiceImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,12 @@ void populateTransaction(BlockTx transaction,
303303
.filter(tx -> tx.getTxHash().equals(transaction.getHash()))
304304
.map(transactionMapper::mapEntityToPoolRetirement)
305305
.toList());
306+
// TODO dRep Vote Delegations
307+
//transaction.setDRepDelegations(fetched.delegations
308+
309+
// TODO governance votes
310+
//transaction.setGovernanceVotes(fetched.);
311+
306312
}
307313

308314
private void populateUtxo(Utxo utxo, Map<UtxoKey, AddressUtxoEntity> utxoMap) {

api/src/main/java/org/cardanofoundation/rosetta/api/construction/service/CardanoConstructionServiceImpl.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@
3333
import com.bloxbean.cardano.client.exception.CborSerializationException;
3434
import com.bloxbean.cardano.client.transaction.spec.*;
3535
import com.bloxbean.cardano.client.transaction.spec.TransactionBody.TransactionBodyBuilder;
36+
import com.bloxbean.cardano.client.transaction.spec.governance.Anchor;
37+
import com.bloxbean.cardano.client.transaction.spec.governance.VotingProcedure;
38+
import com.bloxbean.cardano.client.transaction.spec.governance.VotingProcedures;
3639
import com.bloxbean.cardano.client.util.HexUtil;
3740
import org.apache.commons.lang3.ObjectUtils;
3841
import org.openapitools.client.model.*;
3942

43+
import org.cardanofoundation.rosetta.api.block.model.domain.GovernanceVote;
4044
import org.cardanofoundation.rosetta.api.block.model.domain.ProcessOperations;
4145
import org.cardanofoundation.rosetta.api.block.model.domain.ProcessOperationsReturn;
4246
import org.cardanofoundation.rosetta.api.block.model.domain.ProtocolParams;
@@ -301,9 +305,9 @@ public TransactionWitnessSet getWitnessesForTransaction(List<Signatures> signatu
301305
}
302306

303307
public UnsignedTransaction createUnsignedTransaction(Network network,
304-
List<Operation> operations,
305-
long ttl,
306-
Long calculatedFee) throws CborSerializationException, CborException, AddressExcepion {
308+
List<Operation> operations,
309+
long ttl,
310+
Long calculatedFee) throws CborSerializationException, CborException, AddressExcepion {
307311
log.info(
308312
"[createUnsignedTransaction] About to create an unsigned transaction with {} operations",
309313
operations.size());
@@ -330,6 +334,33 @@ public UnsignedTransaction createUnsignedTransaction(Network network,
330334
if (!CollectionUtils.isEmpty(opRetDto.getWithdrawals())) {
331335
transactionBodyBuilder.withdrawals(opRetDto.getWithdrawals());
332336
}
337+
if (!opRetDto.getGovernanceVotes().isEmpty()) {
338+
VotingProcedures votingProcedures = new VotingProcedures();
339+
340+
for (GovernanceVote governanceVote : opRetDto.getGovernanceVotes()) {
341+
log.info("[createUnsignedTransaction] Processing governance vote: {}",
342+
governanceVote.getGovActionId());
343+
344+
VotingProcedure.VotingProcedureBuilder votingProcedureBuilder = VotingProcedure.builder();
345+
346+
votingProcedureBuilder
347+
.vote(governanceVote.getVote());
348+
349+
if (governanceVote.getAnchor() != null) {
350+
Anchor anchor = governanceVote.getAnchor();
351+
352+
votingProcedureBuilder.anchor(com.bloxbean.cardano.client.transaction.spec.governance.Anchor.builder()
353+
.anchorDataHash(anchor.getAnchorDataHash())
354+
.anchorUrl(anchor.getAnchorUrl())
355+
.build());
356+
}
357+
358+
votingProcedures.add(governanceVote.getVoter(), governanceVote.getGovActionId(), votingProcedureBuilder.build());
359+
}
360+
361+
transactionBodyBuilder.votingProcedures(votingProcedures);
362+
}
363+
333364
transactionBodyBuilder.fee(BigInteger.valueOf(calculatedFee));
334365

335366
TransactionBody transactionBody = transactionBodyBuilder.build();
@@ -400,6 +431,7 @@ private ProcessOperationsReturn fillProcessOperationsReturnObject(ProcessOperati
400431
processOperationsDto.setTransactionInputs(result.getTransactionInputs());
401432
processOperationsDto.setTransactionOutputs(result.getTransactionOutputs());
402433
processOperationsDto.setCertificates(result.getCertificates());
434+
processOperationsDto.setGovernanceVotes(result.getGovernanceVotes());
403435
processOperationsDto.setWithdrawals(result.getWithdrawals());
404436
Set<String> addresses = new HashSet<>(result.getAddresses());
405437
processOperationsDto.setAddresses(addresses);

0 commit comments

Comments
 (0)