jgarzik
Legendary
Offline
Activity: 1596
Merit: 1100
|
|
September 17, 2013, 01:24:27 PM |
|
But this is already the case - if I have both private keys in the wallet, then the balance is displayed by bitcoind. But I'm asking whether a trivial code change would allow me to work with partially controlled multi-sig addresses?
No -- because it's not trivial.
|
Jeff Garzik, Bloq CEO, former bitcoin core dev team; opinions are my own. Visit bloq.com / metronome.io Donations / tip jar: 1BrufViLKnSWtuWGkryPsKsxonV2NQ7Tcj
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
September 17, 2013, 07:12:41 PM |
|
But this is already the case - if I have both private keys in the wallet, then the balance is displayed by bitcoind. But I'm asking whether a trivial code change would allow me to work with partially controlled multi-sig addresses?
No -- because it's not trivial. Thanks, I feared as much from browsing the code.
|
|
|
|
Peter Todd
Legendary
Offline
Activity: 1120
Merit: 1164
|
|
September 17, 2013, 07:31:45 PM |
|
Is it trivial for me to modify my build of bitcoind to consider multi-sig addresses in the balance calculations?
If, and only if, you control all keys involved in a multisig transaction. Otherwise, such a transaction may be considered "partially controlled" and not really part of your "fully controlled" balance. bitcoind cannot prove that you can spend a multisig. Keep in mind Bitcoin actually goes a bit further than that: bitcoind will only add a multi-sig transaction output to your wallet if you have all the keys, instead of only enough keys. I ran into this with my timestamper, which would create 1-of-2 multisig outputs where the other key was actually invalid and was only there to timestamp data. The code that actually implements this is in script.cpp: case TX_MULTISIG: { // Only consider transactions "mine" if we own ALL the // keys involved. multi-signature transactions that are // partially owned (somebody else has a key that can spend // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); return HaveKeys(keys, keystore) == keys.size(); } As a quick hack I changed changed the last line (IIRC) to only require that I had enough of the keys to spend the txout. You can do that too if you just want to test some multisig-using code out, just remember that you'll need to do a fair bit more than that to make it robust against attacks. FWIW my timestamper never ran into the problems mentioned above, but someone certainly could have caused it some trouble if they tried. Remember too that this only applies to bare CHECKMULTISIG txouts; P2SH is different. For a P2SH-using txout the scriptPubKey is just a hash of the real script, so bitcoin has no idea what the actual scriptPubKey is, and hence whether or not you have the keys required to spend the txout. However for real-world applications this is never a problem because you know the scriptPubKey - they're funds you know you have access too. I wrote a patch that adds the RPC command "addredeemscript" to bitcoind to make it possible to add a P2SH redeemscript to your wallet. It's not merged yet (and may never be) but if you are working on the right application it might be useful to you: http://github.com/petertodd/bitcoin/ (github is down right now, but the branch name is "rpc-addredeemscript" or something) Again, remember that you may mess up your wallet experimenting with the above hacks, so don't use it on anything other than testnet for now. Also, if you're working on some code, use P2SH rather than bare CHECKMULTISIG scriptPubKeys - it's possible the later will be made non-standard in the future to discourage people from putting data in the UTXO set.
|
|
|
|
JaSK
Newbie
Offline
Activity: 27
Merit: 0
|
|
October 17, 2013, 01:37:13 PM Last edit: October 17, 2013, 03:12:26 PM by JaSK |
|
I just ran into the same problem while working on the Moneychanger project for open-transactions. A solution is to use getrawtransaction <txid> to check if requested money was sent to the p2sh address in the first place, then getrawmempool to see if it has any confirmations yet, then getblockcount --> getblockhash(count) --> getblock(hash) --> getblock(block->previous) -->... to iterate through the blockchain to figure out how many confirmations it has. If you don't know the txid you could scan the blockchain for new txIDs and use getrawtransaction on each of them to figure out if any outputs were sent to the multi-sig address. Haven't implemented it yet so I'm not sure if it will work like this but so far it looks like it could imho. This all seems very hackish and error-prone to me so I hope we'll soon get proper bitcoin API support for that kind of transactions.
|
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
October 29, 2013, 04:23:08 PM |
|
If you don't know the txid you could scan the blockchain for new txIDs and use getrawtransaction on each of them to figure out if any outputs were sent to the multi-sig address.
Haven't implemented it yet so I'm not sure if it will work like this but so far it looks like it could imho.
Ok, so it turns out that you are right - it *is* possible to tell if someone has sent coins to a multi-sig address. This is the process: * Start from last processed block hash * Call getblock * Iterate through all transaction ids (which really are *all* txids, and not just those in your wallet - this is the first part of the key to the process) { * call getrawtransaction on each txid (which also lets you inspect non-wallet transactions, 2nd part of the key to the process) * Iterate through all vouts { * check each address in the scriptPubKey list to see if it matches your multi-sig address * if it does, you've found a deposit! You can determine the number of confirmations from the call you made to getrawtransaction } } * Goto next block hash This is huge, because it means you can give out multi-sig addresses to your users and actually be able to credit them when the coins arrive! Cheers, Paul.
|
|
|
|
jgarzik
Legendary
Offline
Activity: 1596
Merit: 1100
|
|
October 29, 2013, 09:05:03 PM |
|
This is huge, because it means you can give out multi-sig addresses to your users and actually be able to credit them when the coins arrive!
That's standard fare, with pay-to-script-hash (P2SH). Multisig P2SH addresses are easy. "addmultisig" RPC call handles this, for bitcoind / Bitcoin-QT.
|
Jeff Garzik, Bloq CEO, former bitcoin core dev team; opinions are my own. Visit bloq.com / metronome.io Donations / tip jar: 1BrufViLKnSWtuWGkryPsKsxonV2NQ7Tcj
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
October 29, 2013, 09:28:10 PM |
|
That's standard fare, with pay-to-script-hash (P2SH). Multisig P2SH addresses are easy.
"addmultisig" RPC call handles this, for bitcoind / Bitcoin-QT.
No its not standard fare. As the title of this thread indicates, the standard mechanisms in bitcoind (listsinceblock, listtransactions, getbalance etc etc) do not report any information about coins at multi-sig addresses unless the wallet contains all the private keys. This method I've just described is AFAIK, the *only* way to tell that coins have arrived on an address, making it very far from standard fair, given the amount of work it is.
|
|
|
|
jgarzik
Legendary
Offline
Activity: 1596
Merit: 1100
|
|
October 29, 2013, 09:47:15 PM |
|
Just visit any block explorer, to see the balance at a particular address.
|
Jeff Garzik, Bloq CEO, former bitcoin core dev team; opinions are my own. Visit bloq.com / metronome.io Donations / tip jar: 1BrufViLKnSWtuWGkryPsKsxonV2NQ7Tcj
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
October 29, 2013, 10:05:12 PM |
|
Just visit any block explorer, to see the balance at a particular address.
Fantastic advice, thanks for taking the time.
|
|
|
|
Bitalo_Maciej
Member
Offline
Activity: 80
Merit: 10
Lead developer
|
|
October 30, 2013, 01:27:09 PM |
|
Just visit any block explorer, to see the balance at a particular address.
Fantastic advice, thanks for taking the time. @monsterer: Message me if you'd like to receive an invite code for Bitalo, which implements multi-signature addresses for its wallets and creates/signs transactions on client-side, in Javascript. On the server side it does exactly the process that you described above - iterating over transactions in blocks and crediting users wallet balances. It works quite well at the moment, though we're still testing it to find out any outstanding bugs in the implementation. I'm quite familiar with how this all works so if you have any more technical questions, feel free to ask, I'm glad to help . Edit: Blockexplorers are tricky, because they don't handle P2SH addresses well yet. For example: http://blockexplorer.com/testnet/tx/67a05915c2a90e9743f04fb5e76f954204002401150ab34ebaed6f721056ae23http://testnet.btclook.com/txn/67a05915c2a90e9743f04fb5e76f954204002401150ab34ebaed6f721056ae23This is a valid transaction from P2SH address to another. Blockexplorer lists the inputs and outputs as "Strange", and BTCLook shows " ", even though they are perfectly legal.
|
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
October 31, 2013, 10:23:29 AM Last edit: October 31, 2013, 11:17:55 AM by monsterer |
|
I'm quite familiar with how this all works so if you have any more technical questions, feel free to ask, I'm glad to help . Thanks for the offer, I'll take you up on how to create and sign a multi-sig transaction. I currently have: * sent funds to a 2/2 multi-sig address * created a raw transaction spending the output of the above * signed with one private key (which is in wallet, but I passed it to the function anyway) * signed with the other private key (not in wallet) The process does not fail, but the resulting signed transaction has complete set to 'false', which isn't right. edit: looking at the raw transaction after creating it, signing it once, signing it twice, the hex doesn't change at all, which must mean something very wrong is happening?Hope you can help! Cheers, Paul.
|
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
October 31, 2013, 12:31:02 PM |
|
Ok, managed to get something working but only in the case of using two separate wallets with one private key located in each. In addition both wallets need to have the multi-sig address you're using added to them, despite them already containing one private key each.
Is there a way to have a wallet correctly sign a multi-sig transaction without explicitly adding the multi-sig address to it?
|
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
October 31, 2013, 01:14:00 PM |
|
Ok, managed to get something working but only in the case of using two separate wallets with one private key located in each. In addition both wallets need to have the multi-sig address you're using added to them, despite them already containing one private key each.
Is there a way to have a wallet correctly sign a multi-sig transaction without explicitly adding the multi-sig address to it?
Ok, to answer my own question, *yes* there is a way, and here is how: * use createmultisig 2 [pubKeyA, pubKeyB] to generate a redeemScript and an address * createrawtransaction as usual * signrawtransaction using private key A, and also you must pass, for each transaction output: { txid - this is the txid of the transaction you just created, NOT the funding transaction vout - index of transaction output scriptPubKey - this is from the output (indexed vout) for the transaction you just created redeemScript - this is from createmultisig } * transmit the hex transaction to the other party along with the redeemScript* have the other party perform the same process for signing on their end * transaction is signed correctly. I was unable to determine if the other party could extract the redeemScript from the transaction alone which would save some messing around. note: something which threw me was the offical documentation for signrawtransaction, which omits to mention redeemScript. This could do with fixingCheers, Paul.
|
|
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
October 31, 2013, 02:47:05 PM |
|
I did, but it also threw me off, because its out of date. In that example, gavin passes scriptPubKey and redeemScript to createrawtransaction. This is no longer correct. You must pass these to signrawtransaction now.
|
|
|
|
kjj
Legendary
Offline
Activity: 1302
Merit: 1026
|
|
October 31, 2013, 03:36:12 PM |
|
The gist is incredibly useful. Be sure to copy out the commands and reformat them so you can see what's really going on. The long lines can be hard to follow. If you unpack the commands in the gist, you can see that the redemption info is needed both to create and to sign. The transaction goes through several stages when redeeming multisig. First, a partial transaction is created that specifies which transaction is to be redeemed, and lists the outputs. Second, the information needed to actually redeem the P2SH input are added to the transaction. You can see this as an explicit step if you call signrawtransaction with the redemption info, but without any private keys. Next, one or more signatures are added. This can be one step, or many. Here is example PHP code. This is not a utility suitable for production usage, it is just a demonstration of how things work, but you can use it for one-off redemption, or modify it to accept the payment info. <?php
// this file sets up the $rpc object used for RPC calls later require_once("local_connection_info.php");
// which transaction output are you redeeming? $redeemtxid=""; $redeemvout=0;
// what is the redeemscript that hashes to that address? $redeemscript="";
// It may be possible to derive this from the txout info, but for now you have to enter it $redeempubkey="";
// If you provide enough keys to complete the transaction, it will send automatically // otherwise, it will provide the partially signed transaction // Note that this script isn't capable of continuing a partially signed transaction // (it should be easy to add.) $privkeys=array( "", "", );
// 3 modes // Pay to self - ask bitcoind for a new address, send everything to it (minus the fee) // Pay someone else, change back to me - ask bitcoind for a new address, use it for change (put "SELF" in as $changeaddr, address in as $payaddr) // Pay someone else, change to someone else - specify addresses for both $payaddr and $changeaddr $payaddr="SELF"; $fee=0.00; $payamount=0; // ignored if paying to self $changeaddr=""; // ignored if paying to self
// Thus ends the configuration section. All below is work
// Fetch the info we need from the old transaction $txhex=$rpc->getrawtransaction($redeemtxid); $txbody=$rpc->decoderawtransaction($txhex);
// configure the outputs // change amount is always calculated automatically if("SELF"==$payaddr){ $paddr=$rpc->getnewaddress(); $pays=array($paddr=>$txbody['vout'][$redeemvout]['value']-$fee); }elseif("SELF"==$changeaddr){ $caddr=$rpc->getnewaddress(); $camt=$txbody['vout'][$redeemvout]['value']-$payamount-$fee;; $pays=array($payaddr=>$payamount,$caddr=>$camt); }else{ $camt=$txbody['vout'][$redeemvout]['value']-$payamount-$fee; $pays=array($payaddr=>$payamount,$changeaddr=>$camt); }
// this array holds the info needed to redeem the transaction $redeemblock=array(array("txid"=>$redeemtxid,"vout"=>$redeemvout,"scriptPubKey"=>$redeempubkey,"redeemScript"=>$redeemscript));
// now we create the transaction, complete it, and sign it $raw=$rpc->createrawtransaction($redeemblock,$pays); $signed=$rpc->signrawtransaction($raw,$redeemblock,$privkeys);
// This would be a good place to inspect the output. Uncomment these to verify function. //$decoded=$rpc->decoderawtransaction($signed['hex']); //print_r($decoded) //die();
if(1==$signed['complete']){ $outtxid=$rpc->sendrawtransaction($signed['hex']); echo $outtxid."\n"; }else{ echo $signed['hex']."\n"; }
?>
|
17Np17BSrpnHCZ2pgtiMNnhjnsWJ2TMqq8 I routinely ignore posters with paid advertising in their sigs. You should too.
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
October 31, 2013, 04:05:14 PM |
|
The gist is incredibly useful. Be sure to copy out the commands and reformat them so you can see what's really going on. The long lines can be hard to follow. If you unpack the commands in the gist, you can see that the redemption info is needed both to create and to sign. The transaction goes through several stages when redeeming multisig. First, a partial transaction is created that specifies which transaction is to be redeemed, and lists the outputs. Second, the information needed to actually redeem the P2SH input are added to the transaction. You can see this as an explicit step if you call signrawtransaction with the redemption info, but without any private keys. Next, one or more signatures are added. This can be one step, or many. Here is example PHP code. This is not a utility suitable for production usage, it is just a demonstration of how things work, but you can use it for one-off redemption, or modify it to accept the payment info. <?php
// this file sets up the $rpc object used for RPC calls later require_once("local_connection_info.php");
// which transaction output are you redeeming? $redeemtxid=""; $redeemvout=0;
// what is the redeemscript that hashes to that address? $redeemscript="";
// It may be possible to derive this from the txout info, but for now you have to enter it $redeempubkey="";
// If you provide enough keys to complete the transaction, it will send automatically // otherwise, it will provide the partially signed transaction // Note that this script isn't capable of continuing a partially signed transaction // (it should be easy to add.) $privkeys=array( "", "", );
// 3 modes // Pay to self - ask bitcoind for a new address, send everything to it (minus the fee) // Pay someone else, change back to me - ask bitcoind for a new address, use it for change (put "SELF" in as $changeaddr, address in as $payaddr) // Pay someone else, change to someone else - specify addresses for both $payaddr and $changeaddr $payaddr="SELF"; $fee=0.00; $payamount=0; // ignored if paying to self $changeaddr=""; // ignored if paying to self
// Thus ends the configuration section. All below is work
// Fetch the info we need from the old transaction $txhex=$rpc->getrawtransaction($redeemtxid); $txbody=$rpc->decoderawtransaction($txhex);
// configure the outputs // change amount is always calculated automatically if("SELF"==$payaddr){ $paddr=$rpc->getnewaddress(); $pays=array($paddr=>$txbody['vout'][$redeemvout]['value']-$fee); }elseif("SELF"==$changeaddr){ $caddr=$rpc->getnewaddress(); $camt=$txbody['vout'][$redeemvout]['value']-$payamount-$fee;; $pays=array($payaddr=>$payamount,$caddr=>$camt); }else{ $camt=$txbody['vout'][$redeemvout]['value']-$payamount-$fee; $pays=array($payaddr=>$payamount,$changeaddr=>$camt); }
// this array holds the info needed to redeem the transaction $redeemblock=array(array("txid"=>$redeemtxid,"vout"=>$redeemvout,"scriptPubKey"=>$redeempubkey,"redeemScript"=>$redeemscript));
// now we create the transaction, complete it, and sign it $raw=$rpc->createrawtransaction($redeemblock,$pays); $signed=$rpc->signrawtransaction($raw,$redeemblock,$privkeys);
// This would be a good place to inspect the output. Uncomment these to verify function. //$decoded=$rpc->decoderawtransaction($signed['hex']); //print_r($decoded) //die();
if(1==$signed['complete']){ $outtxid=$rpc->sendrawtransaction($signed['hex']); echo $outtxid."\n"; }else{ echo $signed['hex']."\n"; }
?>
If you remove scriptPubKey and redeemScript from what you pass into createrawtransaction() it will work just as well. That function does not accept those parameters. Only signrawtransaction() needs them.
|
|
|
|
Bitalo_Maciej
Member
Offline
Activity: 80
Merit: 10
Lead developer
|
|
October 31, 2013, 07:25:23 PM |
|
You can recreate the redeemScript using createmultisig command at any time, if you use the same public keys. This will result in same P2SH address & redeemScript pair. Note that the order of keys DOES matter here, if you mix it up, it will result in a different address/redeemScript pair.
You can also extract the redeemScript from the transaction hex data. It's the last parameter of the input you are signing. This could be tricky to implement correctly though, so it's definitely better to store and send the redeemScript along with the transaction.
In the end, did you manage to get "complete": true with your signed transaction? If you get false and the hex data doesn't change, it usually means that the private key was invalid, so bitcoind didn't sign anything.
|
|
|
|
monsterer (OP)
Legendary
Offline
Activity: 1008
Merit: 1007
|
|
November 05, 2013, 11:54:30 AM |
|
You can recreate the redeemScript using createmultisig command at any time, if you use the same public keys. This will result in same P2SH address & redeemScript pair. Note that the order of keys DOES matter here, if you mix it up, it will result in a different address/redeemScript pair.
You can also extract the redeemScript from the transaction hex data. It's the last parameter of the input you are signing. This could be tricky to implement correctly though, so it's definitely better to store and send the redeemScript along with the transaction.
In the end, did you manage to get "complete": true with your signed transaction? If you get false and the hex data doesn't change, it usually means that the private key was invalid, so bitcoind didn't sign anything.
Hi, Thanks for the advice - I guess that transmitting redeemScript with the transaction doesn't expose any potential vulnerability? As to your question, yes I did finally get complete=true Cheers, Paul.
|
|
|
|
Bitalo_Maciej
Member
Offline
Activity: 80
Merit: 10
Lead developer
|
|
November 05, 2013, 12:18:18 PM |
|
I guess that transmitting redeemScript with the transaction doesn't expose any potential vulnerability? No, it doesn't, because as I said, it gets transmitted in the transaction's hex data anyway. Congrats on finally succeeding in signing a multisig transaction. I know how happy I was the first time I received "complete: true" .
|
|
|
|
|