From 837f83d5399792ea6397c210bcd51b58c0e51790 Mon Sep 17 00:00:00 2001 From: Yin Guanhao Date: Tue, 1 Aug 2023 20:00:09 +0800 Subject: [PATCH 1/4] chore: fix README transaction builder example --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 97cb4d422..7d72330f0 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,10 @@ Here is an example to build a CKB transfer transaction with the help of transact String sender = "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq2qf8keemy2p5uu0g0gn8cd4ju23s5269qk8rg4r"; String receiver = "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqg958atl2zdh8jn3ch8lc72nt0cf864ecqdxm9zf"; Iterator iterator = new InputIterator(sender); -TransactionWithScriptGroups txWithGroups = new CkbTransactionBuilder(iterator, Network.TESTNET) +TransactionBuilderConfiguration configuration = new TransactionBuilderConfiguration(Network.TESTNET); +configuration.setFeeRate(1000); +TransactionWithScriptGroups txWithGroups = new CkbTransactionBuilder(configuration, iterator) .addOutput(receiver, 50100000000L) - .setFeeRate(1000) .setChangeOutput(sender) .build(); ``` From c590c086f05ea20d075bab7718c166e392bd736c Mon Sep 17 00:00:00 2001 From: Yin Guanhao Date: Thu, 20 Jul 2023 18:21:00 +0800 Subject: [PATCH 2/4] Fix guava dep for ckb --- ckb/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/ckb/build.gradle b/ckb/build.gradle index 41f4c5720..1ebbe0f31 100644 --- a/ckb/build.gradle +++ b/ckb/build.gradle @@ -8,6 +8,7 @@ dependencies { implementation project(":serialization") implementation "com.google.code.gson:gson:$gsonVersion" + implementation "com.google.guava:guava:$guavaVersion" } apply plugin: 'com.github.johnrengelman.shadow' From 9e8a3ad2482e1b22f2c0e19c758b12aa720a2f45 Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 2 Sep 2023 10:50:18 +0800 Subject: [PATCH 3/4] docs: add docs for tx builder and ScriptHandler --- .../AbstractTransactionBuilder.java | 21 ++++++++ .../transaction/handler/ScriptHandler.java | 51 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/ckb/src/main/java/org/nervos/ckb/transaction/AbstractTransactionBuilder.java b/ckb/src/main/java/org/nervos/ckb/transaction/AbstractTransactionBuilder.java index a21c65e2c..c5b0d4ba1 100644 --- a/ckb/src/main/java/org/nervos/ckb/transaction/AbstractTransactionBuilder.java +++ b/ckb/src/main/java/org/nervos/ckb/transaction/AbstractTransactionBuilder.java @@ -19,6 +19,12 @@ public abstract class AbstractTransactionBuilder { protected List inputsDetail = new ArrayList<>(); protected Transaction tx = new Transaction(); + /** + * Initialites the transaction builder. + * + * @param configuration This is the bundle of configuration for builder, + * such as fee rate and registerred {@link org.nervos.ckb.transaction.handler.ScriptHandler}. + */ public AbstractTransactionBuilder(TransactionBuilderConfiguration configuration, Iterator availableInputs) { this.configuration = configuration; this.availableInputs = availableInputs; @@ -94,9 +100,24 @@ protected static long calculateTxFee(Transaction transaction, long feeRate) { return Calculator.calculateTransactionFee(transaction, feeRate); } + /** + * Builds the transaction with a default context for script handlers. + * + * This function will call script handlers in {@code TransactionConfiguration} with a + * default context null. Use {@link #build(Object...)} to pass custom contexts. + * + * @see org.nervos.ckb.transaction.handler.ScriptHandler#buildTransaction(AbstractTransactionBuilder, ScriptGroup, Object) + */ public TransactionWithScriptGroups build() { return build((Object) null); } + /** + * Builds the transaction with custom contexts for script handlers. + * + * The contexts will be passed to the registered script handlers in the same order. + * + * @see org.nervos.ckb.transaction.handler.ScriptHandler#buildTransaction(AbstractTransactionBuilder, ScriptGroup, Object) + */ abstract TransactionWithScriptGroups build(Object... contexts); } diff --git a/ckb/src/main/java/org/nervos/ckb/transaction/handler/ScriptHandler.java b/ckb/src/main/java/org/nervos/ckb/transaction/handler/ScriptHandler.java index 994e26c8d..255d185d6 100644 --- a/ckb/src/main/java/org/nervos/ckb/transaction/handler/ScriptHandler.java +++ b/ckb/src/main/java/org/nervos/ckb/transaction/handler/ScriptHandler.java @@ -4,7 +4,58 @@ import org.nervos.ckb.sign.ScriptGroup; import org.nervos.ckb.transaction.AbstractTransactionBuilder; +/** + * ScriptHandler implements the building logic for various scripts. + * + * These handlers should be registered in the transaction builder via {@link + * org.nervos.ckb.transaction.TransactionBuilderConfiguration}. + */ public interface ScriptHandler { + /** + * Build the transaction for the script. + * + * This function is called by transaction builder for each script group and each context. + * + * Generally this function should: + * + *
    + *
  1. Check the scriptGroup to determine whether to proceed with the next steps or skip them.
  2. + *
  3. Add cell deps for the script.
  4. + *
  5. + * Prefill the witness for the script so the transaction size will not + * increase after signing, which may invalidate the fee calculation. + *
  6. + *
+ * + *

For example:

+ * + *
{@code
+   * @Override
+   * boolean buildTransaction(AbstractTransactionBuilder txBuilder, ScriptGroup scriptGroup, Object context) {
+   *    // Only change the transaction when the script is used.
+   *    if (scriptGroup == null || !isMatched(scriptGroup.getScript())) {
+   *      return false;
+   *    }
+   *    // Add celldeps
+   *    txBuilder.addCellDeps(cellDeps);
+   *
+   *    // Prefill witness placeholder
+   *    int witnessIndex = scriptGroup.getInputIndices().get(0);
+   *    byte[] dummyWitness = new byte[8];
+   *    txBuilder.setWitness(witnessIndex, WitnessArgs.Type.LOCK, dummyWitness);
+   *
+   *    return true;
+   * }
+   * }
+ * + * @param txBuilder The transaction in building. + * @param scriptGroup Transaction builder calls this callback for each script group found in the transaction. + * The script handler is responsoble for determining whether it should handle the given script group. + * @param context This is null passed from {@link org.nervos.ckb.transaction.AbstractTransactionBuilder#build()}, + * or provided contexts in {@link org.nervos.ckb.transaction.AbstractTransactionBuilder#build(Object...)}. + * @return {@code true} when the script handler has modified the transaction. + * + */ boolean buildTransaction(AbstractTransactionBuilder txBuilder, ScriptGroup scriptGroup, Object context); void init(Network network); From 53105b3e52b5709cce74452e216b09fb187485b6 Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 2 Sep 2023 22:35:13 +0800 Subject: [PATCH 4/4] docs: add docs for ScriptSigner --- .../org/nervos/ckb/sign/ScriptSigner.java | 21 ++++++++++++++++++ .../nervos/ckb/sign/TransactionSigner.java | 22 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/nervos/ckb/sign/ScriptSigner.java b/core/src/main/java/org/nervos/ckb/sign/ScriptSigner.java index 36db6543c..c114b45bd 100644 --- a/core/src/main/java/org/nervos/ckb/sign/ScriptSigner.java +++ b/core/src/main/java/org/nervos/ckb/sign/ScriptSigner.java @@ -2,6 +2,27 @@ import org.nervos.ckb.type.Transaction; +/** + * ScriptSigner implements the signing logic for various scripts. + * + * These signers should be registered in {@link TransactionSigner}. + */ public interface ScriptSigner { + /** + * Sign the transaction for the script. + * + * This function is called by {@link TransactionSigner} + * for each context and each matched script group. + * + * @param transaction The transaction to be signed. + * @param scriptGroup TransactionSigner selects the corresponding ScriptSigner for each script group. + * This selection is based on the registered ScriptSigner's provided script type and hash. + * @param context This is passed from {@code TransactionSigner.signTransaction} + * @return {@code true} when the script handler has modified the transaction. + * + * @see TransactionSigner#signTransaction(TransactionWithScriptGroups, Context...) + * @see TransactionSigner#signTransaction(TransactionWithScriptGroups, Set) + * @see TransactionSigner#signTransaction(TransactionWithScriptGroups, String...) + */ boolean signTransaction(Transaction transaction, ScriptGroup scriptGroup, Context context); } diff --git a/core/src/main/java/org/nervos/ckb/sign/TransactionSigner.java b/core/src/main/java/org/nervos/ckb/sign/TransactionSigner.java index c06243012..498a7ffce 100644 --- a/core/src/main/java/org/nervos/ckb/sign/TransactionSigner.java +++ b/core/src/main/java/org/nervos/ckb/sign/TransactionSigner.java @@ -87,12 +87,25 @@ public TransactionSigner registerLockScriptSigner(String codeHash, ScriptSigner return registerLockScriptSigner(Numeric.hexStringToByteArray(codeHash), scriptSigner); } - + /** + * signTransaction signs the transaction using registered ScriptSigners. + * + * @param transaction Transaction to be signed. + * @param contexts Contexts for {@link org.nervos.ckb.sign.ScriptSigner}. + * @return signed groups indices. + */ public Set signTransaction( TransactionWithScriptGroups transaction, Context... contexts) { return signTransaction(transaction, new HashSet<>(Arrays.asList(contexts))); } + /** + * signTransaction signs the transaction using registered ScriptSigners. + * + * @param transaction Transaction to be signed. + * @param contexts Contexts for {@link org.nervos.ckb.sign.ScriptSigner}. + * @return signed groups indices. + */ public Set signTransaction( TransactionWithScriptGroups transaction, Set contexts) { Set signedGroupsIndices = new HashSet<>(); @@ -122,6 +135,13 @@ public Set signTransaction( return signedGroupsIndices; } + /** + * signTransaction signs the transaction using registered ScriptSigners. + * + * @param transaction Transaction to be signed. + * @param privateKeys Each private key is wrapped in one {@link Context}. These contexts will be passed to ScriptSigners. + * @return signed groups indices. + */ public Set signTransaction( TransactionWithScriptGroups transaction, String... privateKeys) { Contexts contexts = new Contexts();