免责声明: NKaspa 目前是 alpha 版本(0.1.11-alpha),未经安全审计。它是社区项目,不是 Kaspa 官方项目。本教程只在 Kaspa testnet 上演示(仅 testnet-10),请只用测试币和小额。不要用于真实资金。公共节点由第三方运营,不保证可用。
本文介绍如何用社区 .NET 库 NKaspa(NuGet 上可直接安装)在 C# 里使用 Kaspa,也就是一个 Kaspa .NET SDK 的入门:通过 wRPC 连接 testnet-10 节点、检查节点状态、读取 UTXO、构建并签名交易、在本地校验,然后提交。
流程:KaspaRpcClient.Create → ConnectAsync → GetServerInfoAsync → GetUtxosByAddressesAsync → TransactionBuilder → tx.Verify → SubmitTransactionAsync。
用的是 wRPC,不是 gRPC
NKaspa 不包含 gRPC 客户端。它通过 WebSocket 上的 wRPC 与 kaspad 通信,编码可选 JSON 或 Borsh。如果你需要 gRPC,请改用 Kaspa 官方的 proto 文件(见 docs.kaspa.org)。
准备工作
- .NET 8 SDK(仓库的
global.json固定为 8.0.427,rollForward: latestFeature)。 - 安装包:
dotnet add package NKaspa --version 0.1.11-alpha 0.1.11-alpha 已在 nuget.org 上线,可在新建的 net8.0 控制台项目中正常还原(restore)。
- 一个 testnet-10 wRPC 节点。可以自建 kaspad,也可以使用
KaspaResolver源码(src/NKaspa/RPC/KaspaResolver.cs)中列出的两个 testnet-10 回退节点之一:-
wss://vector-10.kaspa.green/kaspa/testnet-10/wrpc/json(或/borsh) -
wss://muon-10.kaspa.blue/kaspa/testnet-10/wrpc/json(或/borsh)
-
这些是第三方节点,不由 NKaspa 运营,不保证可用。撰写时 vector-10 可以连通,muon-10 连不上。
- testnet 测试币:Kaspa 官方 Wiki 列出的 testnet-10 水龙头 https://faucet-tn10.kaspanet.io/ 。请在浏览器中打开,页面会要求人机验证(captcha)。
端口表:选 JSON 还是 Borsh?
| 网络 | gRPC(会被拒绝) | wRPC Borsh | wRPC JSON |
|---|---|---|---|
| mainnet | 16110 | 17110 | 18110 |
| testnet-10 | 16210 | 17210 | 18210 |
| simnet | 16510 | 17510 | 18510 |
| devnet | 16610 | 17610 | 18610 |
使用 KaspaRpcEncoding.Auto(默认值)时,URL 路径优先于端口:路径含 /wrpc/borsh 选 Borsh,含 /wrpc/json 选 JSON;否则按默认 wRPC 端口决定。如果路径和端口都无法识别(例如 443 端口的 wss 且路径没有提示),Create 会抛异常,不会自行猜测。填 gRPC 端口时一律抛 ArgumentException,错误信息会提示改用 wRPC 端口。
如果服务器发来的帧类型不对(JSON 客户端收到二进制帧,或 Borsh 客户端收到文本帧),挂起的请求会立即以 KaspaRpcEncodingMismatchException 失败。客户端不会等到超时,也不会重连。
两种编码功能相同,跟着节点开放的端口选即可。
连接节点
using NKaspa; using NKaspa.RPC; await using var rpc = KaspaRpcClient.Create(url); // returns a KaspaRpcConnection; does NOT connect await rpc.ConnectAsync(cancellationToken); 方法签名:
public static KaspaRpcConnection Create(string url, KaspaRpcEncoding encoding = KaspaRpcEncoding.Auto, KaspaWebSocketOptions? options = null) KaspaRpcConnection 实现了 IKaspaRpcClient 和 IAsyncDisposable,请用 await using 释放。KaspaWebSocketOptions 可设置连接超时(默认 15 秒)、请求超时(默认 30 秒)、AutoReconnect 和 MaxMessageBytes(默认 16 MiB)。
先检查节点
var info = await rpc.GetServerInfoAsync(cancellationToken); Console.WriteLine($"network={info.NetworkId} synced={info.IsSynced} utxoindex={info.HasUtxoIndex}"); if (!info.IsSynced || !info.HasUtxoIndex) return; // stop here 节点未同步或没有开启 UTXO index 时请停止;没有 UTXO index 就无法列出 UTXO。在 testnet-10 上,NetworkId 为 testnet-10。
从助记词派生 testnet 地址
从环境变量读取助记词。不要把它写进源代码、日志或截图。
var words = Environment.GetEnvironmentVariable("NKASPA_MNEMONIC"); var mnemonic = new Mnemonic(words, Wordlist.English); if (!mnemonic.IsValidChecksum) return; using var master = mnemonic.DeriveExtKey(); using var receive = master.Derive(KeyPath.KaspaReceive(0)); using var changeKey = master.Derive(KeyPath.KaspaChange(0)); using var spendKey = receive.PrivateKey; var network = Network.Testnet; var own = spendKey.GetAddress(network); // kaspatest:... var changeAddress = changeKey.PrivateKey.GetAddress(network); 请始终显式传入 Network.Testnet。在 0.1.11-alpha 中,ExtPubKey.Parse 无法区分主网和 testnet 的扩展公钥,网络由你传给 GetAddress 的参数决定。testnet 地址以 kaspatest: 开头。
另见仓库中的 docs/tutorials/generate-kaspa-address-csharp.md。
读取 UTXO 并转换为 Coin
var listed = await rpc.GetUtxosByAddressesAsync(new[] { own.ToString() }, cancellationToken); if (listed.Count == 0) { Console.WriteLine($"No UTXO on {own}. Nothing was submitted."); return; } var coins = new Coin[listed.Count]; for (var i = 0; i < listed.Count; i++) coins[i] = new Coin(listed[i].Outpoint, listed[i].Entry); 其他读取方法:GetBalanceByAddressAsync、GetBalancesByAddressesAsync、GetFeeEstimateAsync。
构建并签名交易
var payee = KaspaAddress.Parse(to, network); var amount = Money.Coins(0.1m); // small amount var builder = new TransactionBuilder(network).AddKeys(spendKey).AddCoins(coins); var tx = builder .Send(payee, amount) .SetChange(changeAddress) .SendMinimumRelayFee() .BuildTransaction(true); // true = sign 说明:
- 也可以用
SendEstimatedFees(FeeRate)代替最低中继手续费。 -
SetVersion(1)构建版本 1 的交易。 - 低于
MinimumChange(默认 0.02 KAS)的找零会并入手续费。可通过builder.ChangeFoldedIntoFee查看并入了多少。 - 参考数据来自仓库的离线示例(不联网):单输入支付 203,600 sompi(0.002036 KAS),中继 mass 为 2,036。这些只是离线示例数值,不代表节点实际收取的费用。
广播前在本地校验
以下各项全部通过才提交:
- 有一个输出按
amount准确支付到收款方脚本。 - 输入总额 ≥ 输出总额。
-
tx.Verify(entries)返回 true(entries为每个输入对应的UtxoEntry,按输入顺序)。也可以用Script.VerifyScript逐个输入检查。 - 手续费等于
MassCalculator.MinimumRelayFee(MassCalculator.Calculate(tx, entries))。
if (!paid || outputSum > inputSum || !tx.Verify(entries)) return; // nothing submitted var fee = inputSum - outputSum; var minimum = MassCalculator.MinimumRelayFee(MassCalculator.Calculate(tx, entries)); if (fee != minimum) return; 构建 entries、inputSum、outputSum 和 paid 的完整循环见 samples/Tutorials/SendTutorial.cs。
提交到 testnet
uint256 id = await rpc.SubmitTransactionAsync(tx, false, cancellationToken); Console.WriteLine($"submitted: {id}"); 签名:Task<uint256> SubmitTransactionAsync(Transaction transaction, bool allowOrphan = false, CancellationToken cancellationToken = default),返回交易 id。
输出示例:
network: testnet fee-sompi: <fee> submitted: <txid> 完整方法是 samples/Tutorials 中的 SendTutorial.SendOnTestnetAsync。它默认使用 Network.Testnet,未设置 NKASPA_MNEMONIC、NKASPA_WRPC_URL、NKASPA_TO 和 NKASPA_AMOUNT 时拒绝运行,默认也拒绝主网。示例的 Main 不会调用它。
未做端到端实测,仅为示例代码。 上文的连接和查询步骤(
ConnectAsync、GetServerInfoAsync、余额和 UTXO 读取)已通过 JSON 和 Borsh 两种编码在vector-10testnet 节点上实测成功。构建、签名和提交步骤尚未在真实节点上运行。请在你自己的环境中用测试币自行验证。
samples/Tutorials 只运行离线检查,不会发送。要试发送路径,请新建一个引用 NKaspa 0.1.11-alpha 的控制台项目,把 SendTutorial.cs 复制进去,然后自己调用:
return await NKaspa.Samples.Tutorials.SendTutorial.SendOnTestnetAsync(Console.Out, Console.Error); export NKASPA_MNEMONIC='<throwaway testnet mnemonic>' export NKASPA_WRPC_URL='wss://vector-10.kaspa.green/kaspa/testnet-10/wrpc/borsh' export NKASPA_TO='<another kaspatest: address>' export NKASPA_AMOUNT='0.1' dotnet run 断线重连与订阅
在 KaspaWebSocketOptions 中设置 AutoReconnect 可在断线后重连。订阅不会自动恢复。 重连后,请重新调用 NotifyUtxosChangedAsync(以及其他 Notify*),并重新查询余额,以免漏掉变化。
KaspaRpcConnection 暴露两个事件:Reconnected(Action,在断开的 socket 重新打开后触发;首次 ConnectAsync 不触发)和 StateChanged(Action<KaspaWebSocketState>)。State 是只读属性,不是事件。
rpc.StateChanged += state => Console.WriteLine($"state: {state}"); rpc.Reconnected += () => { // Subscriptions are gone: subscribe again with NotifyUtxosChangedAsync // and re-read the balance (do async work off this handler). }; 完整的只读(watch-only)示例见 samples/AspNetCheckout(仅在 main 上,于 0.1.11-alpha 发布之后加入;它是示例,不是库 API)。
常见错误排查
- 提到 gRPC 的
ArgumentException— 你用了 16x10 端口。testnet-10 请用 17210(Borsh)或 18210(JSON)。 - 443 端口的 URL 让
Create抛异常 — 在路径中加上/wrpc/json或/wrpc/borsh,或显式传入编码。 -
KaspaRpcEncodingMismatchException— 节点使用的是另一种编码。请更换路径或编码。 - 没有 UTXO — 先从水龙头给地址充值并等待;同时确认节点开启了 UTXO index。
- 节点拒绝手续费 — 按 Toccata 之前的费率(1 sompi/gram)算出的手续费低于 Toccata 之后的下限(100 sompi/gram)。请使用
SendMinimumRelayFee()或当前网络的手续费估算。 - 找零“消失”了 — 找零低于
MinimumChange,已并入手续费;查看ChangeFoldedIntoFee。
常见问题
C# 有 Kaspa SDK 吗? 有一个社区库 NKaspa(NuGet NKaspa,0.1.11-alpha,未审计)。它提供地址、HD 派生、wRPC 客户端(JSON 和 Borsh)以及交易构建和签名。它不是 Kaspa 官方项目。
NKaspa 支持 gRPC 吗? 不支持,只支持 WebSocket 上的 wRPC。把 16x10 的 gRPC 端口传给 KaspaRpcClient.Create 会抛异常,并提示改用 17x10(Borsh)或 18x10(JSON)。
JSON 和 Borsh 选哪个? 都可以,两者提供相同的调用。KaspaRpcEncoding.Auto 会根据 URL 路径或端口自动选择,用节点提供的那种即可。
KaspaRpcClient.Create 会自动连接吗? 不会。它返回 KaspaRpcConnection;需要调用 ConnectAsync,并用 await using 释放。
怎么在 C# 里发送 Kaspa 交易? 读取 UTXO,用 TransactionBuilder 构建并签名,用 tx.Verify 在本地校验,然后调用 SubmitTransactionAsync。本教程只在 testnet 上演示。
断线后订阅还在吗? 不在。开启 AutoReconnect 后连接会恢复,但需要重新订阅。
怎么确保用的是 testnet? 显式传入 Network.Testnet,并检查地址以 kaspatest: 开头。不要依赖 ExtPubKey.Parse 来判断网络。
能离线签名吗? 可以。构建和签名不需要连接。BuildPSKT 可生成部分签名交易。
能用于真实资金吗? 不建议。NKaspa 是 alpha 版本、未经审计。请先在 testnet 上用小额测试,并自行评估风险。
下一步
- 收款:
samples/AspNetCheckout(仅在 main 上)以及收款教程(发布后补链接)。 - 生成地址:
docs/tutorials/generate-kaspa-address-csharp.md。 - NKaspa 仓库中的发送教程:
docs/tutorials/send-kaspa-transaction-dotnet.md。 - PSKT 多签:
samples/PsktMultisig。节点查询:samples/NodeQuery。花费:samples/Spend。 - NBitcoin 用户:
docs/NBitcoin-Mapping.en.md。 - NKaspaDoc(API 参考与指南,GitHub 仓库):https://github.com/lifeme2027/NKaspaDoc
觉得有用就去 GitHub 给 NKaspa 点个 Star:github.com/lifeme2027/NKaspa。dotnet add package NKaspa --version 0.1.11-alpha,在 testnet 上跑一遍,遇到问题欢迎提 issue。
免责声明: NKaspa 目前是 alpha 版本(0.1.11-alpha),未经安全审计。它是社区项目,不是 Kaspa 官方项目。本教程只在 Kaspa testnet 上演示(仅 testnet-10),请只用测试币和小额。不要用于真实资金。公共节点由第三方运营,不保证可用。