【比特币】BIP 0015 详细说明

BIP 0015 详细说明


  BIP: 15
  Layer: Applications
  Title: 别名
  Author: Amir Taaki <genjix@riseup.net>
  Comments-Summary: No comments yet.
  Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0015
  Status: Deferred
  Type: Standards Track
  Created: 2011-12-10

BIP 0070(付款协议)可以被视为替代别名。

使用香草比特币发送资金到目的地,需要一个1Hd44nkJfNAcPJeZyrGC5sKJS1TzgmCTjjZ的地址。 使用地址的问题是他们不容易记住。 如果域名不存在,则需要输入他们喜欢的网站的IP地址。

这个文件的目的是通过仔细的参数来设计一个比特币别名系统。 这是对协议的一个很大的修改,在将来不容易改变,并且有很大的影响。 有第一次纠正的动力。 别名必须是稳健的,安全的。

计划

这里有一些不同的建议和各系统的性能。

FirstBits

FirstBits是将区块链用作地址簿的建议。

当比特币被发送到地址时,该地址就会被记录在区块链中。 因此,知道这个地址存在或确实存在,只要看到有一个付款的地址。 FirstBits是一个具有令人难忘的别名的方法。 首先将地址转换为小写,然后取前几个唯一字符。 这是你的FirstBits别名。

举个例子,布拉格的brmlab hackerspace有一个购买食物或饮料或捐款的地址:

1BRMLAB7nryYgFGrG8x9SYaokb8r2ZwAsX

他们的FirstBits别名变成:

1brmlab

这是足够的信息被给予FirstBits别名1brmlab。 当有人想要购买时,如果没有FirstBits,他们要么手工输入他们的地址,扫描他们的二维码(这需要一个手机,这个作者不拥有),或在互联网上找到他们的地址复制和 粘贴到客户端发送比特币。 FirstBits通过提供简单的付款方式来缓解这种不切实际的情况。

与Vanitygen(虚荣发生器)一起,可以创建令人难忘的唯一命名地址。 地址是有意义的,而不是一个奇怪的字母和数字组合,但添加上下文到目的地。

然而FirstBits有它自己的问题。 一个是可能产生的别名受可用计算能力的限制。 生成一个完整或精确的别名可能是不可行的 - 只有近似值才有可能。 这也是计算资源密集型的,这意味着未来产生独特的别名需要大量的能量消耗,并且在普适计算的环境中不能扩展到家中的个人或手持设备的参与者的水平。

随着网络的增长,FirstBits的规模将非常差。 每个索引器或查找节点需要跟踪每个存在的比特币地址,并提供从别名到这些地址的快速查找。 随着网络线性增长,地址数量应该呈指数增长(假设(n-1)*(n-2)/ 2)的网络效应)使得该方案不可行。

部分merkle根类型的轻客户端依赖于可信第三方的别名查找。 考虑到他们在低资源设备上的典型使用情况,存储每个比特币地址的成本太高。 这个因素多于其他因素,意味着这个方案是次优的,必须被拒绝。

DNS TXT 记录

DNS允许创建包含任意数据的TXT记录。 在比特币别名系统中,由BIP标准共同定义的自定义格式将被用于存储从域名到比特币地址的映射。 这样的格式看起来不在本文的范围之内。

一个问题是,它要求那些希望创建这种映射的人熟悉配置DNS记录,并且能够运行必要的工具集来插入正确的数据。 虽然不是一个大问题,但这是一个可用性问题。

安全方面,DNS是不安全和不安全的设计。 可以通过与另一个主机位于同一网络来欺骗记录。 自二十世纪九十年代以来,DNSSEC幌子下的一系列修改工作已经开始,目前仍在进行中。

截至2011年12月,DNSSEC在互联网上还没有达到事实标准。 如果比特币网络的参与者希望使用DNS TXT记录,则他们除了必须配置DNS之外,还能够设置DNSSEC。 这可能是不可行的,尤其是在一些注册商只通过网页界面访问DNS的情况下。

DNS TXT记录的缺点是更新记录需要时间。 这鼓励人们不要使用每个具有一定安全问题的交易的新地址。

服务器服务

