0%

BitcoinJ中文文档入门(机翻)

BitcoinJ中文文档入门(机翻)

为学习而使用谷歌翻译的文档,有很多不足之处,还望见谅。——XS

原文地址:https://bitcoinj.org/getting-started-java#sending-coins

请配合谷歌搜索+AI工具一起使用效果更好。

初始设置

bitcoinj内置了日志和断言。无论是否-ea指定标志,默认情况下都会检查断言。日志由SLF4J库处理。它允许您选择您喜欢使用的日志系统,例如 JDK 日志、Android 日志等。默认情况下,我们使用简单的记录器,它将大多数感兴趣的内容打印到 stderr。您可以通过切换 lib 目录中的 jar 文件来选择新的记录器。

bitcoinj使用 Gradle 作为其构建系统并通过 git 分发。您可以使用源代码/jar 下载,但直接从源代码存储库获取更安全。

要获取代码并安装它,请获取MavenGradle,并将其添加到您的路径中。还要确保您已安装 Git。您的 Java IDE 可能也有一些 Maven/Gradle 和 Git 集成,但通过命令行使用它们仍然非常有用。

现在获取最新版本的代码。您可以使用使用 Maven使用 Gradle页面上的说明- 只需运行那里的命令,您就会获得正确版本的代码(除非此网站本身受到攻击)。这旨在防止镜像或源下载受到攻击 - 因为 git 使用源树哈希工作,如果您以正确的方式获取源哈希,则保证您最终获得正确的代码。

您可以在此处阅读完整的计划

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.bitcoinj</groupId>
<artifactId>bitcoinj-core</artifactId>
<version>0.16</version> <!-- 可以根据需要更改为最新版本 -->
</dependency>
</dependencies>

如果你没有这个依赖可以选择1.15.9这个版本。

基本结构

bitcoinj应用程序使用以下对象:

  • NetworkParameters选择您所在的网络(生产或测试)的实例。
  • 一个Wallet用于存储您的ECKeys 和其他数据的实例。
  • PeerGroup管理网络连接的实例。
  • 一个BlockChain管理使比特币正常运行的共享全局数据结构的实例。
  • BlockStore将区块链数据结构保存在某处(例如磁盘)的实例。
  • WalletEventListener实现,接收钱包事件。

为了简化设置,还有一个WalletAppKit对象可以创建上述对象并将它们连接在一起。虽然您可以手动执行此操作(对于大多数“真实”应用,您都会这样做),但此演示应用展示了如何使用应用套件。

让我们看一下代码并看看它是如何工作的。

我们只需要会使用这个工具就行了,如果想要深究,请自行查看源码。

设置

我们使用实用函数来配置日志记录,使其具有更紧凑、更简洁的日志格式。然后我们检查命令行参数。

1
2
3
4
5
BriefLogFormatter.init();
if (args.length < 2) {
System.err.println("Usage: address-to-send-back-to [regtest|testnet]");
return;
}

然后我们根据可选的命令行参数选择要使用的网络:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Figure out which network we should connect to. Each one gets its own set of files.
NetworkParameters params;
String filePrefix;
if (args[1].equals("testnet")) {
params = TestNet3Params.get();
filePrefix = "forwarding-service-testnet";
} else if (args[1].equals("regtest")) {
params = RegTestParams.get();
filePrefix = "forwarding-service-regtest";
} else {
params = MainNetParams.get();
filePrefix = "forwarding-service";
}

有多个独立的比特币网络:

  • 人们买卖商品的主要或“生产”网络
  • 公共测试网络(testnet)会不时重置,以便我们尝试新功能。
  • 回归测试模式,这不是一个公共网络,需要您自己使用-regtest标志运行比特币守护进程。

每个网络都有自己的创世块、自己的端口号和自己的地址前缀字节,以防止您意外尝试跨网络发送硬币(这是行不通的)。这些事实被封装在一个NetworkParameters单例对象中。如您所见,每个网络都有自己的类,您可以NetworkParameters通过调用get()其中一个对象来获取相关对象。

强烈建议您在测试网上或使用 regtest 模式开发软件。如果您不小心丢失了测试币,也没什么大不了的,因为它们毫无价值,您可以从TestNet 水龙头免费获得很多。使用完后,请确保将硬币送回水龙头,以便其他人也可以使用它们。

"bitcoin-cli -regtest -generate"在 regtest 模式下没有公共基础设施,但是您可以在运行 regtest 模式 bitcoind 的同一台机器上随时获取新区块,而无需等待新区块。

我询问了一下GPT这段怎么样解释,并且要求写一段代码:

