【问题标题】:Bitcoin address form validation JavaScript and PHP比特币地址表单验证 JavaScript 和 PHP
【发布时间】:2014-02-28 20:34:08
【问题描述】:

I've seen a few Bitcoin Address form validation scripts for various languages,但令人惊讶的是,对于两种常见的网络语言,Javascript 和 PHP,实际上找不到任何东西。

这是 Python 的一个,但 PHP 和/或 JS 有一个吗?

from hashlib import sha256

digits58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def decode_base58(bc, length):
    n = 0
    for char in bc:
        n = n * 58 + digits58.index(char)
    return n.to_bytes(length, 'big')

def check_bc(bc):
    bcbytes = decode_base58(bc, 25)
    return bcbytes[-4:] == sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]

if __name__ == '__main__':
    bc = '1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'
    assert check_bc(bc)
    assert not check_bc( bc.replace('N', 'P', 1) )
    assert check_bc('1111111111111111111114oLvT2')
    assert check_bc("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j")

【问题讨论】:

  • 这个问题似乎是题外话,因为它是关于为你编写代码的。
  • 我还能怎么问呢?我只是以 Python 为例。真的只是在寻找任何见解......
  • 您可以自己尝试一下——如果您对某个特定部分有疑问,经过深入研究后,您可以询问该部分。如果您想完成某事,请雇用。如果您想自己做,请尝试
  • 人为什么这么毒? SO 是一个问答网站。他在问一个问题。并不是所有的代码问题都要求他浪费时间重新发明轮子,然后才能询问是否有人知道现有的解决方案。
  • 所以你回答了你自己的问题......我只是有一个快速的建议。如果您使用的是 blockchain.info 或 coinbase api,您只需汇款即可验证地址。然后尝试catch,如果失败则地址错误。

标签: javascript php validation bitcoin


【解决方案1】:

Here's a JSFiddle:http://jsfiddle.net/timrpeterson/XsCQq/2/

And here's the full code upon which the JSFiddle is based:

<html>
<head>
<script type="text/javascript" src="http://dl.dropboxusercontent.com/u/28441300/BigInt.js"></script> 
<script type="text/javascript" src="http://dl.dropboxusercontent.com/u/28441300/sha256.js"></script> 
</head>
<body>

<div id="text">
</div>

<script type="text/javascript">
var address = "1Eym7pyJcaambv8FG4ZoU8A4xsiL9us2zz";
if (check(address)) {
    document.getElementById('text').innerHTML += "valid";
} else {
    document.getElementById('text').innerHTML += "invalid";
}


function check(address) {
  var decoded = base58_decode(address);     
  if (decoded.length != 25) return false;

  var cksum = decoded.substr(decoded.length - 4); 
  var rest = decoded.substr(0, decoded.length - 4);  

  var good_cksum = hex2a(sha256_digest(hex2a(sha256_digest(rest)))).substr(0, 4);

  if (cksum != good_cksum) return false;
  return true;
}

function base58_decode(string) {
  var table = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
  var table_rev = new Array();

  var i;
  for (i = 0; i < 58; i++) {
    table_rev[table[i]] = int2bigInt(i, 8, 0);
  } 

  var l = string.length;
  var long_value = int2bigInt(0, 1, 0);  

  var num_58 = int2bigInt(58, 8, 0);

  var c;
  for(i = 0; i < l; i++) {
    c = string[l - i - 1];
    long_value = add(long_value, mult(table_rev[c], pow(num_58, i)));
  }

  var hex = bigInt2str(long_value, 16);  

  var str = hex2a(hex);  

  var nPad;
  for (nPad = 0; string[nPad] == table[0]; nPad++);  

  var output = str;
  if (nPad > 0) output = repeat("\0", nPad) + str;

  return output;
}

function hex2a(hex) {
    var str = '';
    for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
}

function a2hex(str) {
    var aHex = "0123456789abcdef";
    var l = str.length;
    var nBuf;
    var strBuf;
    var strOut = "";
    for (var i = 0; i < l; i++) {
      nBuf = str.charCodeAt(i);
      strBuf = aHex[Math.floor(nBuf/16)];
      strBuf += aHex[nBuf % 16];
      strOut += strBuf;
    }
    return strOut;
}

function pow(big, exp) {
    if (exp == 0) return int2bigInt(1, 1, 0);
    var i;
    var newbig = big;
    for (i = 1; i < exp; i++) {
        newbig = mult(newbig, big);
    }

    return newbig;
}

function repeat(s, n){
    var a = [];
    while(a.length < n){
        a.push(s);
    }
    return a.join('');
}
</script>
</body>
</html>

And here is a PHP example (assuming your have PHP BC-Math):

<?php

