Skip to content

Commit

Permalink
Asset Transactions endpoint + DB type specific indexing + asset endpo…
Browse files Browse the repository at this point in the history
…int refactoring (#408)

* fix: #399 Add serializer to convert amount from BigIn to String (#404)

* Blockfrost Block by number/hash api Response Format Mismatch (#405)

* fix: #402 Add next block info and confirmations to BlockDto

Enhanced the BlockDto by adding next block hash and confirmation count. Adjusted the BlockController to populate these new fields. Included new API tests to verify the additional fields in the response.

* Bump yaci version

* fix: #399 Refactor 'amount' handling and add API test for UTxOs (#406)

Refactor the 'amount' field to use the new 'Amount' class instead of 'Amt' across the transaction domain and service layers. Added a new API test to verify the UTXOs retrieval by transaction hash.

* feat: #400 Add asset transaction support and enhance indexing scripts (db specific), refactor existing asset apis

Implemented `findTransactionsByAsset` in  `UtxoStorageReaderImpl`. Added H2 and PostgreSQL specific indexing script executions in `DBIndexService`. Introduced `AssetTransaction` domain class with necessary test cases and API updates, including deprecation warnings for old endpoints.
  • Loading branch information
satran004 authored Nov 28, 2024
1 parent 550992f commit 15df767
Show file tree
Hide file tree
Showing 26 changed files with 581 additions and 88 deletions.
54 changes: 54 additions & 0 deletions api-tests/address.http
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,57 @@ GET {{base_url}}/api/v1/addresses/addr_test1wppg9l6relcpls4u667twqyggkrpfrs5cdge
});
%}

###
### Get utxos by address
GET {{base_url}}/api/v1/addresses/addr_test1qqxnp3khzm7kcj9t23hskehat7428ghsenk0pfew4rqy5v9frnmht7uwrl073q4jvq20z82kh4rksyns540azhndqexqpvhgqr/utxos

> {%
client.test("Get utxos by address", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length >= 1, "No of returned utxos is not greater than 0: " + response.body.length)
});
%}


### By ent address
GET {{base_url}}/api/v1/addresses/addr_test1vqxnp3khzm7kcj9t23hskehat7428ghsenk0pfew4rqy5vq24rmml/utxos?page=0&count=100

> {%
client.test("Get utxos by ent address", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length >= 1, "No of returned utxos is not greater than 0: " + response.body.length)
});
%}

###
### By stake address
GET {{base_url}}/api/v1/addresses/stake_test1uz53eam4lw8plhlgs2exq983r4tt63mgzfc22h73teksvnq5hwnfs/utxos?page=0&count=100

> {%
client.test("Get utxos by stake address", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length >= 1, "No of returned utxos is not greater than 0: " + response.body.length)
});
%}

###
### By address verification key
GET {{base_url}}/api/v1/addresses/addr_vkh1p5cvd4ckl4ky3265du9kdl2l42369uxvanc2wt4gcp9rqzc60ky/utxos?page=0&count=10

> {%
client.test("Get utxos by verification key", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length >= 1, "No of returned utxos is not greater than 0: " + response.body.length)
});
%}

###
### By address verification key and asset
GET {{base_url}}/api/v1/addresses/addr_vkh1p5cvd4ckl4ky3265du9kdl2l42369uxvanc2wt4gcp9rqzc60ky/utxos/0401c735871cb02f76f7e8150f96527efb779c06effa45284dcf9885546573744e4654?page=0&count=10

> {%
client.test("Get utxos by verification key", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length >= 1, "No of returned utxos is not greater than 0: " + response.body.length)
});
%}
110 changes: 110 additions & 0 deletions api-tests/assets.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
###
### Get utxos by asset
GET {{base_url}}/api/v1/assets/436941ead56c61dbf9b92b5f566f7d5b9cac08f8c957f28f0bd60d4b5041594d454e54544f4b454e/utxos?
count=10&page=0&order=desc

> {%
client.test("Get utxos by assets", function () {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length >= 1, "No of returned utxos is more than 0 " + response.body.length)
//assert if unit is there in amounts field
});
%}

### Get transactions by asset
GET {{base_url}}/api/v1/assets/436941ead56c61dbf9b92b5f566f7d5b9cac08f8c957f28f0bd60d4b5041594d454e54544f4b454e/transactions?
count=10&page=0&order=desc

> {%
client.test("Get utxos by assets", function () {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length >= 1, "No of returned transaction is more than 0 " + response.body.length)
//assert if unit is there in amounts field
});
%}

###
### -- Asset store
### supply by unit
GET {{base_url}}/api/v1/assets/436941ead56c61dbf9b92b5f566f7d5b9cac08f8c957f28f0bd60d4b5041594d454e54544f4b454e/supply

> {%
client.test("Get supply by unit", function () {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.unit === "436941ead56c61dbf9b92b5f566f7d5b9cac08f8c957f28f0bd60d4b5041594d454e54544f4b454e", "Unit mismatch " + response.body.unit)
client.assert(response.body.supply >= 1, "Supply is not greater than 1 " + response.body.supply)
//assert if unit is there in amounts field
});
%}

###
### history by unit
GET {{base_url}}/api/v1/assets/436941ead56c61dbf9b92b5f566f7d5b9cac08f8c957f28f0bd60d4b5041594d454e54544f4b454e/history

> {%
client.test("Get history by unit", function () {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length > 0 , "More than 0 transactions should be returned: " + response.body.length);
});
%}


###
### assets in a tx
GET {{base_url}}/api/v1/assets/txs/05212c1b8df78e9c313a038f6417bc0351a5a959b96cad1cd8d21884a4868f6c

> {%
client.test("Get assets in a tx", function () {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body[0].tx_hash === "05212c1b8df78e9c313a038f6417bc0351a5a959b96cad1cd8d21884a4868f6c" , "Tx hash mismatch");
client.assert(response.body[0].quantity === 100000000 , "Quantity doesn't match");
client.assert(response.body[0].mint_type === "MINT" , "mint_type doesn't match");
});
%}


###
### supply by policy
GET {{base_url}}/api/v1/assets/policy/436941ead56c61dbf9b92b5f566f7d5b9cac08f8c957f28f0bd60d4b/supply

> {%
client.test("Get supply by policy", function () {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.policy === "436941ead56c61dbf9b92b5f566f7d5b9cac08f8c957f28f0bd60d4b" , "Policy mismatch");
client.assert(response.body.supply >= 1 , "Total supply should be more than one");
});
%}

###
### history by policy
GET {{base_url}}/api/v1/assets/policy/436941ead56c61dbf9b92b5f566f7d5b9cac08f8c957f28f0bd60d4b/history

> {%
client.test("Get history by policy", function () {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length > 0 , "More than 0 transactions should be returned: " + response.body.length);
});
%}

###
### supply by policy
GET {{base_url}}/api/v1/assets/fingerprint/asset1hdfatxyf7mn8x5rutgy5ehgxpsqxx3un9alm88/supply

> {%
client.test("Get supply by fingerprint", function () {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.fingerprint === "asset1hdfatxyf7mn8x5rutgy5ehgxpsqxx3un9alm88" , "Fingerprint mismatch");
client.assert(response.body.supply >= 1 , "Total supply should be more than one");
});
%}

###
### history by policy
GET {{base_url}}/api/v1/assets/fingerprint/asset1hdfatxyf7mn8x5rutgy5ehgxpsqxx3un9alm88/history

> {%
client.test("Get history by policy", function () {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.length > 0 , "More than 0 transactions should be returned: " + response.body.length);
});
%}
33 changes: 33 additions & 0 deletions api-tests/block.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
###
### Get block by block number
GET {{base_url}}/api/v1/blocks/2925862

> {%
client.test("Get block by block number", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.height === 2925862, "Block number is wrong" + response.body.height)
client.assert(response.body.hash === "1b57a2298737589695f7ec88da9e5b1af8f9f4479e7bd03367c10e165d5cc4bc", "Block hash is wrong" + response.body.hash)
client.assert(response.body.op_cert_counter === "6", "Expected 6 as string")
client.assert(response.body.next_block === "7d50f0dbc258ffeee329fbd61e10ccdc83d65068cb9d99e2a663c07b3a7f15fd", "Mismatch next block")
client.assert(response.body.confirmations > 1, "Confirmations should be greater than 1")
client.assert(response.body.output === "19511865", "Output mismatch")
client.assert(response.body.fees === "488135", "Fees mismatch")
});
%}


### Get block by block hash
GET {{base_url}}/api/v1/blocks/1b57a2298737589695f7ec88da9e5b1af8f9f4479e7bd03367c10e165d5cc4bc

> {%
client.test("Get block by block number", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.height === 2925862, "Block number is wrong" + response.body.height)
client.assert(response.body.hash === "1b57a2298737589695f7ec88da9e5b1af8f9f4479e7bd03367c10e165d5cc4bc", "Block hash is wrong" + response.body.hash)
client.assert(response.body.op_cert_counter === "6", "Expected 6 as string")
client.assert(response.body.next_block === "7d50f0dbc258ffeee329fbd61e10ccdc83d65068cb9d99e2a663c07b3a7f15fd", "Mismatch next block")
client.assert(response.body.confirmations > 1, "Confirmations should be greater than 1")
client.assert(response.body.output === "19511865", "Output mismatch")
client.assert(response.body.fees === "488135", "Fees mismatch")
});
%}
15 changes: 0 additions & 15 deletions api-tests/epoch.http
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
###
### Get epoch params
GET {{base_url}}/api/v1/epochs/parameters

> {%
client.test("Get epoch parameters", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.min_fee_a === 44, "Min Fin A is not 44. Actual value: " + response.body.min_fee_a)
client.assert(response.body.min_fee_b === 155381, "Min Fin A is not 44. Actual value: " + response.body.min_fee_b)
client.assert(response.body.coins_per_utxo_size === "4310", "coins_per_utxo_size is not 4310. Actual value: " + response.body.coins_per_utxo_size)
client.assert(response.body.cost_models.PlutusV1 !== null, "Plutus V1 Cost model is null")
client.assert(response.body.cost_models.PlutusV2 !== null, "Plutus V2 Cost model is null")
});
%}


