- 1.前言
- 2.为什么是区块链编程而不是比特币编程?
- 3.为什么是 C#?
- 4.预备条件
- 5.本书众筹
- 6.补充阅读
- 7.图标
- 8.许可: CC (ASA 3U)
- 9.项目设置
- 1.比特币地址
- 2.交易
- 3.区块链
- 4. 区块链不仅仅是比特币
- 5.支付比特币
- 6.作为真实性验证方法的所有权证明
- 1.足够随机了吗?
- 2.秘钥加密
- 3.秘钥的生成
- 1.P2PK[H] (向公钥付款 [Hash])
- 2.多重签名
- 3.P2SH ( 向脚本哈希付款)
- 4.灵活机动性
- 5.使用 TransactionBuilder
- 1.颜色币
- 2.发行一项资产
- 3.传输资产
- 4.单元测试
- 5.李嘉图合约
- 6.流动的民主
- 7.烧钱和声誉证明
- 8.存在性证明
- 1.比特币发展的挑战
- 2.如何证明一个币存在于区块链上
- 3.如何证明一个颜色币存在于区块链上
- 4.断开与第三方 API 的信任关系
- 5.防止延展性攻击
- 6.保护你的私钥
4.单元测试
前面我硬编码了颜色币的属性。
原因是,我只打算向你展示一下,如何从颜色币构建一个交易。
在实际中,你也可以依赖第三方的 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论