HBitcoin:C#高级比特币钱包库 - 保护您的财产安全

时间:2022-05-03
本文章向大家介绍HBitcoin:C#高级比特币钱包库 - 保护您的财产安全,主要内容包括背景、介绍、一个比特币钱包能做什么?、如何存储密钥?、动态地址、HD(Hierarchical Deterministic)钱包(分层确定性钱包)、关于比特币私钥管理的优化建议、多说无益,让我们在代码中见真章!、如何解密钱包?、创建时间、安全账户和Hd路径类型、结语、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

背景

在几年前,我有写一个叫HiddenBitcoin的钱包库,那是我第一次接触跟比特币相关的项目。多年来我一直在复用这个项目中的代码并不停的改进优化它,虽然到最后我任然没能成功发布一个稳定的版本。可以说这次的HBitcoin就是HiddenBitcoin的后继者了,当初我写HiddenBitcoin的动机是为了学习,然而现在我做HBitcoin是因为某种需求。我一直在不同的Bitcoin项目中重用我的代码。这已经发展到以至于我在这里公布的主类有6个不同的版本。因此,现在是时候把这些代码整理好,并将它们打包成一个我可以随时通过NuGet包安装使用的Bitcoin库。

介绍

HBitcoin库是在NBitcoin的基础上写的。它介于HBitcoin APIBlockchain API之间。它的灵活性要比后者高,但比前者少。如果您要深入的学习Bitcoin,您很有必要去翻阅The C#Bitcoin Book(比特币C#)。

在这学完了这三个部分教程后,您将能够创建一个比特币钱包。

提示 :虽然使用API编程可以帮助开发人员快速开发程序,但开发人员的创新也会被局限于API中。

Nicolas DorierBitcoin的核心开发者,NBitcoinC# Bitcoin库的创建者。在使用本文章的代码库时请记住这个提示。

一个比特币钱包能做什么?

一个比特币钱包有三个关键功能:

  1. 安全地存储密钥并管理对密钥的访问
  2. 监视这些密钥的交易
  3. 创建交易并将它们广播到网络

如何存储密钥?

比特币地址,私钥

比特币地址你应该很熟悉了,你可以往里面存比特币,并且你可以使用相应的私钥来花掉这些比特币。

动态地址

你可能也熟悉钱比特币包并知道他们会为不同的交易生成不同的动态地址。你可能会问为什么这些交易不只使用一个地址?这是为了保护隐私,区块链是公开记账,因此任何人都可以很容易地看到一个比特币地址的收入和支出。因此,避免地址重用是一个更好的主意,尽管这并不能完全解决隐私问题,但这无疑提高了安全性。

HD(Hierarchical Deterministic)钱包(分层确定性钱包)

那么问题又来了,我们如何管理这么多的密钥?存储,监控并花费它们?请注意使用多个密钥会大大地提高比特币钱包的复杂性。

好在HD钱包结构使我们能够只存储一个密钥并从中派生出其他密钥。为了保持我们对NBitcion术语的一致性,我们称之为"BitcoinExtKey"。

关于比特币私钥管理的优化建议

有四种BIP(Bitcoin Improvement Proposal,比特币改进建议)是我们要注意的,分别是BIP32,BIP38,BIP43,BIP44。为了简单起见,你可以把BIP32和BIP38视为相同的BIP。它们定义了一些底层的东西,比如如何派生和加密密钥。这些在NBitcoin中都实现了。BIP43和BIP44建立在BIP32-38的基础上之上,并定义了更多的东西,如关于如何组织和使用密钥的结构。有几种钱包实现了BIP43-44。一开始我也在尝试实现它们,但随后我就决定不用它们了,因为它们不仅让我的项目变得过于复杂,而且我也无法将它应用到以后的一些边缘案例中来。那么这样一来,我就可以为你们提供更简洁的接口了。

多说无益,让我们在代码中见真章!

项目设置

  1. 启动一个新的.NET Core项目
  2. 添加NuGet中的HBitcoin
var network = Network.Main;
//下面输入一个强壮的密码,如"password"
var password = "password";

//用一个密码在指定的网络中的指定路径中创建Safe类
//Safe类用来管理你的私钥种子
//Safe可以自动处理序列
//创建一个Safe类后,它会自动存储在指定的路径中
Mnemonic mnemonic;
Safe safe = Safe.Create(out mnemonic, password, @"WalletsWallet.json", network);

//创建Safe类时会同时创建mnemonic(助记符)类,你可以用它来恢复(或复制)Safe类
//你只会在一开始的时候才会接触到mnemonic类
Console.WriteLine(mnemonic);

// 把Safe类恢复到另一个路径中
Safe recoveredSafe = Safe.Recover(mnemonic, password, @"WalletsSameWallet.json", network);

// 解密/加载一个现存的Safe/钱包文件
Safe loadedSafe = Safe.Load(password, @"WalletshiddenWallet.hid");

// 在加载了一个safe后我们最好检查一下它的网络是否正确
if (network != loadedSafe.Network)
	throw new Exception("Wrong network");

// 列出10个地址
for (var i = 0; i < 10; i++)
{
	Console.WriteLine(safe.GetAddress(i));
}

如何解密钱包?

你只需要获得(密码助记符(mnemonic))密码钱包文件)

然后,您就能调用Recover of Load了。

谁知道密码?用户。

谁知道助记符(mnemonic)?正常情况下,用户可以把它写在一张纸上放在家里作为备份。

谁知道钱包文件?正常情况下,它存储在用户的硬盘上。

其他钱包

通常,只用助记符来恢复一个钱包就够了。但我觉得这不安全,所以我们的这个钱包不能像那样。

创建时间

var safe2 = Safe.Recover(mnemonic, password, "Wallet.json", network,
	  creationTime: DateTimeOffset.ParseExact("2017-02-20", 
	  "yyyy-MM-dd", CultureInfo.InvariantCulture));
Console.WriteLine(safe.CreationTime);

当你创建一个钱包时,它也会自动保存它的创建时间,这对于编写一些SPV钱包来说非常方便。因此,当您恢复钱包时,您还可以使用创建时间作为参数,如果您没有不填写这个参数,它会默认为可能的最早创建时间,这个时间是硬编码的:

public static DateTimeOffset EarliestPossibleCreationTime
	=> DateTimeOffset.ParseExact("2017-02-19", 
	"yyyy-MM-dd", CultureInfo.InvariantCulture);

这是我第一次介绍创建时间这个概念的日期。如果您没有尝试使用早些的创建时间来恢复钱包,则系统会默认使用EarliestPossibleCreationTime。

安全账户和Hd路径类型

var alice = new SafeAccount(id: 2);
safe.GetAddress(index: 1, hdPathType: HdPathType.Receive, account: alice);
safe.GetPrivateKey(index: 1, hdPathType: HdPathType.Receive, account: alice);

你可以随意地创建账户。在上面的代码中,我创建了id = 2的Alice帐号并恢复了一些她的密钥。

你也可以注意到我指定了HdPathType为receive。如果你没有指定其他参数的话将会用这个作为默认值。请注意,在某些术语中使用了“external(外)”和“internal(内)”来接收和更改地址。当您收钱和消费您钱包的资金时,这是很重要的。在接收资金时,一定要用HdPathType.Receive参数;在花费资金时一定要用HdPathType.Change参数来改变地址。这将使您的钱包更加安全。当然你还可以反复使用一个相同的地址来交易,我当然反对这么做。

结语

我建议你不要应用你自己的密钥存储方案,除非你真的胸有成竹。如果由于某种原因,我的钱包方案的不足以满足您的需求,你可以在GitHub发问,我会尽我所能帮助你。