diff --git a/docs/guides/adv/multisig.md b/docs/guides/adv/multisig.md deleted file mode 100644 index 8edaeca95..000000000 --- a/docs/guides/adv/multisig.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -id: multisig -title: Advanced - Working with multi-sig contracts ---- - -In this guide, we will show how to create a multi-sig Account and sign a transaction with it. - -A multi-sig Account can be identified by its verification script. It uses the `CHECKMULTISIG` OpCode (`ae` in hexstring). Thus, if you see any verification scripts ending with `ae`, it is likely to be a multi-sig Account. - -## Creating a new multi-sig contract - -In order to create a multi-sig contract, we need 2 things: - -- Signing Threshold - - This is the minimum number of signatures required for this contract in order to verify a transaction. This number cannot be zero and cannot be larger than the number of public keys given. - -- List of participating public keys. - - This is a list of all public keys that will be part of the contract. Due to the need for a public key, you cannot have a multi-sig contract participating as a member of another multi-sig contract. - -Once the requirements are gathered, just called the function: - -```js -import { wallet } from "@cityofzion/neon-js"; - -const threshold = 2; -const publicKeys = [ - "02028a99826edc0c97d18e22b6932373d908d323aa7f92656a77ec26e8861699ef", - "031d8e1630ce640966967bc6d95223d21f44304133003140c3b52004dc981349c9", - "02232ce8d2e2063dce0451131851d47421bfc4fc1da4db116fca5302c0756462fa" -]; -var multiSigAcct = wallet.Account.createMultiSig(threshold, publicKeys); - -console.log(multiSigAcct.address) // ASo1RcNVLiV3yQ8j3ZyZv5EWfqBBT8s2Yd -``` - -Here, we create a multi-sig contract with 3 public keys and a signing threshold of 2. This means that any transaction with the intention of moving assets from `ASo1RcNVLiV3yQ8j3ZyZv5EWfqBBT8s2Yd` will require only 2 signatures from any of the 3 keys. - -Do note that the order of public keys play an important factor in the generation of the address. A multi-sig contract with keys 1,2,3 is different from a contract with keys 3,2,1. You cannot change the order after creation. Similarly, the threshold cannot be adjusted after creation. - -## Inspecting the Account - -So now that you have created the multi-sig Account, we can take a look at the internals and know what is important. - -```js -console.log(multiSigAcct.contract.script); // VerificationScript -``` - -**This is the most important part of this Account.** This is the verification script of the contract and it is paramount that you maintain a copy of this somewhere. Without this, the system will not be able to generate the multi-sig signature required. - -```js -console.log(multiSigAcct.contract.parameters); // Parameters -``` - -This is the parameters of the verification script. This just tells us what is the signing threshold as the number of parameters is the number of signatures required. You can think of the verification script as a function and these are the required arguments to the function. - -## Signing with multi-sig - -In terms of transaction construction, there is no difference between a normal Account and a multi-sig Account so I shall skip that phase. The only difference lies in the signing. To sign using a multi-sig contract, you have to gather the signatures generated from the different public keys and transform them into a single Witness. We can perform the transformation using the function `Witness.buildMultiSig`. - -We can go two ways here: - -### Using hexstrings - -We can perform every step in hexstrings. First we deserialize the transaction into a hexstring: - -```js -// Let us call the transaction unsignedTx -const hex = unsignedTx.serialize(); -``` - -Now, we send this hexstring to our respective owners of the public keys and request them to sign: - -```js -const signature1 = wallet.sign(hex, privateKey1); -``` - -They will now send back the signatures. They should be just normal hexstrings of 64 characters long. Once we have collected enough signatures, we can assemble the witness: - -```js -const witness = tx.Witness.buildMultiSig( - hex, - [signature1, signature2], - verificationScript -); -unsignedTx.scripts.push(witness); -``` - -Now, your transaction is signed and ready to be transmitted to the network. - -### Using objects - -We can perform the steps using `neon-js` objects. We distribute the transaction and request the key holders to sign the transaction like how they will sign a normal transaction: - -```js -const signedTx1 = unsignedTx.sign(account1); -const signedTx2 = unsignedTx.sign(account2); -``` - -Once all the signed transactions are transmitted back, we extract the individual signatures and compile them using `Witness.buildMultiSig`: - -```js -const individualWitnesses = [signedTx1, signedTx2].map(t => t.scripts[0]); -const witness = tx.Witness.buildMultiSig( - unsignedTx.serialize(), - individualWitnesses, - multiSigAcct -); - -unsignedTx.scripts.push(witness); -``` - -Both methods should achieve the same results. - -## Notes - -- Order of public keys for creation matters but order of signatures for signing does not matter (because we order it within). -- There is no need to diff --git a/docs/guides/basic/multi.md b/docs/guides/basic/multi.md new file mode 100644 index 000000000..f442de8ca --- /dev/null +++ b/docs/guides/basic/multi.md @@ -0,0 +1,111 @@ +--- +id: multi +title: Basic - Signing with a multi-sig account +--- + +This tutorial aims to show how to sign with a multi-sig account. This will be done by sending some assets from a multi-sig account. + +A multi-sig Account can be identified by its verification script. It uses the `CHECKMULTISIG` OpCode (`ae` in hexstring). Thus, if you see any verification scripts ending with `ae`, it is likely to be a multi-sig Account. + +## Setup +Here I will assume that you have already sent some assets over to the multi-sig account. + +```js +const { default: Neon, api, wallet, tx, rpc } = require("@cityofzion/neon-js"); + +const neoscan = new api.neoscan.instance( + "https://neoscan-testnet.io/api/main_net" +); +const rpcNodeUrl = "http://seed2.neo.org:20332"; +``` + +Our multi-sig account in this example is made up of 3 keys with a signing threshold of 2. +Do note that the order of keys in the array matters. A different order will generate a totally different address. + + +```js +const keyA = new wallet.Account( + "7d128a6d096f0c14c3a25a2b0c41cf79661bfcb4a8cc95aaaea28bde4d732344" +); +const keyB = new wallet.Account( + "9ab7e154840daca3a2efadaf0df93cd3a5b51768c632f5433f86909d9b994a69" +); +const keyC = new wallet.Account( + "3edee7036b8fd9cef91de47386b191dd76db2888a553e7736bb02808932a915b" +); + +const multisigAcct = wallet.Account.createMultiSig(2, [ + keyA.publicKey, + keyB.publicKey, + keyC.publicKey +]); + +console.log("\n\n--- Multi-sig ---"); +console.log(`My multi-sig address is ${multisigAcct.address}`); +console.log(`My multi-sig verificationScript is ${multisigAcct.contract.script}`); +``` + +## Construct Transaction +Similar to how we setup a transaction for a normal account transfer, we also do the same for our transfer from a multi-sig account. + + +```js +var constructTx = neoscan.getBalance(multisigAcct.address).then(balance => { + const transaction = Neon.create + .contractTx() + .addIntent("NEO", 1, keyA.address) + .addIntent("GAS", 0.00000001, keyB.address) + .calculate(balance); + + return transaction; +}); +``` + +## Sign Transaction +The only difference is in the signing of transactions. We need to sign the transaction individually by each key first. Then, we combine the signatures together to form a multi-sig witness. We should only see 1 witness attached to the transaction. + + +```js +const signTx = constructTx.then(transaction => { + const txHex = transaction.serialize(false); + + // This can be any 2 out of the 3 keys. + const sig1 = wallet.sign(txHex, keyB.privateKey); + const sig2 = wallet.sign(txHex, keyC.privateKey); + + const multiSigWitness = tx.Witness.buildMultiSig( + txHex, + [sig1, sig2], + multisigAcct + ); + + transaction.addWitness(multiSigWitness); + + console.log("\n\n--- Transaction ---"); + console.log(JSON.stringify(transaction.export(), undefined, 2)); + + console.log("\n\n--- Transaction hash---"); + console.log(transaction.hash) + + console.log("\n\n--- Transaction string ---") + console.log(transaction.serialize(true)); + return transaction; +}); +``` + +## Send Transaction +We send off the transaction using sendrawtransaction RPC call like any other normal transaction. + + +```js +const sendTx = signTx + .then(transaction => { + const client = new rpc.RPCClient(rpcNodeUrl); + return client.sendRawTransaction(transaction.serialize(true)); + }) + .then(res => { + console.log("\n\n--- Response ---"); + console.log(res); + }) + .catch(err => console.log(err)); +``` diff --git a/examples/basic/multi.js b/examples/basic/multi.js index 5409a2209..54e861c55 100644 --- a/examples/basic/multi.js +++ b/examples/basic/multi.js @@ -1,23 +1,23 @@ /** --- -id: multisig-signing -title: Signing with a multi-sig account +id: multi +title: Basic - Signing with a multi-sig account --- This tutorial aims to show how to sign with a multi-sig account. This will be done by sending some assets from a multi-sig account. +A multi-sig Account can be identified by its verification script. It uses the `CHECKMULTISIG` OpCode (`ae` in hexstring). Thus, if you see any verification scripts ending with `ae`, it is likely to be a multi-sig Account. + ## Setup Here I will assume that you have already sent some assets over to the multi-sig account. */ - -const { api, wallet, tx, rpc } = require("@cityofzion/neon-js"); -const Neon = require("@cityofzion/neon-js").default; +const { default: Neon, api, wallet, tx, rpc } = require("@cityofzion/neon-js"); const neoscan = new api.neoscan.instance( "https://neoscan-testnet.io/api/main_net" ); -const rpcNodeUrl = "http://seed5.neo.org:20332"; +const rpcNodeUrl = "http://seed2.neo.org:20332"; /** Our multi-sig account in this example is made up of 3 keys with a signing threshold of 2. @@ -39,8 +39,9 @@ const multisigAcct = wallet.Account.createMultiSig(2, [ keyC.publicKey ]); +console.log("\n\n--- Multi-sig ---"); console.log(`My multi-sig address is ${multisigAcct.address}`); -console.log(`My multi-sig verificationScript is ${multisigAcct.contract}`); +console.log(`My multi-sig verificationScript is ${multisigAcct.contract.script}`); /** ## Construct Transaction @@ -50,8 +51,10 @@ var constructTx = neoscan.getBalance(multisigAcct.address).then(balance => { const transaction = Neon.create .contractTx() .addIntent("NEO", 1, keyA.address) - .addIntent("GAS", 0.00001, keyB.address) + .addIntent("GAS", 0.00000001, keyB.address) .calculate(balance); + + return transaction; }); /** @@ -72,6 +75,15 @@ const signTx = constructTx.then(transaction => { ); transaction.addWitness(multiSigWitness); + + console.log("\n\n--- Transaction ---"); + console.log(JSON.stringify(transaction.export(), undefined, 2)); + + console.log("\n\n--- Transaction hash---"); + console.log(transaction.hash) + + console.log("\n\n--- Transaction string ---") + console.log(transaction.serialize(true)); return transaction; }); @@ -85,6 +97,7 @@ const sendTx = signTx return client.sendRawTransaction(transaction.serialize(true)); }) .then(res => { - console.log(res.result); + console.log("\n\n--- Response ---"); + console.log(res); }) .catch(err => console.log(err)); diff --git a/examples/basic/sendasset.js b/examples/basic/sendasset.js index f494feb68..89044fb8a 100644 --- a/examples/basic/sendasset.js +++ b/examples/basic/sendasset.js @@ -28,7 +28,7 @@ const { default: Neon, api, wallet } = require("@cityofzion/neon-js"); const sendingKey = "9ab7e154840daca3a2efadaf0df93cd3a5b51768c632f5433f86909d9b994a69"; -const receivingAddress = "ALq7AWrhAueN6mJNqk6FHJjnsEoPRytLdW"; +const receivingAddress = "ASo1RcNVLiV3yQ8j3ZyZv5EWfqBBT8s2Yd"; const network = "TestNet"; /** diff --git a/website/i18n/en.json b/website/i18n/en.json index 3a3bcc636..ef126340e 100644 --- a/website/i18n/en.json +++ b/website/i18n/en.json @@ -59,9 +59,6 @@ "guides/adv/apicore": { "title": "Advanced - API Core Components" }, - "guides/adv/multisig": { - "title": "Advanced - Working with multi-sig contracts" - }, "guides/adv/multitx": { "title": "Advanced - Sending multiple transactions in a block" }, @@ -71,9 +68,12 @@ "guides/basic/createscript": { "title": "Basic - Create Smart Contract Script" }, - "guides/basic/invoke": { + "guides/basic/doinvoke": { "title": "Basic - Invoking a Smart Contract" }, + "guides/basic/multi": { + "title": "Signing with a multi-sig account" + }, "guides/basic/privnet": { "title": "Basic - Using a Private Net" }, diff --git a/website/sidebars.json b/website/sidebars.json index e53aa6775..a3e431574 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -7,9 +7,9 @@ "guides/basic/createscript", "guides/basic/doinvoke", "guides/basic/privnet", + "guides/basic/multi", "guides/adv/multitx", - "guides/adv/apicore", - "guides/adv/multisig" + "guides/adv/apicore" ] }, "api": {