返回介绍

4.单元测试

发布于 2024-12-29 22:37:18 字数 6986 浏览 0 评论 0 收藏 0

前面我硬编码了颜色币的属性。

原因是,我只打算向你展示一下,如何从颜色币构建一个交易。

在实际中,你也可以依赖第三方的 API 来获取一个交易的颜色币或者余额。那可能不是一个好办法,因为你的程序就必须依赖于对 API 提供商的信任。

NBitcoin 允许你依赖于一个网络服务,或者提供你自己的实现来获取一个交易的颜色币。这就使得你可以灵活地对代码进行单元测试,使用别人的实现或者自己的。

我们介绍一下 2 位发行人:Silver 和 Gold。还有 3 位参与者:Bob,Alice、Satoshi。

我们创建一个虚拟的交易,支付一些比特币给 Silver,Gold 和 Satoshi。

var gold = new Key();
var silver = new Key();
var goldId = gold.PubKey.ScriptPubKey.Hash.ToAssetId();
var silverId = silver.PubKey.ScriptPubKey.Hash.ToAssetId();

var bob = new Key();
var alice = new Key();
var satoshi = new Key();

var init = new Transaction()
{
    Outputs =
    {
        new TxOut("1.0", gold),
        new TxOut("1.0", silver),
        new TxOut("1.0", satoshi)
    }
};

Init 没有包含任何 Colored Coin issuance 和 Transfer。

但是设想一下,如果你需要确定一下,将会做什么? 在 NBitcoin 中,传输和发行颜色币的概述使用一个类来描述,名叫 ColoredTransaction。

ColoredTransaction 将告诉你:

  • 通过哪个 TxIn 支付哪项资产
  • 哪个 TxOut 发行哪项资产
  • 哪个 TxOut 传输哪项资产

让我们感兴趣的方法是 FetchColor,这个方法允许你从 input 的交易中提取颜色币的信息。

你会发现它依赖于一个 IColoredTransactionRepository。

一个 IColoredTransactionRepository 仅仅是一个商店,它从 txid 给你 ColoredTransaction。然而,你会发现它依赖于 ITransactionRepository 将交易 ID 映射到它的交易。

IColoredTransactionRepository 的一个实现就是 CoinprismColoredTransactionRepository,颜色币操作的公共 API。

然而,你可以自己轻松地完成,下面描述了 FetchColors 是如何工作的。

最简单的例子就是:IColoredTransactionRepository 知道颜色币,在这个例子中 FetchColors 仅仅返回结果。

第二个例子中,IColoredTransactionRepository 不知道交易中关于颜色币的任何信息。

所以 FetchColors 需要根据公开资产详述来计算出颜色币。

但是,为了计算颜色币,FetchColors 需要父交易的颜色币。

所以它就从 ITransactionRepository 得到他们,并在他们身上调用 FetchColors。

一旦 FetchColors 递归解决了父交易颜色币,它就计算交易颜色币,并把结果保存到 IColoredTransactionRepository 的缓存。

按照那样做的话,未来提取交易颜色币的请求都可以很快解决了。

有些 IColoredTransactionRepository 是只读的(就好像 CoinprismColoredTransactionRepository,Put 操作被忽略了)

那么,回到我们的例子:

编写单元测试的技巧就是使用内存中的 IColoredTransactionRepository:

var repo = new NoSqlColoredTransactionRepository();

现在,我们可以将 init 放到交易里面了:

repo.Transactions.Put(init);

注意 Put 是一个扩展的方法,所以你需要添加如下代码:

using NBitcoin.OpenAsset;

放在文件顶部以便访问。

现在,你可以抽取颜色币了:

ColoredTransaction color = ColoredTransaction.FetchColors(init, repo);
Console.WriteLine(color);

结果正如预期,init 交易没有输入,发行方,传输或者颜色币的销毁。

现在,我们把发往 Silver 和 Gold 的两个币当做是发行币。

var issuanceCoins =
    init
    .Outputs
    .AsCoins()
    .Take(2)
    .Select((c, i) => new IssuanceCoin(c))
    .OfType<ICoin>()
    .ToArray();

Gold 是第一个币,Silver 是第二个。

按照上面做法,你可以通过 TransactionBuilder 将 Gold 发送给中本聪,就跟我们在前面的练习中一样,将交易放到仓库中,并打印结果。

var sendGoldToSatoshi =
builder
    .AddKeys(gold)
    .AddCoins(issuanceCoins[0])
    .IssueAsset(satoshi, new Asset(goldId, 10))
    .SetChange(gold)
    .BuildTransaction(true);