你描述的代码片段是一个比特币转发服务(或类似应用)的配置代码,主要涉及日志格式设置和选择比特币网络(主网、测试网或回归测试模式)。我们可以根据命令行参数决定连接到哪个比特币网络。

下面是一个具体的 Java 代码示例,演示了你所提到的 BriefLogFormatter.init() 日志配置、命令行参数处理以及选择比特币网络的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.utils.BriefLogFormatter;

public class BitcoinNetworkExample {

public static void main(String[] args) {
// 初始化日志系统,使日志输出简洁
BriefLogFormatter.init();

// 检查命令行参数,确保至少有两个参数传入
if (args.length < 2) {
System.err.println("Usage: address-to-send-back-to [regtest|testnet|mainnet]");
return;
}

// 根据命令行参数确定使用哪个比特币网络
NetworkParameters params;
String filePrefix;
if (args[1].equals("testnet")) {
params = TestNet3Params.get(); // 选择测试网
filePrefix = "forwarding-service-testnet";
} else if (args[1].equals("regtest")) {
params = RegTestParams.get(); // 选择回归测试模式
filePrefix = "forwarding-service-regtest";
} else {
params = MainNetParams.get(); // 选择主网(生产网络)
filePrefix = "forwarding-service-mainnet";
}

// 输出所选网络信息
System.out.println("Selected network: " + params.getId());
System.out.println("File prefix: " + filePrefix);

// 在这里你可以继续执行更多操作,例如连接比特币节点、发起交易等...
}
}

代码说明:

  1. 日志初始化BriefLogFormatter.init(); 用于初始化日志系统,将日志输出格式设置为更简洁的方式。
  2. 命令行参数检查:程序要求输入两个参数,第一个参数可能是比特币地址,第二个参数决定网络(testnetregtestmainnet)。
  3. 选择比特币网络:根据第二个命令行参数来确定连接哪个网络。
    • TestNet3Params.get():连接到测试网(testnet3)。
    • RegTestParams.get():连接到回归测试网络(regtest)。
    • MainNetParams.get():连接到主网(production/mainnet)。

使用方式:

假设编译为 BitcoinNetworkExample 类,在命令行运行时可以输入如下命令:

1
java BitcoinNetworkExample some-bitcoin-address testnet

这个命令会选择测试网,并输出网络信息。

如果你运行:

1
java BitcoinNetworkExample some-bitcoin-address regtest

这个命令会选择回归测试模式。

实际用途:

  • 测试网:用于在公共测试网络上进行开发,测试币可以从水龙头获得,丢失了也无所谓。
  • 回归测试模式(regtest):你可以在本地快速生成新区块,用于本地的开发和测试,不依赖外部网络。
  • 主网:是实际的生产环境,只有真正的比特币在这里流通。

这个代码模板可以作为你构建比特币应用的基础。在实际开发时,可以在其基础上添加更多逻辑,例如创建交易、监听钱包地址等。

…….这是哪位前辈写的代码哦,主方法传参,这是古老的方式真的有人用吗???

钥匙和地址

比特币交易通常将钱发送到公共椭圆曲线密钥。发送者创建一个包含收件人地址的交易,其中地址是其公钥哈希的编码形式。然后,收件人用自己的私钥签署交易以认领硬币。密钥用类表示ECKeyECKey可以包含私钥,也可以只包含缺少私钥部分的公钥。请注意,在椭圆曲线密码学中,公钥是从私钥派生出来的,因此知道私钥本质上意味着也知道公钥。这与您可能熟悉的其他一些加密系统(如 RSA)不同。

地址是公钥的文本编码。实际上,它是公钥的 160 位哈希值,带有一个版本字节和一些校验和字节,使用比特币特有的base58编码编码成文本。Base58 旨在避免在书写时可能相互混淆的字母和数字,例如 1 和大写字母 i。

1
2
// Parse the address given as the first parameter.
forwardingAddress = new Address(params, args[0]);

由于地址对密钥所针对的网络进行编码,因此我们需要在此处传入网络参数。第二个参数只是用户提供的字符串。如果无法解析或针对错误的网络,则构造函数将抛出异常。

简单来说:生成私钥,私钥推导公钥,公钥推导地址。地址和公钥无法推导私钥。

钱包应用套件

bitcoinj 由多个层组成,每个层的操作级别都低于上一个层。一个典型的想要发送和接收资金的应用程序至少需要一个BlockChain、一个BlockStore、一个PeerGroup和一个Wallet。所有这些对象都需要相互连接,以便数据正确流动。阅读“事物如何组合在一起”以了解有关数据如何通过基于 bitcoinj 的应用程序流动的更多信息。

