diff --git a/README.md b/README.md index 97cb4d42..7d72330f 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(); ``` 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 aeb5e9b0..8e10c886 100644 --- a/ckb/src/main/java/org/nervos/ckb/transaction/AbstractTransactionBuilder.java +++ b/ckb/src/main/java/org/nervos/ckb/transaction/AbstractTransactionBuilder.java @@ -13,6 +13,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; @@ -88,6 +94,14 @@ 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); } @@ -129,5 +143,12 @@ public Map rebuildScriptGroups(Map scr } return ret; } + /** + * 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 3dcfedea..f1035a81 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); 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 36db6543..c114b45b 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 c0624301..498a7ffc 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();