求教,比特币钱包怎么与php建立连接

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求教,比特币钱包怎么与php建立连接相关的知识,希望对你有一定的参考价值。

节点软件bitcoind除了完整实现比特币的核心协议,还包含了一个可选但是重要 的功能模块 —— 钱包:

可以把钱包视为保存着你所有密钥与地址的保险箱,同时也封装了比特币很多 偏技术性的概念与细节,使其可以被极客之外的人群所理解和接受。因此在默认 配置下,bitcoind会启用节点钱包。如果你希望禁止钱包功能,例如你准备自己 管理密钥与地址,那么可以在配置文件中设置disablewallet选项:

disablewallet=1

钱包模块会跟踪其管理的所有地址相关的交易,因此可以及时地更新钱包的余额信息。 这一功能非常重要,因为比特币中没有账户的概念,比特币是散落在一个个交易 中的电子现金,如果没有钱包帮助跟踪与我们地址相关的交易,那么想算清楚自己 总共持有多少个比特币都很困难。

节点提供的很多RPC调用都是由钱包模块来实现的。例如,当我们调用getnewaddress 命令时,就是由钱包模块来生成密钥和地址并自动加入到钱包中, 因此其相关的交易也会自动地影响钱包的余额。同样,当我们调用getbalance时, 也是由钱包模块来汇总所有地址上的比特币并返回总金额。

以上内容摘抄自:比特币开发教程

参考技术A RPC可以实现从php控制钱包,但是,钱包收到比特币的时候,按理来说应该也是通过RPC进行数据返回。但是,没有研究出来钱包应该怎么配置这个RPC。你实现了没?不管什么虚拟币,钱包返回,钱包配置方法是一样的。

比特币隔离见证钱包地址计算

【中文标题】比特币隔离见证钱包地址计算【英文标题】:Bitcoin SegWit Wallet Address Calculation 【发布时间】:2020-09-18 06:41:57 【问题描述】:

对不起,如果这有点误导,但我实际上是用莱特币而不是比特币来做这个,但算法是完全相同的,我很确定答案也会是。事实上,我几乎可以肯定,比特币也会有同样的问题。我很难为给定的公钥生成正确的 SegWit 地址,如下所示:

莱特币地址生成(与比特币相同)

    我采用压缩公钥: 03861b83752e0c47cac36fc5980ae8956f41f6d9792a51f68a6bd5f66cc7364b48 公钥上的 SHA256。 RipeMD160 来自 2 的结果。 添加一个前缀字节,在我的例子中是 0x3a,即 LTC_TESTNET 双 SHA256 结果为 4,并将该结果的前四个字节添加到 RipeMD160 的末尾。 最后,Base58 编码。

一切看起来都很花哨,对吧?弹出 SegWit 地址:QhQxSZvVDWr3JvoKsYVC6BBW3DqkGhesrF

但是,我很确定这个地址不正确。当我将私钥作为 (p2wpkh-p2sh:) 导入 Electrum-LTC 时,它为此私钥生成的地址是:QYyWqgyWSm1AJWph32GnyY7eamG1wUDruk

现在我相信 Electrum-LTC 是正确的,在生成 SegWit 地址时我缺少一些东西,而且地址生成不仅仅是更改网络前缀。 我的公钥哈希是:

e444ac77800cdf904b928fc4642ab6fb6d4d696c

而 Electrum-LTC 的公钥哈希是:

87b3e5bf5b2a1381e6549020d245e45b9ac76c82

由于值非常不同,这表明最初的 SHA256 不只是散列公钥,而且我遗漏了一些东西。我几乎没有想法,希望有人能给出答案,而我在源代码中唯一能找到的东西,在 chainparams.cpp 中是这样的:

base58Prefixes[EXT_PUBLIC_KEY] = 0x04, 0x88, 0xB2, 0x1E;

有人知道我做错了什么吗?

更新 - 有人要代码 - 所以这里是

#include <openssl/sha.h>
#include <openssl/ripemd.h>
#include <cstdint>
#include <iostream>
#include <vector>

std::string base58Encode(const std::vector<uint8_t>& data)

    const uint8_t mapping[] = 
      '1', '2', '3', '4', '5', '6', '7', '8',
      '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
      'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q',
      'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
      'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
      'h', 'i', 'j', 'k', 'm', 'n', 'o', 'p',
      'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
      'y', 'z' ;

    std::vector<uint8_t> digits((data.size() * 137) / 100);
    size_t digitslen = 1;
    for (size_t i = 0; i < data.size(); i++)
    
        uint32_t carry = static_cast<uint32_t>(data[i]);
        for (size_t j = 0; j < digitslen; j++)
        
            carry = carry + static_cast<uint32_t>(digits[j] << 8);
            digits[j] = static_cast<uint8_t>(carry % 58);
            carry /= 58;
        
        for (; carry; carry /= 58)
            digits[digitslen++] = static_cast<uint8_t>(carry % 58);
    
    std::string result;
    for (size_t i = 0; i < data.size() && !data[i]; i++)
        result.push_back(mapping[0]);
    for (size_t i = 0; i < digitslen; i++)
        result.push_back(mapping[digits[digitslen - 1 - i]]);
    return result;


