2

我正在尝试按照使用 php 将 65 字节公钥转换为比特币地址所需的说明进行操作。说明非常明确。任何人都可以帮助我在 php 中执行此操作的实用性吗?

说明是

1 - 取对应生成的公钥(65字节,1字节0x04,32字节对应X坐标,32字节对应Y坐标)

0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6

2 - 对公钥执行 SHA-256 散列

600FFE422B4E00731A59557A5CCA46CC183944191006324A447BDB2D98D4B408

3 - 对 SHA-256 的结果执行 RIPEMD-160 散列

010966776006953D5567439E5E39F86A0D273BEE

4 - 在 RIPEMD-160 哈希前添加版本字节(主网络为 0x00)

00010966776006953D5567439E5E39F86A0D273BEE

5 - 对扩展的 RIPEMD-160 结果执行 SHA-256 哈希

445C7A8007A93D8733188288BB320A8FE2DEBD2AE1B47F0F50BC10BAE845C094

6 - 对前一个 SHA-256 哈希的结果执行 SHA-256 哈希

D61967F63C7DD183914A4AE452C9F6AD5D462CE3D277798075B107615C1A8A30

7 - 取第二个 SHA-256 哈希的前 4 个字节。这是地址校验和

D61967F6

8 - 在点 4 的扩展 RIPEMD-160 散列的末尾添加点 7 的 4 个校验和字节。这是 25 字节的二进制比特币地址。

00010966776006953D5567439E5E39F86A0D273BEED61967F6

9 - 使用 Base58Check 编码将结果从字节字符串转换为 base58 字符串。这是最常用的比特币地址格式

16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM

我的第一次尝试是

// step 1

$publickey='0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6';
$step1=$publickey;

echo "step1 ".$publickey."<br>";

// step 2

$step2=hash("sha256",$step1);
echo "step2 ".$step2."<br>";

// step 3

$step3=hash('ripemd160',$step2);
echo "step3 ".$step3."<br>";

// step 4

$step4="00".$step3;
echo "step4 ".$step4."<br>";

// step 5

$step5=hash("sha256",$step4);
echo "step5 ".$step5."<br>";

// step 6

$step6=hash("sha256",$step5);
echo "step6 ".$step6."<br>";

// step 7

$checksum=substr($step6,0,8);
echo "step7 ".$checksum."<br>";

// step 8

$step8=$step4.$checksum;
echo "step8 ".$step8."<br>";

//step 9

$step9=base58_encode($step8);
echo "step9 ".$step9."<br><br>";

这在第一步失败。任何帮助表示赞赏。

这是输出

step1 0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6
step2 32511e82d56dcea68eb774094e25bab0f8bdd9bc1eca1ceeda38c7a43aceddce
step3 7528c664cdc34c5ce809778eb688d32f89a538c0
step4 007528c664cdc34c5ce809778eb688d32f89a538c0
step5 86e76f4ff0bf0387339ac70a552e0fed615f7def34cc4809df1429e243f6c1fa
step6 b885b7225b370e7ff27ee0afb4f89b52b8675d5dc342d63de3abe7535f86cadb
step7 b885b722
step8 007528c664cdc34c5ce809778eb688d32f89a538c0b885b722
step9 1

Base58 函数是

function base58_encode($input)
{
    $alphabet =     '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
    $base_count = strval(strlen($alphabet));
    $encoded = '';
    while (floatval($input) >= floatval($base_count))
    {
        $div = bcdiv($input, $base_count);
        $mod = bcmod($input, $base_count);
        $encoded = substr($alphabet, intval($mod), 1) . $encoded;
        $input = $div;
    }
    if (floatval($input) > 0)
    {
        $encoded = substr($alphabet, intval($input), 1) . $encoded;
    }
    return($encoded);
}
4

4 回答 4

6

下面的解决方案感谢 Sammitch 发现语法并提供基本转换。

<?php

// step 1

$publickey='0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6';

$step1=hexStringToByteString($publickey);

echo "step1 ".$publickey."<br>";

// step 2

$step2=hash("sha256",$step1);
echo "step2 ".$step2."<br>";

// step 3

$step3=hash('ripemd160',hexStringToByteString($step2));
echo "step3 ".$step3."<br>";

// step 4

$step4="00".$step3;
echo "step4 ".$step4."<br>";

// step 5

$step5=hash("sha256",hexStringToByteString($step4));
echo "step5 ".$step5."<br>";

// step 6

$step6=hash("sha256",hexStringToByteString($step5));
echo "step6 ".$step6."<br>";

