This is my attempt to use the bitcore-lib-cash examples to construct a P2SH multisig transaction.
There are two files:
multisig01.js
generates a 2-of-3 P2SH address.multisig02.js
spends the 2,000 sats in the address.
This is my attempt to use the bitcore-lib-cash examples to construct a P2SH multisig transaction.
There are two files:
multisig01.js
generates a 2-of-3 P2SH address.multisig02.js
spends the 2,000 sats in the address./* | |
Scratchpad trying to generate a 2-of-3 multisig address. | |
Following this example: | |
https://github.com/bitpay/bitcore/blob/master/packages/bitcore-lib-cash/docs/examples.md#create-a-2-of-3-multisig-p2sh-address | |
*/ | |
const BCHJS = require('@psf/bch-js') | |
const bchjs = new BCHJS() | |
const bitcore = require('bitcore-lib-cash') | |
// bitcoincash:qqstkr2f03te8kpcr2dlxcfhhz25uum28slvdvyn4j | |
const aliceMnemonic = 'weekend exchange salute rocket despair cube improve work train fox degree evolve' | |
// bitcoincash:qq2z8psqlvfw55ttc3uqqejjsykn3qvf2ykd48vh0u | |
const bobMnemonic = 'cool elite air budget trial turn west midnight verify minor olive execute' | |
// bitcoincash:qr2jhqtnvtj8yd5gws5uexhdh6270vraaur20ate86 | |
const samMnemonic = 'two sell word immense dignity face glove merry hen wool erupt drop' | |
async function runTest() { | |
try { | |
// Create public key for Alice | |
const aliceSeed = await bchjs.Mnemonic.toSeed(aliceMnemonic) | |
const aliceHDNode = bchjs.HDNode.fromSeed(aliceSeed) | |
const aliceNode = bchjs.HDNode.derivePath(aliceHDNode, "m/44'/145'/0'/0/0") | |
const alicePubKey = bchjs.HDNode.toPublicKey(aliceNode) | |
const alicePrivKey = bchjs.HDNode.toWIF(aliceNode) | |
// Create public key for Bob | |
const bobSeed = await bchjs.Mnemonic.toSeed(bobMnemonic) | |
const bobHDNode = bchjs.HDNode.fromSeed(bobSeed) | |
const bobNode = bchjs.HDNode.derivePath(bobHDNode, "m/44'/145'/0'/0/0") | |
const bobPubKey = bchjs.HDNode.toPublicKey(bobNode) | |
const bobPrivKey = bchjs.HDNode.toWIF(bobNode) | |
// Create public key for Sam | |
const samSeed = await bchjs.Mnemonic.toSeed(samMnemonic) | |
const samHDNode = bchjs.HDNode.fromSeed(samSeed) | |
const samNode = bchjs.HDNode.derivePath(samHDNode, "m/44'/145'/0'/0/0") | |
const samPubKey = bchjs.HDNode.toPublicKey(samNode) | |
const samPrivKey = bchjs.HDNode.toWIF(samNode) | |
const publicKeys = [ | |
new bitcore.PrivateKey(alicePrivKey).toPublicKey(), | |
new bitcore.PrivateKey(bobPrivKey).toPublicKey(), | |
new bitcore.PrivateKey(samPrivKey).toPublicKey(), | |
] | |
const requiredSignatures = 2 | |
// Generate a P2SH multisig address. | |
const address = new bitcore.Address(publicKeys, requiredSignatures) | |
console.log(`P2SH multisig address: ${address}`) | |
// Note: address is actually a Class Object. There is much more to it | |
// than just the string output. | |
// In a normal spend, no one signer would have all the information to | |
// recreate the address object. This object would need to be serialized | |
// and then deserialized by the spending app. | |
// console.log('address object: ', address) | |
} catch(err) { | |
console.error(err) | |
} | |
} | |
runTest() |
/* | |
Scratchpad trying to spend a 2-of-3 multisig wallet. | |
Following this example: | |
https://github.com/bitpay/bitcore/blob/master/packages/bitcore-lib-cash/docs/examples.md#spend-from-a-2-of-2-multisig-p2sh-address | |
Lessons learned: | |
Required information to spend: | |
- P2SH multisig address holding the coins. | |
- The public key of *all* participants. | |
*/ | |
const BCHJS = require('@psf/bch-js') | |
const bchjs = new BCHJS() | |
const bitcore = require('bitcore-lib-cash') | |
const RECEIVER_ADDR = 'bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d' | |
// bitcoincash:qqstkr2f03te8kpcr2dlxcfhhz25uum28slvdvyn4j | |
const aliceMnemonic = 'weekend exchange salute rocket despair cube improve work train fox degree evolve' | |
// bitcoincash:qq2z8psqlvfw55ttc3uqqejjsykn3qvf2ykd48vh0u | |
const bobMnemonic = 'cool elite air budget trial turn west midnight verify minor olive execute' | |
// bitcoincash:qr2jhqtnvtj8yd5gws5uexhdh6270vraaur20ate86 | |
const samMnemonic = 'two sell word immense dignity face glove merry hen wool erupt drop' | |
async function runTest() { | |
try { | |
// Create a private key for Alice | |
const aliceSeed = await bchjs.Mnemonic.toSeed(aliceMnemonic) | |
const aliceHDNode = bchjs.HDNode.fromSeed(aliceSeed) | |
const aliceNode = bchjs.HDNode.derivePath(aliceHDNode, "m/44'/145'/0'/0/0") | |
const alicePrivKey = bchjs.HDNode.toWIF(aliceNode) | |
const alicePubKey = bchjs.HDNode.toPublicKey(aliceNode) | |
// Create HD node for Bob | |
const bobSeed = await bchjs.Mnemonic.toSeed(bobMnemonic) | |
const bobHDNode = bchjs.HDNode.fromSeed(bobSeed) | |
const bobNode = bchjs.HDNode.derivePath(bobHDNode, "m/44'/145'/0'/0/0") | |
const bobPrivKey = bchjs.HDNode.toWIF(bobNode) | |
const bobPubKey = bchjs.HDNode.toPublicKey(bobNode) | |
// Create HD node for Sam | |
const samSeed = await bchjs.Mnemonic.toSeed(samMnemonic) | |
const samHDNode = bchjs.HDNode.fromSeed(samSeed) | |
const samNode = bchjs.HDNode.derivePath(samHDNode, "m/44'/145'/0'/0/0") | |
const samPrivKey = bchjs.HDNode.toWIF(samNode) | |
const samPubKey = bchjs.HDNode.toPublicKey(samNode) | |
// Regnerate the input Script | |
const allPublicKeys = [ | |
new bitcore.PrivateKey(alicePrivKey).toPublicKey(), | |
new bitcore.PrivateKey(bobPrivKey).toPublicKey(), | |
new bitcore.PrivateKey(samPrivKey).toPublicKey(), | |
] | |
const requiredSignatures = 2 | |
const address = new bitcore.Address(allPublicKeys, requiredSignatures) | |
// Use 2 of the 3 private keys. | |
const privateKeys = [ | |
new bitcore.PrivateKey(alicePrivKey), | |
new bitcore.PrivateKey(bobPrivKey) | |
// new bitcore.PrivateKey(samPrivKey) | |
] | |
// 2 of 3 public keys. | |
const publicKeys = privateKeys.map(bitcore.PublicKey) | |
// Get the UTXO | |
const utxos = await bchjs.Utxo.get(address.toString()) | |
const utxo = utxos.bchUtxos[0] | |
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`) | |
// Add properties to the UTXO expected by bitcore | |
utxo.outputIndex = utxo.vout | |
utxo.script = new bitcore.Script(address).toHex() | |
utxo.satoshis = utxo.value | |
console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`) | |
// Generate the transaction object. | |
const txObj = new bitcore.Transaction() | |
.from(utxo, allPublicKeys, requiredSignatures) | |
.to(RECEIVER_ADDR, 1500) | |
.feePerByte(1) | |
.change(address) | |
.sign(privateKeys) | |
// Serialize the transaction to a hex string, ready to broadcast to the network. | |
// const txHex = txObj.toString() | |
let txHex = txObj.toBuffer() | |
txHex = txHex.toString('hex') | |
console.log('hex: ', txHex) | |
// Note: Attempting to broadcast the transaction results in: | |
// hex: 02000000017ef119fc32cfbda48ec0e859e5e8687e12779c8e0258360f0cd2107bd03127ce0000000000ffffffff01a4060000000000001976a9143e31055173cf58d56edb075499daf29d7b488f0988ac00000000 | |
// { error: 'bad-txns-undersize (code 64)' } | |
// Broadcast the transaction to the network. | |
const txid = await bchjs.RawTransactions.sendRawTransaction(txHex) | |
console.log(`txid: ${txid}`) | |
} catch(err) { | |
console.error(err) | |
} | |
} | |
runTest() |