65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# ASP.NET 接受比特币 (BTC) 支付。本文适用于其他加密货币:莱特币 (LTC)、以太坊 (ETH)、瑞波币 (XRP)...

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (50投票s)

2017 年 9 月 22 日

CPOL

31分钟阅读

viewsIcon

161320

downloadIcon

3613

详细指南,介绍如何构建接受比特币 (BTC)、莱特币 (LTC)、以太坊 (ETH) 和其他加密货币的 C# ASP.NET 网站结账系统。

目录

几年前我就想写一篇关于比特币的文章了。自从《C# ASP.NET 开发人员的 PayPal 入门指南》和《Windows Mobile、iPhone、Android - 市场比较》广受好评以来,我一直很享受在 CodeProject 上写作来巩固自己理解的过程。

可惜,过去几年我的时间都很紧张。所以,尽管我在 2013 年底大跌之前就已经关注比特币和其他加密货币 (CC),但最近的飙升才终于促使我下定决心撰写更多关于这项技术的内容。我的看法几乎没有改变——早在 2013 年,我就相信 CCs 将是我们一生中将迎来的下一场重大革命。就像我早在 2009 年预测 Android 会成为市场领导者一样,我能预见到一个未来,届时像比特币这样的货币将是我们人类用来存储和交换价值的方式。

因此,本文将是我尝试呈现一个加密货币成为事实货币的未来愿景的尝试。此外,我将努力使本文成为一个完整的资源——它将为您提供开始正确使用 CCs 所需的一切。就像我做了《移动市场比较》一样——我将提供历史背景、当前情况,然后介绍作为开发人员如何加入。

与我大多数文章一样,请使用上方的索引,这样您可以轻松导航到最感兴趣的内容。

  • 如果您想跳过比特币之前的历史背景,请随意从“比特币的黎明”开始阅读。
  • 如果您只对加密货币的历史和概述感兴趣,请跳至“比特币的高层技术概述”。
  • 如果您只是为了代码而来,并想看到接受加密货币的结账功能,请直接前往“Coinpayments - 接受比特币 (BTC)、莱特币 (LTC)、以太坊 (ETH)”。

系好安全带,我们将开始深入研究过去。

过去

货币简史

货币可能是人类历史上最重要的发明之一。在货币出现之前,价值与具体资源挂钩。这在各种情况下显然是一个巨大的问题。在贸易中,您无法用 3.3 只羊来换一头牛。而且,您能获得的价值受到严重限制。即使您是历史上最优秀的谷物农民——抱歉,您最多只能获得您能换成不易腐烂商品的价值。将价值抽象化并用少量黄金(或其他贵重/稀有代币)来代表,使人类最终能够保存远超先前系统限制的价值。**商品货币**与贸易相结合,人类得以繁荣。

下一个重大革命发生在黄金和其他商品货币被**代表货币**取代时。您不再需要携带 1 公斤黄金,而是携带一张纸,上面写着您拥有 1 公斤黄金。某个中央机构保证了这张纸的有效性,允许您将其兑换成潜在的商品。20 世纪初,大多数国家都采用了金本位制。虽然这种做法被 discouraged,但如果您真的想,您可以用任何国家的纸币兑换一定量的黄金。

最后,在 20 世纪,货币开始变化得更快。第二次世界大战后,大多数国家开始用美元而不是黄金来支持它们的货币。而美元本身又与黄金挂钩。当尼克松在 1971 年暂停美元兑换黄金时,这一切都改变了。

当今世界,所有的货币都是**法定货币**。法定货币是指由发行国政府担保的法定支付手段。与代表货币不同,法定货币纸币下方没有任何实际支撑。如果我拥有 100 美元,它们之所以有价值,仅仅是因为其他人愿意接受它们作为支付方式。或者正如人们更诗意地说的那样:美元之所以有价值,是因为它以“美国政府的完全信任和信用”为后盾。

随着互联网的出现,金钱终于超越了纸张。当然,信用卡在 20 世纪 60 年代之前就已经存在(您甚至可以争辩说,类似的信用形式在 20 世纪之前就已存在)。但正是互联网让金钱真正进入了一个新的——虚拟的领域。如今,在拥有在线银行系统的发达国家,几个月不接触现金是很常见的。

您拥有的金钱数额现在基本上就是您登录网上银行时屏幕上看到的数字的总和。

金钱与自由

回顾历史,很容易看到历代各种形式货币的诸多弊端。我喜欢阅读关于**伪造古代硬币**的文章,例如。或者想想在中世纪,作为一个没有监督的银行家,您如何可以随意印制纸币。然而,纵观历史,人类以这样或那样的方式处理了这些“极端情况”。有人获利,有人破产,生活继续。

如今,您可以争辩说,法定货币只留下了一个大问题:政府控制。大多数人通过通货膨胀来认识到这一点。如果您正在积攒金钱并将其存入银行账户,实际上您每年都在损失其真实价值的 4-5%。我们可以争辩说,这种损失被“更大的善意”——政府主导的社会整体发展——所抵消。基本上,只要您同意您政府的行为——您就不会太在意财政和货币政策的细节。