// step 7

$checksum=substr($step6,0,8);
echo "step7 ".$checksum."<br>";

// step 8

$step8=$step4.$checksum;
echo "step8 ".$step8."<br>";

// step 9
// base conversion is from hex to base58 via decimal. 
// Leading hex zero converts to 1 in base58 but it is dropped
// in the intermediate decimal stage.  Simply added back manually.

$step9="1".bc_base58_encode(bc_hexdec($step8));
echo "step9 ".$step9."<br><br>";

?>

hash 需要一个字节字符串而不是十六进制字符串。hexStringToByteString 是

function hexStringToByteString($hexString){
    $len=strlen($hexString);

    $byteString="";
    for ($i=0;$i<$len;$i=$i+2){
        $charnum=hexdec(substr($hexString,$i,2));
        $byteString.=chr($charnum);
    }

return $byteString;
}

基础转换(感谢 Sammitch - 修改为使用比特币 base58)

// BCmath version for huge numbers
function bc_arb_encode($num, $basestr) {
    if( ! function_exists('bcadd') ) {
        Throw new Exception('You need the BCmath extension.');
    }

    $base = strlen($basestr);
    $rep = '';

    while( true ){
        if( strlen($num) < 2 ) {
            if( intval($num) <= 0 ) {
                break;
            }
        }
        $rem = bcmod($num, $base);
        $rep = $basestr[intval($rem)] . $rep;
        $num = bcdiv(bcsub($num, $rem), $base);
    }
    return $rep;
}

function bc_arb_decode($num, $basestr) {
    if( ! function_exists('bcadd') ) {
        Throw new Exception('You need the BCmath extension.');
    }

    $base = strlen($basestr);
    $dec = '0';

    $num_arr = str_split((string)$num);
    $cnt = strlen($num);
    for($i=0; $i < $cnt; $i++) {
        $pos = strpos($basestr, $num_arr[$i]);
        if( $pos === false ) {
            Throw new Exception(sprintf('Unknown character %s at offset %d', $num_arr[$i], $i));
        }
        $dec = bcadd(bcmul($dec, $base), $pos);
    }
    return $dec;
}


