`

java开发比特币类库bitcoinj入门指南

阅读更多

bitcoinj是一个使用比特币协议的库。它可以维护钱包,发送/接收交易而无需比特币核心的本地副本,并具有许多其他高级功能。它是用Java实现的,但可以通过任何JVM兼容语言中使用:包括Python和JavaScript中的示例。

它附带完整的文档,并建立了许多大型,众所周知的比特币应用程序和服务。下面我们来看看如何使用它。

初始设置

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

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

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

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

你可以在这里阅读完整的程序。

基本结构

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

  • NetworkParameters实例,用于选择你所在的网络(生产或测试)。
  • 用于存储ECKeys和其他数据的Wallet实例。
  • 用于管理网络连接的PeerGroup实例。
  • 一个BlockChain实例,它管理共享的全局数据结构,使比特币工作。
  • 一个BlockStore实例,它将块链数据结构保存在某个位置,就像在磁盘上一样。
  • WalletEventListener实现,用于接收钱包交易。

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

让我们看看代码,看看它是如何工作的。

设置

我们使用实用程序函数将log4j配置为具有更紧凑,更简洁的日志格式。然后我们检查命令行参数。

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

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

// 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单例对象中。如你所见,每个网络都有自己的类,你可以通过在其中一个对象上调用get()来获取相关的NetworkParameters对象。

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

在regtest模式下,没有公共基础设施,但是你可以随时获得一个新的块而不必等待一个通过在regtest模式bitcoind运行的同一台机器上运行bitcoind -regtest setgenerate true

密钥和地址

比特币交易通常将钱汇入公共椭圆曲线键。发件人创建包含收件人地址的交易,其中地址是其公钥哈希的编码形式。接收者然后签署一个交易,用他们自己的私钥声明比特币。密钥用ECKey类表示。ECKey可以包含私钥,或只包含缺少私有部分的公钥。请注意,在椭圆曲线加密中,公钥是从私钥派生的,因此知道私钥本身也意味着知道公钥。这与你可能熟悉的其他一些加密系统不同,例如RSA。

地址是公钥的文本编码。实际上,它是公钥的160位hash,具有版本字节和一些校验和字节,使用名为base58的比特币特定编码编码到文本中。Base58旨在避免在写下时可能相互混淆的字母和数字,例如1和大写i。

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

由于地址对要为其使用密钥的网络进行编码,因此我们需要在此处传递网络参数。第二个参数只是用户提供的字符串。如果构造函数不可解析或者网络错误,它将抛出钱包应用套件例外。

bitcoinj由各种层组成,每层都在比最后一层更低的层次上运行。想要发送和接收资金的典型应用程序至少需要BlockChainBlockStorePeerGroupWallet。所有这些对象需要相互连接,以便数据正确流动。阅读如何融合在一起,了解有关数据如何通过基于bitcoinj的应用程序流动的更多信息。

为了简化这个过程(通常是样板文件),我们提供了一个名为WalletAppKit的高级打包器。它在简化的支付验证模式(而不是完全验证)中配置bitcoinj,这是此时选择的最合适的模式。除非你是专家并且希望尝试(不完整的,可能是错误的)完整模式,它提供了一些简单的属性和钩子,允许你修改默认配置。

将来,可能会有更多的工具包为不同类型的应用程序配置bitcoinj,这些应用程序可能有不同的需求。但就目前而言,只有一个。

// 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是Google广泛使用的实用程序库,它增加了标准Java库以及一些有用的附加功能。服务是一个可以启动和停止的对象(但只能启动一次),并且可以在完成启动或关闭时接收回调。你也可以阻止调用线程,直到它以awaitRunning()启动,这就是我们在这里所做的。

当块链完全同步时,WalletAppKit将认为自己已经启动,这有时需要一段时间。你可以了解如何加快速度,但对于玩具演示应用程序,不需要实现任何额外的优化。

该工具包上有访问器,可以访问它配置的底层对象。在类启动或启动过程之前,你不能调用它们(它们将断言),因为不会创建对象。

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

处理交易

我们想知道什么时候收到钱,所以我们可以转发它。这是一个交易,与bitcoinj中的大多数Java API一样,你通过注册事件侦听器event listeners来了解交易,事件侦听器只是实现接口的对象。库中有一些交易监听器接口:

  • WalletEventListener:用于发生在钱包中的事情。
  • BlockChainListener:用于与块链相关的交易。
  • PeerEventListener:用于与网络中的对等方相关的交易。
  • TransactionConfidence.Listener:用于与交易具有的回滚安全级别相关的交易。

大多数应用程序不需要使用所有这些。因为每个接口都提供一组相关交易,你可能并不关心所有这些交易。

kit.wallet().addCoinsReceivedEventListener(new WalletCoinsReceivedEventListener() {
    @Override
    public void onCoinsReceived(Wallet w, Transaction tx, Coin prevBalance, Coin newBalance) {
        // Runs in the dedicated "user thread".
    }
});

bitcoinj中的交易在专用的后台线程中运行,该线程仅用于运行事件侦听器,称为user thread用户线程。这意味着它可以与应用程序中的其他代码并行运行,如果你正在编写GUI应用程序,则意味着你不能直接修改GUI,因为你不在GUI或main主线程中。但是,事件侦听器本身不需要是线程安全的,因为交易将按顺序排队并执行。你也不必担心使用多线程库时通常会出现的许多其他问题(例如,重新进入库是安全的,并且可以安全地执行阻塞操作)。

关于编写GUI应用程序的说明

大多数小工具工具包(如Swing,JavaFX或Android)都具有所谓的线程关联,这意味着你只能在单个线程中使用它们。要从后台线程返回到主线程,通常会将闭包传递给某个实用程序函数,该函数调度在GUI线程空闲时运行的闭包。

为了简化使用bitcoinj编写GUI应用程序的任务,你可以在注册事件侦听器listener时指定任意Executor。将要求该执行程序运行事件侦听器。默认情况下,这意味着将给定的Runnable传递给用户线程,但你可以像这样覆盖:

Executor runInUIThread = new Executor() {
    @Override public void execute(Runnable runnable) {
        SwingUtilities.invokeLater(runnable);   // For Swing.
        Platform.runLater(runnable);   // For JavaFX.

        // For Android: handler was created in an Activity.onCreate method.
        handler.post(runnable);  
    }
};

kit.wallet().addEventListener(listener, runInUIThread);11

现在,listener上的方法将自动在UI线程中调用。

因为这可能会重复且烦人,你还可以更改默认执行程序,因此所有交易始终在你的UI线程上运行:

Threading.USER_THREAD = runInUIThread;

在某些情况下,bitcoinj可以非常快速地生成大量交易,这在将块链与具有大量交易的钱包同步时是典型的,因为每个交易都可以生成交易可信度confidence更改交易(因为它们隐藏的很深)。未来钱包交易的工作方式很可能会改变以避免这个问题,但是现在这就是API的工作方式。如果用户线程落后,则当事件侦听器listener调用在堆上排队时,可能会发生内存膨胀。为避免这种情况,你可以使用Threading.SAME_THREAD作为执行程序注册交易处理程序,在这种情况下,它们将立即在bitcoinj控制的后台线程上运行。但是,在使用此模式时必须格外小心——代码中出现的任何异常都可能会解开bitcoinj堆栈并导致对等断开连接,同样,重新进入库可能会导致锁定反转或其他问题。通常你应该避免这样做,除非你真的需要额外的表现,并确切知道你在做什么。

收钱

kit.wallet().addCoinsReceivedEventListener(new WalletCoinsReceivedEventListener() {
    @Override
    public void onCoinsReceived(Wallet w, Transaction tx, Coin prevBalance, Coin newBalance) {
        // Runs in the dedicated "user thread".
        //
        // The transaction "tx" can either be pending, or included into a block (we didn't see the broadcast).
        Coin value = tx.getValueSentToMe(w);
        System.out.println("Received tx for " + value.toFriendlyString() + ": " + tx);
        System.out.println("Transaction will be forwarded after it confirms.");
        // Wait until it's made it into the block chain (may run immediately if it's already there).
        //
        // For this dummy app of course, we could just forward the unconfirmed transaction. If it were
        // to be double spent, no harm done. Wallet.allowSpendingUnconfirmedTransactions() would have to
        // be called in onSetupCompleted() above. But we don't do that here to demonstrate the more common
        // case of waiting for a block.
        Futures.addCallback(tx.getConfidence().getDepthFuture(1), new FutureCallback<TransactionConfidence>() {
            @Override
            public void onSuccess(TransactionConfidence result) {
                // "result" here is the same as "tx" above, but we use it anyway for clarity.
                forwardCoins(result);
            }

            @Override
            public void onFailure(Throwable t) {}
        });
    }
});

在这里我们可以看到当我们的应用收到钱时会发生什么,我们打印出我们收到了多少,使用静态实用程序方法格式化为文本。

然后我们做了一些更先进的事情。我们称之为这种方法:

ListenableFuture<TransactionConfidence> future = tx.getConfidence().getDepthFuture(1);

每个交易都有一个与之关联的confidence对象。confidence的概念体现了比特币是一个全球共识系统这一事实,该系统不断努力就全球交易顺序达成一致。因为这是一个难题(当遇到恶意行为者时),交易可能会被双倍花费(在比特币术语中我们说它已经dead)。也就是说,我们有可能相信我们已经收到了钱,后来我们发现世界其他地方不同意我们的看法。

Confidence对象包含我们可以用来做出基于风险的决策的数据,这些决策是关于我们实际收到钱的可能性。它们还可以帮助我们在信心变化或达到某个阈值时学习。

Futures是并发编程中的一个重要概念,bitcoinj大量使用它们,特别是我们将Guava扩展用于标准的Java Future类,称为ListenableFutureListenableFuture表示未来某种计算或状态的结果。你可以等待它完成(阻止调用线程),或者注册将被调用的回调。期货也可能会失败,在这种情况下,你会收到异常而不是结果。

在这里,我们要求depth future。当交易被链中的至少那么多块掩埋时,这个future就完成了。深度为1表示它出现在链中的顶部块中。所以在这里,我们说“当交易至少有一个确认时运行此代码”。通常你会使用一个名为Futures.addCallback的实用工具方法,虽然还有另一种注册监听器的方法,可以在下面的代码片段中看到。

然后,当发送给我们钱的交易确认时,我们只调用一个我们自己定义的方法叫做forwardCoins

这里有一件重要的事情需要注意。depth future可能会运行,然后交易的depth变为小于future的参数。这是因为在任何时候比特币网络都可能经历“重组”,其中最着名的链从一个切换到另一个。如果你的交易出现在新链中的其他位置,则depth实际上可能会下降而不是向上。处理入库付款时,你应确保如果交易信心下降,你会尝试中止你为该资金提供的任何服务。你可以通过阅读SPV安全模型了解有关此主题的更多信息。

处理re-orgs和double spends是一个复杂的主题,本教程未涉及。你可以通过阅读其他文章了解更多信息。

发送比特币

ForwardingService的最后一部分是发送我们刚刚收到的比特币。

Coin value = tx.getValueSentToMe(kit.wallet());
System.out.println("Forwarding " + value.toFriendlyString() + " BTC");
// Now send the coins back! Send with a small fee attached to ensure rapid confirmation.
final Coin amountToSend = value.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
final Wallet.SendResult sendResult = kit.wallet().sendCoins(kit.peerGroup(), forwardingAddress, amountToSend);
System.out.println("Sending ...");
// Register a callback that is invoked when the transaction has propagated across the network.
// This shows a second style of registering ListenableFuture callbacks, it works when you don't
// need access to the object the future returns.
sendResult.broadcastComplete.addListener(new Runnable() {
    @Override
    public void run() {
         // The wallet has changed now, it'll get auto saved shortly or when the app shuts down.
         System.out.println("Sent coins onwards! Transaction hash is " + sendResult.tx.getHashAsString());
    }
});

首先,我们查询我们收到多少钱(当然,由于我们的应用程序的性质,这与上面的onCoinsReceived回调中的newBalance相同)。

然后我们决定发送多少——它与我们收到的相同,减去费用。我们不需要附加费用,但如果我们不这样做,可能需要一段时间才能确认。默认费用很低。

要发送比特币,我们使用钱包sendCoins方法。它需要三个参数:TransactionBroadcaster(通常是PeerGroup),发送比特币的地址(这里我们使用我们之前从命令行解析的地址)以及要发送多少钱。

sendCoins返回一个SendResult对象,该对象包含已创建的交易和一个ListenableFuture,可用于查明网络何时接受付款。如果钱包没有足够的钱,sendCoins方法将抛出一个异常,其中包含一些关于缺少多少钱的信息。

自定义发送过程和设置费用

比特币交易可以附加费用。这对于反拒绝服务机制很有用,但它主要是为了在通货膨胀率下降时激励系统后期的采矿。你可以通过自定义发送请求来控制附加到交易的费用:

SendRequest req = SendRequest.to(address, value);
req.feePerKb = Coin.parseCoin("0.0005");
Wallet.SendResult result = wallet.sendCoins(peerGroup, req);
Transaction createdTx = result.tx;

请注意,在这里,我们实际上设置了每千字节创建的交易的费用。这就是比特币的工作原理——交易的优先级由费用除以大小决定,因此较大的交易要求较高的费用被视为与较小的交易“相同”。

写在最后

bitcoinj还有许多其他功能,本教程不涉及这些功能。你可以阅读其他文章以了解有关完整验证,钱包加密等的更多信息,当然JavaDocs还详细介绍了完整的API。

我建议你浏览我们的区块链教程和区块链技术博客,深入了解区块链,比特币,加密货币,以太坊,和智能合约。

  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • php比特币开发教程本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。

这里是原文 

分享到:
评论

相关推荐

    比特币 价格 历史数据 2010年7月-2024年2月

    比特币 价格 最高价 最低价 开盘价 收盘价 交易量 历史数据 2010 年7月-2024年2月 比特币 价格 最高价 最低价 开盘价 收盘价 交易量 历史数据 2010年7月-2024年2月 比特币 价格 最高价 最低价 开盘价 收盘价 交易...

    比特币 - 以太坊价格数据集 BTC - ETH(2014-2024,5K+ 记录)CSV

    该数据集提供比特币和以太坊的历史每日价格信息。它包括从 2014 年 1 月 1 日到现在的比特币每日收盘价,以及从 2017 年 1 月 1 日到现在的以太坊的每日收盘价。对于任何对分析加密货币价格趋势、执行时间序列预测或...

    区块链技术分享 区块链技术应用-比特币介绍 谈谈未来的钱-比特币 共27页.pptx

    与大多数货币不同,比特币不依靠特定货币机构发行,它依据特定算法,通过大量的计算产生,比特币经济使用整个P2P网络中众多节点构成的分布式数据库来确认并记录所有的交易行为,并使用密码学的设计来确保货币流通...

    比特币白皮书:一种点对点的电子现金系统(中文版)

    文件格式:Word 总页数:17 目录: 1. 简介 2. 交易(Transactions) 3. 时间戳服务器(Timestamp server) 4. 工作量证明(Proof-of-Work) 5. 网络 6. 激励 7. 回收硬盘空间 8. 简化的支付确认(Simplified Payment ...

    基于Paddle构建简单神经网络比特币价格预测

    【作品名称】:基于Paddle构建简单神经网络比特币价格预测 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】: 基于...

    基于LSTM预测比特币和黄金价格走势的综合决策模型源码+数据+项目说明.zip

    基于LSTM预测比特币和黄金价格走势的综合决策模型.zip 基于LSTM预测比特币和黄金价格走势的综合决策模型.zip 基于LSTM预测比特币和黄金价格走势的综合决策模型.zip 基于LSTM预测比特币和黄金价格走势的综合决策模型....

    比特币0.9.2版编译中文手册.pdf

    【比特币0.9.2版编译中文手册】 在IT领域,比特币是一种去中心化的数字货币,基于区块链技术,由中本聪所创建。比特币0.9.2版本是比特币客户端的一个...对于想要深入研究比特币技术和开发的人员,这是必不可少的步骤。

    (源码)基于Python和Qt框架的比特币钱包管理系统.zip

    本项目是一个基于Python和Qt框架开发的比特币钱包管理系统,旨在为用户提供一个安全、便捷的比特币存储和交易平台。通过该系统,用户可以管理自己的比特币地址、进行交易、查看交易历史以及进行钱包备份和恢复等操作...

    比特币:一种点对点电子货币系统.pdf

    ### 比特币:一种点对点电子货币系统 #### 概述 比特币是一种全新的电子货币系统,它采用点对点技术实现了无须通过中间金融机构即可直接进行交易的功能。与传统的电子支付方式不同,比特币的设计核心在于通过密码...

    比特币10年价格数据(2014-2024).zip

    自2009年比特币诞生以来,在市场情绪、政策变化、技术进步等多重因素共同作用下,它经历了频繁的大幅价格波动,这些波动不仅体现了投资者对比特币价值的认知变化,同时也映射出全球经济环境和技术发展的脉络。...

    比特币历史价格数据集.zip

    《比特币历史价格数据集》是针对数字货币领域,特别是比特币(Bitcoin)进行数据分析的重要资源。这个数据集包含了比特币与美元的汇率变化,对于研究加密货币市场动态、投资策略分析以及预测模型构建具有重大价值。 ...

    比特Java研发工程师JavaSE.zip

    JavaSE(Java Standard Edition)是Java平台的基础,它提供了构建桌面应用程序、网络应用程序和服务的基本工具和类库。"比特Java研发工程师JavaSE.zip"这个压缩包很可能是为Java初学者或希望深入JavaSE的开发者准备...

    基于LSTM预测比特币和黄金价格走势的综合决策模型.zip

    基于LSTM预测比特币和黄金价格走势的综合决策模型.zip 基于LSTM预测比特币和黄金价格走势的综合决策模型.zip 基于LSTM预测比特币和黄金价格走势的综合决策模型.zip 基于LSTM预测比特币和黄金价格走势的综合决策模型....

    Java开发编码规范手册

    **Java Runtime Environment (Java运行时环境)**:JRE包括Java虚拟机(JVM)和其他组件,如类库等,提供完整的Java运行环境。 **Java Development Kit (Java开发工具包)**:JDK包含编译工具、文档生成工具以及其他...

    java毕业设计基于SpringBoot+WebSocket+RSA的仿比特币点对点交易系统源码+使用文档+全部资料(优秀项目)

    java毕业设计基于SpringBoot+WebSocket+RSA的仿比特币点对点交易系统源码+使用文档+全部资料(优秀项目)java毕业设计基于SpringBoot+WebSocket+RSA的仿比特币点对点交易系统源码+使用文档+全部资料(优秀项目)java...

    基于GARCH模型比特币的VaR和ES计算R语言

    以BTC-USD数据为例,基于GARCH模型,分析计算了比特币的VaR(风险价值 )与ES(期望损失)。同时也可以计算其他资产的VaR与ES,代码便于模仿,直接替 换即可。 即使没有R语言基础的也可以模仿,同时代码中有中文注释,...

Global site tag (gtag.js) - Google Analytics