但是,假设您生活在一个您不同意政府的国家。举个例子——假设您是叙利亚内战中的一名叙利亚人。突然,您发现您一生赚取的钱不再属于您。政府控制着它。所以,如果政府决定购买坦克然后增发货币——您无能为力。实际上,在任何时候您都可能被迫纳税。

您可以尝试获取外国政府的货币。但那样您就受制于其他政府。而且您还需要找到其他人愿意交易该货币。

考虑到这一切——很容易看到现代货币体系与中世纪封建社会之间的相似之处。在中世纪,如果您出生在某个村庄,您就是某个领主的附庸。只要您留在该领主的土地上,您就有义务服务。并非您有多少真正的选择——无论您走到哪里,总会有位领主拥有那片土地。

现在,既然这是一篇编程文章,我不会进行更深入的社会经济分析。之前所有的讨论都可以归结为一个简单的问题:

您是否相信个人应该拥有真正拥有自己金钱的自由?

在这个倡导个人自由的时代,我几乎总是听到第一个回答是“是的,当然”。但,很自然,事情并非如此简单。我将在文章的结尾部分详细阐述这个问题。

比特币的黎明

需要注意的是,比特币是在**2008 年金融危机**最严重的时候发布的。阅读有关这次危机的资料并不能完全体现其严重性,视频更能捕捉到当时普遍存在的恐慌。

比特币的创造者中本聪(Satoshi Nakamoto)对整个局势有自己的看法。因此,他在启动区块链时,在他的**创世区块**中包含了以下文字:

The Times 03/Jan/2009 Chancellor on brink of second bailout for banks

人们对中本聪知之甚少。多年来,许多人试图找出这位显然是化名背后的人物。这种追寻远不止是好奇,据估计,中本聪拥有约 100 万比特币。以当前 BTC 的价格计算,这相当于约 40 亿美元,使他成为世界上最富有的人之一。

无论中本聪是谁,他在 2008 年 10 月至 2010 年末期间的工作成果,已经催生了一个价值超过 1000 亿美元的市场。那么,比特币是什么?支撑它的理念又是什么,竟然在不到十年的时间里创造了如此庞大的产业?

您可以从 Satoshi 在密码学邮件列表上的第一篇帖子中大致了解比特币。引用帖子开头的话:

I've been working on a new electronic cash system that's fully
peer-to-peer, with no trusted third party.

The paper is available at:
http://www.bitcoin.org/bitcoin.pdf

The main properties:
 Double-spending is prevented with a peer-to-peer network.
 No mint or other trusted parties.
 Participants can be anonymous.
 New coins are made from Hashcash style proof-of-work.
 The proof-of-work for new coin generation also powers the
    network to prevent double-spending.

https://www.mail-archive.com/cryptography@metzdowd.com/msg09959.html

关键概念是——当然——没有可信的第三方。正如我们在“货币简史”中所见;我们人类一直有某种形式的中央权威在负责。无论链有多长,总有人在链的末端,操纵着一切,做出决定,确保一切都按照规则运行。

通过比特币,我们第一次能够建立一个**没有中央权威的信任网络**。所有参与者都可以进行交互和交换,轻松验证交易是否在明确定义的规则内得到处理。

我认为**信任网络**是大多数人忽略的部分。暂时忘掉金钱——想想两个方之间的任何一种协议。在我们目前生活的系统中,协议很可能由合同记录。根据司法管辖区的不同,存在某个中央机构(法院)来执行合同条款。从某种意义上说——合同怎么说并不重要。重要的是法院如何解释合同所说的……以及法院有多少权力让您遵守其认为您应承担的合同义务。

比特币之所以具有革命性,是因为它消除了信任网络所必需的中央权威。只要您能够实现/编码合同——一旦它成为区块链的一部分,您就可以放心,它将在规定的条件下执行。无需任何中央权威的干预。

一枚硬币统治一切。或者不是?

当许多人忙于将比特币称为**21 世纪版**的**郁金香危机**时,那些足够有知识的人花时间创建了利用这种无需中央权威的信任系统的新自由的新系统。

以太坊 (ETH)

这方面最雄心勃勃的项目可能是**以太坊 (ETH)**,它旨在提供编码几乎任何类型智能合同的能力,并将其作为其点对点网络的一部分执行。我有点怀疑 ETH 的可行性……但从长远来看,看到这个想法如何展开很有趣。**Vitalik** 和他的团队无疑为自己设定了极其宏伟的目标;正如他们所遇到的**障碍**所见。但没有人是通过冒险取得成功的,对吧?到目前为止,考虑到 ETH 目前拥有 260 亿美元的市场份额(仅次于比特币),他们的表现似乎不错。

门罗币 (XMR)

**门罗币是另一个非常有趣的去中心化信任系统实现**。它让大多数人感到恐惧,因为它将匿名性推向了另一个层面。与比特币不同,门罗币的交易**默认是无法追踪的**。通过输入端的环签名和输出端的隐身地址,参与者的匿名性得到了密码学的保证。在比特币中,如果您能将身份与某个公开地址相关联,就可以追踪交易。在门罗币中,即使您设法确定了某个公开地址的身份(这非常不可能),下一笔交易也会变成“无迹可寻”。简而言之,您无法百分之百确定资金的确切去向。

虽然我是门罗币的忠实粉丝,但我明白为什么它让大多数人感到恐惧。简单来说,无法区分系统内的行为者。让我们假设我们发现比特币生态系统中有些个人,其中 99.99% 的人认为他们正在做一些可鄙的事情。理论上,我们可以识别与那 0.01% 群体相关的公开地址,将其隔离,并阻止他们与系统的其余部分进行交互。这不会是一项容易的壮举……但仍然是可能的。在门罗币中,甚至没有理论上的可能性可以做到这一点。只要门罗币系统存在并且其中有参与者,所有参与者的完全匿名性就得到了密码学的保证。这是一段很好的视频,概述了使这一切成为可能的概念。

莱特币 (LTC)

莱特币基本上就是比特币,但有几个重要的区别:更快的区块(2.5 分钟而不是 10 分钟)和公开的领导者。**查理·李(Charlie Lee)在推特上非常活跃**。考虑到他创始人的身份,他能够按照自己的意愿引导莱特币的未来。在过去的几年里,这证明是资产——当比特币内战爆发时,莱特币领域却没有任何戏剧性。矛盾的是,今天的莱特币基本上是比特币扩容辩论双方都想要的货币——一个在 10 分钟内有效使用 4x1MB 区块并支持 SegWit 的货币。

比特现金 (BCH)

好的,好的……比特现金。**我必须得做这件事**;)。不深入探讨 8 月 1 日分叉之前持续数年的戏剧性(我需要另一篇文章来介绍),让我们直奔主题——扩容辩论。随着比特币在过去几年中越来越受欢迎,系统中交易量也越来越多,BTC 开始遇到了交易数量的瓶颈。最初设计系统时,Satoshi 将区块限制为 1MB。这大约是每 10 分钟 2000 笔交易。所以,如果您想支持每 10 分钟 4000 笔交易,您只需要将区块限制增加到 2MB,对吧?

好吧,这正是多年前整个令人头痛的比特币扩容辩论的开始。你看,那个 1MB 的区块限制也是为了防止垃圾信息。要让您的交易包含在下一个区块中,您需要为矿工提供有吸引力的费用来包含您的交易。如果没有交易费,恶意行为者就可以创建大量的交易,进行垃圾信息攻击并瘫痪比特币网络。然后还有矿工的论点——区块越大,挖矿中心化的动力就越大。更大的区块需要更长的时间在网络上传播,并且区块链增长得更快。此外,当然,我们可以就有限的区块链空间和交易费背后的经济理论进行无休止的讨论。

最终,形成了两个阵营:

  1. 希望通过增加区块大小来增加交易量的阵营
  2. 出于各种原因希望保持区块大小不变,并寻找替代扩容方法的阵营(主要是通过**隔离见证 (SegWit)**)

**经过几年激烈的在线极客争论**,第二阵营最终获胜,并计划在 2017 年 8 月下旬激活 SegWit。由于第一阵营不希望将 SegWit 包含在比特币中,他们决定分叉源代码。因此,他们构建了一个不包含 SegWit 的客户端,允许 8 MB 的区块,并引入了一些修复(其中一些非常棒)。这一切都发生在东部时间 8 月 1 日 14:14,第一个 BCH 区块被挖出。它包含 6,985 笔交易,区块大小为 1.92 MB。比特现金由此诞生。

现在,作为一名一生大部分时间都在创业公司工作的人——我非常理解这种情况背后的动态。简单来说,每个大项目都需要不同思维模式的人。如果您是某个项目的投资者,即使您和项目开发人员同属一支团队——您的思维模式和目标也不同。优秀的开发人员希望构建优秀的系统。优秀的投资者希望获得丰厚的利润。正是在这种情况下,像史蒂夫·乔布斯这样的领导者才能留下持久的印记。

比特币的设计中没有这个。随着 Satoshi 的消失,没有任何中央权力。就任何新事物达成共识都极其困难。

回过头来看,我认为 BCH 分叉可能是最好的结果。那些认为更大的区块是扩容答案的人现在有了自己的币。那些出于任何原因拒绝更大区块的人也有了自己的币。而像我这样对两种解决方案都持开放态度的人,现在可以自由地在两个区块链之间交易之前获得的价值,随心所欲。

其他加密货币

尽管前面的段落会让你认为并非如此,但我们才刚刚触及如今可用的加密货币的冰山一角。在任何一天,全球各种交易所的交易量都超过 20 亿美元。只需访问**Coinmarketcap.com**,亲自查看。如果您想要我个人推荐值得关注的货币,我会推荐:

  • 瑞波币 (XRP) - 是一个非常有趣的 CC 实现。与比特币相比,它在技术上存在很大差异。我推荐**David Swartz 的这个演讲作为瑞波币的介绍**。
  • IOTA (MIOTA) - 是另一个非常吸引人的 CC。**IOTA 联合创始人 David Sønstebø 的这段采访**应该是一个很好的介绍。

我想以一条简单的信息结束本节——**我邀请您超越金钱的范畴进行思考**。去中心化的信任网络将彻底改变我们社会的许多方面。无论好坏,金钱只是这些方面中的第一个。

现在

比特币的高层技术概述

理解比特币的技术视角需要两个关键组成部分:公钥密码学和区块链。让我们分别看看它们。

非对称/公钥密码学

公钥密码学是比特币技术的基础。对于完全没有非对称密码学知识的人来说——它是一种方案,您拥有一组两个相关的密钥:私钥和公钥。生成密钥对并将公钥发布到野外后,您可以进行两件伟大的事情:

  1. 公钥加密 - 任何想与您安全通信的人,都会使用公钥加密消息。这些消息只能用私钥解密。加密还可以保持加密消息的完整性——它在传输过程中无法被修改。

  2. 数字签名 - 您可以在不共享私钥的情况下证明您拥有私钥。使用私钥,您可以加密预定义的消息,生成签名。现在任何人都可以使用您的公钥解密签名,并验证它是否与预定义的消息匹配。这证明您确实拥有公钥背后的私钥。

实现这种魔术的数学非常令人费解。我推荐这个视频,它介绍了 ECC(在比特币中用于生成私钥和公钥)

在比特币中,生成流程是:私钥 -> 公钥 -> 钱包地址。生成过程背后的数学允许我们只能从左到右进行。也就是说,如果您有私钥,您可以轻松生成公钥和钱包地址。但您无法反向操作——您无法从公钥推导出私钥。钱包地址也是如此,它是通过 Base58Checksum 编码后进行串联生成的:

[版本字节][Hash160(公钥)][Base58 校验和]

当您拥有私钥(因此也拥有公钥和钱包地址)时,您实际上就可以接收和发送比特币交易了。简单来说,任何想给您发送 BTC 的人只需要知道您的钱包地址。然后,使用您的私钥,您就可以控制发送到您钱包地址的 BTC,并将其进一步转移到新地址。

区块链

现在,最大的问题——双重支付。假设您有一个与您的钱包地址关联的 1 BTC。您决定做一个坏人,生成两个交易,将同一个 1 BTC 发送到两个不同的地址。哪个交易是有效的?

当有中央机构时,这是一个微不足道的问题。交易按时间排序,因此第一个到达的交易将成功扣除余额,然后第二个将被拒绝。在去中心化系统中,您不能依赖时间——您必须以某种方式确保“正确”的状态沿着**所有节点**传播。但是,当您有两个冲突的交易时,您如何决定哪个状态是“正确”的?

这就是区块链解决的问题。简而言之,区块链是已确认有效的比特币交易的账本。平均每 10 分钟,一个矿工就会通过一次哈希竞赛的全球胜利者来创建一个新的区块。该矿工通过以下方式格式化区块:

  1. 挑选他能在区块中容纳的尽可能多的待处理交易(还记得 1MB 的限制吗?)
    • 矿工很可能选择交易费最高的交易;他们可以保留这些费用。
    • 冲突的交易将被处理——在双重支付的情况下,只能选择其中一个包含在区块中。
    • 无效的交易将被拒绝——例如,如果存在一笔交易试图花费已经花费过的输入。
  2. 附加一个特殊的(创世)交易,将挖矿奖励(目前为 12.5 BTC)和交易费发送到矿工控制的钱包地址。
  3. 生成一个有效的区块头,确保区块内容的完整性并链接到前一个区块。


区块头的示例

在候选区块形成后,哈希竞赛开始。比特币的**工作量证明**算法是对区块头进行双重 SHA256 哈希。大多数矿工只是更改 nonce 字段,并对区块头执行双重 SHA256,直到他们产生一个低于目标难度的哈希。

难度由哈希中前导零的数量决定。例如,这里有一个最近的区块哈希:00000000000000000085e7274fe5c9dbeee67128c3e081d12c5f5a151a7769ce。即使比特币网络的算力接近每秒 10,000 万亿次哈希——找到具有此哈希的有效区块也需要大约 10 分钟。

当新的区块找到后,并且我们开始讨论的交易之一被包含在内时,就开始了在这个区块之上创建新区块的竞赛。理论上,即使交易已包含在新、顶部的区块中(1 次确认),双重支付攻击也可能成功。所有需要做的就是,不包含先前已确认交易的替代链成为最长链。但这种情况**极其不可能**,并且对攻击者来说**成本非常高**。

为了更好地说明我在这里介绍的内容,请观看 YouTube 上的 CuriousInvestor 的视频。

使用 C# 发送您的第一个 BTC 交易

让我们终于把前面的讨论变成代码。运行以下几个示例的前提条件是:

  1. Visual Studio(任何版本或类型)
  2. NBitcoin - 一个很棒的 C# 比特币库。作者似乎是 CodeProject 上的传奇人物,所以一定要查看**他关于 NBitcoin 库的文章**。
    -如果您想浏览源代码,您需要下载 Visual Studio 2017 或 Visual Studio Code。旧版本的 VS 将难以打开 .csproj 文件。
    -如果您不关心源代码,您可以在程序包管理器控制台中执行:Install-Package NBitcoin

比特币支持单独的 TestNet,我们将在示例中使用它。基本上,您可以自由地在 TestNet 上构建和测试您的比特币相关应用程序,当它们准备就绪后,您可以通过更改一些参数将其迁移到生产环境。

对于我们的第一个示例,让我们在 TestNet 上生成私钥+公钥对以及相应的地址。