// base 58 alias
function bc_base58_encode($num) {   
    return bc_arb_encode($num, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
}
function bc_base58_decode($num) {
    return bc_arb_decode($num, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
}

//hexdec with BCmath
function bc_hexdec($num) {
    return bc_arb_decode(strtolower($num), '0123456789abcdef');
}
function bc_dechex($num) {
    return bc_arb_encode($num, '0123456789abcdef');
}

最终输出

step1 0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6
step2 600ffe422b4e00731a59557a5cca46cc183944191006324a447bdb2d98d4b408
step3 010966776006953d5567439e5e39f86a0d273bee
step4 00010966776006953d5567439e5e39f86a0d273bee
step5 445c7a8007a93d8733188288bb320a8fe2debd2ae1b47f0f50bc10bae845c094
step6 d61967f63c7dd183914a4ae452c9f6ad5d462ce3d277798075b107615c1a8a30
step7 d61967f6
step8 00010966776006953d5567439e5e39f86a0d273beed61967f6
step9 16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM
于 2013-10-08T08:43:33.013 回答
2

很重要!

替换这个: '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'

有了这个: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

在这里使用错误的代码会导致比特币交易失败或更糟,导致硬币消失在一个幻影钱包中,永远无法取回。

我不是开发人员,但我确认了更正。Base58 符号图表在这里,https ://en.bitcoin.it/wiki/Base58Check_encoding

我在这里检查了我的工作 http://brainwallet.org/

输入密码:“测试地址”,不带引号。

那么公钥是:047969a753f71135d4c792f384e546cd508514024b4ee40d12a014019b77d1b292763dfb8a108cf7a7119f80ca4a06e81b92464f5d8a7544d627cd2e641023a

您的地址结果:1gBG1mbVtyNTgGZhggJ21A6mnjbNtqPCSr

我的结果:1Gch1MBvUZotGhzHGGj21b6MNKBOURpdsS

Brainwallet.org 结果: 1Gch1MBvUZotGhzHGGj21b6MNKBOURpdsS

我希望这可以使某人免于费时或代价高昂的错误。

于 2014-01-04T18:06:54.707 回答
2

仔细查看您的变量名称。$publickey 与 $publicKey 不同——注意大写。

于 2013-10-07T19:48:36.137 回答
2

您的问题如下:

  1. 变量名 $publickey 和 $publicKey 不等价。
  2. $checksum=substr($step6,0,4);应该是$checksum=substr($step6,0,8);因为您需要两个十六进制字符来表示一个字节。
  3. $step8=$step4+$checksum;应该$step8=$step4.$checksum;

另外,我不知道你的base58_encode()函数来自哪里,但我希望它使用 BCmath,因为00ba084d3f143f2896809d3f1d7dffed472b39d8de7a39cf51[step 8's result] 表示的数字太大,PHP 无法在内部处理。

编辑

我今天的工作非常无聊,这是我的转换代码,带有额外的 BCmath 用于巨大的 [比如说,58 位?] 数字。

<?php

// original arbitrary encode function
function arb_encode($num, $basestr) {
    $base = strlen($basestr);
    $rep = '';

    while($num > 0) {
        $rem = $num % $base;
        $rep = $basestr[$rem] . $rep;
        $num = ($num - $rem) / $base;
    }
    return $rep;
}

function arb_decode($num, $basestr) {
    $base = strlen($basestr);
    $dec = 0;

    $num_arr = str_split((string)$num);
    $cnt = strlen($num);
    for($i=0; $i < $cnt; $i++) {
        $pos = strpos($basestr, $num_arr[$i]);
        if( $pos === false ) {
            Throw new Exception(sprintf('Unknown character %s at offset %d', $num_arr[$i], $i));
        }
        $dec = ($dec * $base) + $pos;
    }
    return $dec;
}

// BCmath version for huge numbers
function bc_arb_encode($num, $basestr) {
    if( ! function_exists('bcadd') ) {
        Throw new Exception('You need the BCmath extension.');
    }

    $base = strlen($basestr);
    $rep = '';

    while( true ){
        if( strlen($num) < 2 ) {
            if( intval($num) <= 0 ) { break; }
        }
        $rem = bcmod($num, $base);
        $rep = $basestr[intval($rem)] . $rep;
        $num = bcdiv(bcsub($num, $rem), $base);
    }
    return $rep;
}

function bc_arb_decode($num, $basestr) {
    if( ! function_exists('bcadd') ) {
        Throw new Exception('You need the BCmath extension.');
    }

    $base = strlen($basestr);
    $dec = '0';

    $num_arr = str_split((string)$num);
    $cnt = strlen($num);
    for($i=0; $i < $cnt; $i++) {
        $pos = strpos($basestr, $num_arr[$i]);
        if( $pos === false ) {
            Throw new Exception(sprintf('Unknown character %s at offset %d', $num_arr[$i], $i));
        }
        $dec = bcadd(bcmul($dec, $base), $pos);
    }
    return $dec;
}


// base 58 alias
function bc_base58_encode($num) {
    return bc_arb_encode($num, '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ');
}
function bc_base58_decode($num) {
    return bc_arb_decode($num, '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ');
}

//hexdec with BCmath
function bc_hexdec($num) {
    return bc_arb_decode(strtolower($num), '0123456789abcdef');
}
function bc_dechex($num) {
    return bc_arb_encode($num, '0123456789abcdef');
}

// example
$orig    = '00ba084d3f143f2896809d3f1d7dffed472b39d8de7a39cf51';
$bten    = bc_hexdec($orig);
$base58  = bc_base58_encode($bten);
$backten = bc_base58_decode($base58);
$back    = bc_dechex($backten);
echo "Orig: " . $orig . "\n";
echo "bten: " . $bten . "\n";
echo "58:   " . $base58 . "\n";
echo "ag10: " . $backten . "\n";
echo "Back:   " . $back  . "\n";

编辑2

不要base_convert()用于这么大的数字,它似乎不可靠。我正在编写必要的bc_arb_decode()对应项,发现输入和输出在使用base_convert()vs时有所不同arb_convert($num, '0123456789abcdef');,在将结果与Wolfram Alpha进行比较后,似乎 PHP 错误地转换了数字。

Hex:           00ba084d3f143f2896809d3f1d7dffed472b39d8de7a39cf51
PHP's decode:  4561501878697786606686086062428080084446806606846864824262
Mine:          4561501878697784703577561586669353227270827349968709865297
Wolfram Alpha: 4561501878697784703577561586669353227270827349968709865297

你可以看到 PHP 已经很遥远了。[1.9E42 又名 1.9 quintillion septillion] 我已经更新了我的代码以包含arb_decode()看起来可以正确执行操作的函数。

于 2013-10-07T19:53:02.833 回答