Author

Topic: [PHP] Generate a sendmany with multiple outputs to the same address (Read 4311 times)

member
Activity: 93
Merit: 10
What was the workaround?
Well, it's a workaround only if you're fine with single transaction per a recipient address (I was).
In such case, just sum up the amounts using a map with the destination address as the key.
Something like:
Code:
$addrs = array();
for(...) {
    $destaddr = ...
    $amount = ...
    if (array_key_exists($destaddr, $addrs)) {
        $addrs[$destaddr] += $amount;
    } else {
        $addrs[$destaddr] = $amount;
    }
}
After this you have only single addresses in $addrs (with the amounts summed up), so just:
Code:
$rpc = 'sendmany "'.ACCOUNT.'" \'{';
$com = false;
foreach(array_keys($addrs) as $da) {
$am = round($addrs[$da], 8);
if ($am>0) {
if ($com)  $rpc .= ', ';
$rpc .= '"'.$da.'":'.sprintf('%.8f',$am);
$com = true;
}
}
$rpc .= '}\'';

This look like what i need. where did you add this code?
sr. member
Activity: 527
Merit: 250

TXOUT Amounts?
0.00100000
0.00100000
0.01000010
0.01001101
0.01001110
0.01100001
0.01100011
0.01100011
0.01100100
0.01100101
0.01100101
0.01100110
0.01101001
0.01101001
0.01101001
0.01101100
0.01101110
0.01110010
0.01110100
0.01110101
0.49730788
0.80211920

It says "Nice Minecraft Build" xD
legendary
Activity: 2053
Merit: 1356
aka tonikt
What was the workaround?
Well, it's a workaround only if you're fine with single transaction per a recipient address (I was).
In such case, just sum up the amounts using a map with the destination address as the key.
Something like:
Code:
$addrs = array();
for(...) {
    $destaddr = ...
    $amount = ...
    if (array_key_exists($destaddr, $addrs)) {
        $addrs[$destaddr] += $amount;
    } else {
        $addrs[$destaddr] = $amount;
    }
}
After this you have only single addresses in $addrs (with the amounts summed up), so just:
Code:
$rpc = 'sendmany "'.ACCOUNT.'" \'{';
$com = false;
foreach(array_keys($addrs) as $da) {
$am = round($addrs[$da], 8);
if ($am>0) {
if ($com)  $rpc .= ', ';
$rpc .= '"'.$da.'":'.sprintf('%.8f',$am);
$com = true;
}
}
$rpc .= '}\'';
legendary
Activity: 1400
Merit: 1005
Yeah, it's a bummer that you cannot specify the same address twice - I once suffered from this as well, but a workaround was fairly simple, especially in PHP.

A bigger issue I've had with sendmany is that the command requires the "" parameter and you cannot just say to it like "whatever account"...
So the way I use my client, having coins spread across several "accounts" (which I personally only use to label addresses), then it seems like someone had made this parameter mandatory just to piss me off... Wink
What was the workaround?
legendary
Activity: 2053
Merit: 1356
aka tonikt
Yeah, it's a bummer that you cannot specify the same address twice - I once suffered from this as well, but a workaround was fairly simple, especially in PHP.

A bigger issue I've had with sendmany is that the command requires the "" parameter and you cannot just say to it like "whatever account"...
So the way I use my client, having coins spread across several "accounts" (which I personally only use to label addresses), then it seems like someone had made this parameter mandatory just to piss me off... Wink
legendary
Activity: 1400
Merit: 1005
The bitcoind sendmany RPC call uses destination addresses as JSON Object keys, so you can't send to the same address multiple times in one transaction.

If you REALLY want to do that... first, why do you want to do that? I suppose if you want to use the blockchain as a messaging system then sending 0.123+0.567+etc might be an inefficient way of sending a message... but please don't do that.

Anyway, if you do REALLY want to do that, you'll have to write code to construct the transaction yourself. Then you could pass it to the signrawtransaction/sendrawtransaction RPC methods to broadcast it.  (you can't use createrawtransaction to create it, because it uses the same JSON syntax as sendmany for destination outputs).
Interesting, thanks for the response Gavin.  At the very least, blockchain.info has written the necessary code, and potentially, whoever it was that sent me a binary message earlier on in the year may have also programmed their own code for it.

At least this answers my question of whether it was a bitcoind issue or not.  Now I know that no bitcoind-based solution will accomplish this.  Perhaps time to learn the blockchain.info API, then.  Wink

Why do you say "but please don't do that"?  Blockchain bloat?
legendary
Activity: 1652
Merit: 2301
Chief Scientist
The bitcoind sendmany RPC call uses destination addresses as JSON Object keys, so you can't send to the same address multiple times in one transaction.

If you REALLY want to do that... first, why do you want to do that? I suppose if you want to use the blockchain as a messaging system then sending 0.123+0.567+etc might be an inefficient way of sending a message... but please don't do that.

Anyway, if you do REALLY want to do that, you'll have to write code to construct the transaction yourself. Then you could pass it to the signrawtransaction/sendrawtransaction RPC methods to broadcast it.  (you can't use createrawtransaction to create it, because it uses the same JSON syntax as sendmany for destination outputs).
kjj
legendary
Activity: 1302
Merit: 1026
Code:

  "out":[
    {
      "value":"1.00000000",
      "scriptPubKey":"OP_DUP OP_HASH160 ad4c20d5b1956015ea626f02afdaebe8ed17e1a8 OP_EQUALVERIFY OP_CHECKSIG"[/b]
    },
    {
      "value":"1.00000000",
      "scriptPubKey":"OP_DUP OP_HASH160 ad4c20d5b1956015ea626f02afdaebe8ed17e1a8 OP_EQUALVERIFY OP_CHECKSIG"[/b]
    }
  ]
}

Constructing a transaction like this is:

