主页 > imtoken不让安装 > NBitcoin:最完整的比特币端口(第 1 部分:加密货币)

NBitcoin:最完整的比特币端口(第 1 部分:加密货币)

imtoken不让安装 2023-05-22 06:33:35

你可以喜欢或鄙视维基解密或斯诺登所做的事情,但你不能忽视它们,就像很久以前的苹果广告一样。 但现在,假设你是一名举报人。 你住在瑞典,揭露了一些腐败官员和政府官员的非法活动。 现在,在这些官员的干预下,没有任何审判,官员们已经冻结了你所有的支付媒介。 没有贝宝,没有电汇,没有信用卡,没有签证,什么都没有,他们会在没有任何审判的情况下将你饿死。 但无论他们做什么,你都可以依靠比特币,没有任何实体可以颠覆比特币,即使是比特币的创造者。

黑暗的观点

现在,让我们承认比特币不仅仅是出于勇敢的原因而被使用。 但我可以说,目前犯罪分子可以通过信件或通过一些黑手党控制的银行汇款。 普通票据可以像比特币一样容易被用来洗钱。 在媒体上,当一篇文章说“纽约的洗钱”时,没有人注意。 如果一篇文章说:“在纽约用比特币洗钱”,那肯定会引起注意。

比特币不是黑暗的或由黑手党经营,它是地球上存在的最开放和民主的货币形式,你可以证明它是开源的。 但让我们面对现实吧:作为我们心爱的美元钞票,犯罪分子也会使用它。 (我稍后会谈到比特币的民主部分,社区有一个有趣的比特币“投票”功能)但是,比特币更容易追溯到通过黑银行的钱。 (参见汇丰银行)

被称为 MtGox 的比特币银行倒闭了,他们的客户赔了钱。 Mt Gox 说是因为黑客攻击,我认为是因为银行抢劫。 没有人能证明 Mount Gox 是对的,或者我是对的。 不管怎样,如果你和比特币打交道,请记住下面这句话,并传播给大家。

如果您不拥有私钥。 你不拥有比特币。

Mt Gox 是一家银行,他们为您保管私钥,但不会给您并声称会保护您的资金安全。 但是,如果你没有私钥,你就没有办法证明他们确实保管了你的钱,你也没有权利直接在比特币网络上花钱。 再次。

如果您不拥有私钥。 你不拥有比特币。

说起来容易,给我看代码开始

首先,代码在 GitHub 上。 其次举报身边玩比特币,二进制文件在 Nuget 上。 那么让我们开始吧,新的控制台项目,然后添加对 nuget 包 NBitcoin 的引用。

对于依赖,BouncyCastle用于加密部分,比特币使用ECDSA非对称密钥,我不想自己实现。 在 C++ 代码中,改用 OpenSSL。

Mono.NAT 和 SQLite 仅在您打算创建自己的比特币节点时使用(下一篇文章)。 Mono.NAT 将使用 UPNP 在您的网关上打开一个比特币端口来运行您的节点。 SQLite 是节点服务器用来存储事务、块和对等点的嵌入式数据库。 它适用于 x86 和 x64 架构。 如果您不打算与比特币网络通信,可以将其删除。

比特币地址

正如我所说,如果你想给我发送一些比特币,请将其发送到 15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe。 但是,作为开发者,这个地址代表什么呢? 官方规范在这里,但让我们用 NBitcoin 来探索它。 该字符串是 base58 字符串编码的字节数组。 让我们看看它的十六进制表示形式下的地址。

static void Main(string[] args)
{
    byte[] byteArray = Encoders.Base58.DecodeData("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe");
    string hex = Encoders.Hex.EncodeData(byteArray);
    Console.WriteLine(hex);
}

复制

它给我们:00356facdac5f5bcae995d13e667bb5864fd1e7d59fb69d021。 00:标识比特币数据结构类型的前缀,在本例中为公钥哈希(见下文) 356facdac5f5bcae995d13e667bb5864fd1e7d59:前一个 1b69d02f 的实际公钥哈希(20 字节) 数据验证(检测输入错误)

