BitcoinJ中文文档入门(机翻)
为学习而使用谷歌翻译的文档,有很多不足之处,还望见谅。——XS
原文地址:https://bitcoinj.org/getting-started-java#sending-coins
请配合谷歌搜索+AI工具一起使用效果更好。
初始设置
bitcoinj内置了日志和断言。无论是否-ea
指定标志,默认情况下都会检查断言。日志由SLF4J库处理。它允许您选择您喜欢使用的日志系统,例如 JDK 日志、Android 日志等。默认情况下,我们使用简单的记录器,它将大多数感兴趣的内容打印到 stderr。您可以通过切换 lib 目录中的 jar 文件来选择新的记录器。
bitcoinj使用 Gradle 作为其构建系统并通过 git 分发。您可以使用源代码/jar 下载,但直接从源代码存储库获取更安全。
要获取代码并安装它,请获取Maven或Gradle,并将其添加到您的路径中。还要确保您已安装 Git。您的 Java IDE 可能也有一些 Maven/Gradle 和 Git 集成,但通过命令行使用它们仍然非常有用。
现在获取最新版本的代码。您可以使用使用 Maven或使用 Gradle页面上的说明- 只需运行那里的命令,您就会获得正确版本的代码(除非此网站本身受到攻击)。这旨在防止镜像或源下载受到攻击 - 因为 git 使用源树哈希工作,如果您以正确的方式获取源哈希,则保证您最终获得正确的代码。
您可以在此处阅读完整的计划。
1 | <dependencies> |
如果你没有这个依赖可以选择1.15.9这个版本。
基本结构
bitcoinj应用程序使用以下对象:
NetworkParameters
选择您所在的网络(生产或测试)的实例。- 一个
Wallet
用于存储您的ECKey
s 和其他数据的实例。 PeerGroup
管理网络连接的实例。- 一个
BlockChain
管理使比特币正常运行的共享全局数据结构的实例。 BlockStore
将区块链数据结构保存在某处(例如磁盘)的实例。WalletEventListener
实现,接收钱包事件。
为了简化设置,还有一个WalletAppKit
对象可以创建上述对象并将它们连接在一起。虽然您可以手动执行此操作(对于大多数“真实”应用,您都会这样做),但此演示应用展示了如何使用应用套件。
让我们看一下代码并看看它是如何工作的。
我们只需要会使用这个工具就行了,如果想要深究,请自行查看源码。
设置
我们使用实用函数来配置日志记录,使其具有更紧凑、更简洁的日志格式。然后我们检查命令行参数。
1 | BriefLogFormatter.init(); |
然后我们根据可选的命令行参数选择要使用的网络:
1 | // Figure out which network we should connect to. Each one gets its own set of files. |
有多个独立的比特币网络:
- 人们买卖商品的主要或“生产”网络
- 公共测试网络(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);
// 在这里你可以继续执行更多操作,例如连接比特币节点、发起交易等...
}
}代码说明:
- 日志初始化:
BriefLogFormatter.init();
用于初始化日志系统,将日志输出格式设置为更简洁的方式。- 命令行参数检查:程序要求输入两个参数,第一个参数可能是比特币地址,第二个参数决定网络(
testnet
、regtest
或mainnet
)。- 选择比特币网络:根据第二个命令行参数来确定连接哪个网络。
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):你可以在本地快速生成新区块,用于本地的开发和测试,不依赖外部网络。
- 主网:是实际的生产环境,只有真正的比特币在这里流通。
这个代码模板可以作为你构建比特币应用的基础。在实际开发时,可以在其基础上添加更多逻辑,例如创建交易、监听钱包地址等。
…….这是哪位前辈写的代码哦,主方法传参,这是古老的方式真的有人用吗???
钥匙和地址
比特币交易通常将钱发送到公共椭圆曲线密钥。发送者创建一个包含收件人地址的交易,其中地址是其公钥哈希的编码形式。然后,收件人用自己的私钥签署交易以认领硬币。密钥用类表示ECKey
。ECKey
可以包含私钥,也可以只包含缺少私钥部分的公钥。请注意,在椭圆曲线密码学中,公钥是从私钥派生出来的,因此知道私钥本质上意味着也知道公钥。这与您可能熟悉的其他一些加密系统(如 RSA)不同。
地址是公钥的文本编码。实际上,它是公钥的 160 位哈希值,带有一个版本字节和一些校验和字节,使用比特币特有的base58编码编码成文本。Base58 旨在避免在书写时可能相互混淆的字母和数字,例如 1 和大写字母 i。
1 | // Parse the address given as the first parameter. |
由于地址对密钥所针对的网络进行编码,因此我们需要在此处传入网络参数。第二个参数只是用户提供的字符串。如果无法解析或针对错误的网络,则构造函数将抛出异常。
简单来说:生成私钥,私钥推导公钥,公钥推导地址。地址和公钥无法推导私钥。
钱包应用套件
bitcoinj 由多个层组成,每个层的操作级别都低于上一个层。一个典型的想要发送和接收资金的应用程序至少需要一个BlockChain
、一个BlockStore
、一个PeerGroup
和一个Wallet
。所有这些对象都需要相互连接,以便数据正确流动。阅读“事物如何组合在一起”以了解有关数据如何通过基于 bitcoinj 的应用程序流动的更多信息。
为了简化这个过程(这通常相当于样板),我们提供了一个名为的高级包装器WalletAppKit
。它将 bitcoinj 配置为简化的付款验证模式(而不是完整验证),这是目前最合适的模式,除非您是专家并希望尝试(不完整、可能有缺陷的)完整模式。它提供了一些简单的属性和钩子,让您可以修改默认配置。
1 | // Start up a basic app using a class that automates some boilerplate. Ensure we always have at least one key. |
该套件有三个参数 - 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,所以我修改了代码。