function checkAddress($address)
{
    $origbase58 = $address;
    $dec = "0";

    for ($i = 0; $i < strlen($address); $i++)
    {
        $dec = bcadd(bcmul($dec,"58",0),strpos("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",substr($address,$i,1)),0);
    }

    $address = "";

    while (bccomp($dec,0) == 1)
    {
        $dv = bcdiv($dec,"16",0);
        $rem = (integer)bcmod($dec,"16");
        $dec = $dv;
        $address = $address.substr("0123456789ABCDEF",$rem,1);
    }

    $address = strrev($address);

    for ($i = 0; $i < strlen($origbase58) && substr($origbase58,$i,1) == "1"; $i++)
    {
        $address = "00".$address;
    }

    if (strlen($address)%2 != 0)
    {
        $address = "0".$address;
    }

    if (strlen($address) != 50)
    {
        return false;
    }

    if (hexdec(substr($address,0,2)) > 0)
    {
        return false;
    }

    return substr(strtoupper(hash("sha256",hash("sha256",pack("H*",substr($address,0,strlen($address)-8)),true))),0,8) == substr($address,strlen($address)-8);
}

?>

【讨论】:

  • 优秀的答案。我将它们组合在一个模块中。在这里,它被包裹在一个匿名函数中并被缩小。它公开了一个全局函数:“checkAddress”,并且时钟只有 5.1k:julianhaight.com/btcvalid.js
  • @tim-peterson - 您使用的 base58_decode 似乎存在问题。我用这个替换了它:github.com/cryptocoinjs/bs58,效果更好。更新了我的缩小库(删除了一些未使用的东西,现在时钟为 3.9k)。
  • JS 代码不适用于多重签名比特币地址,例如 3P37h58Az9sqUv8vDW4c1UGgwB5cPKs8DB 有效但被 JS 代码检测为无效。
  • 即使地址是VALID也返回无效地址
  • 不适用于多重签名地址 3LutQT57xPExqBrYPUTpX8q1EJYgBCVC4s
【解决方案2】:

我根据上面的答案编写了一个简单的 PHP 库来执行此操作。可以找到at my related github repo

<?php
class Btc_address_validator {

    /**
     * [validate description]
     * @param  String $address BTC Address string
     * @return Boolean validation result
     */
    public function validate($address)
    {        
        $addr = $this->decode_base58($address);
        if (strlen($addr) != 50)
        {
          return false;
        }        
        $check = substr($addr, 0, strlen($addr) - 8);
        $check = pack("H*", $check);
        $check = strtoupper(hash("sha256", hash("sha256", $check, true)));
        $check = substr($check, 0, 8);
        return $check == substr($addr, strlen($addr) - 8);
    }
    private function encode_hex($dec)
    {
        $hexchars = "0123456789ABCDEF";
        $return = "";
        while (bccomp($dec, 0) == 1)
        {
            $dv = (string) bcdiv($dec, "16", 0);
            $rem = (integer) bcmod($dec, "16");
            $dec = $dv;
            $return = $return . $hexchars[$rem];
        }
        return strrev($return);
   }
    /**
    * Convert a Base58-encoded integer into the equivalent hex string representation
    *
    * @param string $base58
    * @return string
    * @access private
    */
    private function decode_base58($base58)
    {
        $origbase58 = $base58;    
        $base58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 
        $return = "0";
        for ($i = 0; $i < strlen($base58); $i++)
        {
          $current = (string) strpos($base58chars, $base58[$i]);
          $return = (string) bcmul($return, "58", 0);
          $return = (string) bcadd($return, $current, 0);
        }
        $return = $this->encode_hex($return);
        //leading zeros
        for ($i = 0; $i < strlen($origbase58) && $origbase58[$i] == "1"; $i++)
        {
          $return = "00" . $return;
        }
        if (strlen($return) % 2 != 0)
        {
          $return = "0" . $return;
        }
        return $return;
    }
}