在比特币中,一个地址属于一个网络。 我们有两个网络:Main 和 Test。 您可以在测试网上获得免费的比特币用于测试目的。 但是,每个网络都有不同的前缀来标识公钥哈希。 00是主网上公钥哈希的前缀。 但对于测试网,它的前缀是 6f。 这样做是为了让您无法将资金发送到属于另一个网络的地址。

您可以使用 BitcoinAddress 类型以多种方式为您进行转换。

static void Main(string[] args)
{
    BitcoinAddress address = (BitcoinAddress)Network.CreateFromBase58Data("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe");
    //BitcoinAddress address = Network.Main.CreateBitcoinAddress("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe");
    //BitcoinAddress address = new BitcoinAddress("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe", Network.Main);
    Console.WriteLine(address.ID);
    //Print 356facdac5f5bcae995d13e667bb5864fd1e7d59
}

复制

但是我从哪里得到这个“公钥散列”呢? 当有人用比特币向您汇款时,他们会向网络发送交易。 所有交易都包含一个或多个 TxIn(交易输入)和一个或多个 TxOut(交易输出)。 任何包含您的公钥散列的 TxOut 都供您使用。

当轮到您付款时,您向网络发送一个新交易,但这次您将包括一个 TxIn 和一个对您想要花费的 TxOut 的引用(我们将这样的引用称为 OutPoint)。 但是,您将使用与您要使用的 TxOut 中存在的公钥哈希相关联的私钥签署交易。 从而证明您对网络的所有权。

这是创建新密钥对的过程。

static void Main(string[] args)
{
    Key privateKey = new Key(); //创建私钥
    BitcoinAddress address = privateKey.PubKey.GetAddress(Network.Main); //获得公钥,并导出主网络上的地址
    Console.WriteLine(address); //打印我的地址: 15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe
}

复制

从表面上看举报身边玩比特币,如果你想存储私钥,

Key privateKey = new Key(); //创建私钥
BitcoinSecret secret = privateKey.GetBitcoinSecret(Network.Main);
Console.Write(secret); //打印base58编码的字符串

复制

下面是所有类的小图,你可以看到 BitcoinAddress 只是一个公钥哈希,不允许你推断出 PubKey。

隐私保护

如果每笔交易都广播到网络,这意味着任何人都应该能够跟踪我收到了多少,这会引发一些隐私问题。 是的,你可以看到有人给我发了 0.0026 BTC。

您可以创建所需的私钥。 如果你想保留私钥,你可以在几个地址之间分配你的接收。 (一组私钥称为钱包。)如果您是一家企业并且需要自动付款,您还有另一种选择:为每笔业务交易创建一个不同的比特币地址。

但是,有两个问题:

该解决方案称为分层确定性钱包。 因此,您可以授予支付服务器生成公钥的权利,而无需提供私钥。 如果支付数据库被盗或遭到破坏,您将一无所有。

在 NBitcoin 中,此功能由两个类实现:ExtKey 和 ExtPubKey。 ExtKey会为对应的ID生成Key,ExtPubKey会为对应的ID生成PubKey。

ExtKey privateKey = new ExtKey();
ExtPubKey pubKey = privateKey.Neuter();
//现在,把pubkey发送给您的付款服务器
//....
//付款服务器收到一个订单,注意服务器不需要私钥来生成地址
uint orderID = 1001;
BitcoinAddress address = pubKey.Derive(orderID).PubKey.GetAddress(Network.Main);
Console.WriteLine(address);
//现在,在有权限访问私钥的服务器上,你从订单ID得到了私钥
Key key = privateKey.Derive(orderID).Key;
BitcoinSecret secret = key.GetBitcoinSecret(Network.Main);
Console.WriteLine(secret); //打印一个很棒的秘钥字符串

复制

在这个例子中,我们只使用一个orderId来生成Key/Pubkey,但实际上你可以使用更多的数据,这就是为什么我们需要一个分层钱包。

例如

pubKey.Derive(departmentID).Derive(orderID).PubKey

复制

这样,您就可以为每个部门生成一个专用的私钥。 (如果你有管理员 ExtKey,你也可以使用他们的资金)

使用比特币密钥对进行身份验证

亲爱的读者,我如何证明我确实是 15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe 的私钥所有者?

这是证明:

消息:“我是 Nicolas Dorier,地址 15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe 的所有者”。

签名:“IGkC4NKGXOXJ6CNT8T6Dx0egqaiSb8rAlBdsmanStOhbVfILmY+3p88Z/Fhb/jSkUhHFhsbcxFZydoPrh/2LNY0="

我是如何生成这个签名的?

BitcoinSecret secret = new BitcoinSecret("base58 private key", Network.Main);
string signature = secret.Key.SignMessage("I am Nicolas Dorier, owner of this address 15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe");
Console.WriteLine(signature);

复制

在你身边,你如何验证?

string message = "I am Nicolas Dorier, owner of this address 15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe";
string signature = "IGkC4NKGXOXJ6CNT8T6Dx0egqaiSb8rAlBdsmanStOhbVfILmY+3p88Z/Fhb/jSkUhHFhsbcxFZydoPrh/2LNY0=";
BitcoinAddress address = new BitcoinAddress("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe", Network.Main);
bool verified = address.VerifyMessage(message, signature);
Console.WriteLine(verified);

复制

底层实现

为了简化我的解释,我做了一些捷径来解释什么是比特币地址。 我说,一个TXOUT把钱汇到一个比特币地址,当你要花钱的时候,在你引用这个TXOUT的交易中加上一个TxIn,用你的私钥签名。

但比特币比这更灵活。 TxOut 不一定包含您的地址。 TxOut 包含一个脚本,我们称之为 ScriptPubKey。 这个脚本就像一个算法,告诉你需要做什么来使用这个 TxOut。 似乎 TxIn 有一个名为 ScriptSig 的脚本,它执行 ScriptPubKey 想要对 TxOut 执行的操作。 这些脚本是一种没有任何循环的堆栈语言。

当 SigPubKey 连接到 ScriptSig 时,将执行算法并将其结果压入堆栈。

到目前为止,如果你想给我汇款,ScriptPubKey 看起来就像那样。 (以可读形式)

BitcoinAddress to = new BitcoinAddress("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe", Network.Main);
PayToPubkeyHashScriptTemplate template = new PayToPubkeyHashScriptTemplate();
Script script = template.GenerateOutputScript(to);
Console.WriteLine(script);

复制

OP_DUP OP_HASH160 356facdac5f5bcae995d13e667bb5864fd1e7d59(公钥哈希)OP_EQUALVERIFY OP_CHECKSIG

另一方面,如果我想使用这笔资金,我的脚本将是

BitcoinSecret secret = new BitcoinSecret("base58 secret key", Network.Main);
byte[] transactionSignature = secret.Key.Sign(transactionHash);
PayToPubkeyHashScriptTemplate template = new PayToPubkeyHashScriptTemplate();
Script scriptSig = template.GenerateInputScript(new TransactionSignature(transactionSignature, SigHash.All), secret.Key.PubKey);
Console.WriteLine(scriptSig);

复制

生成的脚本会将两个值压入堆栈:交易签名和公钥。

如果我们连接我们得到的两个脚本

OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG

它的意思是

再推

哈希、弹出、推送

验证等于,弹出,弹出

使用 Push , pop , pop 校验签名,校验成功push true,校验失败push false

共享所有权

为什么脚本系统如此重要? 因为它允许新的所有权方式,当有人将资金发送到 N 个钱包中的一个 M 时,有一个称为“M of N 钱包”的特殊脚本,这意味着花费者需要提供 M 个可能地址的列表。 N个签名。

通过这种方式,您可以推动三分之二的人同意花钱以强制达成共识这一事实。 如果您想与某人分享金钱,您可以使用两者之一。 这与共享私钥之间的区别是不可否认的。 当你使用“2个钱包之一”时,你可以证明那个付款人的身份。这样的脚本可以这样生成

PayToMultiSigScriptTemplate template = new PayToMultiSigScriptTemplate();
Script scriptPubKey = template.GenerateOutputScript(1, new PubKey[] { nicoPubKey, bobPubKey });

复制

01 02 OP_CHECKMULTISIG

还有其他标准脚本,我一定会在下一篇文章中讨论。 这是他们的类图。

综上所述

最后,这篇文章比我想象的要长......下次我将介绍更多关于比特币协议的内部结构,以及讨论其他标准脚本。 如果您喜欢这篇文章,请告诉我。

[新文章:第 2 部分在这里]