public class Example1
{
    public static void Run(string[] args)
    {
        var key = new Key();
        var address = key.PubKey.GetAddress(Network.TestNet);
        Console.WriteLine("Your BTC address: {0}\n\n", address);

        Console.ReadLine(); // wait for user input to proceed
    }
}

如您所见——NBitcoin 使开始比特币编码变得**极其容易**。现在,让我们构建一个基本的钱包应用程序。第一次启动时,我们将生成私钥。该私钥将使用密码加密并存储在磁盘上,以在重启后保留。这将使我们拥有一个持久的公共地址,以便在未来的示例中使用。

public class Example2
{
    private const string PRIVATE_KEY_PATH = "secret.key";
    private static readonly Network _network = Network.TestNet;
    public static void Run(string[] args)
    {
        // recover private key if it was saved and encrypted with password
        Key _privateKey = null;
        if (File.Exists(PRIVATE_KEY_PATH))
        {
            var sec = new BitcoinEncryptedSecretNoEC(File.ReadAllBytes(PRIVATE_KEY_PATH), _network);

            do
            {
                var password = ask("Private key password?");
                try
                {
                    _privateKey = sec.GetKey(password);
                    break; // stop while
                }
                catch (SecurityException)
                {
                    Console.WriteLine("Invalid password");
                }
            }
            while (true);
        }
        else
        {
            _privateKey = new Key();
        }

        // Display wallet address for receiving money
        var address = _privateKey.PubKey.GetAddress(_network);
        Console.WriteLine("Your BTC address: {0}\n\n", address);

        Console.ReadLine(); // wait for user input to proceed

        // Save private key to local file that's encrypted with password
        var persPass = ask("If you want to save this private key, enter password");
        if (!String.IsNullOrWhiteSpace(persPass))
        {
            var encKey = _privateKey.GetEncryptedBitcoinSecret(persPass, _network);
            File.WriteAllBytes(PRIVATE_KEY_PATH, encKey.ToBytes());
        }
    }

    private static string ask(string message)
    {
        Console.WriteLine(message);
        return Console.ReadLine();
    }
}

这样,我们现在就拥有了自己持久的私钥、公钥对,它们被安全地存储在磁盘上(好吧,这取决于密码以及在 Console.ReadLine() 期间是否有人在看您的屏幕)。现在,让我们接收一些资金到该钱包。在 TestNet 上,有一些水龙头(faucets)可以发送一些资金给您进行测试。您可以搜索“Bitcoin TestNet faucet”……我现在使用的是:https://testnet.manu.backend.hamburg/faucet

在水龙头处理您的请求并显示已发送资金的通知后,您很可能会收到一个比特币 Test Net 交易 ID。现在,我们可以使用支持 TestNet 的在线区块链浏览器来查看发送到我们钱包的交易。我推荐**BlockCypher 的 TestNet 区块链浏览器**。**钱包地址或交易 ID** 都可以让您获取信息,例如:

因此,我已将 1 BTC 存入我的钱包。现在,让我们做一个好公民,将这 1 BTC(减去交易费用)发送回我们收到它的地址。在代码中,我们需要构建一个交易,该交易将引用我们收到的输入,然后将所有内容作为输出发送回原始钱包。

public class Example3
{
    private const string PRIVATE_KEY_PATH = "secret.key";
    private const double BTC_AMOUNT_RECEIVED = 1.1;
    private static readonly Network _network = Network.TestNet;
    public static void Run(string[] args)
    {
        // recover private key if it was saved and encrypted with password
        Key _privateKey = null;
        if (File.Exists(PRIVATE_KEY_PATH))
        {
            var sec = new BitcoinEncryptedSecretNoEC(File.ReadAllBytes(PRIVATE_KEY_PATH), _network);

            do
            {
                var password = ask("Private key password?");
                try
                {
                    _privateKey = sec.GetKey(password);
                    break; // stop while
                }
                catch (SecurityException)
                {
                    Console.WriteLine("Invalid password");
                }
            }
            while (true);
        }
        else
        {
            throw new Exception("Run this example after Example2.cs");
        }

        var destAddress = ask("Enter address to which all funds should be sent");
        if (!String.IsNullOrWhiteSpace(destAddress))
        {
            var funding = new Transaction()
            {
                Outputs = { new TxOut(BTC_AMOUNT_RECEIVED.ToString(), _privateKey.GetBitcoinSecret(_network).GetAddress()) }
            };
            Coin[] sendingCoins = funding.Outputs
                .Select((o, i) => new Coin(new OutPoint(funding.GetHash(), i), o))
                .ToArray();

            var dest = _network.CreateBitcoinAddress(destAddress);
            var txBuilder = new TransactionBuilder();
            var tx = txBuilder
                .AddCoins(sendingCoins)
                .AddKeys(_privateKey)
                .Send(dest, (BTC_AMOUNT_RECEIVED - 0.01).ToString())
                .SendFees(0.01.ToString())
                .SetChange(_privateKey.ScriptPubKey)
                .BuildTransaction(true);

            var resTransaction = txBuilder.Verify(tx); //check fully signed
            if (resTransaction)
            {
                Console.WriteLine(tx.ToString());
                Console.ReadLine();
            }
        }
    }