repo.Transactions.Put(sendGoldToSatoshi);
color = ColoredTransaction.FetchColors(sendGoldToSatoshi, repo);
Console.WriteLine(color);

这意味着第一个 TxOut 承担 10 个 gold。

假设中本聪想要发送 4 个 gold 给 Alice。

首先他将从交易中提取颜色币。

var goldCoin = ColoredCoin.Find(sendGoldToSatoshi, color).FirstOrDefault();

然后,如下建立一个交易:

builder = new TransactionBuilder();
var sendToBobAndAlice =
    builder
    .AddKeys(satoshi)
    .AddCoins(goldCoin)
    .SendAsset(alice, new Asset(goldId, 4))
    .SetChange(satoshi)
    .BuildTransaction(true);

除非你得到异常 NotEnoughFundsException。

原因是交易包含 600 聪的输入(goldCoin),1200 聪的输出。(一个 TxOut 用于发送资产给 Alice,一个用于发回找零给 Satoshi)

这就意味着你发出去 600 聪了。

你可以在 satoshi 的 init 交易中增加最后 1BTC,从而解决问题。

var satoshiBtc = init.Outputs.AsCoins().Last();
builder = new TransactionBuilder();
var sendToAlice =
    builder
    .AddKeys(satoshi)
    .AddCoins(goldCoin, satoshiBtc)
    .SendAsset(alice, new Asset(goldId, 4))
    .SetChange(satoshi)
    .BuildTransaction(true);
repo.Transactions.Put(sendToAlice);
color = ColoredTransaction.FetchColors(sendToAlice, repo);

Let s see the transaction and its colored part:
Console.WriteLine(sendToAlice);
Console.WriteLine(color);
{
    ….
    "in": [
    {
        "prev_out": {
            "hash": "46117f3ef44f2dfd87e0bc3f461f48fe9e2a3a2281c9b3802e339c5895fc325e",
            "n": 0
        },
        "scriptSig":
        "304502210083424305549d4bb1632e2c67736383558f3e1d7fb30ce7b5a3d7b87a53cdb3940220687
        ea53db678b467b98a83679dec43d27e89234ce802daf14ed059e7a09557e801
        03e232cda91e719075a95ede4c36ea1419efbc145afd8896f36310b76b8020d4b1"
    },
    {
        "prev_out": {
            "hash": "aefa62270999baa0d57ddc7d2e1524dd3828e81a679adda810657581d7d6d0f6",
            "n": 2
        },
        "scriptSig":
        "30440220364a30eb4c8a82cc2a79c54d0518b8ba0cf4e49c73a5bbd17fe1a5683a0dfa640220285e98f
        3d336f1fa26fb318be545162d6a36ce1103c8f6c547320037cb1fb8e901
        03e232cda91e719075a95ede4c36ea1419efbc145afd8896f36310b76b8020d4b1"
    }
    ],
    "out": [
    {
        "value": "0.00000000",
        "scriptPubKey": "OP_RETURN 4f41010002060400"
    },
    {
        "value": "0.00000600",
        "scriptPubKey": "OP_DUP OP_HASH160 5bb41cd29f4e838b4b0fdcd0b95447dcf32c489d
        OP_EQUALVERIFY OP_CHECKSIG"
    },
    {
        "value": "0.00000600",
        "scriptPubKey": "OP_DUP OP_HASH160 469c5243cb08c82e78a8020360a07ddb193f2aa8
        OP_EQUALVERIFY OP_CHECKSIG"
    },
    {
        "value": "0.99999400",
        "scriptPubKey": "OP_DUP OP_HASH160 5bb41cd29f4e838b4b0fdcd0b95447dcf32c489d
        OP_EQUALVERIFY OP_CHECKSIG"
    }
    ]
}
Colored :
{
    "inputs": [
    {
        "index": 0,
        "asset": " ATEwaRSNeCgBjxjcur7JtfypFjqQgAtLJs ",
        "quantity": 10
    }
    ],
    "issuances": [],
    "transfers": [
    {
        "index": 1,
        "asset": " ATEwaRSNeCgBjxjcur7JtfypFjqQgAtLJs ",
        "quantity": 6
    },
    {
        "index": 2,
        "asset": " ATEwaRSNeCgBjxjcur7JtfypFjqQgAtLJs ",
        "quantity": 4
    }
    ],
    "destructions": []
}

我们最终创建了一个单元测试,并且发行和传输了若干资产,其中没有依赖任何外部帮助。

如果你不想依赖于第三方机构服务的话,你可以创建自己的 IColoredTransactionRepository。

在 NBitoin 测试中你可以找到更多复杂的场景,在我 codeproject 的文章 Build them all 中也有很多。(比如多重签名发行和颜色币互换)。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文