bitcoin-php icon indicating copy to clipboard operation
bitcoin-php copied to clipboard

signing / checking 2:2 multisig transaction

Open ghost opened this issue 5 years ago • 3 comments

I'm trying to sign a 2:2 multisig transaction using this library. I've used this example to create the process of signing.

For my current script the process of signing should be like this:

  1. Private + public key of user A are known
  2. Public key of user B is known
  3. A transaction is created which involves user A and user B
  4. User A is signing the transaction (done by the script)
  5. The signed (from user A) transaction is displayed to user B
  6. User B is signing the transaction using his private key (using coinb.in )
  7. User B is providing the signed transaction to the script
  8. Script is performing a check if the transaction is signed completely

My code at the moment:

$privKeyFactory = new PrivateKeyFactory();
$privateKey1 = $privKeyFactory->fromHexCompressed('7bca8cbb9e0c108445281ade9d8f6b7d8bb18edb0b5ca4dc3aa660362b96f831', true);
$publicKey1 = $privateKey1->getPublicKey();

$pubKeyFactory = new PublicKeyFactory();
//private key is L5bu4amxMmYjrjJebb6ieKe48pHXtL5pjXLXTmhKdEnowBZ4fGvs | public key is 038044e721ea7b8bad6d50009d12717f9b2f49479e09a08cde78d0eee8d473450c | address is 15gqKRorfxtvTBCBp4dPWT33uB7sdKmmH3
$publicKey2 = $pubKeyFactory->fromHex('038044e721ea7b8bad6d50009d12717f9b2f49479e09a08cde78d0eee8d473450c');


// The witnessScript needs to be known when spending
$witnessScript = new WitnessScript(
    ScriptFactory::scriptPubKey()->multisig(2, [$publicKey1, $publicKey2])
);
$spendFromAddress = $witnessScript->getAddress();
$addressCreator = new AddressCreator();
$sendToAddress = $addressCreator->fromString('19GvpVPydKsM4ZguZWk6LyQFsC7wgCRHwp');
echo "Spend from {$spendFromAddress->getAddress()}\n";
echo "Send to {$sendToAddress->getAddress()}\n";
$addressCreator = new AddressCreator();
$transaction = TransactionFactory::build()
    ->input('87f7b7639d132e9817f58d3fe3f9f65ff317dc780107a6c10cba5ce2ad1e4ea1', 0)
    ->payToAddress(1500000, $sendToAddress)
    ->payToAddress(12345123, $spendFromAddress) // don't forget your change output!
    ->get();
$txOut = new TransactionOutput(1501000, $witnessScript->getOutputScript());
$signData = (new SignData())
    ->p2wsh($witnessScript)
;
$signer = new Signer($transaction);
$input = $signer->input(0, $txOut, $signData);
$input->sign($privateKey1);

//signing would be done on a different machine / by the user itself, not by script...
//$input->sign($privateKey2);
$signed = $signer->get();

echo "</br>";
echo "</br>";
echo "txid: {$signed->getTxId()->getHex()}\n";
echo "</br>";
echo "</br>";
echo "raw: {$signed->getHex()}\n";
echo "</br>";
echo "</br>";

//load here the completely signed transaction and perform check

echo "input valid? " . ($input->verify() ? "true" : "false") . PHP_EOL;

Questions:

  1. Is my input correct? If not, how to calculate input for the both public keys (adresses)?
  2. How to perform the check on a string as signed transaction, providing both public keys?

ghost avatar Feb 27 '19 16:02 ghost

Hey, thanks for the issue. at a glace the code looks good. there's some weird quirks in coinbin (if a p2sh tx is completely unsigned, you need to use the redeemScript AS the input.script, not a script that contains the redeemscript as a data push.. just a heads up, you might need to work around this if signing w/ coinbase first)

  • Is my input correct? If not, how to calculate input for the both public keys (adresses)?

I'm not totally sure what you're asking here. The code looks fine - and if you had both privkeys sign in the script, this line would echo 'true'

echo "input valid? " . ($input->verify() ? "true" : "false") . PHP_EOL;

2. How to perform the check on a string as signed transaction, providing both public keys?

You mean verifying each input in the transaction? parse the hex tx, then your app needs to find the scriptPubKey,value for each (txid,vout) being signed. then use the signer to loop over inputs and call verify() as above.

afk11 avatar Feb 27 '19 17:02 afk11

First of all, great library!

I tried above example, but when verifying it with coinb.in the script of the input seems to be empty, it is not marked as signed and not marked as a multisig transaction. From my understanding the transaction should have one signature, right?

01000000000101a14e1eade25cba0cc1a6070178dc17f35ff6f9e33f8df517982e139d63b7f7870000000000ffffffff0260e31600000000001976a9145ac42bde10756fd9fdd9fde7d9989512a9598a1b88ac235fbc0000000000220020f4e644e3b5eae483c8ffa3106547e23f252d3a3d31cea1f3b89f9cf64790e9e1030047304402206653b56fce19e8e575bf727fffc06433960a3b622234cb388ea1dfe3093278a4022053d68bde2e9ae74f2e6d135b69706a0f17abcc8a6ffcad0f7ba08a9e0ea0e8d201475221020a48636b5df07a3f8fbcb9c3d0af1dbe2c6c8250dd7a66c006c44d106acc0a7221038044e721ea7b8bad6d50009d12717f9b2f49479e09a08cde78d0eee8d473450c52ae00000000 Is this related to your comment above using the redeemScript as input script? Would this look like this?


$transaction = TransactionFactory::build()
      ->input('87f7b7639d132e9817f58d3fe3f9f65ff317dc780107a6c10cba5ce2ad1e4ea1', 0, $witnessScript->getOutputScript()

If it should, this has no influence on the output transaction or the problem above.

Thanks for your help.

U77bna avatar Nov 06 '19 22:11 U77bna

Hi @U77bna - I think that was the trick, for P2SH anyway (not sure about P2WSH). Sorry I don't have any more info - it's been years since I've used it for anything

afk11 avatar Nov 06 '19 23:11 afk11