    private static string ask(string message)
    {
        Console.WriteLine(message);
        return Console.ReadLine();
    }
}

这将产生一个 JSON 格式的交易,如下所示:

<code>{
  "hash": "0fc964cdedd26f1598503e9a0677cdad304d68de1f64b0e147bd25c7d99fc55e",
  "ver": 1,
  "vin_sz": 1,
  "vout_sz": 1,
  "lock_time": 0,
  "size": 192,
  "in": [
    {
      "prev_out": {
        "hash": "4644a8ac4531aa82c8de5a555d6ba83671b7f696aea01fd91c283e0829a28b1e",
        "n": 0
      },
      "scriptSig": "30450221008214a424a0488f1c9ac32855f6fa40ea3e88e929a774bff064fdffd221cc3b7b02204a8f2330c9ec5f6562bd132bc06bbf0d563343bae5cd1715d8c296ea29c0775e01 031f7fc0b8de7be2c82164b1f9eafa3cd3af78f284ecadabbdf1bc3959d3259c48"
    }
  ],
  "out": [
    {
      "value": "1.09000000",
      "scriptPubKey": "OP_DUP OP_HASH160 b34fad2260a6fb3e8e6390752de4761a98bdcc6f OP_EQUALVERIFY OP_CHECKSIG"
    }
  ]
}
</code>

为了使此交易能够传播,我们需要成为比特币网络的一部分。这可以通过两种方式完成:

  1. 作为比特币点对点网络上的完整节点参与。这要求您下载完整的区块链,以便验证传入的交易并完全生成传出的交易。然后,当您的计算机上有了完整的区块链(目前为 150GB+)后,您就可以开始与其他节点建立网络连接并参与其中。
  2. 通过一些公开可用的 API 进行参与。几年前,**Blockchain.info** 是我的首选,但我**转向了 BlockCypher**。

在本文中,我们将继续使用第二种方法——使用 BlockCypher。请注意,这会引入互联网连接。但是,考虑到您正在 CodeProject 上阅读本文,我相信您会宁愿传输一两个 MB 而不是下载 150 GB 来继续;)。别担心——另一个库已经准备就绪,可以处理大部分工作。

通过 C# 和 BlockCypher API 处理比特币

Boris Scheiman 编写了一个很棒的 C# 库,作为 BlockCypher API 的封装。去年我分叉了这个库并进行了一些改进。不幸的是,这些更改尚未在 NuGet 包中提供。我将使用的代码示例基于存储库中的最新代码,因此如果您执行 Install-Package BlockCypher,您可能会缺少某些功能。我将联系 Boris,看看他是否可以更新 NuGet,与此同时,请知悉示例中提供的 DLL 是从存储库中的最新代码构建的。

首先,您需要访问**https://www.blockcypher.com/** 并创建一个开发者帐户。处理 HTTP 调用是收费的,您对抗垃圾信息唯一的方法就是为请求分配帐户。当您准备好令牌后,就可以开始发出 API 调用并从比特币网络获取实时统计信息。让我们通过一个在先前示例中明显缺失的功能开始:检查余额。

class Example4
{
    public static async Task RunAsync(string[] args)
    {
        var privateKey = getPrivateKeyFromDisk();

        var api = new Blockcypher(MySettings.Default.BlockcypherToken, Endpoint.BtcTest3);
        var address = privateKey.PubKey.GetAddress(Network.TestNet).ToString();
        var balance = await api.GetBalanceForAddress(address);

        Console.WriteLine("Balance of {0} is {1} BTC", address, balance.FinalBalance.Btc);
    }
}

注意两个库处理 TestNet 的方式。即使在此之前我们与比特币网络没有任何联系,我们仍然能够创建有效的钱包地址并接收资金。这就是冷存储和纸钱包的工作原理——您完全离线生成密钥,然后只需向实时比特币网络提供钱包地址。这个概念用于一些非常棒的项目,例如比特币实体币。

现在,让我们生成一个将资金退还给水龙头钱包的发送交易。Blockcypher API 和库尽可能简化了交易发送。您只需要:

  1. 用于在本地签名交易的私钥
  2. 与您的私钥相关联的钱包地址,您将从该地址发送
  3. 目标钱包地址
  4. 您要发送的金额(以聪为单位)。-1 将允许您发送钱包中的所有资金。
class Example5
{
    public static async Task RunAsync(string[] args)
    {
        var privateKey = getPrivateKeyFromDisk();

        var api = new Blockcypher(MySettings.Default.BlockcypherToken, Endpoint.BtcTest3);
        var address = privateKey.PubKey.GetAddress(Network.TestNet).ToString();

        // -1 for satoshi "sweeps" input wallet transfering all BTC into new wallet
        // http://dev.blockcypher.com/#creating-transactions
        var sendingAmt = -1;

        var privateKeyBytes = privateKey.GetBitcoinSecret(Network.TestNet).ToBytes();
        var hexPrivate = Encoders.Hex.EncodeData(privateKeyBytes);

        var taskRes = await api.Send(address, "mws4hQ4dMkvdoXDzHyDTrsXf9ohPwj6J2b", hexPrivate, sendingAmt);
        if (taskRes.IsError)
        {
            foreach (var error in taskRes.Errors)
                Console.WriteLine(error);
        }
        else
        {
            Console.WriteLine("Transaction sent. Hash: {0}", taskRes.Transactions.Hash);
        }
    }
}

