diff --git a/CHANGELOG.md b/CHANGELOG.md index e25a99086..a689e430a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # 2.1.1 (2023-01-16) - feat: support with_cycles for get_block and get_block_by_number rpc +- feat: support packed rpcs +- feat: support indexer exact search mod ## 🚀 Features # 2.1.0 (2022-12-26) diff --git a/ckb-indexer/src/main/java/org/nervos/indexer/model/ScriptSearchMode.java b/ckb-indexer/src/main/java/org/nervos/indexer/model/ScriptSearchMode.java new file mode 100644 index 000000000..62fc1fcc6 --- /dev/null +++ b/ckb-indexer/src/main/java/org/nervos/indexer/model/ScriptSearchMode.java @@ -0,0 +1,13 @@ +package org.nervos.indexer.model; + +import com.google.gson.annotations.SerializedName; + +public enum ScriptSearchMode { + // search script with prefix + @SerializedName("prefix") + Prefix, + // search script with exact match + @SerializedName("exact") + Exact, + ; +} diff --git a/ckb-indexer/src/main/java/org/nervos/indexer/model/SearchKey.java b/ckb-indexer/src/main/java/org/nervos/indexer/model/SearchKey.java index caa7e00f8..10f4b4516 100644 --- a/ckb-indexer/src/main/java/org/nervos/indexer/model/SearchKey.java +++ b/ckb-indexer/src/main/java/org/nervos/indexer/model/SearchKey.java @@ -6,6 +6,14 @@ public class SearchKey { public Script script; public ScriptType scriptType; + /** + * Script search mode, optional default is prefix, means search script with prefix + */ + public ScriptSearchMode scriptSearchMode; public Filter filter; + /** + * bool, optional default is true, if with_data is set to false, the field of returning cell.output_data is null in the result + */ + public Boolean withData; public boolean groupByTransaction; } diff --git a/ckb-indexer/src/main/java/org/nervos/indexer/model/SearchKeyBuilder.java b/ckb-indexer/src/main/java/org/nervos/indexer/model/SearchKeyBuilder.java index 8ed1be107..473fead32 100644 --- a/ckb-indexer/src/main/java/org/nervos/indexer/model/SearchKeyBuilder.java +++ b/ckb-indexer/src/main/java/org/nervos/indexer/model/SearchKeyBuilder.java @@ -13,38 +13,58 @@ public class SearchKeyBuilder { private Filter filter; - public void script(Script script) { + public SearchKeyBuilder script(Script script) { this.script = script; + return this; } - public void scriptType(ScriptType scriptType) { + public SearchKeyBuilder scriptType(ScriptType scriptType) { this.scriptType = scriptType; + return this; } - public void filterScript(Script script) { + public SearchKeyBuilder filterScript(Script script) { initFilter(); this.filter.script = script; + return this; } - public void filterOutputDataLenRange(int inclusive, int exclusive) { + public SearchKeyBuilder filterOutputDataLenRange(int inclusive, int exclusive) { initFilter(); this.filter.outputDataLenRange = new ArrayList<>(2); this.filter.outputDataLenRange.add(inclusive); this.filter.outputDataLenRange.add(exclusive); + return this; } - public void filterOutputCapacityRange(long inclusive, long exclusive) { + public SearchKeyBuilder filterOutputCapacityRange(long inclusive, long exclusive) { initFilter(); this.filter.outputCapacityRange = new ArrayList<>(2); this.filter.outputCapacityRange.add(inclusive); this.filter.outputCapacityRange.add(exclusive); + return this; } - public void filterBlockRange(int inclusive, int exclusive) { + public SearchKeyBuilder filterBlockRange(int inclusive, int exclusive) { initFilter(); this.filter.blockRange = new ArrayList<>(2); this.filter.blockRange.add(inclusive); this.filter.blockRange.add(exclusive); + return this; + } + + private ScriptSearchMode _scriptSearchMode; + + public SearchKeyBuilder scriptSearchMode(ScriptSearchMode scriptSearchMode) { + this._scriptSearchMode = scriptSearchMode; + return this; + } + + private Boolean _withData; + + public SearchKeyBuilder withData(Boolean withData) { + this._withData = withData; + return this; } public SearchKey build() { @@ -52,6 +72,9 @@ public SearchKey build() { searchKey.script = this.script; searchKey.scriptType = this.scriptType; searchKey.filter = this.filter; + searchKey.scriptSearchMode = this._scriptSearchMode; + searchKey.withData = this._withData; + // searchKey.groupByTransaction controlled by api function return searchKey; } diff --git a/ckb/src/test/java/service/ApiTest.java b/ckb/src/test/java/service/ApiTest.java index 0a34eab57..f00d808d1 100644 --- a/ckb/src/test/java/service/ApiTest.java +++ b/ckb/src/test/java/service/ApiTest.java @@ -8,6 +8,7 @@ import org.nervos.ckb.type.*; import org.nervos.ckb.utils.Numeric; import org.nervos.indexer.model.Order; +import org.nervos.indexer.model.ScriptSearchMode; import org.nervos.indexer.model.SearchKeyBuilder; import org.nervos.indexer.model.resp.*; @@ -58,6 +59,7 @@ public void testGetBlockByNumberWithCycles() throws IOException { Assertions.assertArrayEquals(packedResponse.getBlockBytes(), packedResponse0.getBlockBytes()); Assertions.assertNull(packedResponse0.cycles); } + @Test public void testGetBlockByNumberWithCycles_NotExist() throws IOException { long blockNumber = block_number_not_exist; @@ -130,6 +132,7 @@ public void testGetPackedBlock_NotExist() throws IOException { PackedBlockWithCycles packedBlockBytes0 = api.getPackedBlock(blockHash, false); Assertions.assertNull(packedBlockBytes0); } + @Test public void testGetBlockWithCycles() throws IOException { byte[] blockHash = @@ -179,6 +182,7 @@ public void testGetTransactionVerbosity1() throws IOException { Assertions.assertArrayEquals(transactionVerbosity1.txStatus.blockHash, transactionVerbosity2.txStatus.blockHash); Assertions.assertEquals(transactionVerbosity1.cycles, transactionVerbosity2.cycles); } + @Test public void testGetTransactionVerbosity1_NotExist() throws IOException { byte[] transactionHash = BLOCK_HASH_NOT_EXIST; @@ -188,6 +192,7 @@ public void testGetTransactionVerbosity1_NotExist() throws IOException { TransactionWithStatus transactionVerbosity2 = api.getTransaction(transactionHash); Assertions.assertEquals(TransactionWithStatus.Status.UNKNOWN, transactionVerbosity1.txStatus.status); } + @Test public void testPackedTransaction() throws IOException { byte[] transactionHash = @@ -289,6 +294,7 @@ public void testGetHeaderByNumber_NotExist() throws IOException { PackedHeader packedHeader = api.getPackedHeaderByNumber(block_number_not_exist); Assertions.assertNull(packedHeader); } + @Test public void testGetConsensus() throws IOException { Consensus consensus = api.getConsensus(); @@ -570,6 +576,49 @@ void testGetTransactions() throws IOException { Assertions.assertTrue(txs.objects.size() > 0); } + @Test + void testGetTransactions_prefix_partial() throws IOException { + SearchKeyBuilder key = new SearchKeyBuilder(); + key.script( + new Script( + Numeric.hexStringToByteArray( + "0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63"), + Numeric.hexStringToByteArray("0xe53f35ccf63bb37a3bb0ac3b7f89808077a78eae".substring(0, 4)), + Script.HashType.TYPE)); + key.scriptType(ScriptType.LOCK); + key.scriptSearchMode(ScriptSearchMode.Prefix); + TxsWithCell txs = api.getTransactions(key.build(), Order.ASC, 10, null); + Assertions.assertTrue(txs.objects.size() > 0); + } + + @Test + void testGetTransactions_exact_partial() throws IOException { + SearchKeyBuilder key = new SearchKeyBuilder(); + key.script( + new Script( + Numeric.hexStringToByteArray( + "0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63"), + Numeric.hexStringToByteArray("0xe53f35ccf63bb37a3bb0ac3b7f89808077a78eae".substring(0, 4)), + Script.HashType.TYPE)); + key.scriptType(ScriptType.LOCK).scriptSearchMode(ScriptSearchMode.Exact); + TxsWithCell txs = api.getTransactions(key.build(), Order.ASC, 10, null); + Assertions.assertEquals(0, txs.objects.size()); + } + + @Test + void testGetTransactions_exact_full() throws IOException { + SearchKeyBuilder key = new SearchKeyBuilder(); + key.script( + new Script( + Numeric.hexStringToByteArray( + "0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63"), + Numeric.hexStringToByteArray("0xe53f35ccf63bb37a3bb0ac3b7f89808077a78eae"), + Script.HashType.TYPE)); + key.scriptType(ScriptType.LOCK).scriptSearchMode(ScriptSearchMode.Exact); + TxsWithCell txs = api.getTransactions(key.build(), Order.ASC, 10, null); + Assertions.assertTrue(txs.objects.size() > 0); + } + @Test void testTransactionsGrouped() throws IOException { SearchKeyBuilder key = new SearchKeyBuilder(); @@ -705,7 +754,5 @@ public void testGetFeeRateStatics() throws IOException { statics = api.getFeeRateStatics(102); Assertions.assertNotNull(statics); Assertions.assertTrue(statics.mean > 0 && statics.median > 0); - - Assertions.assertThrows(IOException.class, () -> api.getFeeRateStatics(-1)); } } diff --git a/core/src/main/java/org/nervos/ckb/service/RpcService.java b/core/src/main/java/org/nervos/ckb/service/RpcService.java index 445ff6e8d..f52f8aa70 100644 --- a/core/src/main/java/org/nervos/ckb/service/RpcService.java +++ b/core/src/main/java/org/nervos/ckb/service/RpcService.java @@ -51,7 +51,8 @@ public T post(@NotNull String method, List params, Type cls) throws IOExcept public T post(@NotNull String method, List params, Type cls, Gson gson) throws IOException { RequestParams requestParams = new RequestParams(method, params); - RequestBody body = RequestBody.create(gson.toJson(requestParams), JSON_MEDIA_TYPE); + String gson_params = gson.toJson(requestParams); + RequestBody body = RequestBody.create(gson_params, JSON_MEDIA_TYPE); Request request = new Request.Builder().url(url).post(body).build(); Response response = client.newCall(request).execute(); String responseBody = Objects.requireNonNull(response.body()).string();