为了简化这个过程(这通常相当于样板),我们提供了一个名为的高级包装器WalletAppKit。它将 bitcoinj 配置为简化的付款验证模式(而不是完整验证),这是目前最合适的模式,除非您是专家并希望尝试(不完整、可能有缺陷的)完整模式。它提供了一些简单的属性和钩子,让您可以修改默认配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Start up a basic app using a class that automates some boilerplate. Ensure we always have at least one key.
kit = new WalletAppKit(params, new File("."), filePrefix) {
@Override
protected void onSetupCompleted() {
// This is called in a background thread after startAndWait is called, as setting up various objects
// can do disk and network IO that may cause UI jank/stuttering in wallet apps if it were to be done
// on the main thread.
if (wallet().getKeyChainGroupSize() < 1)
wallet().importKey(new ECKey());
}
};

if (params == RegTestParams.get()) {
// Regression test mode is designed for testing and development only, so there's no public network for it.
// If you pick this mode, you're expected to be running a local "bitcoind -regtest" instance.
kit.connectToLocalHost();
}

// Download the block chain and wait until it's done.
kit.startAsync();
kit.awaitRunning();

该套件有三个参数 - NetworkParameters(库中的几乎所有 API 都需要这个)、用于存储文件的目录以及作为任何创建文件前缀的可选字符串。如果您在同一目录中有多个不同的 bitcoinj 应用程序,而您希望将它们分开,那么这将非常有用。在这种情况下,文件前缀是“forwarding-service”加上网络名称,如果不是主网络(请参阅上面的代码)。

它还提供了一个可重写的方法,我们可以在其中放入自己的代码,以自定义它为我们创建的对象。我们在这里重写它。请注意,appkit 实际上将在后台线程上创建和设置对象,因此onSetupCompleted也是从后台线程调用的。

这里,我们只需检查钱包是否至少有一个密钥,如果没有,则添加一个新密钥。如果我们从磁盘加载钱包,那么当然不会采用此代码路径。

接下来,我们检查是否正在使用 regtest 模式。如果是,那么我们告诉工具包仅连接到预计在 regtest 模式下运行 bitcoind 的本地主机。

最后,我们调用kit.startAsync()WalletAppKit是一个Guava 服务。 Guava 是 Google 广泛使用的实用程序库,它为标准 Java 库增加了一些有用的附加功能。 服务是一个可以启动和停止(但只能启动一次)的对象,您可以在它完成启动或关闭时收到回调。 您也可以阻止调用线程,直到它启动awaitRunning(),这就是我们在这里所做的。

WalletAppKit当区块链完全同步时,它将被视为已启动,有时可能需要一段时间。您可以了解如何加快速度,但对于玩具演示应用程序,无需实施任何额外的优化。

该套件具有访问器,可用于访问其配置的底层对象。在类启动或启动过程中,您无法调用这些访问器(它们将断言),因为不会创建对象。

应用程序启动后,您会注意到应用程序运行的目录中有两个文件:一个 .wallet 文件和一个 .spvchain 文件。它们是一起的,不能分开。

我使用Testnet3并且重新询问了GPT:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.bitcoinj.core.Address;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.script.Script;
import org.bitcoinj.wallet.KeyChainGroup;
import org.bitcoinj.wallet.Wallet;
public class BitcoinWallet {
public static void main(String[] args) {
// 使用 TestNet3 网络配置
NetworkParameters params = TestNet3Params.get();
// 创建一个空的钱包
Wallet wallet = new Wallet(params, KeyChainGroup.createBasic(params));
// 检查钱包是否有至少一个密钥,如果没有则创建
if (wallet.getKeyChainGroupSize() < 1) {
// 生成一个新的密钥
wallet.importKey(new ECKey());
}
// 获取钱包中的第一个 ECKey
ECKey ecKey = wallet.getImportedKeys().get(0);
System.out.println("PrivKey: " + ecKey.getPrivKey());
System.out.println("PrivateKeyAsHex: " + ecKey.getPrivateKeyAsHex());
System.out.println("PrivateKeyAsWiF: " + ecKey.getPrivateKeyAsWiF(params));
// 获取地址
System.out.println("Address.fromKey: " + Address.fromKey(params, ecKey, Script.ScriptType.P2PKH).toString());
System.out.println("比特币钱包应用已启动并运行!使用的是Testnet3网络。");
}
}

代码本身没有问题,但是我删除了同步区块的操作,kit必须同步区块后才能进行下一步操作。

所以,我不准备使用Kit,所以我修改了代码。