Last active
September 11, 2019 03:09
-
-
Save fkfk/5b76ad525b35c1db93681338f7212e6b to your computer and use it in GitHub Desktop.
test multisig
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const bitcoin = require('bitcoinjs-lib') | |
// for testnet | |
/* | |
const network = { | |
messagePrefix: '\x19Monacoin Signed Message:\n', | |
bip32: { | |
public: 0x043587cf, | |
private: 0x04358394 | |
}, | |
pubKeyHash: 111, | |
scriptHash: 117, | |
wif: 239, | |
bech32: "tmona" | |
} | |
*/ | |
// for regtest | |
const network = { | |
messagePrefix: '\x19Monacoin Signed Message:\n', | |
bip32: { | |
public: 0x043587cf, | |
private: 0x04358394 | |
}, | |
pubKeyHash: 111, | |
scriptHash: 117, | |
wif: 239, | |
bech32: "rmona" | |
} | |
const keys = [ | |
{ | |
mnemonic: 'coach meat purchase planet spray east elbow adapt play comfort burden arch', | |
seed: '38d960288f7a0ef4fec8d58244f3c7e47babf1cdd92ced50e7d8468a6a9ed2f115b8033c506e802b182a98a83ef4594452e2ae5638fdb52235e6f379c1448a47', | |
privateKey: '136c1e1e7aa9a3d193b435e284c6d3c2e5f7affd4ab78a82672e87989379519c', | |
publicKey: '03c2b54c7d0cab9283479d16d67ce34492d8589e7ac02413aace08a178312487c4' | |
}, | |
{ | |
mnemonic: 'quick blind gauge nerve category expose cheap general orient path inject sock', | |
seed: '675bdfcccd1e7b974fede91b8b3b1882ae2bd8ecffd5103af36e3a768b6d299cf878db21fddfc4e0b7df0ecc5ae40a5b3eaeb542bebb7e85ae282775260a6873', | |
privateKey: '0503a06bd63b2665e24dbda21cd77b07926f5803faadbaa92d452d80d70374ce', | |
publicKey: '03de6d9a37de85a67c888d04dc02dcf4c6e4457eeba8c9beab81bbe4879bfbac81' | |
}, | |
{ | |
mnemonic: 'volume ecology snake dune glide tonight ancient dice act series blood foot', | |
seed: '47197bb1cd12bcc04ba7ee0deb30c409bdddb634f572a2507774acab2ad34e392b8d9d6ac2e39126b9c50c95209238ddf1f3504862f60bdea1cae1467275857b', | |
privateKey: '866313171ec1f368cbb9972798d36aae18285aa4f97073c4fec1adef82db37eb', | |
publicKey: '0347902fd32739a57bd6675296c4e01ec83e927eb35bd267f8e9ea9ea2fe5867c6' | |
} | |
] | |
const utxos = [ | |
{ | |
address: "pNgCocTd1gEyjMRZU5V8UvX1XnK7uXcLxk", | |
txid: "195cf80babf9fa97c44cbbbdd3e48b4d7f268b67f84010aff11d34bb5dec9892", | |
vout: 0, | |
scriptPubKey: "a914bbeb9a8e3c6a93d19340c0c197d4c443325742ac87", | |
amount: 10, | |
value: 1000000000, | |
satoshis: 1000000000, | |
height: 111 | |
}, | |
{ | |
address: "pNgCocTd1gEyjMRZU5V8UvX1XnK7uXcLxk", | |
txid: "5096ef230c7d82a1d571824fa5255edb9057f4f220c93eea1665599d4322909d", | |
vout: 0, | |
scriptPubKey: "a914bbeb9a8e3c6a93d19340c0c197d4c443325742ac87", | |
amount: 10, | |
value: 1000000000, | |
satoshis: 1000000000, | |
height: 111 | |
}, | |
{ | |
address: "pNgCocTd1gEyjMRZU5V8UvX1XnK7uXcLxk", | |
txid: "806d7c48dfb7c27949890aad6208033273c526b58b22dfa6fe33f03dfad11fb5", | |
vout: 1, | |
scriptPubKey: "a914bbeb9a8e3c6a93d19340c0c197d4c443325742ac87", | |
amount: 10, | |
value: 1000000000, | |
satoshis: 1000000000, | |
height: 111 | |
}, | |
] | |
const pubkeys = keys.map((key) => Buffer.from(key.publicKey, 'hex')) | |
const { address } = bitcoin.payments.p2sh({ | |
redeem: bitcoin.payments.p2ms({ m: 2, pubkeys, network }), | |
network | |
}) | |
const txBuilder = new bitcoin.TransactionBuilder(network) | |
const inputs = [ | |
{ | |
address: 'pNgCocTd1gEyjMRZU5V8UvX1XnK7uXcLxk', | |
txid: '195cf80babf9fa97c44cbbbdd3e48b4d7f268b67f84010aff11d34bb5dec9892', | |
vout: 0, | |
scriptPubKey: 'a914bbeb9a8e3c6a93d19340c0c197d4c443325742ac87', | |
amount: 10, | |
value: 1000000000, | |
satoshis: 1000000000, | |
height: 111 | |
} | |
] | |
const outputs = [ | |
{ address: 'mujr6nBZ7MpaiVZ3upDfkrzGy4ewaP7ueB', value: 5 }, | |
{ value: 999966245 } | |
] | |
const fee = 33750 | |
inputs.forEach(input => txBuilder.addInput(input.txid, input.vout)) | |
outputs.forEach(output => { | |
if (output.address === undefined) { | |
output.address = address | |
} | |
txBuilder.addOutput(output.address, output.value) | |
}) | |
const unsigned = txBuilder.buildIncomplete() | |
const unsignedBuffer = unsigned.toBuffer() | |
const keyPairs = keys.map((key) => bitcoin.bip32.fromSeed(Buffer.from(key.seed, 'hex'), network)) | |
const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys: pubkeys, network }) | |
const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network }) | |
const unsignedTx = bitcoin.Transaction.fromBuffer(unsignedBuffer, true) | |
// 普通に署名した場合 | |
txBuilder.sign(0, keyPairs[0], p2sh.redeem.output) | |
txBuilder.sign(0, keyPairs[1], p2sh.redeem.output) | |
signed = txBuilder.build() | |
// バラバラに署名を集める場合 | |
// scriptPubKeyのコンパイル | |
const pubkeyScript = bitcoin.script.compile([ | |
bitcoin.script.OPS.OP_2, | |
pubkeys[0], | |
pubkeys[1], | |
pubkeys[2], | |
bitcoin.script.OPS.OP_3, | |
bitcoin.script.OPS.OP_CHECKMULTISIG | |
]) | |
// シグネチャハッシュを生成してそれぞれの秘密鍵で署名 | |
const signatureHash = unsignedTx.hashForSignature(0, p2sh.redeem.output, bitcoin.Transaction.SIGHASH_ALL) | |
const sig1 = bitcoin.script.signature.encode(keyPairs[0].sign(signatureHash, false), bitcoin.Transaction.SIGHASH_ALL) | |
const sig2 = bitcoin.script.signature.encode(keyPairs[1].sign(signatureHash, false), bitcoin.Transaction.SIGHASH_ALL) | |
// signature Scriptのコンパイル | |
const signatureScript = bitcoin.script.compile([ | |
bitcoin.script.OPS.OP_0, | |
sig1, | |
sig2, | |
pubkeyScript | |
]) | |
// 未署名トランザクションにsignature scriptを差し込む | |
unsignedTx.ins[0].script = signatureScript | |
// 普通に署名した場合と比較してhexが一致しているか確認 | |
console.log(unsignedTx.toHex() === signed.toHex()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const bitcoin = require('bitcoinjs-lib') | |
// for regtest | |
const network = { | |
messagePrefix: '\x19Monacoin Signed Message:\n', | |
bip32: { | |
public: 0x043587cf, | |
private: 0x04358394 | |
}, | |
pubKeyHash: 111, | |
scriptHash: 117, | |
wif: 239, | |
bech32: "rmona" | |
} | |
const privkeys = [ | |
Buffer.from('136c1e1e7aa9a3d193b435e284c6d3c2e5f7affd4ab78a82672e87989379519c', 'hex'), | |
Buffer.from('0503a06bd63b2665e24dbda21cd77b07926f5803faadbaa92d452d80d70374ce', 'hex'), | |
Buffer.from('866313171ec1f368cbb9972798d36aae18285aa4f97073c4fec1adef82db37eb', 'hex') | |
] | |
const pubkeys = [ | |
Buffer.from('03c2b54c7d0cab9283479d16d67ce34492d8589e7ac02413aace08a178312487c4', 'hex'), | |
Buffer.from('03de6d9a37de85a67c888d04dc02dcf4c6e4457eeba8c9beab81bbe4879bfbac81', 'hex'), | |
Buffer.from('0347902fd32739a57bd6675296c4e01ec83e927eb35bd267f8e9ea9ea2fe5867c6', 'hex') | |
] | |
const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys: pubkeys, network }) | |
const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network }) | |
const privateECs = privkeys.map((key) => bitcoin.ECPair.fromPrivateKey(key)) | |
const publicECs = pubkeys.map((key) => bitcoin.ECPair.fromPublicKey(key)) | |
// シグネチャハッシュと、それを元とした署名の場合は検証が通ることを確認する | |
const unsignedTx = bitcoin.Transaction.fromHex('02000000019298ec5dbb341df1af1040f8678b267f4d8be4d3bdbb4cc497faf9ab0bf85c1900000000fdfd000047304402205c185831c3f5bb04e5cbdf5281fc2b86d4fa7dff58118d98e971395ddcbb4b00022067151c3150c08bfaa3de0a805c5a11b1801727c38c5dd63695523f83abcaee8b01483045022100f8a6d3e35c47e5db7500da698db5af2b18d4acea623c20a0e0efe1f5b934cb7902203731717fb40b9c01f5af6d4837dc298a9edbcbfa14b5f399030d7e1c3d7f3c88014c69522103c2b54c7d0cab9283479d16d67ce34492d8589e7ac02413aace08a178312487c42103de6d9a37de85a67c888d04dc02dcf4c6e4457eeba8c9beab81bbe4879bfbac81210347902fd32739a57bd6675296c4e01ec83e927eb35bd267f8e9ea9ea2fe5867c653aeffffffff0205000000000000001976a9149c01ff6705bd4a825ca3125f041c5fc20deb456088ac25469a3b0000000017a914bbeb9a8e3c6a93d19340c0c197d4c443325742ac8700000000') | |
const signatureHash = unsignedTx.hashForSignature(0, p2sh.redeem.output, bitcoin.Transaction.SIGHASH_ALL) | |
const sig = bitcoin.script.signature.encode(privateECs[0].sign(signatureHash, false), bitcoin.Transaction.SIGHASH_ALL) | |
console.log(publicECs.some((ec) => ec.verify(signatureHash, bitcoin.script.signature.decode(sig).signature))) // => true | |
// マルチシグアドレスに関わりのない鍵ペアによる署名は検証を通らないことを確認 | |
const random = bitcoin.ECPair.makeRandom() | |
const bagSig = bitcoin.script.signature.encode(random.sign(signatureHash, false), bitcoin.Transaction.SIGHASH_ALL) | |
console.log(publicECs.some((ec) => ec.verify(signatureHash, bitcoin.script.signature.decode(bagSig).signature))) // => false | |
// 異なるトランザクションは検証に通らないことを確認 | |
const anotherUnsignedTx = bitcoin.Transaction.fromHex('02000000019d9022439d596516ea3ec920f2f45790db5e25a54f8271d5a1827d0c23ef965000000000fc00473044022031c72f3fe04db6a4c12652ad6bb0b303ec8f29b6940554960099cd8cc4bd171c02204a6a7fc01e366cacc8763d17343c82f5400175c82eeae06b1ef3e9f8fae713490147304402207bb3d92d82d247675d3a126ad4de6cceb2826f58f98df9d8a749be20ed7fb0f20220710ee0ad786b32f6f7bd72fb64a941bed857f3d9656de6d96212ff7940ecd13c014c69522103c2b54c7d0cab9283479d16d67ce34492d8589e7ac02413aace08a178312487c42103de6d9a37de85a67c888d04dc02dcf4c6e4457eeba8c9beab81bbe4879bfbac81210347902fd32739a57bd6675296c4e01ec83e927eb35bd267f8e9ea9ea2fe5867c653aeffffffff0205000000000000001976a9149c01ff6705bd4a825ca3125f041c5fc20deb456088ac25469a3b0000000017a914bbeb9a8e3c6a93d19340c0c197d4c443325742ac8700000000') | |
const anotherSignatureHash = anotherUnsignedTx.hashForSignature(0, p2sh.redeem.output, bitcoin.Transaction.SIGHASH_ALL) | |
const anotherSig = bitcoin.script.signature.encode(privateECs[0].sign(anotherSignatureHash, false), bitcoin.Transaction.SIGHASH_ALL) | |
console.log(publicECs.some((ec) => ec.verify(signatureHash, bitcoin.script.signature.decode(anotherSig).signature))) // => false | |
console.log(publicECs.some((ec) => ec.verify(anotherSignatureHash, bitcoin.script.signature.decode(anotherSig).signature))) // => true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment