From 81ab4f13dfc7f24944d5bf5c57b5603110b0f6b1 Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Mon, 5 May 2025 11:51:05 +0800 Subject: [PATCH 1/2] Update Multisig Signed-off-by: Eval EXEC --- .../TransactionBuilderConfiguration.java | 4 +- ...256k1Blake160MultisigAllScriptHandler.java | 59 +++++++++++++++---- .../nervos/ckb/sign/TransactionSigner.java | 8 ++- .../org/nervos/ckb/type/MultisigVersion.java | 35 +++++++++++ .../main/java/org/nervos/ckb/type/Script.java | 10 +++- .../org/nervos/ckb/utils/address/Address.java | 6 +- core/src/test/java/utils/AddressTest.java | 15 ++++- .../ckb/example/SendCkbMultisigExample.java | 44 +++++++++++++- 8 files changed, 159 insertions(+), 22 deletions(-) create mode 100644 core/src/main/java/org/nervos/ckb/type/MultisigVersion.java diff --git a/ckb/src/main/java/org/nervos/ckb/transaction/TransactionBuilderConfiguration.java b/ckb/src/main/java/org/nervos/ckb/transaction/TransactionBuilderConfiguration.java index d4176151a..8a10a4da7 100644 --- a/ckb/src/main/java/org/nervos/ckb/transaction/TransactionBuilderConfiguration.java +++ b/ckb/src/main/java/org/nervos/ckb/transaction/TransactionBuilderConfiguration.java @@ -2,6 +2,7 @@ import org.nervos.ckb.Network; import org.nervos.ckb.transaction.handler.*; +import org.nervos.ckb.type.MultisigVersion; import javax.annotation.Nullable; import java.util.ArrayList; @@ -22,7 +23,8 @@ public TransactionBuilderConfiguration(Network network) { Objects.requireNonNull(network); this.network = network; registerScriptHandler(Secp256k1Blake160SighashAllScriptHandler.class); - registerScriptHandler(Secp256k1Blake160MultisigAllScriptHandler.class); + registerScriptHandler(new Secp256k1Blake160MultisigAllScriptHandler(MultisigVersion.Legacy).getClass()); + registerScriptHandler(new Secp256k1Blake160MultisigAllScriptHandler(MultisigVersion.V2).getClass()); registerScriptHandler(SudtScriptHandler.class); registerScriptHandler(DaoScriptHandler.class); registerScriptHandler(OmnilockScriptHandler.class); diff --git a/ckb/src/main/java/org/nervos/ckb/transaction/handler/Secp256k1Blake160MultisigAllScriptHandler.java b/ckb/src/main/java/org/nervos/ckb/transaction/handler/Secp256k1Blake160MultisigAllScriptHandler.java index 3e73dbb71..07915a77d 100644 --- a/ckb/src/main/java/org/nervos/ckb/transaction/handler/Secp256k1Blake160MultisigAllScriptHandler.java +++ b/ckb/src/main/java/org/nervos/ckb/transaction/handler/Secp256k1Blake160MultisigAllScriptHandler.java @@ -4,10 +4,7 @@ import org.nervos.ckb.sign.ScriptGroup; import org.nervos.ckb.sign.signer.Secp256k1Blake160MultisigAllSigner; import org.nervos.ckb.transaction.AbstractTransactionBuilder; -import org.nervos.ckb.type.CellDep; -import org.nervos.ckb.type.OutPoint; -import org.nervos.ckb.type.Script; -import org.nervos.ckb.type.WitnessArgs; +import org.nervos.ckb.type.*; import org.nervos.ckb.utils.Numeric; import java.util.Arrays; @@ -16,8 +13,11 @@ public class Secp256k1Blake160MultisigAllScriptHandler implements ScriptHandler { private List cellDeps; private byte[] codeHash; + private Script.HashType hashType; + private MultisigVersion multisigVersion; - public Secp256k1Blake160MultisigAllScriptHandler() { + public Secp256k1Blake160MultisigAllScriptHandler(MultisigVersion multisigVersion) { + this.multisigVersion = multisigVersion; } public List getCellDeps() { @@ -36,15 +36,51 @@ public void setCodeHash(byte[] codeHash) { this.codeHash = codeHash; } + public Script.HashType getHashType() { + return hashType; + } + + public void setHashType(Script.HashType hashType) { + this.hashType = hashType; + } + + public MultisigVersion getMultisigVersion() { + return multisigVersion; + } + + public void setMultisigVersion(MultisigVersion multisigVersion) { + this.multisigVersion = multisigVersion; + } + @Override public void init(Network network) { OutPoint outPoint = new OutPoint(); if (network == Network.MAINNET) { - outPoint.txHash = Numeric.hexStringToByteArray("0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c"); - outPoint.index = 1; + switch (this.multisigVersion) { + case Legacy: + outPoint.txHash = Numeric.hexStringToByteArray("0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c"); + outPoint.index = 1; + break; + case V2: + outPoint.txHash = Numeric.hexStringToByteArray("0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738"); + outPoint.index = 0; + break; + default: + throw new IllegalArgumentException("Unsupported multisig version"); + } } else if (network == Network.TESTNET) { - outPoint.txHash = Numeric.hexStringToByteArray("0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37"); - outPoint.index = 1; + switch (this.multisigVersion) { + case Legacy: + outPoint.txHash = Numeric.hexStringToByteArray("0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37"); + outPoint.index = 1; + break; + case V2: + outPoint.txHash = Numeric.hexStringToByteArray("0x2eefdeb21f3a3edf697c28a52601b4419806ed60bb427420455cc29a090b26d5"); + outPoint.index = 0; + break; + default: + throw new IllegalArgumentException("Unsupported multisig version"); + } } else { throw new IllegalArgumentException("Unsupported network"); } @@ -52,14 +88,15 @@ public void init(Network network) { cellDep.outPoint = outPoint; cellDep.depType = CellDep.DepType.DEP_GROUP; cellDeps = Arrays.asList(cellDep); - this.codeHash = Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH; + this.codeHash = this.multisigVersion.codeHash(); + this.hashType = this.multisigVersion.hashType(); } private boolean isMatched(Script script) { if (script == null) { return false; } - return Arrays.equals(script.codeHash, codeHash); + return Arrays.equals(script.codeHash, codeHash) && script.hashType == hashType; } @Override 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 498a7ffce..ad18ddce4 100644 --- a/core/src/main/java/org/nervos/ckb/sign/TransactionSigner.java +++ b/core/src/main/java/org/nervos/ckb/sign/TransactionSigner.java @@ -19,7 +19,9 @@ public class TransactionSigner { .registerLockScriptSigner( Script.SECP256K1_BLAKE160_SIGNHASH_ALL_CODE_HASH, new Secp256k1Blake160SighashAllSigner()) .registerLockScriptSigner( - Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH, new Secp256k1Blake160MultisigAllSigner()) + Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_LEGACY, new Secp256k1Blake160MultisigAllSigner()) + .registerLockScriptSigner( + Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_V2, new Secp256k1Blake160MultisigAllSigner()) .registerLockScriptSigner( Script.ANY_CAN_PAY_CODE_HASH_TESTNET, new AcpSigner()) .registerLockScriptSigner( @@ -31,7 +33,9 @@ Script.PW_LOCK_CODE_HASH_TESTNET, new PwSigner()) .registerLockScriptSigner( Script.SECP256K1_BLAKE160_SIGNHASH_ALL_CODE_HASH, new Secp256k1Blake160SighashAllSigner()) .registerLockScriptSigner( - Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH, new Secp256k1Blake160MultisigAllSigner()) + Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_LEGACY, new Secp256k1Blake160MultisigAllSigner()) + .registerLockScriptSigner( + Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_V2, new Secp256k1Blake160MultisigAllSigner()) .registerLockScriptSigner( Script.ANY_CAN_PAY_CODE_HASH_MAINNET, new AcpSigner()) .registerLockScriptSigner( diff --git a/core/src/main/java/org/nervos/ckb/type/MultisigVersion.java b/core/src/main/java/org/nervos/ckb/type/MultisigVersion.java new file mode 100644 index 000000000..d8e4118d2 --- /dev/null +++ b/core/src/main/java/org/nervos/ckb/type/MultisigVersion.java @@ -0,0 +1,35 @@ +package org.nervos.ckb.type; + +public enum MultisigVersion { + /// Multisig Script deployed on Genesis Block + /// https://explorer.nervos.org/script/0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8/type + Legacy, + /// Latest multisig script, Enhance multisig handling for optional since value + /// https://github.com/nervosnetwork/ckb-system-scripts/pull/99 + /// https://explorer.nervos.org/script/0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29/data1 + V2; + + public byte[] codeHash(){ + switch (this) + { + case Legacy: + return Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_LEGACY; + case V2: + return Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_V2; + default: + throw new IllegalArgumentException("Unknown multisig version: " + this); + } + } + + public Script.HashType hashType() { + switch (this) + { + case Legacy: + return Script.HashType.TYPE; + case V2: + return Script.HashType.DATA1; + default: + throw new IllegalArgumentException("Unknown multisig version: " + this); + } + } +} diff --git a/core/src/main/java/org/nervos/ckb/type/Script.java b/core/src/main/java/org/nervos/ckb/type/Script.java index f9acd677b..8f9502f16 100644 --- a/core/src/main/java/org/nervos/ckb/type/Script.java +++ b/core/src/main/java/org/nervos/ckb/type/Script.java @@ -11,11 +11,19 @@ import static org.nervos.ckb.utils.MoleculeConverter.packByte32; import static org.nervos.ckb.utils.MoleculeConverter.packBytes; + public class Script { public static final byte[] SECP256K1_BLAKE160_SIGNHASH_ALL_CODE_HASH = Numeric.hexStringToByteArray("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"); - public static final byte[] SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH = + // Multisig Script deployed on Genesis Block + // https://explorer.nervos.org/script/0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8/type + public static final byte[] SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_LEGACY = Numeric.hexStringToByteArray("0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8"); + // Latest multisig script, Enhance multisig handling for optional since value + // https://github.com/nervosnetwork/ckb-system-scripts/pull/99 + // https://explorer.nervos.org/script/0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29/data1 + public static final byte[] SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_V2= + Numeric.hexStringToByteArray("0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29"); public static final byte[] ANY_CAN_PAY_CODE_HASH_MAINNET = Numeric.hexStringToByteArray("0xd369597ff47f29fbc0d47d2e3775370d1250b85140c670e4718af712983a2354"); public static final byte[] ANY_CAN_PAY_CODE_HASH_TESTNET = diff --git a/core/src/main/java/org/nervos/ckb/utils/address/Address.java b/core/src/main/java/org/nervos/ckb/utils/address/Address.java index 40184336a..2418edf1a 100644 --- a/core/src/main/java/org/nervos/ckb/utils/address/Address.java +++ b/core/src/main/java/org/nervos/ckb/utils/address/Address.java @@ -85,7 +85,7 @@ private static Address decodeShort(byte[] payload, Network network) { if (args.length != 20) { throw new AddressFormatException("Invalid args length " + args.length); } - codeHash = Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH; + codeHash = Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_LEGACY; } else if (codeHashIndex == 0x02) { if (args.length < 20 || args.length > 22) { throw new AddressFormatException("Invalid args length " + args.length); @@ -145,7 +145,7 @@ public String encodeShort() { byte[] codeHash = script.codeHash; if (Arrays.equals(codeHash, Script.SECP256K1_BLAKE160_SIGNHASH_ALL_CODE_HASH)) { codeHashIndex = 0x00; - } else if (Arrays.equals(codeHash, Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH)) { + } else if (Arrays.equals(codeHash, Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_LEGACY)) { codeHashIndex = 0x01; } else if ((network == Network.MAINNET && Arrays.equals(codeHash, Script.ANY_CAN_PAY_CODE_HASH_MAINNET) || (network == Network.TESTNET && Arrays.equals(codeHash, Script.ANY_CAN_PAY_CODE_HASH_TESTNET)))) { @@ -260,4 +260,4 @@ public int hashCode() { result = 31 * result + network.hashCode(); return result; } -} \ No newline at end of file +} diff --git a/core/src/test/java/utils/AddressTest.java b/core/src/test/java/utils/AddressTest.java index 5961dde75..c2b502907 100644 --- a/core/src/test/java/utils/AddressTest.java +++ b/core/src/test/java/utils/AddressTest.java @@ -2,6 +2,7 @@ import org.junit.jupiter.api.Test; import org.nervos.ckb.Network; +import org.nervos.ckb.type.MultisigVersion; import org.nervos.ckb.type.Script; import org.nervos.ckb.utils.Numeric; import org.nervos.ckb.utils.address.Address; @@ -22,7 +23,7 @@ public void testDecodeDecode() { testShort(script, Network.MAINNET, "ckb1qyqt8xaupvm8837nv3gtc9x0ekkj64vud3jqfwyw5v"); testShort(script, Network.TESTNET, "ckt1qyqt8xaupvm8837nv3gtc9x0ekkj64vud3jq5t63cs"); - script = generateScript(Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH, + script = generateScript(Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_LEGACY, "4fb2be2e5d0c1a3b8694f832350a33c1685d477a", Script.HashType.TYPE); testShort(script, Network.MAINNET, "ckb1qyq5lv479ewscx3ms620sv34pgeuz6zagaaqklhtgg"); testShort(script, Network.TESTNET, "ckt1qyq5lv479ewscx3ms620sv34pgeuz6zagaaqt6f5y5"); @@ -59,6 +60,18 @@ public void testDecodeDecode() { "b39bbc0b3673c7d36450bc14cfcdad2d559c6c64", Script.HashType.DATA1); testFullBech32m(script, Network.MAINNET, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq4nnw7qkdnnclfkg59uzn8umtfd2kwxceqcydzyt"); testFullBech32m(script, Network.TESTNET, "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq4nnw7qkdnnclfkg59uzn8umtfd2kwxceqkkxdwn"); + + // multiscript v2 + + script = generateScript(MultisigVersion.V2.codeHash(), + "986b5c23988427044e57d188fee45530d8877bcc", MultisigVersion.V2.hashType()); + testFullBech32m(script, Network.MAINNET, "ckb1qqmvjudc6s0mm992hjnhm367sfnjntycg3a5d7g7qpukz4wamvxjjq5cddwz8xyyyuzyu4733rlwg4fsmzrhhnqvclulh"); + testFullBech32m(script, Network.TESTNET, "ckt1qqmvjudc6s0mm992hjnhm367sfnjntycg3a5d7g7qpukz4wamvxjjq5cddwz8xyyyuzyu4733rlwg4fsmzrhhnqz25n40"); + + script = generateScript(MultisigVersion.Legacy.codeHash(), + "986b5c23988427044e57d188fee45530d8877bcc", MultisigVersion.Legacy.hashType()); + testFullBech32m(script, Network.MAINNET, "ckb1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sqvcddwz8xyyyuzyu4733rlwg4fsmzrhhnqq5gm75"); + testFullBech32m(script, Network.TESTNET, "ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sqvcddwz8xyyyuzyu4733rlwg4fsmzrhhnqwxr55v"); } private void testShort(Script script, Network network, String encoded) { diff --git a/example/src/main/java/org/nervos/ckb/example/SendCkbMultisigExample.java b/example/src/main/java/org/nervos/ckb/example/SendCkbMultisigExample.java index bb82e96d7..cd1b22175 100644 --- a/example/src/main/java/org/nervos/ckb/example/SendCkbMultisigExample.java +++ b/example/src/main/java/org/nervos/ckb/example/SendCkbMultisigExample.java @@ -8,6 +8,7 @@ import org.nervos.ckb.sign.signer.Secp256k1Blake160MultisigAllSigner; import org.nervos.ckb.transaction.CkbTransactionBuilder; import org.nervos.ckb.transaction.TransactionBuilderConfiguration; +import org.nervos.ckb.type.MultisigVersion; import org.nervos.ckb.type.Script; import org.nervos.ckb.type.TransactionInput; import org.nervos.ckb.utils.Numeric; @@ -18,16 +19,16 @@ import java.util.Iterator; public class SendCkbMultisigExample { - public static void main(String[] args) throws IOException { + public static void example_legacy() throws IOException { Network network = Network.TESTNET; Secp256k1Blake160MultisigAllSigner.MultisigScript multisigScript = new Secp256k1Blake160MultisigAllSigner.MultisigScript(0, 2, "0x7336b0ba900684cb3cb00f0d46d4f64c0994a562", "0x5724c1e3925a5206944d753a6f3edaedf977d77f"); Script lock = new Script( - Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH, + MultisigVersion.Legacy.codeHash(), multisigScript.computeHash(), - Script.HashType.TYPE); + MultisigVersion.Legacy.hashType()); // ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sqdunqvd3g2felqv6qer8pkydws8jg9qxlca0st5v String sender = new Address(lock, network).encode(); @@ -47,4 +48,41 @@ public static void main(String[] args) throws IOException { byte[] txHash = api.sendTransaction(txWithGroups.getTxView()); System.out.println("Transaction hash: " + Numeric.toHexString(txHash)); } + + + public static void example_v2() throws IOException { + Network network = Network.TESTNET; + Secp256k1Blake160MultisigAllSigner.MultisigScript multisigScript = + new Secp256k1Blake160MultisigAllSigner.MultisigScript(0, 2, + "0x7336b0ba900684cb3cb00f0d46d4f64c0994a562", + "0x5724c1e3925a5206944d753a6f3edaedf977d77f"); + Script lock = new Script( + MultisigVersion.V2.codeHash(), + multisigScript.computeHash(), + MultisigVersion.V2.hashType() + ); + // ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sqdunqvd3g2felqv6qer8pkydws8jg9qxlca0st5v + String sender = new Address(lock, network).encode(); + + TransactionBuilderConfiguration configuration = new TransactionBuilderConfiguration(network); + Iterator iterator = new InputIterator(sender); + TransactionWithScriptGroups txWithGroups = new CkbTransactionBuilder(configuration, iterator) + .addOutput("ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq2qf8keemy2p5uu0g0gn8cd4ju23s5269qk8rg4r", + 50100000001L) + .setChangeOutput(sender) + .build(multisigScript); + + TransactionSigner signer = TransactionSigner.getInstance(network); + signer.signTransaction(txWithGroups, new Context("0x4fd809631a6aa6e3bb378dd65eae5d71df895a82c91a615a1e8264741515c79c", multisigScript)); + signer.signTransaction(txWithGroups, new Context("0x7438f7b35c355e3d2fb9305167a31a72d22ddeafb80a21cc99ff6329d92e8087", multisigScript)); + + Api api = new Api("https://testnet.ckb.dev", false); + byte[] txHash = api.sendTransaction(txWithGroups.getTxView()); + System.out.println("Transaction hash: " + Numeric.toHexString(txHash)); + } + + public static void main(String[] args) throws IOException { + example_legacy(); + example_v2(); + } } From c0d8bdcc510212f1619ed39f38734bbb63d811fc Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Mon, 26 May 2025 18:53:07 +0800 Subject: [PATCH 2/2] Secp256k1Blake160MultisigAllScriptHandler check multiisgVersion in runtime Signed-off-by: Eval EXEC --- .../main/java/org/nervos/ckb/CkbRpcApi.java | 4 +- .../main/java/org/nervos/ckb/service/Api.java | 6 +- .../TransactionBuilderConfiguration.java | 3 +- ...256k1Blake160MultisigAllScriptHandler.java | 84 ++++++++----------- .../nervos/ckb/sign/TransactionSigner.java | 8 +- .../org/nervos/ckb/type/EntryCompleted.java | 6 ++ .../ckb/example/SendCkbMultisigExample.java | 13 ++- 7 files changed, 57 insertions(+), 67 deletions(-) create mode 100644 core/src/main/java/org/nervos/ckb/type/EntryCompleted.java diff --git a/ckb/src/main/java/org/nervos/ckb/CkbRpcApi.java b/ckb/src/main/java/org/nervos/ckb/CkbRpcApi.java index 02b98af70..718e39042 100644 --- a/ckb/src/main/java/org/nervos/ckb/CkbRpcApi.java +++ b/ckb/src/main/java/org/nervos/ckb/CkbRpcApi.java @@ -111,9 +111,9 @@ byte[] sendTransaction(Transaction transaction, OutputsValidator outputsValidato byte[] sendTestTransaction(Transaction transaction, OutputsValidator outputsValidator) throws IOException; - byte[] testTxPoolAccept(Transaction transaction) throws IOException; + EntryCompleted testTxPoolAccept(Transaction transaction) throws IOException; - byte[] testTxPoolAccept(Transaction transaction, OutputsValidator outputsValidator) + EntryCompleted testTxPoolAccept(Transaction transaction, OutputsValidator outputsValidator) throws IOException; NodeInfo localNodeInfo() throws IOException; diff --git a/ckb/src/main/java/org/nervos/ckb/service/Api.java b/ckb/src/main/java/org/nervos/ckb/service/Api.java index 9cbeeae72..ff95a3392 100644 --- a/ckb/src/main/java/org/nervos/ckb/service/Api.java +++ b/ckb/src/main/java/org/nervos/ckb/service/Api.java @@ -315,7 +315,7 @@ public byte[] sendTestTransaction(Transaction transaction, OutputsValidator outp } @Override - public byte[] testTxPoolAccept(Transaction transaction) throws IOException { + public EntryCompleted testTxPoolAccept(Transaction transaction) throws IOException { return rpcService.post( "test_tx_pool_accept", Arrays.asList(Convert.parseTransaction(transaction), OutputsValidator.PASSTHROUGH), @@ -323,12 +323,12 @@ public byte[] testTxPoolAccept(Transaction transaction) throws IOException { } @Override - public byte[] testTxPoolAccept(Transaction transaction, OutputsValidator outputsValidator) + public EntryCompleted testTxPoolAccept(Transaction transaction, OutputsValidator outputsValidator) throws IOException { return rpcService.post( "test_tx_pool_accept", Arrays.asList(Convert.parseTransaction(transaction), outputsValidator), - byte[].class); + EntryCompleted.class); } @Override diff --git a/ckb/src/main/java/org/nervos/ckb/transaction/TransactionBuilderConfiguration.java b/ckb/src/main/java/org/nervos/ckb/transaction/TransactionBuilderConfiguration.java index 8a10a4da7..125dae1fd 100644 --- a/ckb/src/main/java/org/nervos/ckb/transaction/TransactionBuilderConfiguration.java +++ b/ckb/src/main/java/org/nervos/ckb/transaction/TransactionBuilderConfiguration.java @@ -23,8 +23,7 @@ public TransactionBuilderConfiguration(Network network) { Objects.requireNonNull(network); this.network = network; registerScriptHandler(Secp256k1Blake160SighashAllScriptHandler.class); - registerScriptHandler(new Secp256k1Blake160MultisigAllScriptHandler(MultisigVersion.Legacy).getClass()); - registerScriptHandler(new Secp256k1Blake160MultisigAllScriptHandler(MultisigVersion.V2).getClass()); + registerScriptHandler(Secp256k1Blake160MultisigAllScriptHandler.class); registerScriptHandler(SudtScriptHandler.class); registerScriptHandler(DaoScriptHandler.class); registerScriptHandler(OmnilockScriptHandler.class); diff --git a/ckb/src/main/java/org/nervos/ckb/transaction/handler/Secp256k1Blake160MultisigAllScriptHandler.java b/ckb/src/main/java/org/nervos/ckb/transaction/handler/Secp256k1Blake160MultisigAllScriptHandler.java index 07915a77d..3ffea2655 100644 --- a/ckb/src/main/java/org/nervos/ckb/transaction/handler/Secp256k1Blake160MultisigAllScriptHandler.java +++ b/ckb/src/main/java/org/nervos/ckb/transaction/handler/Secp256k1Blake160MultisigAllScriptHandler.java @@ -9,54 +9,18 @@ import java.util.Arrays; import java.util.List; +import java.util.Optional; public class Secp256k1Blake160MultisigAllScriptHandler implements ScriptHandler { - private List cellDeps; - private byte[] codeHash; - private Script.HashType hashType; - private MultisigVersion multisigVersion; + private Network network; - public Secp256k1Blake160MultisigAllScriptHandler(MultisigVersion multisigVersion) { - this.multisigVersion = multisigVersion; + public Secp256k1Blake160MultisigAllScriptHandler() { } - public List getCellDeps() { - return cellDeps; - } - - public void setCellDeps(List cellDeps) { - this.cellDeps = cellDeps; - } - - public byte[] getCodeHash() { - return codeHash; - } - - public void setCodeHash(byte[] codeHash) { - this.codeHash = codeHash; - } - - public Script.HashType getHashType() { - return hashType; - } - - public void setHashType(Script.HashType hashType) { - this.hashType = hashType; - } - - public MultisigVersion getMultisigVersion() { - return multisigVersion; - } - - public void setMultisigVersion(MultisigVersion multisigVersion) { - this.multisigVersion = multisigVersion; - } - - @Override - public void init(Network network) { + public List getCellDeps(MultisigVersion multisigVersion) { OutPoint outPoint = new OutPoint(); - if (network == Network.MAINNET) { - switch (this.multisigVersion) { + if (this.network == Network.MAINNET) { + switch (multisigVersion) { case Legacy: outPoint.txHash = Numeric.hexStringToByteArray("0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c"); outPoint.index = 1; @@ -68,8 +32,8 @@ public void init(Network network) { default: throw new IllegalArgumentException("Unsupported multisig version"); } - } else if (network == Network.TESTNET) { - switch (this.multisigVersion) { + } else if (this.network == Network.TESTNET) { + switch (multisigVersion) { case Legacy: outPoint.txHash = Numeric.hexStringToByteArray("0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37"); outPoint.index = 1; @@ -87,23 +51,41 @@ public void init(Network network) { CellDep cellDep = new CellDep(); cellDep.outPoint = outPoint; cellDep.depType = CellDep.DepType.DEP_GROUP; - cellDeps = Arrays.asList(cellDep); - this.codeHash = this.multisigVersion.codeHash(); - this.hashType = this.multisigVersion.hashType(); + return Arrays.asList(cellDep); + } + + + @Override + public void init(Network network) { + this.network = network; } - private boolean isMatched(Script script) { + private Optional isMatched(Script script) { if (script == null) { - return false; + return Optional.empty(); + } + + if (Arrays.equals(script.codeHash, MultisigVersion.Legacy.codeHash()) && script.hashType == MultisigVersion.Legacy.hashType()) { + return Optional.of(MultisigVersion.Legacy); + } else if (Arrays.equals(script.codeHash, MultisigVersion.V2.codeHash()) && script.hashType == MultisigVersion.V2.hashType()) { + return Optional.of(MultisigVersion.V2); + } else { + return Optional.empty(); } - return Arrays.equals(script.codeHash, codeHash) && script.hashType == hashType; } @Override public boolean buildTransaction(AbstractTransactionBuilder txBuilder, ScriptGroup scriptGroup, Object context) { - if (scriptGroup == null || !isMatched(scriptGroup.getScript())) { + if (scriptGroup == null) { return false; } + Optional multisigVersion = isMatched(scriptGroup.getScript()); + if (!multisigVersion.isPresent()) { + return false; + } + + List cellDeps = this.getCellDeps(multisigVersion.get()); + Secp256k1Blake160MultisigAllSigner.MultisigScript multisigScript; if (context instanceof Secp256k1Blake160MultisigAllSigner.MultisigScript) { multisigScript = (Secp256k1Blake160MultisigAllSigner.MultisigScript) 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 ad18ddce4..b847a20c7 100644 --- a/core/src/main/java/org/nervos/ckb/sign/TransactionSigner.java +++ b/core/src/main/java/org/nervos/ckb/sign/TransactionSigner.java @@ -20,7 +20,7 @@ public class TransactionSigner { Script.SECP256K1_BLAKE160_SIGNHASH_ALL_CODE_HASH, new Secp256k1Blake160SighashAllSigner()) .registerLockScriptSigner( Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_LEGACY, new Secp256k1Blake160MultisigAllSigner()) - .registerLockScriptSigner( + .registerLockScriptData1Signer( Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_V2, new Secp256k1Blake160MultisigAllSigner()) .registerLockScriptSigner( Script.ANY_CAN_PAY_CODE_HASH_TESTNET, new AcpSigner()) @@ -34,7 +34,7 @@ Script.PW_LOCK_CODE_HASH_TESTNET, new PwSigner()) Script.SECP256K1_BLAKE160_SIGNHASH_ALL_CODE_HASH, new Secp256k1Blake160SighashAllSigner()) .registerLockScriptSigner( Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_LEGACY, new Secp256k1Blake160MultisigAllSigner()) - .registerLockScriptSigner( + .registerLockScriptData1Signer( Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_V2, new Secp256k1Blake160MultisigAllSigner()) .registerLockScriptSigner( Script.ANY_CAN_PAY_CODE_HASH_MAINNET, new AcpSigner()) @@ -87,6 +87,10 @@ public TransactionSigner registerLockScriptSigner(byte[] codeHash, ScriptSigner return register(codeHash, Script.HashType.TYPE, ScriptType.LOCK, scriptSigner); } + public TransactionSigner registerLockScriptData1Signer(byte[] codeHash, ScriptSigner scriptSigner) { + return register(codeHash, Script.HashType.DATA1, ScriptType.LOCK, scriptSigner); + } + public TransactionSigner registerLockScriptSigner(String codeHash, ScriptSigner scriptSigner) { return registerLockScriptSigner(Numeric.hexStringToByteArray(codeHash), scriptSigner); } diff --git a/core/src/main/java/org/nervos/ckb/type/EntryCompleted.java b/core/src/main/java/org/nervos/ckb/type/EntryCompleted.java new file mode 100644 index 000000000..9ba9fb397 --- /dev/null +++ b/core/src/main/java/org/nervos/ckb/type/EntryCompleted.java @@ -0,0 +1,6 @@ +package org.nervos.ckb.type; + +public class EntryCompleted { + public long cycles; + public long fee; +} diff --git a/example/src/main/java/org/nervos/ckb/example/SendCkbMultisigExample.java b/example/src/main/java/org/nervos/ckb/example/SendCkbMultisigExample.java index cd1b22175..fc8f19b31 100644 --- a/example/src/main/java/org/nervos/ckb/example/SendCkbMultisigExample.java +++ b/example/src/main/java/org/nervos/ckb/example/SendCkbMultisigExample.java @@ -8,9 +8,7 @@ import org.nervos.ckb.sign.signer.Secp256k1Blake160MultisigAllSigner; import org.nervos.ckb.transaction.CkbTransactionBuilder; import org.nervos.ckb.transaction.TransactionBuilderConfiguration; -import org.nervos.ckb.type.MultisigVersion; -import org.nervos.ckb.type.Script; -import org.nervos.ckb.type.TransactionInput; +import org.nervos.ckb.type.*; import org.nervos.ckb.utils.Numeric; import org.nervos.ckb.utils.address.Address; import org.nervos.ckb.transaction.InputIterator; @@ -44,7 +42,7 @@ public static void example_legacy() throws IOException { signer.signTransaction(txWithGroups, new Context("0x4fd809631a6aa6e3bb378dd65eae5d71df895a82c91a615a1e8264741515c79c", multisigScript)); signer.signTransaction(txWithGroups, new Context("0x7438f7b35c355e3d2fb9305167a31a72d22ddeafb80a21cc99ff6329d92e8087", multisigScript)); - Api api = new Api("https://testnet.ckb.dev", false); + Api api = new Api("https://testnet.ckb.dev", true); byte[] txHash = api.sendTransaction(txWithGroups.getTxView()); System.out.println("Transaction hash: " + Numeric.toHexString(txHash)); } @@ -61,7 +59,7 @@ public static void example_v2() throws IOException { multisigScript.computeHash(), MultisigVersion.V2.hashType() ); - // ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sqdunqvd3g2felqv6qer8pkydws8jg9qxlca0st5v + // ckt1qqmvjudc6s0mm992hjnhm367sfnjntycg3a5d7g7qpukz4wamvxjjq4unqvd3g2felqv6qer8pkydws8jg9qxlc3r8v40 String sender = new Address(lock, network).encode(); TransactionBuilderConfiguration configuration = new TransactionBuilderConfiguration(network); @@ -76,13 +74,14 @@ public static void example_v2() throws IOException { signer.signTransaction(txWithGroups, new Context("0x4fd809631a6aa6e3bb378dd65eae5d71df895a82c91a615a1e8264741515c79c", multisigScript)); signer.signTransaction(txWithGroups, new Context("0x7438f7b35c355e3d2fb9305167a31a72d22ddeafb80a21cc99ff6329d92e8087", multisigScript)); - Api api = new Api("https://testnet.ckb.dev", false); + Api api = new Api("https://testnet.ckb.dev", true); +// EntryCompleted completed = api.testTxPoolAccept(txWithGroups.getTxView(), OutputsValidator.PASSTHROUGH); byte[] txHash = api.sendTransaction(txWithGroups.getTxView()); System.out.println("Transaction hash: " + Numeric.toHexString(txHash)); } public static void main(String[] args) throws IOException { - example_legacy(); +// example_legacy(); example_v2(); } }