返回介绍

4.单元测试

发布于 2024-12-29 22:37:18 字数 6986 浏览 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 中也有很多。(比如多重签名发行和颜色币互换)。

发布评论

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