就是这样!BlockCypher 库使得创建和发送交易变得非常容易,因为它会自动引用未花费的输出。更不用说正确生成交易并将其传播到网络了。

当然,发送只是等式的一半。通过某种结账系统接收付款是大多数开发人员感兴趣的。那么,如何构建一个比特币结账系统呢?理论上,这非常简单:

  1. 您获取订单详情并生成一个用于接收付款的钱包地址(例如 1 BTC)。**确定性钱包**在这种情况下非常有用——它们允许您只创建一个私钥并派生出无限数量的公钥(以及因此的钱包地址)。
  2. 向客户显示钱包地址,允许他们发送资金以完成交易。
  3. 监控付款钱包地址以接收入站交易。一旦交易到达(并经过一定次数的确认),您就可以释放客户正在购买的产品。

过去,几乎所有 API 提供商(如 BlockCypher 和 Blockchain.info)都免费提供此服务。但正如您可以想象的——随着比特币的普及和区块链规模的增长——托管和运行免费结账变得无利可图。目前,我不知道有任何 API 提供商可以免费生成和监控转发地址。

幸运的是,另一家公司现在提供了一种解决方案,可以轻松接受几乎任何加密货币的付款。

Coinpayments - 使用 C# 接受比特币 (BTC)、莱特币 (LTC)、以太坊 (ETH)

披露——我偶尔会遇到 Coinpayments 的问题。与他们的开发团队沟通后,问题最终得到了解决。我希望他们能投入更多资源来支持希望与他们的系统集成的开发人员。我认为他们提供的服务很有价值,交易费用仅为 0.50%。显然,如果我不喜欢他们的平台,我不会在这里介绍它。

我已编写并开源了一个库来抽象他们的 C# API。这意味着您只需几行代码即可接受付款。大部分时间将花在设置您的 Coinpayments 帐户和配置它——您想接受的货币、IPN 处理器的位置等。让我们按照步骤进行:

  1. 创建帐户并登录**仪表板**。
  2. **在“币种设置”中,选择您愿意接受的加密货币。您可以找到包括比特币 (BTC)、莱特币 (LTC)、以太坊 (ETH) 等在内的许多币种。在我们的示例中,我们将使用莱特币 TestNet (LTCT)。**
  3. **转到“帐户设置”并选择名为“商家设置”的选项卡。在这里您需要:**
    - IPN 密钥:将其设置为 20 位以上的字母数字字符串。
    - IPN 验证方法:HMAC
    - IPN URL:当用户付款时,即时付款通知 (IPN) 将发送到此 URL。确保您的 Web 服务器已配置为接受入站 HTTP 连接(如果需要,请允许防火墙通过端口)。
  4. **转到“API 密钥”并生成新密钥。然后,单击“编辑权限”按钮。出于测试目的,您可以允许所有权限。至少,您需要 create_transaction 权限来生成付款钱包地址。**请**在 API 文档中阅读有关所有方法的更多信息**。

现在您已完成设置过程,您需要更新 CoinpaymentsApi 库中的变量,即:

  1. 公钥(来自 API 密钥页面)
  2. 私钥(来自 API 密钥页面)
  3. IPN 密钥(来自帐户设置,商家设置)
  4. 商家 ID(来自帐户设置)

由于这些变量在 .settings 文件中,您可以跨项目更改它们,以便支持各种环境(开发、暂存、生产)。此外,您可以将它们复制到您的 web.config 中,如果您正在构建 **ASP.NET** 网站,并在自动构建/部署期间即时更改它们。

现在,让我们看看允许我们为用户生成一个支付 10 美元 LTCT 的结账页面的代码。

protected async void btnBuy_Click(object sender, EventArgs e)
{
    var purchase = await CoinpaymentsApi.CreateTransaction(10, "USD", "LTCT", "peusa@outlook.com");
    Response.Redirect(purchase.Result.StatusUrl);
}

是的,信不信由你——两行代码。第一行初始化用户的交易,传递他的电子邮件,以便 Coinpayments 也可以向客户发送付款 URL。在第二行中,我们将客户重定向到付款 URL,以便他可以轻松扫描 QR 码并跟踪付款状态。

要发送付款,您可以使用**Litecoin Test Tools**。**此网页**允许您将最多 10 LTCT 发送到您想要的任何钱包,一次或多次交易。一旦付款进来,它将由我们的即时付款通知 (IPN) 处理程序处理。

