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 d4176151a..125dae1fd 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; 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..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 @@ -4,69 +4,88 @@ 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; import java.util.List; +import java.util.Optional; public class Secp256k1Blake160MultisigAllScriptHandler implements ScriptHandler { - private List cellDeps; - private byte[] codeHash; + private Network network; 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; - } - - @Override - public void init(Network network) { + public List getCellDeps(MultisigVersion multisigVersion) { OutPoint outPoint = new OutPoint(); - if (network == Network.MAINNET) { - outPoint.txHash = Numeric.hexStringToByteArray("0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c"); - outPoint.index = 1; - } else if (network == Network.TESTNET) { - outPoint.txHash = Numeric.hexStringToByteArray("0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37"); - outPoint.index = 1; + if (this.network == Network.MAINNET) { + switch (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 (this.network == Network.TESTNET) { + switch (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"); } CellDep cellDep = new CellDep(); cellDep.outPoint = outPoint; cellDep.depType = CellDep.DepType.DEP_GROUP; - cellDeps = Arrays.asList(cellDep); - this.codeHash = Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH; + return Arrays.asList(cellDep); } - private boolean isMatched(Script script) { + + @Override + public void init(Network network) { + this.network = network; + } + + 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); } @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 498a7ffce..b847a20c7 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()) + .registerLockScriptData1Signer( + 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()) + .registerLockScriptData1Signer( + Script.SECP256K1_BLAKE160_MULTISIG_ALL_CODE_HASH_V2, new Secp256k1Blake160MultisigAllSigner()) .registerLockScriptSigner( Script.ANY_CAN_PAY_CODE_HASH_MAINNET, new AcpSigner()) .registerLockScriptSigner( @@ -83,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/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..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,8 +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.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; @@ -18,16 +17,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(); @@ -43,8 +42,46 @@ public static void main(String[] args) 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)); } + + + 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() + ); + // ckt1qqmvjudc6s0mm992hjnhm367sfnjntycg3a5d7g7qpukz4wamvxjjq4unqvd3g2felqv6qer8pkydws8jg9qxlc3r8v40 + 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", 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_v2(); + } }