###
### Get latest epoch params
Expand Down
12 changes: 12 additions & 0 deletions api-tests/transaction.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
###
### Get utxos by tx hash
GET {{base_url}}/api/v1/txs/d9301af967fc9fe8995c297be05561859401b89c0c8237264e4016081dd92011/utxos

> {%
client.test("Get utxos by tx hash", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.outputs.length === 3, "No of returned utxos is not 3: " + response.body.outputs.length)
client.assert(response.body.outputs[0].amount[0].quantity === "2000000", "Quantity mismatch : " + response.body.outputs[0].amount[0].quantity)
client.assert(response.body.outputs[1].amount[0].quantity === "5000000", "Quantity mismatch : " + response.body.outputs[1].amount[0].quantity)
});
%}
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,7 @@ private void runDeleteIndexes() {

log.info("Deleting optional indexes to speed-up the sync process ..... " + scriptPath);
indexRemoved.set(true);
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource(scriptPath));
populator.execute(this.dataSource);
executeSqlScript(scriptPath);

log.info("Optional indexes have been removed successfully.");
} catch (Exception e) {
Expand All @@ -102,22 +99,53 @@ private void reApplyIndexes() {
log.info("Re-applying optional indexes after sync process ..... " + scriptPath);
indexApplied.set(true);

ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource(scriptPath));
populator.execute(this.dataSource);
executeSqlScript(scriptPath);

//If h2 db, then try any additional indexes in h2 file
if (isH2()) {
log.info("Running additional indexes for H2 ...");
String h2Script = "sql/extra-index-h2.sql";
executeSqlScript(h2Script);
} else if(isPostgres()) {
log.info("Running additional indexes for Postgresql ...");
String postgresScript = "sql/extra-index-postgresql.sql";
executeSqlScript(postgresScript);
}

log.info("Optional indexes have been re-applied successfully.");
} catch (Exception e) {
log.error("Filed to re-apply indexes.", e);
}
}

private void executeSqlScript(String scriptPath) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource(scriptPath));
populator.execute(this.dataSource);
}

private boolean isMysql() throws SQLException {
var vendor = dataSource.getConnection().getMetaData().getDatabaseProductName();
if (vendor != null && vendor.toLowerCase().contains("mysql"))
return true;
else
return false;
}

private boolean isH2() throws SQLException {
var vendor = dataSource.getConnection().getMetaData().getDatabaseProductName();
if (vendor != null && vendor.toLowerCase().contains("h2"))
return true;
else
return false;
}

private boolean isPostgres() throws SQLException {
var vendor = dataSource.getConnection().getMetaData().getDatabaseProductName();
if (vendor != null && vendor.toLowerCase().contains("postgres"))
return true;
else
return false;
}
}
4 changes: 4 additions & 0 deletions applications/all/src/main/resources/sql/extra-index-h2.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Additional H2 specific indexes

CREATE INDEX if not exists idx_address_utxo_amounts
ON address_utxo(amounts);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Additional postgresql specific indexes

CREATE INDEX if not exists idx_address_utxo_amounts
ON address_utxo USING gin (amounts);
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,53 @@ private void reApplyIndexes() {
log.info("Re-applying optional indexes after sync process ..... " + scriptPath);
indexApplied.set(true);

ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource(scriptPath));
populator.execute(this.dataSource);
executeSqlScript(scriptPath);

//If h2 db, then try any additional indexes in h2 file
if (isH2()) {
log.info("Running additional indexes for H2 ...");
String h2Script = "sql/extra-index-h2.sql";
executeSqlScript(h2Script);
} else if(isPostgres()) {
log.info("Running additional indexes for Postgresql ...");
String postgresScript = "sql/extra-index-postgresql.sql";
executeSqlScript(postgresScript);
}

log.info("Optional indexes have been re-applied successfully.");
} catch (Exception e) {
log.error("Filed to re-apply indexes.", e);
}
}

private void executeSqlScript(String scriptPath) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource(scriptPath));
populator.execute(this.dataSource);
}

private boolean isMysql() throws SQLException {
var vendor = dataSource.getConnection().getMetaData().getDatabaseProductName();
if (vendor != null && vendor.toLowerCase().contains("mysql"))
return true;
else
return false;
}

private boolean isH2() throws SQLException {
var vendor = dataSource.getConnection().getMetaData().getDatabaseProductName();
if (vendor != null && vendor.toLowerCase().contains("h2"))
return true;
else
return false;
}

private boolean isPostgres() throws SQLException {
var vendor = dataSource.getConnection().getMetaData().getDatabaseProductName();
if (vendor != null && vendor.toLowerCase().contains("postgres"))
return true;
else
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Additional H2 specific indexes

CREATE INDEX if not exists idx_address_utxo_amounts
ON address_utxo(amounts);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Additional postgresql specific indexes

CREATE INDEX if not exists idx_address_utxo_amounts
ON address_utxo USING gin (amounts);
Loading

0 comments on commit 15df767

Please sign in to comment.