免责声明
NKaspa 目前是 alpha 版本(0.1.11-alpha),未经安全审计。它是社区项目,不是 Kaspa 官方项目,也不是 NBitcoin 的官方分支。本指南只做概念对照,请先在 Kaspa testnet 用小额验证。不要把本文理解成对 NBitcoin 或任何项目的安全/性能评价。
如果你已经用 NBitcoin 写过比特币钱包或脚本,想在 Kaspa 上做同样的事,这篇 NBitcoin 到 NKaspa 的迁移对照会告诉你:Money、Coin、ExtPubKey、地址、TransactionBuilder、手续费和确认数在 NKaspa 里分别对应什么,哪些习惯可以照搬,哪些地方容易踩坑。NKaspa 的 API 命名刻意接近 NBitcoin,但行为按 Kaspa 协议来。文中 NKaspa 代码对照已发布的 NuGet 包 0.1.11-alpha(GitHub tag v0.1.11-alpha),NBitcoin 对照其公开仓库 MetacoSA/NBitcoin(访问日期 2026-11-09)。所有示例默认使用 testnet。
谁适合读,什么时候继续用 NBitcoin
- 做比特币,继续用 NBitcoin。 它面向比特币及同源链,生态成熟,支持 PSBT 和丰富的地址类型。
- 做 Kaspa,用面向 Kaspa 的库。 Kaspa 的地址前缀、按 mass 计的手续费、DAA score、wRPC 节点接口都和比特币不同,不要指望用 NBitcoin 直接编 Kaspa 交易。
- NKaspa 的定位: 一个面向 Kaspa 的 .NET 库,类型名尽量沿用 NBitcoin 的叫法,方便你把已有的概念迁移过来。仓库里另有更细的对照文档 docs/NBitcoin-Mapping.md。
NBitcoin 与 NKaspa 概念映射表
| 概念 | NBitcoin | NKaspa(0.1.11-alpha) | 迁移时注意 |
|---|---|---|---|
| 金额 | Money(最小单位 satoshi) | Money(最小单位 sompi,Money.SompiPerKas = 100_000_000) | Money.Coins(decimal) 传入的是 KAS。 |
| UTXO / 币 | Coin / ICoin | Coin = OutPoint + UtxoEntry | UtxoEntry 带 Amount、ScriptPublicKey、BlockDaaScore、IsCoinbase。 |
| 密钥 | Key / PubKey,私钥常用 BitcoinSecret(WIF) | Key / PubKey(32 字节 Schnorr x-only),另有 PubKeyECDSA(33 字节压缩) | 默认付款地址走 Schnorr;没有比特币 WIF 和 BitcoinSecret。 |
| 扩展公钥 | ExtPubKey.Parse(string, Network),解析时按网络校验 | 只有 ExtPubKey.Parse(string),返回对象没有 Network 属性 | 见下文「已知坑」。前缀:主网 kpub,testnet/simnet/devnet 都是 ktub。ToString(Network) 可按网络前缀写出。 |
| 地址 | BitcoinAddress(Base58、Bech32/Bech32m 等) | KaspaAddress(KaspaPubKeyAddress / KaspaPubKeyEcdsaAddress / KaspaScriptHashAddress) | 格式是 前缀:载荷,前缀为 kaspa / kaspatest / kaspasim / kaspadev。Parse(string, Network) 和 TryParse(string, Network, out …) 会校验网络。 |
| 地址编码 | Bech32Encoder(BIP-173 / BIP-350) | KaspaBech32Encoder(cashaddr 风格,校验和 8 个字符 / 40 位,字符表相同) | 不是 BIP-173,也不是 Bech32m。 |
| 网络 | Network.Main / Network.TestNet 等 | Network.Mainnet / Testnet / Simnet / Devnet | Network.Testnet 对应公开的 testnet-10(地址前缀 kaspatest)。 |
| 交易构建 | TransactionBuilder:AddKeys / AddCoins / Send / SetChange / SendFees / SendEstimatedFees / BuildTransaction | 同名的精简构建器,另有 SendMinimumRelayFee()、MustSpend(...)、BuildPSKT() | 默认交易版本 0,SetVersion(1) 用于版本 1;构建失败抛 KaspaTransactionBuilderException。 |
| 手续费 | FeeRate(内部按每 kB 存,常换算成 sat/vB) | FeeRate,单位 sompi/gram(gram 是交易 mass 单位,不是 kB) | 手续费 = 费率 × 交易 mass。mass 取 compute、transient、KIP-9 storage 三者(归一化后)的最大值,节点也按这个口径排序。最低中继费率 100 sompi/gram,但节点准入下限只看 compute 和 transient:一笔标准单输入 Schnorr 付款(带找零)的准入下限约 0.002 KAS,即 SendMinimumRelayFee() 的值。小额输出会让 storage mass 大幅上升:1 KAS 输入付 0.2 KAS 时 storage mass 约 5.3 万 gram,按 100 sompi/gram 计约 0.053 KAS,这是 SendEstimatedFees(FeeRate.MinimumRelay) 的值;付款输出在约 0.02 KAS 及以下时会超过 50 万 gram 的上限,交易无法构建;刚高于这个值时,SendEstimatedFees 的手续费(按 100 sompi/gram)约 0.5 KAS,即 50 万 gram 上限对应的费用。只付准入下限不会被拒,但节点按含 storage mass 的口径算出的费率很低(这个例子只有约 3.9 sompi/gram),拥堵时会排在最后甚至被挤出。先看节点的费率估算:var est = await rpc.GetFeeEstimateAsync();。est.PriorityBucket 和 est.NormalBuckets 的 Feerate 都在 100 sompi/gram(网络空闲)时,SendMinimumRelayFee() 只付准入下限也能被接纳;高于 100 或交易迟迟不进块时,改用 SendEstimatedFees(费率),费率可取 FeeEstimator.PayableFeerate(est)(最低档,不低于 100)或 normal 档向上取整。调高 MinimumRelayFeeRate 再用 SendMinimumRelayFee() 只按 relay mass 加价,对小额付款几乎无效。注意按含 storage mass 的口径,小额付款的手续费大约是"0.01 KAS ÷ 付款 KAS 数":付 0.2 KAS 约 0.053 KAS,付 0.1 KAS 约 0.10 KAS,付 0.05 KAS 约 0.20 KAS。签名前先检查手续费。 |
| 确认数 | 常按区块高度计算 | 没有「查确认数」的专用 RPC | 样例用「当前 virtual DAA score − UtxoEntry.BlockDaaScore」近似;也可自选 blue score 尺度。不要按区块高度算。 |
| 部分签名 | PSBT(BIP-174) | PSKT(NKaspa 内的 JSON 格式) | 名字相近、格式不同,不要把 PSBT 字节当 PSKT 用。 |
| 节点访问 | RPCClient(比特币 JSON-RPC) | KaspaRpcClient.Create(url) → KaspaRpcConnection(wRPC,JSON 或 Borsh) | 不是比特币 RPC;URL 与端口规则见对照文档。 |
并排小任务:在 testnet 上对照着写
下面的 NKaspa 片段对照 v0.1.11-alpha 编写。片段 1–4 只在本机运行,不连网;片段 5 需要连接 testnet 节点。示例中的私钥是公开测试向量,不能用来存放任何真实资金;助记词一律用占位符。
1. 从助记词派生 testnet 收款地址
在比特币上你习惯 Mnemonic → ExtKey → Neuter() → Derive() → GetAddress()。在 NKaspa 里步骤一样,只是派生路径和网络换成 Kaspa 的:
using NKaspa; var mnemonic = Mnemonic.Parse("<你的 testnet 测试助记词>"); var master = mnemonic.DeriveExtKey(); // 账户扩展公钥:m/44'/111111'/0',testnet 写出来是 ktub 前缀 string ktub = master.Derive(KeyPath.KaspaAccount(0)).Neuter().ToString(Network.Testnet); // 第 0 个收款地址:m/44'/111111'/0'/0/0,形如 kaspatest:... KaspaPubKeyAddress receive0 = master.Derive(KeyPath.KaspaReceive(0)).Neuter().GetAddress(Network.Testnet); 提示:Mnemonic.ToString() 不会返回助记词本身,需要原文时得显式调用 ToPhrase(),日志里更不容易误打出来。
2. 解析并校验 kaspatest: 地址
在 NBitcoin 里你会用 BitcoinAddress.Create(str, network)。在 NKaspa 里用 KaspaAddress.Parse 或 TryParse,并传入期望的网络:
if (KaspaAddress.TryParse(input, Network.Testnet, out var address)) { // address.Network 就是 Network.Testnet } else { // 不是合法的 testnet-10 地址(主网 kaspa: 地址也会在这里被拒绝) } 3. 离线组一笔 testnet 付款
TransactionBuilder 的链式写法和 NBitcoin 很像。下面这笔交易只在内存里构建和签名,不会广播:
// 公开测试向量私钥,仅用于演示,不能存放真实资金 using var payer = Key.Parse("0000000000000000000000000000000000000000000000000000000000000003"); var payee = KaspaAddress.Parse("<收款方 kaspatest: 地址>", Network.Testnet); var change = payer.GetAddress(Network.Testnet); var coin = new Coin( new OutPoint(new uint256(70), 0), // 演示用的假 outpoint new UtxoEntry(Money.Coins(1m), payer.PubKey.GetScriptPubKey(), blockDaaScore: 1, isCoinbase: false)); Transaction tx = new TransactionBuilder(Network.Testnet) .AddKeys(payer) .AddCoins(coin) .Send(payee, Money.Coins(0.2m)) .SetChange(change) .SendMinimumRelayFee() // 准入下限约 0.002 KAS;本例按含 storage 的口径费率仅约 3.9 sompi/gram,拥堵时改用 SendEstimatedFees(本例约 0.053 KAS) .BuildTransaction(true); 和 NBitcoin 的差别主要在手续费:这里没有 sat/vB,费用按 mass(gram)计算。SendMinimumRelayFee() 只付节点准入下限,不含 storage mass;SendEstimatedFees(FeeRate) 按包含 storage mass 的完整 mass 计费。先看节点的费率估算:var est = await rpc.GetFeeEstimateAsync();。est.PriorityBucket 和 est.NormalBuckets 的 Feerate 都在 100 sompi/gram(网络空闲)时,SendMinimumRelayFee() 只付准入下限也能被接纳;高于 100 或交易迟迟不进块时,改用 SendEstimatedFees(费率),费率可取 FeeEstimator.PayableFeerate(est)(最低档,不低于 100)或 normal 档向上取整。注意按含 storage mass 的口径,小额付款的手续费大约是"0.01 KAS ÷ 付款 KAS 数":付 0.2 KAS 约 0.053 KAS,付 0.1 KAS 约 0.10 KAS,付 0.05 KAS 约 0.20 KAS。签名前先检查手续费。仓库里的 samples/Tutorials 有一个完整的离线版本可对照运行。
4. 解析扩展公钥时自己核对前缀
这一段对应下文的「已知坑」。在 NBitcoin 里你会写 ExtPubKey.Parse(text, Network.TestNet) 让库替你校验网络;在 0.1.11-alpha 的 NKaspa 里,Parse 只接受一个字符串,所以要自己先查前缀:
static ExtPubKey ParseTestnetAccountKey(string text) { var trimmed = text.Trim(); if (!trimmed.StartsWith("ktub", StringComparison.Ordinal)) throw new FormatException("需要 ktub(非主网)扩展公钥。"); return ExtPubKey.Parse(trimmed); } 这个检查只能排除主网的 kpub,不能证明对方给的就是 testnet-10 的 key,原因见下一节。
5. 读取 UTXO 的确认深度近似
比特币上常用「当前高度 − 所在区块高度 + 1」。Kaspa 没有对应的「查确认数」RPC,可以用 DAA score 差值做近似(这一步需要连接 testnet 节点):
using NKaspa.RPC; await using var rpc = KaspaRpcClient.Create("<testnet-10 节点 wRPC 地址>"); await rpc.ConnectAsync(); var info = await rpc.GetServerInfoAsync(); var utxos = await rpc.GetUtxosByAddressesAsync(new[] { "<你的 kaspatest: 地址>" }); foreach (var u in utxos) { ulong depth = info.VirtualDaaScore > u.Entry.BlockDaaScore ? info.VirtualDaaScore - u.Entry.BlockDaaScore : 0; Console.WriteLine($"{u.Outpoint}: {u.Entry.Amount}, depth≈{depth}"); } 需要多少深度才算「确认」,要按你的业务风险自己定。更多细节见 DAA score、确认数与时间锁 FAQ。
已知坑:ExtPubKey.Parse 不绑定网络
当前发布的 0.1.11-alpha 里,ExtPubKey.Parse 不绑定网络,请自行核对 kpub/ktub 前缀;带网络参数的重载计划在后续版本提供。注意 ktub 为 testnet、simnet、devnet 共用,不能单靠前缀区分这三者。
具体来说:
-
ExtPubKey.Parse(string)接受所有合法的公钥版本前缀,包括kpub、ktub,以及对照文档中提到的比特币测试向量前缀。传入主网的kpub也能解析成功。 - 返回的
ExtPubKey对象没有Network属性。它虽然有Version(Base58 版本字节),但 testnet、simnet、devnet 用的是同一个版本字节,所以事后也无法从对象上判断它属于哪个网络。 -
Network.Testnet、Network.Simnet、Network.Devnet的扩展公钥版本相同,写出来都是ktub。前缀检查能区分「主网 / 非主网」,但不能区分这三个非主网网络。 - 仓库 main 分支上的 AspNetCheckout 样例(不在
v0.1.11-alpha标签内,位置以 main 为准),用KeyGuard按字符串前缀只接受ktub,并拒绝kpub、私钥前缀和疑似助记词的输入。这是应用层自己加的防护,不代表Parse绑定了网络。
作为对照:NBitcoin 的 ExtPubKey.Parse(string wif, Network expectedNetwork) 在解析时就会按期望网络校验(ExtPubKey.cs,访问日期 2026-11-09)。从 NBitcoin 迁移过来时,最容易忽略的就是这一步校验已经不在库里了。
地址字符串校验:NKaspa 与 NBitcoin PR #1375
两边最近都在关注地址字符串的输入校验,这里只陈述公开事实。
- NKaspa(0.1.11-alpha,
KaspaBech32Encoder.TryDecode): 地址必须是前缀:载荷的形式,且只能有一个冒号;前缀里的每个字符必须在可打印 ASCII 范围 33–126 内;载荷里的每个字符必须属于 Kaspa 的 32 字符字符表,否则直接拒绝;之后再校验 8 个字符的 cashaddr 校验和。KaspaAddress.Parse(string, Network)还会在前缀和期望网络不匹配时失败(源码)。 - NBitcoin PR #1375(Reject non-ASCII characters in Bech32 strings): 这个 PR 指出,BIP-173 要求 Bech32 字符串的每个字符都在 US-ASCII 33–126 范围内,而
Bech32Encoder.DecodeDataCore目前没有检查这一点,某些非 ASCII 字符可能被解码成和正常地址相同的结果。PR 提议在解码前对超出范围的字符抛出FormatException。截至 2026-11-09,该 PR 仍为 open,尚未合并,所以不能说 NBitcoin 已经改了这一行为。 - 对迁移者的结论: Kaspa 地址有自己的「前缀 + 冒号 + cashaddr 校验和」规则,不能用 NBitcoin 的 Bech32 API 去解析
kaspa:或kaspatest:地址,请用KaspaAddress.Parse/TryParse。
常见问题(FAQ)
NKaspa 是 NBitcoin 的替代品吗?
不是。NBitcoin 继续服务比特币;NKaspa 是面向 Kaspa 的 .NET 库,API 命名刻意接近 NBitcoin,方便迁移概念。两者解决的是不同链上的问题。
NKaspa 的 Money 单位和 NBitcoin 一样吗?
都叫 Money,但 NKaspa 的最小单位是 sompi(1 KAS = 100,000,000 sompi),不是 satoshi。用 Money.Coins 时传入的是 KAS。
Kaspa 地址能用 NBitcoin 的 Bech32 解析吗?
不能。Kaspa 地址是 kaspa: / kaspatest: 等前缀加 cashaddr 风格编码,校验和长度和 BIP-173 不同。请用 KaspaAddress.Parse 或 TryParse,并传入期望的网络。
NKaspa 的 ExtPubKey.Parse 会绑定网络吗?
当前已发布的 0.1.11-alpha 不会。它不带 Network 参数,返回对象也没有 Network 属性;请自行校验 kpub(主网)/ ktub(非主网)前缀。带网络参数的重载计划在后续版本提供。另外,ktub 为 testnet、simnet、devnet 共用,不能单靠前缀区分这三者。
Kaspa 手续费怎么估?
手续费 = FeeRate(sompi/gram)× 交易 mass(gram),mass 包含 KIP-9 storage mass。先看节点的费率估算:var est = await rpc.GetFeeEstimateAsync();。est.PriorityBucket 和 est.NormalBuckets 的 Feerate 都在 100 sompi/gram(网络空闲)时,SendMinimumRelayFee() 只付准入下限也能被接纳;高于 100 或交易迟迟不进块时,改用 SendEstimatedFees(费率),费率可取 FeeEstimator.PayableFeerate(est)(最低档,不低于 100)或 normal 档向上取整。注意按含 storage mass 的口径,小额付款的手续费大约是"0.01 KAS ÷ 付款 KAS 数":付 0.2 KAS 约 0.053 KAS,付 0.1 KAS 约 0.10 KAS,付 0.05 KAS 约 0.20 KAS。签名前先检查手续费。不要按比特币的 sat/vB 硬套。
Kaspa 确认数按区块高度算吗?
不是。NKaspa 样例用当前 virtual DAA score 减去 UtxoEntry.BlockDaaScore 做近似;也可以自选 blue score 尺度。阈值按业务风险自己定。
PSBT 能直接用吗?
不能。Kaspa 这边是 PSKT,是 NKaspa 内的 JSON 格式,和比特币 PSBT 名字相近但格式不同。多签场景可以看仓库里的 samples/PsktMultisig。
能直接上主网放大额资金吗?
不建议。NKaspa 是 alpha、未经安全审计的社区项目;请先在 testnet 用小额验证。
下一步
- 用 C# 连接 Kaspa 节点并发交易(dev.to 教程):英文 · 中文
- DAA score、确认数与时间锁 FAQ
- NuGet 包:
NKaspa(已发布版本含0.1.11-alpha) - NKaspaDoc:github.com/lifeme2027/NKaspaDoc
先在 testnet 对照着跑:github.com/lifeme2027/NKaspa(见 docs/NBitcoin-Mapping.md 与 samples/Tutorials)
觉得有用就给 NKaspa 点个 Star;迁移中遇到的坑欢迎开 issue,维护者会看并排期。
来源(访问日期 2026-11-09)
- NKaspa 源码与对照文档:https://github.com/lifeme2027/NKaspa (
docs/NBitcoin-Mapping.md) - NKaspa 地址编码实现:https://github.com/lifeme2027/NKaspa/blob/main/src/NKaspa/DataEncoders/KaspaBech32Encoder.cs
- NKaspa
ExtPubKey:https://github.com/lifeme2027/NKaspa/blob/main/src/NKaspa/BIP32/ExtPubKey.cs - NuGet 版本列表:https://api.nuget.org/v3-flatcontainer/nkaspa/index.json
- NBitcoin 源码:https://github.com/MetacoSA/NBitcoin ;
ExtPubKey.cs:https://github.com/MetacoSA/NBitcoin/blob/master/NBitcoin/BIP32/ExtPubKey.cs - NBitcoin PR #1375:https://github.com/MetacoSA/NBitcoin/pull/1375
免责声明
NKaspa 目前是 alpha 版本(0.1.11-alpha),未经安全审计。它是社区项目,不是 Kaspa 官方项目,也不是 NBitcoin 的官方分支。本指南只做概念对照,请先在 Kaspa testnet 用小额验证。不要把本文理解成对 NBitcoin 或任何项目的安全/性能评价。

![Path Planning and Autonomous Navigation — Ch.11: Towing AMR Navigation [EN]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyvsxdf311sqylulf87m4.png)