1. Either identical to sending the destination 2 BTC, in that clients consider the transaction to be adding 2BTC to the balance as one output (Bitcoin can't differentiate which output of these two might later have been spent, because they are identical with an identical transaction hash),

or

2. A good way to lose half your money because Bitcoin can't differentiate between which output of these two might later have been spent, because they are identical with an identical transaction hash.

https://en.bitcoin.it/wiki/BIP_0030
Quote
So far, the Bitcoin reference implementation always assumed duplicate transactions (transactions with the same identifier) didn't exist.


No, you are confusing transactions with outputs.  Transaction with duplicate hashes are no longer allowed, but duplicate outputs are.  The reason is that the transaction hash is the sole transaction identifier, while an output is identified by both the transaction hash and the output sequence number.  In this case, even though the transaction has two identical outputs, one is named .0 and the other is named .1.

In normal operation, the reference client hides the coin details from the user, and will present it to the user as a 2 BTC payment.  But behind the scenes, it knows that there are two outputs there and will act correctly, fully capable of redeeming and spending them one at a time.
legendary
Activity: 2940
Merit: 1333
legendary
Activity: 1400
Merit: 1005
So obviously, it is possible.  But deepceleron, you are saying the one sent to me wasn't a "normal" transaction, per say?
legendary
Activity: 1512
Merit: 1036

TXOUT Amounts?
0.00100000
0.00100000
0.01000010
0.01001101
0.01001110
0.01100001
0.01100011
0.01100011
0.01100100
0.01100101
0.01100101
0.01100110
0.01101001
0.01101001
0.01101001
0.01101100
0.01101110
0.01110010
0.01110100
0.01110101
0.49730788
0.80211920

If you look at that address on blockexplorer (scroll to block 181957 at http://blockexplorer.com/address/18TKNbSLTrd3a2W8mtoH5uNzFhWRWNcuHU) you can see that it is freaked out, and considers the transaction to have been 0.9995 BTC, not 1.49680788.

It looks like the outputs are able to be spent by reference to the n index location of the output. Here is the spending transaction (although note a few outputs, index #4 & #14, for a total of .002 aren't spent here):
http://blockexplorer.com/rawtx/bf9d5f83dc9cc6d2876725abc90f0dba92d52a7146da6c7e5a10bbb154895b34
legendary
Activity: 1400
Merit: 1005
legendary
Activity: 1512
Merit: 1036
Code:

  "out":[
    {
      "value":"1.00000000",
      "scriptPubKey":"OP_DUP OP_HASH160 ad4c20d5b1956015ea626f02afdaebe8ed17e1a8 OP_EQUALVERIFY OP_CHECKSIG"[/b]
    },
    {
      "value":"1.00000000",
      "scriptPubKey":"OP_DUP OP_HASH160 ad4c20d5b1956015ea626f02afdaebe8ed17e1a8 OP_EQUALVERIFY OP_CHECKSIG"[/b]
    }
  ]
}

Constructing a transaction like this is:

1. Either identical to sending the destination 2 BTC, in that clients consider the transaction to be adding 2BTC to the balance as one output (Bitcoin can't differentiate which output of these two might later have been spent, because they are identical with an identical transaction hash),

or

2. A good way to lose half your money because Bitcoin can't differentiate between which output of these two might later have been spent, because they are identical with an identical transaction hash.

https://en.bitcoin.it/wiki/BIP_0030
Quote
So far, the Bitcoin reference implementation always assumed duplicate transactions (transactions with the same identifier) didn't exist.
legendary
Activity: 1400
Merit: 1005
You might want to wrap the amounts in floatval(); Also you need to json_encode the array that is the issue you can't just pass the array.
floatval() wrapping is valuable advice, as I found.  Smiley

But the issue is that the array is organized in such a way as to use each address for the identifier of the element in the array.  You cannot use the same ID in the same array twice, thus eliminating the possibility of conducting a sendmany with multiple outputs to the same address with this particular PHP code.

Exactly.
Is there any other way to do this?

Yes after you build your array, do a search and combine the address into one.
The goal is to NOT combine the outputs to a single address into one.  I.e., you want to send multiple outputs to the same address at the same time.
legendary
Activity: 1498
Merit: 1000
You might want to wrap the amounts in floatval(); Also you need to json_encode the array that is the issue you can't just pass the array.
floatval() wrapping is valuable advice, as I found.  Smiley

But the issue is that the array is organized in such a way as to use each address for the identifier of the element in the array.  You cannot use the same ID in the same array twice, thus eliminating the possibility of conducting a sendmany with multiple outputs to the same address with this particular PHP code.

Exactly.
Is there any other way to do this?

Yes after you build your array, do a search and combine the address into one.
legendary
Activity: 952
Merit: 1000
You might want to wrap the amounts in floatval(); Also you need to json_encode the array that is the issue you can't just pass the array.
floatval() wrapping is valuable advice, as I found.  Smiley

But the issue is that the array is organized in such a way as to use each address for the identifier of the element in the array.  You cannot use the same ID in the same array twice, thus eliminating the possibility of conducting a sendmany with multiple outputs to the same address with this particular PHP code.

Exactly.
Is there any other way to do this?
legendary
Activity: 1400
Merit: 1005
You might want to wrap the amounts in floatval(); Also you need to json_encode the array that is the issue you can't just pass the array.
floatval() wrapping is valuable advice, as I found.  Smiley

But the issue is that the array is organized in such a way as to use each address for the identifier of the element in the array.  You cannot use the same ID in the same array twice, thus eliminating the possibility of conducting a sendmany with multiple outputs to the same address with this particular PHP code.
legendary
Activity: 1498
Merit: 1000
You might want to wrap the amounts in floatval(); Also you need to json_encode the array that is the issue you can't just pass the array.
legendary
Activity: 952
Merit: 1000
Bitcoin classes doesn't have a sendtomany function, but if you want to do sendtomany with a jsonRPCClient then you just need an array that is setup like $array("address"=>amount); or $array['address']=amount;

Cause you it will have to be ran thru json_encode to get it to the correct format.

I didn't realize he's using "Bitcoin Classes".
I'm using jsonRPCClient to connect with bitcoind.

I'm doing this:

$to=Array(
    "1address" => 1,
    "1address" => 2
);
sendmany("from", $to);

It will only send one payment to "1address".

The problem is that it has a duplicated key, so it's impossible to send multiple payments to the same address in the same transaction using this method.
blockchain.info allows us to do this, but I don't know how they do it.
legendary
Activity: 1498
Merit: 1000
Bitcoin classes doesn't have a sendtomany function, but if you want to do sendtomany with a jsonRPCClient then you just need an array that is setup like $array("address"=>amount); or $array['address']=amount;

Cause you it will have to be ran thru json_encode to get it to the correct format.
legendary
Activity: 952
Merit: 1000
legendary
Activity: 1400
Merit: 1005
I am utilizing the "Bitcoin Classes" by Mike Gogulski and theymos in some PHP code I am playing with.  As I understand it, the format for a sendmany is as follows:

$array[address[amount]]

Well, I can't send multiple times to the same address in the same sendmany in this format, since the address would be non-unique.  So, what would be the proper array format to sendmany to the same address multiple times?
Jump to: