Browse Source

Add Multisig guide with Wallet API

beginners-guide
Node 7 years ago
parent
commit
589fc7cdd2
No known key found for this signature in database GPG Key ID: 8E1B4DC29040BD90
  1. 200
      guides-markdown/multisig-tx.md

200
guides-markdown/multisig-tx.md

@ -283,3 +283,203 @@ await spend1.fund([coin], {
});
```
## Using Wallet API
While it's possible to use `bcoin` for manually constructing transaction with just private keys, it's not
convenient to handle all logic manually, we even skipped HD wallet parts. So if you have node running
and you have access to it via HTTP, you can use `bcoin.http.Client` and `bcoin.http.Wallet`. These classes
provide all API methods described on bcoin and will communicate to running nodes Wallets.
*NOTE: You can check [API Docs][API-DOCS]
### Step 1: Address Creation
In this step we'll create two new wallets for two cosigners. In this demo, they will exist on same node,
but it shouldn't matter if these two wallets are on the same node or not.
```js
'use strict';
const assert = require('assert');
const bcoin = require('../bcoin');
const {Client, Wallet} = bcoin.http;
const network = 'regtest';
const m = 2;
const n = 2;
// Wrapper for skipping errors, when you rerun the script
// It could have been as simple as
// await client.createWallet(options);
const createMultisigWallet = async function createMultisigWallet(client, options, skipExists) {
assert(client instanceof Client, 'client should be bcoin.http.Client');
assert(options.id, 'You need to provide id in options');
const defaultOpts = {
type: 'multisig',
m: m,
n: n
};
Object.assign(defaultOpts, options);
let res;
try {
res = await client.createWallet(defaultOpts);
} catch (e) {
if (skipExists && e.message === 'WDB: Wallet already exists.') {
return null;
}
throw e;
}
return res;
};
// Wrapper for skipping errors, when you rerun the script
// It could have been as simple as
// await client.addSharedKey(account, xpubkey);
const addSharedKey = async function addSharedKey(client, account, xpubkey, skipRemoveError) {
assert(client instanceof Wallet, 'client should be bcoin.http.Wallet');
assert(account, 'should provide account');
assert(xpubkey, 'should provide xpubkey');
let res;
try {
res = await client.addSharedKey(account, xpubkey);
} catch (e) {
if (e.message === 'Cannot remove key.') {
return null;
}
throw e;
}
return res;
};
(async () => {
const client = new Client({ network });
// Let's create wallets if they don't exist
await createMultisigWallet(client, { id: 'cosigner1' }, true);
await createMultisigWallet(client, { id: 'cosigner2' }, true);
// Initialize wallet http clients
// They will be talking to Node's API
const wallet1 = new Wallet({ id: 'cosigner1', network });
const wallet2 = new Wallet({ id: 'cosigner2', network });
// It wasn't necessary, but you can either create new
// accounts under wallets and use them
// or skip accounts when they are 'default'
const wallet1account = 'default';
const wallet2account = 'default';
// Both wallets need to exchange XPUBKEYs to each other
// in order to generate receiving and change addresses.
// Let's take it from the default account.
const wallet1info = await wallet1.getInfo();
const wallet2info = await wallet2.getInfo();
// Grab the xpubkey from wallet, we need to share them
const wallet1xpubkey = wallet1info.account.accountKey;
const wallet2xpubkey = wallet2info.account.accountKey;
// Here we share xpubkeys to each other
await addSharedKey(wallet1, wallet1account, wallet2xpubkey);
await addSharedKey(wallet2, wallet2account, wallet1xpubkey);
// Now we can get address from both wallets
// NOTE: that both wallets should be on the same index
// (depth) of derivation to geth the same addresses
// NOTE: Each time you createAddress index(depth) is
// incremented an new address is generated
const address1 = await wallet1.createAddress(wallet1account);
const address2 = await wallet2.createAddress(wallet2account);
// Address for both shouuld be the same
// Unless they were run separately. (Or by manually triggering API)
console.log(address1);
console.log(address2);
})().catch((e) => {
console.error(e);
process.exit(1);
});
```
You will notice that we grab the `.account.accountKey`, first key is the xpubkey
and both will be using xpubkey key derivation to come up with new addresses.
You won't need to share any other public keys, they will derive them for you.
Depth of the account is the only thing you'll need to keep in mind.
addSharedKey in wallet/account is used for adding cosigner xpubkeys keys.
### Step 2: Generate Transaction
We have received transaction
```
Transaction ID: 3c12e1b260354fd2a2848030222c4a66339892f1d63b18752ff80ef4eb0197d2
Output index: 0
Amount: 100 BTC
```
We are going to send `1 BTC` to `RBg1TLaNuRpH6UTFzogFXhjqubPYZaqWgs` on the regtest network.
But we won't need this information, it will be automatically allocated from coins
by bcoin node wallet service.
```js
'use strict';
const bcoin = require('../bcoin');
const {Client, Wallet} = bcoin.http;
const Amount = bcoin.amount;
const network = 'regtest';
const sendTo = 'RBg1TLaNuRpH6UTFzogFXhjqubPYZaqWgs';
(async () => {
const client = new Client({ network });
const wallet1 = new Wallet({ id: 'cosigner1', network });
const wallet2 = new Wallet({ id: 'cosigner2', network });
// Because we can't sign and spend from account
// we can't use `spend` and publish directly to network.
// So we first create the transaction
const outputs = [{ address: sendTo, value: Amount.fromBTC(1).toValue() }];
const options = {
// rate: 1000,
outputs: outputs
};
// This will automatically find coins and fund the transaction (Sign it),
// also create changeAddress and calculate fee
const tx1 = await wallet1.createTX(options);
// Now you can share this raw output
const raw = tx1.hex;
// Wallet2 will also sign the transaction
const tx2 = await wallet2.sign(raw);
// Now we can broadcast this transaction to the network
const broadcast = await client.broadcast(tx2.hex);
console.log(broadcast);
})().catch((e) => {
console.error(e);
process.exit(1);
});
```
Here you can see it's much cleaner and easier.
We still need to manually, using other means, share
raw transaction data for signing.
`wallet1.createTX(options)` will automatically find the coins
sent to the multisig wallet, allocate them for spending,
send remaining - fee to change address and sign it.
`wallet2.sign` will take raw transaction and sign it with according key.
After that we can just broadcast the transaction to the network.
[API-DOCS]: http://bcoin.io/api-docs/index.html

Loading…
Cancel
Save