除了使用DNS TXT记录,另一种可能性是使用域名系统查找主机,然后联系预定义的端口上运行的服务,以获得比特币地址。

  1. 用户希望发送到foo@bar.net
  2. 客户端使用DNS查找bar.net的IP地址:123.123.123.123
  3. 客户端连接到端口123.123.123.123:4567,并请求用户foo的比特币地址
  4. 服务器响应地址或错误代码并终止连接。
  5. 客户将资金发送到地址

该服务将负责提供更改和存储服务映射的机制。 可以为希望使用该服务并在服务器上定制其帐户的用户提供前端Web界面。

这种方法具有积极的作用,为实现者提供最好的灵活性,然后将它们存储在数据库或纯文本文件中,然后使用通常用C编写的小型服务器端守护进程快速提供这些记录。这种方法具有高度可扩展性。

但是,这种方法也面临着依赖于DNS的问题,因此也容易受到欺骗。 因此DNSSEC也是必需的。 这种方法比DNS TXT记录稍好,因为它使得插入新用户和修改别名非常容易,这使得人们可以更便宜地运行这些服务器服务。

HTTPS Web服务

HTTPS通过加密连接提供额外的安全层,为用户提供非常需要的隐私。 与使用证书颁发机构一起,它解决了使用DNSSEC的问题,因为会引发错误,有人试图欺骗本地网络上的域名。

当试图发送到:

genjix@foo.org

这个请求在@的最后一个出现处被分解成句柄(genjix)和域(foo.org)。 客户端然后构造一个将查询地址的请求。

https://foo.org/bitcoin-alias/?handle=genjix

比特币别名已经被选为查询后缀,因为它允许这个系统在另一个web根目录中很容易共存,而不用担心名称冲突。

查询将返回一个用于付款的地址。

1Hd44nkJfNAcPJeZyrGC5sKJS1TzgmCTjjZ

每个查询是否返回一个唯一的地址,是否从一个预先存在的地址池中获取一个地址,等等的细节是每个服务器唯一的实现细节。 设置映射映射的别名依赖于可能具有Web界面的站点,并向用户提供免费服务,或者是为预先存在的地址提供私人定制服务。 这是留给管理层的政策,故意不在这里定义。

一个Web服务是微不足道的安装和成本低。 网络上有许多免费的提供商,允许任何具有最基本的网络技术知识的人创建自己的网站。 通过为用户提供一个软件包,任何人都可以快速设置自己的比特币别名。 它可以像PHP脚本一样简单,用户可以使用自定义设置进行编辑并上传自己的网站。

它也可以合理扩展 - 任何希望运行命名服务的人都可以使用各种数据库技术来附加后端,然后为用户提供一个Web前端来自定义和创建自己的别名。

作为一个例子,下面提供了一个天真的实现。

// resolv.h
#ifndef NOMRESOLV_H__
#define NOMRESOLV_H__

#include <string>
#include "curl/curl.h"

using std::string;

/*

这个类解决了服务器查找地址。
为了不与比特币地址冲突,我们在这里指的是人的手柄。
一个句柄的形式是:

   genjix@foo.org

大多数字符对用户名+密码有效(并进行相应处理),但域遵循通常的Web标准。 如果需要,可以粘贴一条路径,

   genjix@bar.com/path/to/

*/

class NameResolutionService
{
public:
    NameResolutionService();
    ~NameResolutionService();

    // Three main methods map to RPC actions.
    string FetchAddress(const string& strHandle, string& strAddy);

private:
    // A POST block
    class PostVariables
    {
    public:
        PostVariables();
        ~PostVariables();
        // Add a new key, value pair
        bool Add(const string& strKey, const string& strVal);
        curl_httppost* operator()() const;
    private:
        // CURL stores POST blocks as linked lists.
        curl_httppost *pBegin, *pEnd;
    };

    // Explodes user@domain => user, domain
    static void ExplodeHandle(const string& strHandle, string& strNickname, string& strDomain);
    // Perform the HTTP request. Returns true on success.
    bool Perform();

    // CURL error message
    char pErrorBuffer[CURL_ERROR_SIZE];
    // CURL response
    string strBuffer;
    // CURL handle
    CURL *curl;
};

#endif
// resolv.cpp
#include "resolv.h"

#include <boost/lexical_cast.hpp>

#include "access.h"

// callback used to write response from the server
static int writer(char *pData, size_t nSize, size_t nNmemb, std::string *pBuffer)
{
  int nResult = 0;
  if (pBuffer != NULL)
  {
    pBuffer->append(pData, nSize * nNmemb);
    // How much did we write?
    nResult = nSize * nNmemb;
  }
  return nResult;
}

