Skip to content

Commit c3e6685

Browse files
authored
Feat/ra 22 implement construction payload (#91)
* feat: initial implementation of construction preprocess * refactor: refactoring this pullrequest * refactor: renamed functions and added utility classes * refactor: added cardano metadata objects to api.json and api.yaml. Removed all unused metadata classes. Reworked the code to be working again * doc: added documentation of api extensions * chore: added tests * chore: merged main * refactor: renamed domain classes * feat: implemented construction Payload * refactor: refactored and added postman tests * refactor: renamed file * chore: added Tests for encoder * chore: added Tests for encoder * chore: merged master * chore: Merged master * refactored and preparing the pull Request * refactored and preparing the pull Request * refactor: fixed a potential error regarding networkTypes
1 parent b250190 commit c3e6685

39 files changed

+1566
-3652
lines changed

api/src/main/java/org/cardanofoundation/rosetta/api/construction/service/impl/ConstructionApiServiceImpl.java

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,27 @@
55
import co.nstant.in.cbor.model.UnicodeString;
66
import com.bloxbean.cardano.client.exception.AddressExcepion;
77
import com.bloxbean.cardano.client.exception.CborSerializationException;
8+
import java.util.List;
89
import java.util.Optional;
910
import lombok.RequiredArgsConstructor;
1011
import lombok.extern.slf4j.Slf4j;
12+
import org.apache.commons.lang3.ObjectUtils;
1113
import org.cardanofoundation.rosetta.api.block.model.entity.ProtocolParams;
1214
import org.cardanofoundation.rosetta.common.enumeration.NetworkIdentifierType;
15+
import org.cardanofoundation.rosetta.common.enumeration.NetworkIdentifierType;
16+
import org.cardanofoundation.rosetta.common.exception.ExceptionFactory;
1317
import org.cardanofoundation.rosetta.common.mapper.DataMapper;
1418
import org.cardanofoundation.rosetta.common.enumeration.AddressType;
1519

1620
import org.cardanofoundation.rosetta.common.enumeration.NetworkEnum;
21+
import org.cardanofoundation.rosetta.common.model.cardano.transaction.UnsignedTransaction;
1722
import org.cardanofoundation.rosetta.common.services.CardanoAddressService;
23+
import org.cardanofoundation.rosetta.common.services.CardanoConfigService;
1824
import org.cardanofoundation.rosetta.common.services.CardanoService;
1925
import org.cardanofoundation.rosetta.api.construction.service.ConstructionApiService;
2026
import org.cardanofoundation.rosetta.common.services.LedgerDataProviderService;
2127
import org.cardanofoundation.rosetta.common.util.Constants;
28+
import org.cardanofoundation.rosetta.common.util.CborEncodeUtil;
2229
import org.openapitools.client.model.*;
2330
import org.springframework.stereotype.Service;
2431

@@ -33,6 +40,7 @@ public class ConstructionApiServiceImpl implements ConstructionApiService {
3340
private final CardanoAddressService cardanoAddressService;
3441
private final CardanoService cardanoService;
3542
private final LedgerDataProviderService ledgerService;
43+
private final CardanoConfigService cardanoConfigService;
3644
private final DataMapper dataMapper;
3745

3846
@Override
@@ -101,8 +109,50 @@ public ConstructionMetadataResponse constructionMetadataService(
101109

102110
@Override
103111
public ConstructionPayloadsResponse constructionPayloadsService(
104-
ConstructionPayloadsRequest constructionPayloadsRequest) {
105-
return null;
112+
ConstructionPayloadsRequest constructionPayloadsRequest)
113+
throws CborException, AddressExcepion, IOException, CborSerializationException {
114+
int ttl = constructionPayloadsRequest.getMetadata().getTtl();
115+
List<Operation> operations = constructionPayloadsRequest.getOperations();
116+
117+
checkOperationsHaveIdentifier(operations);
118+
119+
NetworkIdentifierType networkIdentifier = NetworkIdentifierType.findByName(constructionPayloadsRequest.getNetworkIdentifier().getNetwork());
120+
log.info(operations + "[constuctionPayloads] Operations about to be processed");
121+
122+
ProtocolParameters protocolParameters = constructionPayloadsRequest.getMetadata()
123+
.getProtocolParameters();
124+
String keyDeposit;
125+
String poolDeposit;
126+
// TODO need to convert OpenAPI ProcotolParameters to domain ProtocolParams. Then merge with the one from indexer/config
127+
if(protocolParameters != null) {
128+
keyDeposit = protocolParameters.getKeyDeposit();
129+
poolDeposit = protocolParameters.getPoolDeposit();
130+
} else {
131+
ProtocolParams protocolParametersFromIndexerAndConfig = ledgerService.findProtocolParametersFromIndexerAndConfig();
132+
keyDeposit = protocolParametersFromIndexerAndConfig.getKeyDeposit().toString();
133+
poolDeposit = protocolParametersFromIndexerAndConfig.getPoolDeposit().toString();
134+
}
135+
136+
UnsignedTransaction unsignedTransaction = cardanoService.createUnsignedTransaction(
137+
networkIdentifier, operations, ttl,
138+
new DepositParameters(keyDeposit,
139+
poolDeposit));
140+
List<SigningPayload> payloads = cardanoService.constructPayloadsForTransactionBody(
141+
unsignedTransaction.hash(), unsignedTransaction.addresses());
142+
String unsignedTransactionString = CborEncodeUtil.encodeExtraData(
143+
unsignedTransaction.bytes(),
144+
constructionPayloadsRequest.getOperations(),
145+
unsignedTransaction.metadata());
146+
return new ConstructionPayloadsResponse(unsignedTransactionString, payloads);
147+
}
148+
149+
private static void checkOperationsHaveIdentifier(List<Operation> operations) {
150+
for (int i = 0; i < operations.size(); i++) {
151+
if (operations.get(i).getOperationIdentifier() == null) {
152+
throw ExceptionFactory.unspecifiedError(
153+
"body[" + i + "]" + " should have required property operation_identifier");
154+
}
155+
}
106156
}
107157

108158
@Override

api/src/main/java/org/cardanofoundation/rosetta/common/enumeration/NetworkIdentifierType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
public enum NetworkIdentifierType {
66
CARDANO_MAINNET_NETWORK("mainnet", 1, 764824073L),
77
CARDANO_TESTNET_NETWORK("testnet", 0, 1097911063L),
8-
CARDANO_PREPROD_NETWORK("preprod", 0, 1097911063L);
8+
CARDANO_PREPROD_NETWORK("preprod", 0, 1);
99

1010
private String networkId;
1111
private int value;

api/src/main/java/org/cardanofoundation/rosetta/common/mapper/DataMapper.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public static Amount mapAmount(String value) {
167167
* @return The Rosetta compatible Amount
168168
*/
169169
public static Amount mapAmount(String value, String symbol, Integer decimals,
170-
Map<String, Object> metadata) {
170+
CurrencyMetadata metadata) {
171171
if (Objects.isNull(symbol)) {
172172
symbol = Constants.ADA;
173173
}
@@ -197,7 +197,8 @@ public static AccountBalanceResponse mapToAccountBalanceResponse(Block block, Li
197197
if (sum > 0) {
198198
amounts.add(mapAmount(String.valueOf(sum)));
199199
}
200-
nonLovelaceBalances.forEach(balance -> amounts.add(mapAmount(balance.quantity().toString(), Hex.encodeHexString(balance.assetName().getBytes()), Constants.MULTI_ASSET_DECIMALS, Map.of("policyId", balance.policy()))));
200+
nonLovelaceBalances.forEach(balance -> amounts.add(mapAmount(balance.quantity().toString(), Hex.encodeHexString(balance.assetName().getBytes()), Constants.MULTI_ASSET_DECIMALS, new CurrencyMetadata(
201+
balance.policy()))));
201202
return AccountBalanceResponse.builder()
202203
.blockIdentifier(BlockIdentifier.builder()
203204
.hash(block.getHash())

api/src/main/java/org/cardanofoundation/rosetta/common/model/cardano/OperationMetadata.java

Whitespace-only changes.

api/src/main/java/org/cardanofoundation/rosetta/common/model/cardano/transaction/UnsignedTransaction.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,4 @@
77

88
import java.util.Set;
99

10-
@Getter
11-
@Setter
12-
@AllArgsConstructor
13-
@NoArgsConstructor
14-
public class UnsignedTransaction {
15-
private String hash;
16-
private String bytes;
17-
private Set<String> addresses;
18-
private String metadata;
19-
}
10+
public record UnsignedTransaction (String hash, String bytes, Set<String> addresses, String metadata){}

api/src/main/java/org/cardanofoundation/rosetta/common/services/CardanoService.java

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import com.bloxbean.cardano.client.exception.AddressExcepion;
77
import com.bloxbean.cardano.client.exception.CborSerializationException;
88
import com.bloxbean.cardano.client.transaction.spec.TransactionWitnessSet;
9+
import java.util.Set;
10+
import org.cardanofoundation.rosetta.api.block.model.domain.ProcessOperations;
11+
import com.bloxbean.cardano.client.transaction.spec.TransactionWitnessSet;
912
import java.math.BigInteger;
1013
import org.cardanofoundation.rosetta.api.block.model.domain.ProcessOperations;
1114
import org.cardanofoundation.rosetta.api.block.model.entity.ProtocolParams;
@@ -20,31 +23,40 @@
2023
import java.util.ArrayList;
2124
import java.util.List;
2225
import java.util.Map;
26+
import org.cardanofoundation.rosetta.common.model.cardano.transaction.UnsignedTransaction;
27+
import org.openapitools.client.model.DepositParameters;
28+
import org.openapitools.client.model.Operation;
29+
import org.openapitools.client.model.SigningPayload;
2330

2431
public interface CardanoService {
2532

2633

27-
String getHashOfSignedTransaction(String signedTransaction);
28-
Array decodeExtraData(String encoded);
29-
Long calculateTtl(Long ttlOffset);
30-
Double checkOrReturnDefaultTtl(Integer relativeTtl);
31-
Long updateTxSize(Long previousTxSize, Long previousTtl, Long updatedTtl) throws CborSerializationException, CborException;
32-
Long calculateTxMinimumFee(Long transactionSize, ProtocolParams protocolParameters);
34+
String getHashOfSignedTransaction(String signedTransaction);
35+
Array decodeExtraData(String encoded);
36+
Long calculateTtl(Long ttlOffset);
37+
Double checkOrReturnDefaultTtl(Integer relativeTtl);
38+
Long updateTxSize(Long previousTxSize, Long previousTtl, Long updatedTtl) throws CborSerializationException, CborException;
39+
Long calculateTxMinimumFee(Long transactionSize, ProtocolParams protocolParameters);
40+
41+
Signatures signatureProcessor(EraAddressType eraAddressType, AddressType addressType,
42+
String address);
43+
44+
Double calculateTxSize(NetworkIdentifierType networkIdentifierType, List<Operation> operations, int ttl, DepositParameters depositParameters) throws IOException, CborException, AddressExcepion, CborSerializationException;
3345

34-
Signatures signatureProcessor(EraAddressType eraAddressType, AddressType addressType,
35-
String address);
46+
String buildTransaction(String unsignedTransaction,
47+
List<Signatures> signaturesList, String transactionMetadata);
3648

37-
Double calculateTxSize(NetworkIdentifierType networkIdentifierType, List<Operation> operations, int ttl, DepositParameters depositParameters) throws IOException, CborException, AddressExcepion, CborSerializationException;
49+
TransactionWitnessSet getWitnessesForTransaction(
50+
List<Signatures> signaturesList);
3851

39-
String buildTransaction(String unsignedTransaction,
40-
List<Signatures> signaturesList, String transactionMetadata);
52+
List<SigningPayload> constructPayloadsForTransactionBody(String transactionBodyHash,
53+
Set<String> addresses);
4154

42-
TransactionWitnessSet getWitnessesForTransaction(
43-
List<Signatures> signaturesList);
55+
Long calculateFee(ArrayList<BigInteger> inputAmounts, ArrayList<BigInteger> outputAmounts,
56+
ArrayList<BigInteger> withdrawalAmounts, Map<String, Double> depositsSumMap);
4457

45-
Long calculateFee(ArrayList<BigInteger> inputAmounts, ArrayList<BigInteger> outputAmounts,
46-
ArrayList<BigInteger> withdrawalAmounts, Map<String, Double> depositsSumMap);
58+
ProcessOperations convertRosettaOperations(NetworkIdentifierType networkIdentifierType,
59+
List<Operation> operations) throws IOException;
4760

48-
ProcessOperations convertRosettaOperations(NetworkIdentifierType networkIdentifierType,
49-
List<Operation> operations) throws IOException;
61+
UnsignedTransaction createUnsignedTransaction(NetworkIdentifierType networkIdentifier, List<Operation> operations, int ttl, DepositParameters depositParameters) throws IOException, CborSerializationException, AddressExcepion, CborException;
5062
}

api/src/main/java/org/cardanofoundation/rosetta/common/services/impl/CardanoServiceImpl.java

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,21 @@
3737
import org.cardanofoundation.rosetta.common.util.Constants;
3838
import org.cardanofoundation.rosetta.common.util.OperationParseUtil;
3939
import org.cardanofoundation.rosetta.common.util.ValidateParseUtil;
40-
import org.jetbrains.annotations.NotNull;
40+
import org.openapitools.client.model.AccountIdentifier;
4141
import org.openapitools.client.model.DepositParameters;
4242
import org.openapitools.client.model.Operation;
43+
import org.openapitools.client.model.SignatureType;
44+
import org.openapitools.client.model.SigningPayload;
45+
import org.jetbrains.annotations.NotNull;
4346
import org.springframework.stereotype.Service;
4447

4548
import java.io.IOException;
4649
import java.math.BigInteger;
47-
import java.security.InvalidKeyException;
48-
import java.security.NoSuchAlgorithmException;
49-
import java.security.SignatureException;
50-
import java.security.spec.InvalidKeySpecException;
5150
import java.util.ArrayList;
5251
import java.util.HashMap;
5352
import java.util.HashSet;
5453
import java.util.List;
5554
import java.util.Map;
56-
import java.util.Objects;
5755
import java.util.Set;
5856

5957
import static java.math.BigInteger.valueOf;
@@ -136,7 +134,7 @@ public Signatures signatureProcessor(EraAddressType eraAddressType, AddressType
136134
@Override
137135
public Double calculateTxSize(NetworkIdentifierType networkIdentifierType, List<Operation> operations, int ttl, DepositParameters depositParameters) throws IOException, CborException, AddressExcepion, CborSerializationException {
138136
UnsignedTransaction unsignedTransaction = createUnsignedTransaction(networkIdentifierType, operations, ttl, !ObjectUtils.isEmpty(depositParameters) ? depositParameters : new DepositParameters(Constants.DEFAULT_KEY_DEPOSIT.toString(), Constants.DEFAULT_POOL_DEPOSIT.toString()));
139-
List<Signatures> signaturesList = (unsignedTransaction.getAddresses()).stream().map(address -> {
137+
List<Signatures> signaturesList = (unsignedTransaction.addresses()).stream().map(address -> {
140138
EraAddressType eraAddressType = CardanoAddressUtil.getEraAddressType(address);
141139
if (eraAddressType != null) {
142140
return signatureProcessor(eraAddressType, null, address);
@@ -148,7 +146,7 @@ public Double calculateTxSize(NetworkIdentifierType networkIdentifierType, List<
148146
throw ExceptionFactory.invalidAddressError(address);
149147
}).toList();
150148

151-
String transaction = buildTransaction(unsignedTransaction.getBytes(), signaturesList, unsignedTransaction.getMetadata());
149+
String transaction = buildTransaction(unsignedTransaction.bytes(), signaturesList, unsignedTransaction.metadata());
152150
return ((double) transaction.length() / 2);
153151

154152
}
@@ -241,7 +239,8 @@ public TransactionWitnessSet getWitnessesForTransaction(List<Signatures> signatu
241239
}
242240
}
243241

244-
private UnsignedTransaction createUnsignedTransaction(NetworkIdentifierType networkIdentifierType, List<Operation> operations, int ttl, DepositParameters depositParameters) throws IOException, CborSerializationException, AddressExcepion, CborException {
242+
@Override
243+
public UnsignedTransaction createUnsignedTransaction(NetworkIdentifierType networkIdentifierType, List<Operation> operations, int ttl, DepositParameters depositParameters) throws IOException, CborSerializationException, AddressExcepion, CborException {
245244
log.info("[createUnsignedTransaction] About to create an unsigned transaction with {} operations", operations.size());
246245
ProcessOperationsReturn processOperationsReturnDto = processOperations(networkIdentifierType, operations, depositParameters);
247246

@@ -268,13 +267,21 @@ private UnsignedTransaction createUnsignedTransaction(NetworkIdentifierType netw
268267
String transactionBytes = HexUtil.encodeHexString(com.bloxbean.cardano.yaci.core.util.CborSerializationUtil.serialize(mapCbor));
269268
log.info("[createUnsignedTransaction] Hashing transaction body");
270269
String bodyHash = com.bloxbean.cardano.client.util.HexUtil.encodeHexString(Blake2bUtil.blake2bHash256(CborSerializationUtil.serialize(mapCbor)));
271-
UnsignedTransaction toReturn = new UnsignedTransaction(HexUtil.encodeHexString(HexUtil.decodeHexString(bodyHash)), transactionBytes, processOperationsReturnDto.getAddresses(), null);
270+
UnsignedTransaction toReturn = new UnsignedTransaction(
271+
HexUtil.encodeHexString(HexUtil.decodeHexString(bodyHash)),
272+
transactionBytes,
273+
processOperationsReturnDto.getAddresses(),
274+
getHexEncodedAuxiliaryMetadataArray(processOperationsReturnDto));
275+
log.info(toReturn + "[createUnsignedTransaction] Returning unsigned transaction, hash to sign and addresses that will sign hash");
276+
return toReturn;
277+
}
278+
279+
private static String getHexEncodedAuxiliaryMetadataArray(ProcessOperationsReturn processOperationsReturnDto) throws CborSerializationException, CborException {
272280
if (!ObjectUtils.isEmpty(processOperationsReturnDto.getVoteRegistrationMetadata())) {
273281
Array array = getArrayOfAuxiliaryData(processOperationsReturnDto);
274-
toReturn.setMetadata(HexUtil.encodeHexString(CborSerializationUtil.serialize(array)));
282+
return HexUtil.encodeHexString(CborSerializationUtil.serialize(array));
275283
}
276-
log.info(toReturn + "[createUnsignedTransaction] Returning unsigned transaction, hash to sign and addresses that will sign hash");
277-
return toReturn;
284+
return null;
278285
}
279286

280287
@NotNull
@@ -287,6 +294,14 @@ private static Array getArrayOfAuxiliaryData(ProcessOperationsReturn processOper
287294
return array;
288295
}
289296

297+
@Override
298+
public List<SigningPayload> constructPayloadsForTransactionBody(String transactionBodyHash,
299+
Set<String> addresses) {
300+
return addresses.stream().map(
301+
address -> new SigningPayload(null, new AccountIdentifier(address, null, null), transactionBodyHash,
302+
SignatureType.ED25519)).toList();
303+
}
304+
290305
private ProcessOperationsReturn processOperations(NetworkIdentifierType networkIdentifierType, List<Operation> operations, DepositParameters depositParameters) throws IOException {
291306
ProcessOperations result = convertRosettaOperations(networkIdentifierType, operations);
292307
double refundsSum = result.getStakeKeyDeRegistrationsCount() * Long.parseLong(depositParameters.getKeyDeposit());

0 commit comments

Comments
 (0)