public class IpnHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var req = IpnBase.Parse<IpnApi>(context.Request.Form);

        var hmac = context.Request.Headers["HMAC"];
        if (hmac == null || !req.SigIsValid(hmac))
        {
            response(context, HttpStatusCode.BadRequest, "Invalid HMAC / MerchantId");
            return;
        }
        
        if (checkForDuplicate(req))
        {
            response(context, HttpStatusCode.OK, "Duplicate transactions");
            return;
        }

        if (req.SuccessStatusLax() && req.IpnType == "api")
        {
            // TODO: Process payment as needed, release product
        }

        response(context, HttpStatusCode.OK, "1");
    }

    private bool checkForDuplicate(IpnApi req)
    {
        // TODO: Implement check against database if needed
        return false;
    }

    private void response(HttpContext context, HttpStatusCode statusCode, string text)
    {
        context.Response.StatusCode = (int)statusCode;
        context.Response.ContentType = "text/plain";
        context.Response.Write(text);
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

请注意,您可能需要设置您的 IIS / IIS Express 以允许外部连接到您的网站托管 IPN 处理程序。防火墙也是如此;尤其是在使用自定义端口的情况下。本文附带的代码中有一个示例 **ASP.NET** 网站,您可以使用自己的 Coinpayments 凭据运行和设置。

就是这样!您现在拥有了一个可以轻松接受任何加密货币支付的支付网关。

未来

需要注意的是,我们在此讨论的几乎所有加密货币都是开源的。因此,理论上,没有什么能阻止您访问它们各自的 GitHub 存储库并加入开发。实际上——我们讨论的是目前价值超过一些热门科技公司的金融系统。因此,在花费任何时间和精力进行编码之前,请务必联系现有的开发团队并查看问题跟踪器。

在我看来,毫无疑问,未来的金融系统将由类似加密货币当前正在做的事情所取代。这种变革当然不会容易。**经济自由是所有其他自由的基础**。我同意,大政府不太可能就说——当然,我们放弃对货币的控制。但是,与所有其他先进技术一样,去中心化的信任系统将为专注于它们的国家提供巨大的好处。**日本已经超过美国,成为第一大比特币交易市场**。**俄罗斯在 2014 年因美国领导的金融制裁而陷入困境后,正在考虑通过以太坊实现其金融系统的现代化**。

对我来说,加密货币的发展和采用将类似于互联网的发生。一些国家试图监管和控制互联网。有些国家甚至直接禁止它。结果,它们只扼杀了自己的发展。最终,创新和个人自由将永远占上风。

2017 年底更新

本文的第一版是在 2017 年 9 月发布的。比特币价格在从 4500 美元回落至 3500 美元后刚刚反弹。现在是 12 月底,在从 20,000 美元的高位健康回调后,BTC 稳定在 14,500 美元。

令人惊讶的是,加密货币领域的状况一如既往。我记得比特币突破 100 美元时,人们大喊它是人类历史上最大的骗局。然后有人预测 100 美元之后下一个目标是 10,000 美元。老实说,我一直觉得这两个阵营都有些“疯狂”。我确实相信比特币背后的技术(而且仍然相信),但作为一个深入参与该领域的人,我也非常清楚存在许多不那么公开的问题。

因此,这种持续不断的似曾相识的感觉让我感到非常奇怪。现在价格已经飙升,有更多的人大喊“ Monopoly money”!多头则反驳称 1 BTC 将达到 1,000,000 美元。我个人……我不知道。我可以看到两种情况都可能发生。Satoshi 在被问及比特币是否有价值且会被使用时,他自己说得很好:

我确信,20 年后,要么交易量会非常大,要么根本没有交易量。

不幸的是,似乎只有通过极端才能达到中间地带。然而,重要的是,BTC/USD 的汇率在此时此刻并不重要。您可以达成分布式共识的无需信任的点对点网络将被实现,并将彻底改变我们社会的各个方面。在 1990 年代互联网普及之前,我甚至无法想象我可以写一篇文章,然后将其分享给世界各地成千上万的人,而无需通过成熟出版商的审核过程。即便如此,我的文章也只能被购买杂志的少数人阅读。然后像 CodeProject 这样的网站出现了。

无论好坏,金钱现在也将如此。您无需与守门人打交道,而是可以自由地将“金钱”发送给任何人,并接收来自任何人的“金钱”。没有银行账户,没有支付处理器……即使是国家也无法冻结您的资金。所有这些都通过简单的私钥和公钥对实现。当然,这是如何使用这种新获得自由的问题。可以理解很多人感到害怕。我希望人类将以与我们处理大多数新获得自由相同的方式来处理它。

为了说明去年情况如何,这是 Coinmarketcap.com 的第一个截图,日期为 2016-12-25:

一年后,2017-12-25,情况变成了这样:

我希望 2018 年对加密货币来说就像 1998 年对互联网一样。希望我们还有一两年的时间才会有大跌;)没有人喜欢经历漫长的复苏。但无论发生什么,技术都会得以生存。我们将看到区块链被应用到慈善事业中,以便捐助者可以透明地看到他们的钱花在了哪里。我们将看到大规模的**工作量证明**系统,它们消耗的电力甚至比今天的比特币还要多。密码学复兴将继续,我们将从中受益数十年。

所以,如果您是程序员,请忘记 BTC/USD 的汇率,忽略炒作。像我们之前的人们在 1998 年、1988 年、1978 年一样,点燃您的 IDE。我们还有一些代码要写……

猫咪税

未来更新通知

我计划在未来几个月内更新这篇文章。它已经是一篇很长的读物了,所以我需要回顾一些部分并排除一些主题。

在此期间,欢迎评论和提问——如果我能提供任何帮助,我将不胜感激。如果您在这里联系不上我,可以尝试**LinkedIn**……尤其是在您在加密货币领域做一些有趣的事情时。

© . All rights reserved.