NameResolutionService::NameResolutionService()
{
    // Initialise CURL with our various options.
    curl = curl_easy_init();
    // This goes first in case of any problems below. We get an error message.
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, pErrorBuffer);
    // fail when server sends >= 404
    curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
    curl_easy_setopt(curl, CURLOPT_HEADER, 0);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_302);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);
    curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
    // server response goes in strBuffer
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strBuffer);
    pErrorBuffer[0] = '\0';
}
NameResolutionService::~NameResolutionService()
{
    curl_easy_cleanup(curl);
}

void NameResolutionService::ExplodeHandle(const string& strHandle, string& strNickname, string& strDomain)
{
    // split address at @ furthrest to the right
    size_t nPosAtsym = strHandle.rfind('@');
    strNickname = strHandle.substr(0, nPosAtsym);
    strDomain = strHandle.substr(nPosAtsym + 1, strHandle.size());
}
bool NameResolutionService::Perform()
{
    // Called after everything has been setup. This actually does the request.
    CURLcode result = curl_easy_perform(curl);
    return (result == CURLE_OK);
}

string NameResolutionService::FetchAddress(const string& strHandle, string& strAddy)
{
    // GET is defined for 'getting' data, so we use GET for the low risk fetching of people's addresses
    if (!curl)
        // For some reason CURL didn't start...
        return pErrorBuffer;
    // Expand the handle
    string strNickname, strDomain;
    ExplodeHandle(strHandle, strNickname, strDomain);
    // url encode the nickname for get request
    const char* pszEncodedNick = curl_easy_escape(curl, strNickname.c_str(), strNickname.size());
    if (!pszEncodedNick)
        return "Unable to encode nickname.";
    // construct url for GET request
    string strRequestUrl = strDomain + "/bitcoin-alias/?handle=" + pszEncodedNick;
    // Pass URL to CURL
    curl_easy_setopt(curl, CURLOPT_URL, strRequestUrl.c_str());
    if (!Perform())
        return pErrorBuffer;
    // Server should respond with a JSON that has the address in.
    strAddy = strBuffer;
    return "";  // no error
}

NameResolutionService::PostVariables::PostVariables()
{
    // pBegin/pEnd *must* be null before calling curl_formadd
    pBegin = NULL;
    pEnd = NULL;
}
NameResolutionService::PostVariables::~PostVariables()
{
    curl_formfree(pBegin);
}
bool NameResolutionService::PostVariables::Add(const string& strKey, const string& strVal)
{
    // Copy strings to this block. Return true on success.
    return curl_formadd(&pBegin, &pEnd, CURLFORM_COPYNAME, strKey.c_str(), CURLFORM_COPYCONTENTS, strVal.c_str(), CURLFORM_END) == CURL_FORMADD_OK;
}

curl_httppost* NameResolutionService::PostVariables::operator()() const
{
    return pBegin;
}
</source>

<source lang="cpp">
// rpc.cpp
...

const Object CheckMaybeThrow(const string& strJsonIn)
{
    // Parse input JSON
    Value valRequest;
    if (!read_string(strJsonIn, valRequest) || valRequest.type() != obj_type)
        throw JSONRPCError(-32700, "Parse error");
    const Object& request = valRequest.get_obj();
    // Now check for a key called "error"
    const Value& error  = find_value(request, "error");
    // It's an error JSON! so propagate the error.
    if (error.type() != null_type)
        throw JSONRPCError(-4, error.get_str());
    // Return JSON object
    return request;
}

const string CollectAddress(const string& strIn)
{
    // If the handle does not have an @ in it, then it's a normal base58 bitcoin address
    if (strIn.find('@') == (size_t)-1)
        return strIn;

    // Open the lookup service
    NameResolutionService ns;
    // We established that the input string is not a BTC address, so we use it as a handle now.
    string strHandle = strIn, strAddy;
    string strError = ns.FetchAddress(strHandle, strAddy);
    if (!strError.empty())
        throw JSONRPCError(-4, strError);

    const Object& request(CheckMaybeThrow(strAddy));
    // Get the BTC address from the JSON
    const Value& address = find_value(request, "address");
    if (address.type() != str_type)
        throw JSONRPCError(-32600, "Server responded with malformed reply.");
    return address.get_str();
}

