【问题标题】:Determine if a Bitcoin wallet address is "valid"确定比特币钱包地址是否“有效”
【发布时间】:2014-10-10 04:19:08
【问题描述】:

我知道可以使用 Regex (^[13][a-km-zA-HJ-NP-Z0-9]{26,33}$) 验证比特币钱包地址 - 但这不是 100% 准确的,并且允许将无效地址检测为有效。

是否有可以验证比特币钱包地址的公开可用的 C# 算法?我一直在谷歌搜索,但找不到任何东西。

【问题讨论】:

标签: c# bitcoin


【解决方案1】:

是的,Bitcoin-Address-Utility 项目是一个包含此类验证的开源 C# 工具。尤其是Casascius.Bitcoin.Util.Base58CheckToByteArray()

【讨论】:

  • 这是一个有效的答案——如果我能找到更轻的东西我会很高兴——不需要这么多额外的 DLL。但是,如果我没有找到更好的东西 - 这个分支已经包含所需的 DLL:github.com/mikepfrank/Bitcoin-Address-Utility
【解决方案2】:

这里是Util.Bitcoin Git repository that contains only code needed for offline verification of BTC Wallet address

代码是从其他答案引用的Bitcoin-Address-Utility project 中提取的,但此存储库包含必要的 DLL,而且它是类项目而不是 Windows 应用程序,因此可以直接引用。

