3

I am trying to read the value from a Bitcoin transaction - https://en.bitcoin.it/wiki/Transaction

The wiki says it is an 8 byte integer, but nothing I have tried with unpack gives me the right value as a decimal.

I've been able to read everything else from the transaction except the values out the output - given the other fields I have managed to parse all line up with the correct values it appears I am reading the correct 8 bytes.

With:

$bin = hex2bin('00743ba40b000000');
$value = unpack('lvalue', $bin);
// 'value' => int -1539607552

and

$bin = hex2bin('d67e690000000000');
$value = unpack('lvalue', $bin);
// 'value' => int 6913750

That should be 500 and 0.0691375.

4
  • What is the $data you're working with here? Commented Jul 17, 2013 at 19:17
  • Hmm, what part of that page is the binary data, though? The "raw transaction" still looks like JSON. Commented Jul 17, 2013 at 19:22
  • You have to download the transaction from the network yourself to get the full binary version, that site only explains the format, it does not dispense the binary version. For the sake of simplicity, I have converted the binary to hex, added the hex to my question and converted it back to binary. Commented Jul 17, 2013 at 19:26
  • @PeterO. I cannot change how these numbers are stored - it is part of the established protocol. Commented Jul 17, 2013 at 19:35

1 Answer 1

2

As the documentation for PHP's unpack() function notes, strange things may happen if you try to unpack an unsigned value with length equal to PHP's native integer size (i.e. 32 or 64 bits depending on how your PHP was compiled):

"Note that PHP internally stores integral values as signed. If you unpack a large unsigned long and it is of the same size as PHP internally stored values the result will be a negative number even though unsigned unpacking was specified."

Arguably, this is a PHP bug: if I ask for an unsigned value, I damn well expect to get an unsigned value, even if it has to be internally represented as a float.

In any case, one way to work around this "quirk" is to unpack the binary string in smaller chunks of, say, 8 or 16 bits each, and reassemble them manually, like this:

$bin = pack( 'H*', '00743ba40b000000' );

$words = array_reverse( unpack( 'v*', $bin ) );
$n = 0;
foreach ( $words as $word ) {
    $n = (1 << 16) * $n + $word;
}

echo "$n\n";  // prints "50000000000"
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks - I managed to get it working using BC Math just before you posted but I prefer your version because it doesn't require a library and because I'm a new user I couldn't post it for another 5 hours - answer accepted. Just a note: Bitcoin inflates its numbers, that should be 500 - you may want to stick a / 100000000 in there.
Indeed it does. Although if you want to avoid rounding errors, you may want to skip the / 100000000 and just store the values as whole numbers of Satoshis instead of as fractions of BTC.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.