// Named this way to prevent possible conflicts.
Value rpc_send(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 2)
        throw runtime_error(
            "send <name@domain or address> <amount>\n"
            "<amount> is a real and is rounded to the nearest 0.01");

    // Intelligent function which looks up address given handle, or returns address
    string strAddy = CollectAddress(params[0].get_str());
    int64 nAmount = AmountFromValue(params[1]);
    // Do the send
    CWalletTx wtx;
    string strError = SendMoneyToBitcoinAddress(strAddy, nAmount, wtx);
    if (!strError.empty())
        throw JSONRPCError(-4, strError);
    return wtx.GetHash().GetHex();
}

...

IP交易

一个IP交易是比特币中一个旧的交易格式,被禁用,可能会被弃用。 它涉及被给予一个IP地址付款。 当连接到节点并使用“checkorder”请求他们的公钥时,他们将用以下格式的脚本进行响应:

<public key> OP_CHECKSIG

类似于coinbase输出交易。 IP事务具有能够包含额外的元数据的优点,这在许多事务中可能是有用的。 目前没有进行认证,使得该方案对中间人(MITM)攻击不安全。

此提案旨在为IP事务启用DNS查找。

“checkorder”消息将包含一个目标帐户,该目标帐户可映射到在同一主机下运行的不同的独立密钥对/钱包集。 从checkorder参考信息到本地系统的确切映射是实现定义的。

通过使用DNS查找,通过将公钥存储在DNS TXT记录中,可以缓解IP事务的MITM问题。 这个公钥将被用于所有将来从该主机发出的“回复”消息。 首次使用需要确认接受该公钥; 像SSH一样。 如果“回复”消息与接受的公钥不匹配,则主机将被给出错误。

Namecoin ID

此建议使用Namecoin区块链将别名与比特币地址相关联。 比特币查询名称币结点。 这将检索包含与该别名相关联的比特币地址的结构化数据。

使用像Namecoin这样的分散的域名系统,意味着不像这里列出的其他提案那样需要信任外部服务器或实体。 这表明系统具有高可用性和易于输入的优点(不限制用户创建别名)。

下面介绍两个例子。 第一个显示了一个更简单的格式,而第二个显示了几个比特币地址的结构化格式。

$ namecoind name_show id/khal
 {
   "bitcoin" : "1KHAL8bUjnkMRMg9yd2dNrYnJgZGH8Nj6T"
 }
 ```

$ namecoind name_show id/khal
{
“bitcoin” :
{
“default” : “1KHAL8bUjnkMRMg9yd2dNrYnJgZGH8Nj6T”,
“donation”: “1J3EKMfboca3SESWGrQKESsG1MA9yK6vN4”
}
}



更多可能性:

- 允许安全使用不安全的渠道

你可以把一个网址和一个比特币地址,用来签署的结果。 这意味着对这个URL的查询将返回一个比特币地址和签名。 比特币然后可以检查(使用verify_message函数)返回的地址还没有被另一个替换。

$ namecoind name_show id/khal
{
“bitcoin” :
{
“url” : “http://merchant.com/bitcoin/getnewaddres/“,
“signedWith” : “1KHAL8bUjnkMRMg9yd2dNrYnJgZGH8Nj6T”
}
}


- 允许每次或每个用户获得不同的地址,每个订单等

$ namecoind name_show id/khal
{
“bitcoin” :
{
“url” : “http://merchant.com/bitcoin/getaddres/{Your customer id}”,
“signedWith” : “1KHAL8bUjnkMRMg9yd2dNrYnJgZGH8Nj6T”,
“useOnce”: false
}
}
“`

在上面的例子中,比特币会要求用户输入“Your customer id”,然后在发出http请求之前将该值替换为url。 商家将收到请求,并为用户提供与该客户相关的付款地址。

任何文字可以被放入括号,允许商家以使其适应他们的需求。

  • 规范是可扩展的

可以稍后添加新功能来支持未发现的案例。
有关更多信息,请参阅Namecoin ID的规范。

总结

11年提出来的,现在被搁置,问题是比特币的地址太长,所以想通过别名的方式来解决。

原文档的连接已经失效了,但是内容还能谷歌出来。

参考资料

上一篇:CloudStack 打开实例Console报错分析


下一篇:【智能合约】客户端和web端对智能合约的事件Event进行调用的代码示例