【讨论】:

    【解决方案3】:

    我拼凑了一个适用于 dotnet core 2.0 的简单版本的 Casascius.Bitcoin.Util.Base58CheckToByteArray() - 唯一的参考是 -->Org.BouncyCastle.Crypto.Digests;

    public class Validator
    {
        public static bool IsValidAddress(string Address)
        {
            byte[] hex = Base58CheckToByteArray(Address);
            if (hex == null || hex.Length != 21)
                return false;
            else
                return true;
        }
    
        public static byte[] Base58CheckToByteArray(string base58)
        {
    
            byte[] bb = Base58.ToByteArray(base58);
            if (bb == null || bb.Length < 4) return null;
    
            Sha256Digest bcsha256a = new Sha256Digest();
            bcsha256a.BlockUpdate(bb, 0, bb.Length - 4);
    
            byte[] checksum = new byte[32];  
            bcsha256a.DoFinal(checksum, 0);
            bcsha256a.BlockUpdate(checksum, 0, 32);
            bcsha256a.DoFinal(checksum, 0);
    
            for (int i = 0; i < 4; i++)
            {
                if (checksum[i] != bb[bb.Length - 4 + i]) return null;
            }
    
            byte[] rv = new byte[bb.Length - 4];
            Array.Copy(bb, 0, rv, 0, bb.Length - 4);
            return rv;
        }
    
    }
    

    } - 从上面借来的

    public class Base58
    {
        /// <summary>
        /// Converts a base-58 string to a byte array, returning null if it wasn't valid.
        /// </summary>
        public static byte[] ToByteArray(string base58)
        {
            Org.BouncyCastle.Math.BigInteger bi2 = new Org.BouncyCastle.Math.BigInteger("0");
            string b58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
    
            foreach (char c in base58)
            {
                if (b58.IndexOf(c) != -1)
                {
                    bi2 = bi2.Multiply(new Org.BouncyCastle.Math.BigInteger("58"));
                    bi2 = bi2.Add(new Org.BouncyCastle.Math.BigInteger(b58.IndexOf(c).ToString()));
                }
                else
                {
                    return null;
                }
            }
    
            byte[] bb = bi2.ToByteArrayUnsigned();
    
            // interpret leading '1's as leading zero bytes
            foreach (char c in base58)
            {
                if (c != '1') break;
                byte[] bbb = new byte[bb.Length + 1];
                Array.Copy(bb, 0, bbb, 1, bb.Length);
                bb = bbb;
            }
    
            return bb;
        }
    
        public static string FromByteArray(byte[] ba)
        {
            Org.BouncyCastle.Math.BigInteger addrremain = new Org.BouncyCastle.Math.BigInteger(1, ba);
    
            Org.BouncyCastle.Math.BigInteger big0 = new Org.BouncyCastle.Math.BigInteger("0");
            Org.BouncyCastle.Math.BigInteger big58 = new Org.BouncyCastle.Math.BigInteger("58");
    
            string b58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
    
            string rv = "";
    
            while (addrremain.CompareTo(big0) > 0)
            {
                int d = Convert.ToInt32(addrremain.Mod(big58).ToString());
                addrremain = addrremain.Divide(big58);
                rv = b58.Substring(d, 1) + rv;
            }
    
            // handle leading zeroes
            foreach (byte b in ba)
            {
                if (b != 0) break;
                rv = "1" + rv;
    
            }
            return rv;
        }
    
    }
    

    } - 测试

    [TestClass]
    public class ValidatorTests
    {
        [TestMethod]
        public void IsValidAddress_Test_AbnCoin()
        {
            var Address = "1QF4NgxgF86SH4dizN4JPHMprWBHbKdSmJ";
            Assert.IsTrue(Validator.IsValidAddress(Address));
        }
    
        [TestMethod]
        public void IsValidAddress_Test_BitCoin()
        {
            var Address = "1QF4NgxgF86SH4dizN4JPHMprWBHbKdSmJ";
            Assert.IsTrue(Validator.IsValidAddress(Address));
        }
    
        [TestMethod]
        public void IsValidAddress_Test_BitCoinTestnet()
        {
            var Address = "mpMwtvqaLQ4rCJsnoceAoLShKb4inV8uUi";
            Assert.IsTrue(Validator.IsValidAddress(Address));
        }
    
        [TestMethod]
        public void IsValidAddress_Test_BitCoinGold()
        {
            var Address = "GRiDm3LEjXAMMJhWaYqN8nSjuU7PSqZMUe";
            Assert.IsTrue(Validator.IsValidAddress(Address));
        }
    
        [TestMethod]
        public void IsValidAddress_Test_Dash()
        {
            var Address = "Xb9Edf28eYR9RRDwj7MBBVBc5vgGgT2vLV";
            Assert.IsTrue(Validator.IsValidAddress(Address));
        }
    
        [TestMethod]
        public void IsValidAddress_Test_Litecoin()
        {
            var Address = "LUdpZosHDP3M97ZSfvj3p1qygNFMNpXBr3";
            Assert.IsTrue(Validator.IsValidAddress(Address));
        }
    
        [TestMethod]
        public void IsValidAddress_Test_False_TooShort()
        {
            var Address = "1QF4NgxgF86SH4dizN4JPHMprWBHbKdSm";
            Assert.IsFalse(Validator.IsValidAddress(Address));
        }
    
        [TestMethod]
        public void IsValidAddress_Test_False_TooLong()
        {
            var Address = "1QF4NgxgF86SH4dizN4JPHMprWBHbKdSmJS";
            Assert.IsFalse(Validator.IsValidAddress(Address));
        }
    
        [TestMethod]
        public void IsValidAddress_Test_False_BadChecksum()
        {
            var Address = "1QF5NgxgF86SH4dizN4JPHMprWBHbKdSmJ";
            Assert.IsFalse(Validator.IsValidAddress(Address));
        }
    
        [TestMethod]
        public void IsValidAddress_False_NotBase58()
        {
            var Address = "lQF4NgxgF86SH4dizN4JPHMprWBHbKdSmJ";
            Assert.IsFalse(Validator.IsValidAddress(Address));
        }
    }
    

    }

    【讨论】:

      【解决方案4】:

      tl;博士: 遇到了同样的问题,因此构建了适合我(希望您的)需求的东西: https://github.com/Sofoca/CoinUtils

      我的具体要求是……

      • 支持比特币和莱特币(未来可能还有其他类似的山寨币)
      • 支持所有地址类型(P2SH 和 P2PKH)和编码(Base58 和 Bech32)
      • 最好没有外部(想想 NuGet)依赖项

      虽然上述替代方案都没有满足所有这些要求,但我从以前的答案和参考项目中获得了一些灵感。谢谢!

      希望这可以帮助那些正在寻找一个完整而轻量级的解决方案来解决这个问题的人。

      【讨论】:

        猜你喜欢
        • 2014-04-03
        • 1970-01-01
        • 2020-09-18
        • 1970-01-01
        • 2014-06-13
        • 1970-01-01
        • 1970-01-01
        • 2012-01-28
        • 2015-11-08
        相关资源
        最近更新 更多