Skip to content

Commit 90134b1

Browse files
authored
Feat/implement construction hash (#80)
* feat: implement /construction/hash * chore: updated readme * chore: updated readme
1 parent 899803f commit 90134b1

File tree

6 files changed

+159
-38
lines changed

6 files changed

+159
-38
lines changed

README.md

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,21 @@ to fetch the data from the node.
1414
- [x] Integration test setup
1515
- API calls
1616
- Data API
17-
- [x] /network/list
18-
- [x] /network/status
19-
- [x] /network/options
20-
- [x] /block/*
21-
- [ ] /mempool/*
22-
- [x] /account/*
17+
- [x] /network/list
18+
- [x] /network/status
19+
- [x] /network/options
20+
- [x] /block/*
21+
- [ ] /mempool/*
22+
- [x] /account/*
2323
- Construction API
24-
- [x] /construction/derive
25-
- [ ] /construction/preprocess
26-
- [ ] /construction/metadata
27-
- [ ] /construction/payloads
28-
- [ ] /construction/combine
29-
- [ ] /construction/parse
30-
- [ ] /construction/hash
31-
- [ ] /construction/submit
32-
24+
- [x] /construction/derive
25+
- [ ] /construction/preprocess
26+
- [ ] /construction/metadata
27+
- [ ] /construction/payloads
28+
- [ ] /construction/combine
29+
- [ ] /construction/parse
30+
- [x] /construction/hash
31+
- [ ] /construction/submit
3332

3433
## Getting Started
3534

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.cardanofoundation.rosetta.api.service;
22

33

4-
public interface CardanoService {
4+
import co.nstant.in.cbor.model.Array;
55

6+
public interface CardanoService {
7+
String getHashOfSignedTransaction(String signedTransaction);
8+
Array decodeExtraData(String encoded);
69
}

api/src/main/java/org/cardanofoundation/rosetta/api/service/impl/CardanoServiceImpl.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
package org.cardanofoundation.rosetta.api.service.impl;
22

3+
import co.nstant.in.cbor.model.Array;
4+
import co.nstant.in.cbor.model.DataItem;
5+
import com.bloxbean.cardano.client.common.cbor.CborSerializationUtil;
6+
import com.bloxbean.cardano.client.crypto.Blake2bUtil;
7+
import com.bloxbean.cardano.client.transaction.spec.Transaction;
8+
import com.bloxbean.cardano.client.transaction.spec.TransactionBody;
9+
import com.bloxbean.cardano.client.util.HexUtil;
310
import lombok.extern.slf4j.Slf4j;
11+
import org.cardanofoundation.rosetta.api.exception.ExceptionFactory;
412
import org.cardanofoundation.rosetta.api.service.CardanoService;
13+
import org.cardanofoundation.rosetta.api.util.CardanoAddressUtils;
514
import org.springframework.stereotype.Service;
615

716

@@ -10,5 +19,43 @@
1019
@Service
1120
public class CardanoServiceImpl implements CardanoService {
1221

22+
@Override
23+
public String getHashOfSignedTransaction(String signedTransaction) {
24+
try {
25+
log.info("[getHashOfSignedTransaction] About to hash signed transaction {}",
26+
signedTransaction);
27+
byte[] signedTransactionBytes = HexUtil.decodeHexString(signedTransaction);
28+
log.info(
29+
"[getHashOfSignedTransaction] About to parse transaction from signed transaction bytes");
30+
Transaction parsed = Transaction.deserialize(signedTransactionBytes);
31+
log.info("[getHashOfSignedTransaction] Returning transaction hash");
32+
TransactionBody body = parsed.getBody();
33+
byte[] hashBuffer;
34+
if (body == null ||
35+
CborSerializationUtil.serialize(body.serialize())
36+
== null) {
37+
hashBuffer = null;
38+
} else {
39+
hashBuffer = Blake2bUtil.blake2bHash256(
40+
com.bloxbean.cardano.client.common.cbor.CborSerializationUtil.serialize(
41+
body.serialize()));
42+
}
43+
return CardanoAddressUtils.hexFormatter(hashBuffer);
44+
} catch (Exception error) {
45+
log.error(error.getMessage()
46+
+ "[getHashOfSignedTransaction] There was an error parsing signed transaction");
47+
throw ExceptionFactory.parseSignedTransactionError();
48+
}
49+
}
1350

51+
@Override
52+
public Array decodeExtraData(String encoded) {
53+
try {
54+
DataItem dataItem = com.bloxbean.cardano.client.common.cbor.CborSerializationUtil.deserialize(
55+
HexUtil.decodeHexString(encoded));
56+
return (Array) dataItem;
57+
} catch (Exception e) {
58+
throw ExceptionFactory.cantBuildSignedTransaction();
59+
}
60+
}
1461
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.cardanofoundation.rosetta.api.service.impl;
22

33
import co.nstant.in.cbor.CborException;
4+
import co.nstant.in.cbor.model.Array;
5+
import co.nstant.in.cbor.model.UnicodeString;
46
import com.bloxbean.cardano.client.exception.AddressExcepion;
57
import com.bloxbean.cardano.client.exception.CborDeserializationException;
68
import com.bloxbean.cardano.client.exception.CborSerializationException;
@@ -12,6 +14,7 @@
1214

1315
import org.cardanofoundation.rosetta.api.model.enumeration.NetworkEnum;
1416
import org.cardanofoundation.rosetta.api.service.CardanoAddressService;
17+
import org.cardanofoundation.rosetta.api.service.CardanoService;
1518
import org.cardanofoundation.rosetta.api.service.ConstructionApiService;
1619
import org.openapitools.client.model.*;
1720
import org.springframework.stereotype.Service;
@@ -25,6 +28,7 @@
2528
public class ConstructionApiServiceImpl implements ConstructionApiService {
2629

2730
private final CardanoAddressService cardanoAddressService;
31+
private final CardanoService cardanoService;
2832

2933
@Override
3034
public ConstructionDeriveResponse constructionDeriveService(ConstructionDeriveRequest constructionDeriveRequest) throws IllegalAccessException, CborSerializationException {
@@ -76,7 +80,12 @@ public ConstructionCombineResponse constructionCombineService(ConstructionCombin
7680

7781
@Override
7882
public TransactionIdentifierResponse constructionHashService(ConstructionHashRequest constructionHashRequest) {
79-
return null;
83+
Array array = cardanoService.decodeExtraData(constructionHashRequest.getSignedTransaction());
84+
log.info("[constructionHash] About to get hash of signed transaction");
85+
String transactionHash = cardanoService.getHashOfSignedTransaction(
86+
((UnicodeString) array.getDataItems().get(0)).getString());
87+
log.info("[constructionHash] About to return hash of signed transaction");
88+
return new TransactionIdentifierResponse(new TransactionIdentifier(transactionHash), null);
8089
}
8190

8291
@Override

postmanTests/Rosetta-java-env.postman_environment.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,20 @@
8585
"value": "Base",
8686
"type": "default",
8787
"enabled": true
88+
},
89+
{
90+
"key": "signedTransaction",
91+
"value": "827901c43834613430303831383235383230326632336664386363613833356166323166336163333735626163363031663937656164373566326537393134336264663731666532633462653034336538663031303138323832353831643631626234306631613634376263383863316264366237333864623865623636333537643932363437346561356666643662616137366339666231393237313038323538316436316262343066316136343762633838633162643662373338646238656236363335376439323634373465613566666436626161373663396662313939633430303231393963343030333139303365386131303038313832353832303162343030643630616166333465616636646362616239626261343630303161323334393738383663663131303636663738343639333364333065356164336635383430366339323530383133356362303630313837613237303661646538313534373832383637623135323665393631356430363734326265356335366630333761623835383934633039386332616230373937313133336330343737626165653932616466333532376164376363383136663133653165346333363130343132303666356636a16a6f7065726174696f6e7381a6746f7065726174696f6e5f6964656e746966696572a265696e646578006d6e6574776f726b5f696e64657800647479706565696e707574667374617475736773756363657373676163636f756e74a16761646472657373783a616464723176786135707564786737376733736461646465636d773874766336686d796e79776e34396c6c747434666d766e3763706e6b63707866616d6f756e74a26576616c7565662d39303030306863757272656e6379a26673796d626f6c6341444168646563696d616c73066b636f696e5f6368616e6765a26f636f696e5f6964656e746966696572a16a6964656e7469666965727842326632336664386363613833356166323166336163333735626163363031663937656164373566326537393134336264663731666532633462653034336538663a316b636f696e5f616374696f6e6a636f696e5f7370656e74",
92+
"enabled": true
93+
},
94+
{
95+
"key": "hashedSignedTransaction",
96+
"value": "333a6ccaaa639f7b451ce93764f54f654ef499fdb7b8b24374ee9d99eab9d795",
97+
"type": "default",
98+
"enabled": true
8899
}
89100
],
90101
"_postman_variable_scope": "environment",
91-
"_postman_exported_at": "2024-03-06T12:46:41.009Z",
92-
"_postman_exported_using": "Postman/10.23.8"
102+
"_postman_exported_at": "2024-03-07T11:54:40.373Z",
103+
"_postman_exported_using": "Postman/10.23.10"
93104
}

postmanTests/rosetta-java.postman_collection.json

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -341,60 +341,87 @@
341341
"response": []
342342
},
343343
{
344-
"name": "/mempool",
344+
"name": "/mempool/transaction",
345345
"request": {
346346
"method": "POST",
347347
"header": [],
348348
"body": {
349349
"mode": "raw",
350-
"raw": "{\n \"network_identifier\": {\n \"blockchain\": \"cardano\",\n \"network\": \"{{networkId}}\"\n },\n \"metadata\": {\n }\n}",
350+
"raw": "{\n \"network_identifier\": {\n \"blockchain\": \"cardano\",\n \"network\": \"{{networkId}}\"\n },\n \"transaction_identifier\": {\n \"hash\": {{mempoolTransaction}}\n },\n \"metadata\": {}\n}",
351351
"options": {
352352
"raw": {
353353
"language": "json"
354354
}
355355
}
356356
},
357357
"url": {
358-
"raw": "{{URL}}/mempool",
358+
"raw": "{{URL}}/mempool/transaction",
359359
"host": [
360360
"{{URL}}"
361361
],
362362
"path": [
363-
"mempool"
363+
"mempool",
364+
"transaction"
364365
]
365366
}
366367
},
367368
"response": []
368369
},
369370
{
370-
"name": "/mempool/transaction",
371+
"name": "/construction/derive",
372+
"event": [
373+
{
374+
"listen": "test",
375+
"script": {
376+
"exec": [
377+
"pm.test('Status code is 200', function () {",
378+
" pm.response.to.have.status(200);",
379+
"})",
380+
"",
381+
"pm.test('Contains Json Body', function () {",
382+
" pm.response.to.have.jsonBody('address');",
383+
"})",
384+
"",
385+
"pm.test(\"Correct Address\", function () {",
386+
" const testAccountBaseAddress = pm.environment.get('TestAccountBaseAddress');",
387+
" var responseData = pm.response.json();",
388+
" pm.expect(responseData).to.be.an('object');",
389+
" pm.expect(responseData.address).to.eql(testAccountBaseAddress);",
390+
"});",
391+
"",
392+
""
393+
],
394+
"type": "text/javascript"
395+
}
396+
}
397+
],
371398
"request": {
372399
"method": "POST",
373400
"header": [],
374401
"body": {
375402
"mode": "raw",
376-
"raw": "{\n \"network_identifier\": {\n \"blockchain\": \"cardano\",\n \"network\": \"{{networkId}}\"\n },\n \"transaction_identifier\": {\n \"hash\": {{mempoolTransaction}}\n },\n \"metadata\": {}\n}",
403+
"raw": "{\n \"network_identifier\": {\n \"blockchain\": \"cardano\",\n \"network\": \"{{networkId}}\"\n },\n \"public_key\": {\n \"hex_bytes\": \"{{TestAccountPubKeyHexBytes}}\",\n \"curve_type\": \"{{curveType}}\"\n },\n \"metadata\": {\n \"address_type\": \"{{address_type}}\",\n \"staking_credential\": {\n \"hex_bytes\": \"{{TestAccountStakePubKeyHex}}\",\n \"curve_type\": \"{{curveType}}\"\n }\n }\n}",
377404
"options": {
378405
"raw": {
379406
"language": "json"
380407
}
381408
}
382409
},
383410
"url": {
384-
"raw": "{{URL}}/mempool/transaction",
411+
"raw": "{{URL}}/construction/derive",
385412
"host": [
386413
"{{URL}}"
387414
],
388415
"path": [
389-
"mempool",
390-
"transaction"
416+
"construction",
417+
"derive"
391418
]
392419
}
393420
},
394421
"response": []
395422
},
396423
{
397-
"name": "/construction/derive",
424+
"name": "/construction/hash",
398425
"event": [
399426
{
400427
"listen": "test",
@@ -405,17 +432,16 @@
405432
"})",
406433
"",
407434
"pm.test('Contains Json Body', function () {",
408-
" pm.response.to.have.jsonBody('address');",
435+
" pm.response.to.have.jsonBody('transaction_identifier');",
409436
"})",
410437
"",
411438
"pm.test(\"Correct Address\", function () {",
412-
" const testAccountBaseAddress = pm.environment.get('TestAccountBaseAddress');",
439+
" const hashedSignedTransaction = pm.environment.get('hashedSignedTransaction');",
413440
" var responseData = pm.response.json();",
414441
" pm.expect(responseData).to.be.an('object');",
415-
" pm.expect(responseData.address).to.eql(testAccountBaseAddress);",
416-
"});",
417-
"",
418-
""
442+
" pm.expect(responseData.transaction_identifier).to.be.an('object');",
443+
" pm.expect(responseData.transaction_identifier.hash).to.eql(hashedSignedTransaction);",
444+
"});"
419445
],
420446
"type": "text/javascript"
421447
}
@@ -426,21 +452,21 @@
426452
"header": [],
427453
"body": {
428454
"mode": "raw",
429-
"raw": "{\n \"network_identifier\": {\n \"blockchain\": \"cardano\",\n \"network\": \"{{networkId}}\"\n },\n \"public_key\": {\n \"hex_bytes\": \"{{TestAccountPubKeyHexBytes}}\",\n \"curve_type\": \"{{curveType}}\"\n },\n \"metadata\": {\n \"address_type\": \"{{address_type}}\",\n \"staking_credential\": {\n \"hex_bytes\": \"{{TestAccountStakePubKeyHex}}\",\n \"curve_type\": \"{{curveType}}\"\n }\n }\n}",
455+
"raw": "{\n \"network_identifier\": {\n \"blockchain\": \"cardano\",\n \"network\": \"devkit\"\n },\n \"signed_transaction\": \"{{signedTransaction}}\"\n}",
430456
"options": {
431457
"raw": {
432458
"language": "json"
433459
}
434460
}
435461
},
436462
"url": {
437-
"raw": "{{URL}}/construction/derive",
463+
"raw": "{{URL}}/construction/hash",
438464
"host": [
439465
"{{URL}}"
440466
],
441467
"path": [
442468
"construction",
443-
"derive"
469+
"hash"
444470
]
445471
}
446472
},
@@ -472,6 +498,32 @@
472498
}
473499
},
474500
"response": []
501+
},
502+
{
503+
"name": "/mempool",
504+
"request": {
505+
"method": "POST",
506+
"header": [],
507+
"body": {
508+
"mode": "raw",
509+
"raw": "{\n \"network_identifier\": {\n \"blockchain\": \"cardano\",\n \"network\": \"{{networkId}}\"\n },\n \"metadata\": { }\n}",
510+
"options": {
511+
"raw": {
512+
"language": "json"
513+
}
514+
}
515+
},
516+
"url": {
517+
"raw": "{{URL}}/mempool",
518+
"host": [
519+
"{{URL}}"
520+
],
521+
"path": [
522+
"mempool"
523+
]
524+
}
525+
},
526+
"response": []
475527
}
476528
],
477529
"variable": [

0 commit comments

Comments
 (0)