std::vector<uint8_t> SHA256_Hash(const std::vector<uint8_t>& data)

    std::vector<uint8_t> SHA256_Digest(SHA256_DIGEST_LENGTH);
    SHA256_CTX ctx;
    SHA256_Init(&ctx);
    SHA256_Update(&ctx, data.data(), data.size());
    SHA256_Final(SHA256_Digest.data(), &ctx);
    return SHA256_Digest;


std::vector<uint8_t> RipeMD160_Hash(const std::vector<uint8_t>& data)

    std::vector<uint8_t> RipeMD160_Digest(RIPEMD160_DIGEST_LENGTH);
    RIPEMD160_CTX ctx;
    RIPEMD160_Init(&ctx);
    RIPEMD160_Update(&ctx, data.data(), data.size());
    RIPEMD160_Final(RipeMD160_Digest.data(), &ctx);
    return RipeMD160_Digest;


std::string getWalletAddress(const std::vector<uint8_t>& public_key, uint8_t id)

    std::vector<uint8_t> addr_hash = SHA256_Hash(key);
    std::vector<uint8_t> addr_ripe = RipeMD160_Hash(addr_hash);
    addr_ripe.insert(addr_ripe.begin(), id);
    addr_hash = SHA256_Hash(SHA256_Hash(addr_ripe));
    addr_ripe.insert(addr_ripe.end(), addr_hash.begin(), addr_hash.begin() + sizeof(uint32_t));
    return base58Encode(addr_ripe);


int main(int argc, char** argv)

    const uint8_t LTC_TESTNET = 0x3a;

    std::vector<uint8_t> public_key
        0x03, 0x86, 0x1b, 0x83, 0x75, 0x2e, 0x0c, 0x47, 0xca, 0xc3, 0x6f, 0xc5, 0x98, 0x0a, 0xe8, 0x95,
        0x6f, 0x41, 0xf6, 0xd9, 0x79, 0x2a, 0x51, 0xf6, 0x8a, 0x6b, 0xd5, 0xf6, 0x6c, 0xc7, 0x36, 0x4b, 0x48;

    std::cout << "Wallet Address: " << getWalletAddress(public_key, LTC_TESTNET) << std::endl;
    // The above ouputs:  QhQxSZvVDWr3JvoKsYVC6BBW3DqkGhesrF
    // But it should be:  QYyWqgyWSm1AJWph32GnyY7eamG1wUDruk
    return 0;

【问题讨论】:

请在您的问题中添加MRE。如果没有代码,我们将无法提供太多帮助。 【参考方案1】:

终于想通了……计算p2wpkh-p2sh地址时,不只是地址的前缀不同。实际上,我在这里找到了上述问题的答案:https://bitcoin.stackexchange.com/questions/72775/is-it-possible-to-convert-an-address-from-p2pkh-to-p2sh(虽然它的解释方式相当神秘)。

我的问题中的上述代码对于比特币/莱特币等的 p2pkh 地址生成非常有效,但是当生成 Q(用于莱特币)、2(用于比特币)时,它不会起作用,因为它不仅仅是公众散列的密钥。它实际上是一个脚本,由 0x00 (DUP_0) 和一段公钥哈希 (0x14) 组成。

所以,修复上面的代码,如果地址生成代码改为:

std::string getWalletAddress(const std::vector<uint8_t>& public_key, uint8_t id, bool is_p2sh)

    std::vector<uint8_t> addr_hash = SHA256_Hash(key);
    std::vector<uint8_t> addr_ripe = RipeMD160_Hash(addr_hash);
    if (is_p2sh)
    
        addr_ripe.insert(addr_ripe.begin(),  0x00, static_cast<uint8_t>(addr_ripe.size()) );
        addr_hash = SHA256_Hash(addr_ripe);
        addr_ripe = RipeMD160_Hash(addr_ripe);
    
    addr_ripe.insert(addr_ripe.begin(), id);
    addr_hash = SHA256_Hash(SHA256_Hash(addr_ripe));
    addr_ripe.insert(addr_ripe.end(), addr_hash.begin(), addr_hash.begin() + sizeof(uint32_t));
    return base58Encode(addr_ripe);


// This will now work for both p2pkh addresses and p2wpkh-p2sh addresses

getWalletAddress(public_key, LTC_TESTNET, true);  // Produces: QYyWqgyWSm1AJWph32GnyY7eamG1wUDruk

【讨论】:

以上是关于求教,比特币钱包怎么与php建立连接的主要内容,如果未能解决你的问题,请参考以下文章

在中国想要投资比特币,该如何开始,怎么申请钱包?

bitpie有收款地址怎么查询

比特币核心有啥用?

比特币家园|比特币价格|比特币钱包下载

钱包与支付

比特币隔离见证钱包地址计算