【讨论】:

    【解决方案3】:

    这是@Tim-Peterson 答案的更好版本。它修复了他正在使用的 base58 实现(不会验证地址“12EJmB3cMGRNveskzA7g7kxW32gSbo2dHF”。

    我将验证代码与所有需要的库结合起来,并删除了很多不需要的。它只公开一个 api:“checkAddress”。我为它创建了一个小主页,您可以在其中下载模块源或缩小版:http://www.julianhaight.com/javascript.shtml

    更正后的 base58_decode(来自https://github.com/cryptocoinjs/bs58):

    // from https://github.com/cryptocoinjs/bs58
    // Base58 encoding/decoding
    // Originally written by Mike Hearn for BitcoinJ
    // Copyright (c) 2011 Google Inc
    // Ported to JavaScript by Stefan Thomas
    // Merged Buffer refactorings from base58-native by Stephen Pair
    // Copyright (c) 2013 BitPay Inc
    
    var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
    var ALPHABET_MAP = {}
    for(var i = 0; i < ALPHABET.length; i++) {
      ALPHABET_MAP[ALPHABET.charAt(i)] = i
    }
    var BASE = 58
    
    function base58_decode(string) {
      if (string.length === 0) return []
    
      var i, j, bytes = [0]
      for (i = 0; i < string.length; i++) {
        var c = string[i]
        if (!(c in ALPHABET_MAP)) throw new Error('Non-base58 character')
    
        for (j = 0; j < bytes.length; j++) bytes[j] *= BASE
        bytes[0] += ALPHABET_MAP[c]
    
        var carry = 0
        for (j = 0; j < bytes.length; ++j) {
          bytes[j] += carry
    
          carry = bytes[j] >> 8
          bytes[j] &= 0xff
        }
    
        while (carry) {
          bytes.push(carry & 0xff)
    
          carry >>= 8
        }
      }
    
      // deal with leading zeros
      for (i = 0; string[i] === '1' && i < string.length - 1; i++) bytes.push(0)
    
      bytes = bytes.reverse()
      output = '';
      for (i=0; i<bytes.length; i++) {
          output += String.fromCharCode(bytes[i]);
      }
      return output;
    }
    

    【讨论】:

    • 好东西@Julian,你能解释一下为什么那个比特币地址没有验证吗?
    • @timperterson 我挖到了 base58-decode。它从该函数中给出了错误的结果。我没有深入研究它,因为我有一个工作替代品。我注意到我使用的替换功能有一个标记为“处理前导零”的部分,我认为错误地址中确实有前导零。我在这里发布了工作代码,因此您可以根据需要比较 b58decode 的不同之处:julianhaight.com/btcvalid.source.js
    • 上面的代码适用于像'3B9FDFDhmBVVpCLmkP5A6vUjwMmU83zAPed'这样的地址?
    • @Codebrekers,其他应用程序也说地址不好。你收到了吗?它应该是坏的还是好的?
    【解决方案4】:

    比特币地址(例如:3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC)在许多 PHP 示例中无效。特别适用于上述地址的示例之一是:

    Click here to see the PHP Function to validate Bitcoin Address

    【讨论】:

    • 链接失效了,能否提供最新的链接或代码
    【解决方案5】:

    这是一个简短而现代的 Javascript 实现,它依赖于 CryptoJS

    import sha256 from 'crypto-js/sha256'
    import CryptoJS from 'crypto-js'
    
    function isBTCAddress (address) {
      if (!/^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(address)) return false
      const bufferLength = 25
      let buffer = new Uint8Array(bufferLength)
      const digits58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
      for (var i = 0; i < address.length; i++) {
        const num = digits58.indexOf(address[i])
        // buffer = buffer * 58 + num
        let carry = 0
        for (var j = bufferLength - 1; j >= 0; --j) {
          // num < 256, so we just add it to last
          const result = buffer[j] * 58 + carry + (j === bufferLength - 1 ? num : 0)
          buffer[j] = result % (1 << 8)
          carry = Math.floor(result / (1 << 8))
        }
      }
      // check whether sha256(sha256(buffer[:-4]))[:4] === buffer[-4:]
      const hashedWords1 = sha256(CryptoJS.lib.WordArray.create(buffer.slice(0, 21)))
      const hashedWords = sha256(hashedWords1).words
      // get buffer[-4:] with big-endian
      const lastWordAddress = new DataView(buffer.slice(-4).buffer).getInt32(0, false)
      const expectedLastWord = hashedWords[0]
      return lastWordAddress === expectedLastWord
    }
    

    【讨论】:

      【解决方案6】:

      对于那些使用javascript的,你可以使用wallet-address-validator javascript插件。

      <script src="wallet-address-validator.min.js"></script>
      
      // WAValidator is stored in the windows object
      

      networkType - 可选。使用“prod”(默认)强制执行标准地址,使用“testnet”强制执行测试网地址,使用“both”强制不强制。

      var valid = WAValidator.validate('12h7E1q5UUoPgZ1VtcYb57maFF9Cbk4u5X','BTC','both');
      if(valid){
          alert('This is a valid address');
      } else {
          alert('Address INVALID');
      }
      // will alert "This is a valid address"
      
      var valid = WAValidator.validate('12h7E1q5UUoPgZ1VtcYb57maFF9Cbk4u5X', 'ETH', 'both');
      if(valid){
          alert('This is a valid address');
      } else {
          alert('Address INVALID');
      }
      // will alert "Address INVALID"
      

      【讨论】:

      • 如果不支持该货币,这将崩溃。
      【解决方案7】:

      这是来自this answer 的一个很好的正则表达式,我已经为你制作了一个函数。仅适用于非隔离见证地址。

      function validate_bitcoin_address(btc_address)
       {
           return btc_address.match("^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$") !== null;
       }
      
       alert(validate_bitcoin_address("16CbQcqDtBak5NzPbmFP1v9Pi4DwP5G4Wn")); //example usage
      

      正则表达式匹配以下字符串:

      • 以 1 或 3 开头
      • 之后,a-z、A-Z 或 0-9 的 25 到 34 个字符,但不包括 l、I、O 和 0

      正则表达式排除了比特币地址中不允许出现的字符(l、I、O 和 0)

      【讨论】:

      • 此答案适用于某些人但无法验证所有钱包,因为 BTC 地址可以以 bc (Segwit) 开头
      • @NicosKaralis 谢谢,我已经相应地更新了我的答案
      • 嗨,这个答案是否能够检查以 bc 开头的 BTC 地址?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-28
      • 2019-05-16
      • 1970-01-01
      • 1970-01-01
      • 2021-10-21
      • 2014-01-17
      • 2019-04-05
      相关资源
      最近更新 更多