如何在PHP中验证以太坊地址

7

我使用PHP和curl与我的geth服务器进行json交互。

除了一个问题,我能够实现我想要的所有功能:根据以太坊钱包格式检查用户输入的地址是否有效。

我看到了一个javascript函数在这里,但我主要使用PHP,对JS不熟悉。

有任何想法如何在PHP中验证以太坊地址吗?


1
此外,阅读通用算法(通常如何检查)并使用PHP编写它。 - Pavel Janicek
你能否提供任何关于PHP中以太坊/正则表达式验证的建设性答案,还是你只是偶然来到这里? - btc4cash
2个回答

18

这是一个基于EIP 55规范的以太坊地址验证的PHP实现。如需了解其工作原理,请查看注释。

<?php

use kornrunner\Keccak; // composer require greensea/keccak

class EthereumValidator
{
    public function isAddress(string $address): bool
    {
        // See: https://github.com/ethereum/web3.js/blob/7935e5f/lib/utils/utils.js#L415
        if ($this->matchesPattern($address)) {
            return $this->isAllSameCaps($address) ?: $this->isValidChecksum($address);
        }

        return false;
    }

    protected function matchesPattern(string $address): int
    {
        return preg_match('/^(0x)?[0-9a-f]{40}$/i', $address);
    }

    protected function isAllSameCaps(string $address): bool
    {
        return preg_match('/^(0x)?[0-9a-f]{40}$/', $address) || preg_match('/^(0x)?[0-9A-F]{40}$/', $address);
    }

    protected function isValidChecksum($address)
    {
        $address = str_replace('0x', '', $address);
        $hash = Keccak::hash(strtolower($address), 256);

        // See: https://github.com/web3j/web3j/pull/134/files#diff-db8702981afff54d3de6a913f13b7be4R42
        for ($i = 0; $i < 40; $i++ ) {
            if (ctype_alpha($address{$i})) {
                // Each uppercase letter should correlate with a first bit of 1 in the hash char with the same index,
                // and each lowercase letter with a 0 bit.
                $charInt = intval($hash{$i}, 16);

                if ((ctype_upper($address{$i}) && $charInt <= 7) || (ctype_lower($address{$i}) && $charInt > 7)) {
                    return false;
                }
            }
        }

        return true;
    }
}

依赖关系

为了验证校验和地址,我们需要一个现成的keccak-256实现,该实现不受内置的hash()函数支持。您需要将greensea/keccak composer包作为依赖项导入。


感谢@WebSpanner指出SHA3散列的问题。


2
@sepehr 这个实现在算法上是正确的,但使用的哈希算法(sha3-256)不符合规范。正确的哈希算法是keccak-256,它本质上是相同的哈希算法,只是填充常量不同,因此产生不同的结果。将这里的Sha3类替换为greensea/keccak包中的Keccak类即可获得所需的结果。我没有添加新答案,因为我认为sepehr应该编辑这个答案,因为除了这个小改变,我只会使用他的代码。 - WebSpanner
1
进一步阅读:https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md https://github.com/status-im/nim-keccak-tiny/issues/1 - WebSpanner
Sha3生成在这里不正确!例如:地址:0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359,哈希值:9abe8ec2cc3d024b8cec105fd0c6da114c2b6f3413e0f080242c304024f5f7ec,我已经尝试了Node.js代码来处理此地址,但返回的哈希值不同:d5272d0fe053f9072ef335d27ac67d481da35108f4e59711486ae6cff4a16ee5。 - Svyat P.
1
你的回答非常有帮助,还有 ethereum-address-validator 也可以返回规范化地址。 - jaggedsoft
在isAllSameCaps中,您不需要使用正则表达式,因为格式已经在matchesPattern中检查过了。相反,您可以测试地址是否等于其小写/大写版本:return strtolower($value) === $value || strtoupper($value) === $value; - SameOldNick
显示剩余2条评论

6

基本上,您可以完全将JavaScript转换为PHP。 在这里,我已经成功将验证以太坊地址的JavaScript代码转换并测试为PHP代码。

/**
 * Checks if the given string is an address
 *
 * @method isAddress
 * @param {String} $address the given HEX adress
 * @return {Boolean}
*/
function isAddress($address) {
    if (!preg_match('/^(0x)?[0-9a-f]{40}$/i',$address)) {
        // check if it has the basic requirements of an address
        return false;
    } elseif (!preg_match('/^(0x)?[0-9a-f]{40}$/',$address) || preg_match('/^(0x)?[0-9A-F]{40}$/',$address)) {
        // If it's all small caps or all all caps, return true
        return true;
    } else {
        // Otherwise check each case
        return isChecksumAddress($address);
    }
}

/**
 * Checks if the given string is a checksummed address
 *
 * @method isChecksumAddress
 * @param {String} $address the given HEX adress
 * @return {Boolean}
*/
function isChecksumAddress($address) {
    // Check each case
    $address = str_replace('0x','',$address);
    $addressHash = hash('sha3',strtolower($address));
    $addressArray=str_split($address);
    $addressHashArray=str_split($addressHash);

    for($i = 0; $i < 40; $i++ ) {
        // the nth letter should be uppercase if the nth digit of casemap is 1
        if ((intval($addressHashArray[$i], 16) > 7 && strtoupper($addressArray[$i]) !== $addressArray[$i]) || (intval($addressHashArray[$i], 16) <= 7 && strtolower($addressArray[$i]) !== $addressArray[$i])) {
            return false;
        }
    }
    return true;
}

与此同时,对于希望查验以太坊地址是否有效的人(例如将其用作HTML字段的模式属性),这个非常简单的正则表达式可能就足够了。

^(0x)?[0-9a-fA-F]{40}$

2
未知的哈希算法:sha3 - Alex
1
我们生活在一个令人惊叹的世界,不仅有不工作的代码,还有错误的实现却被接受。 - sepehr
2
@tormuto 我不认为我需要帮助,谢谢 :) 我想知道在hash()不支持SHA3的情况下如何“使用”代码,而第二组preg_match调用实际上是错误地被否定了。 - sepehr
1
PHP中没有sha3哈希算法。请使用sha3-256代替,该算法从PHP 7.1.0开始可用。 - Paul Basenko
1
不支持sha3,而且使用sha3-256是错误的哈希方式。@sepehr的解决方案可行... - CodingYourLife
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接