用C#和BSV库写一个加密日记本:从私钥管理到OP_RETURN数据上链全流程
用C#和BSV构建加密日记本从零实现区块链数据存储引言在这个数字时代隐私和数据安全变得前所未有的重要。想象一下如果你能将自己的私人日记永久地、不可篡改地存储在区块链上同时确保只有你本人能够解密和阅读那会是怎样的体验这正是我们将要构建的加密日记本应用的核心价值。对于C#开发者来说利用BSVBitcoin SV区块链的OP_RETURN功能存储加密数据是一个既实用又有趣的项目。BSV区块链以其稳定的协议、低廉的交易费用和大区块容量成为存储数据的理想选择。本文将带你从零开始使用C#和NBitcoin库构建一个完整的WinForms应用实现日记的加密、上链存储和解密阅读全流程。1. 项目准备与环境搭建1.1 创建WinForms项目首先在Visual Studio中创建一个新的Windows Forms App (.NET Framework)项目。我们将使用.NET Framework 4.7.2或更高版本以确保与所需库的兼容性。// 在Program.cs中确保应用程序以单实例运行 static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } }1.2 安装必要的NuGet包通过NuGet包管理器安装以下关键依赖NBitcoin用于处理比特币协议和加密操作Newtonsoft.Json用于处理JSON数据System.Security.Cryptography提供AES加密支持Install-Package NBitcoin Install-Package Newtonsoft.Json1.3 设计主界面创建一个简洁直观的用户界面包含以下核心元素私钥输入文本框日记内容编辑区域加密/解密操作按钮历史记录显示区域// MainForm.Designer.cs中的关键控件 private System.Windows.Forms.TextBox txtPrivateKey; private System.Windows.Forms.RichTextBox rtbDiaryContent; private System.Windows.Forms.Button btnEncryptSave; private System.Windows.Forms.Button btnDecryptLoad; private System.Windows.Forms.ListBox lstHistory;2. 密钥管理与钱包功能2.1 生成和管理BSV密钥对安全地生成和管理密钥是应用的核心。我们使用NBitcoin库来生成符合BSV标准的密钥对。public class KeyManager { public static string GenerateNewPrivateKey(bool testnet true) { var network testnet ? Network.TestNet : Network.Main; var privateKey new Key(); return privateKey.GetWif(network).ToWif(); } public static bool ValidatePrivateKey(string wifKey, out BitcoinSecret secret) { try { secret new BitcoinSecret(wifKey); return true; } catch { secret null; return false; } } }2.2 从私钥派生地址一旦有了有效的私钥我们可以派生出对应的BSV地址用于交易和查询。public static string GetAddressFromPrivateKey(string wifKey) { if (!ValidatePrivateKey(wifKey, out var secret)) throw new ArgumentException(Invalid private key); return secret.PubKey.Hash.GetAddress(secret.Network).ToString(); }注意私钥是访问资金的唯一凭证必须妥善保管。应用中应该考虑实现密钥的安全存储机制如使用Windows凭据管理器或硬件安全模块(HSM)。3. 日记加密与解密3.1 实现AES加密使用.NET内置的AES算法对日记内容进行加密确保只有私钥持有者能够解密。public class AesEncryption { public static byte[] Encrypt(string plainText, string password) { using (Aes aes Aes.Create()) { // 从密码派生密钥和IV var key new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(BSVEncDiary123), 10000); aes.Key key.GetBytes(32); aes.IV key.GetBytes(16); using (var encryptor aes.CreateEncryptor()) using (var ms new MemoryStream()) { using (var cs new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) using (var sw new StreamWriter(cs)) { sw.Write(plainText); } return ms.ToArray(); } } } public static string Decrypt(byte[] cipherText, string password) { // 类似Encrypt方法的实现使用CreateDecryptor } }3.2 数据编码与压缩在将加密数据上链前我们需要进行适当的编码和压缩以节省链上存储空间和费用。public static class DataHelper { public static string CompressAndEncode(byte[] data) { using (var ms new MemoryStream()) { using (var gzip new GZipStream(ms, CompressionMode.Compress)) { gzip.Write(data, 0, data.Length); } return Base58CheckEncoder.Encode(ms.ToArray()); } } public static byte[] DecodeAndDecompress(string encoded) { var bytes Base58CheckEncoder.DecodeData(encoded); using (var ms new MemoryStream(bytes)) using (var gzip new GZipStream(ms, CompressionMode.Decompress)) using (var output new MemoryStream()) { gzip.CopyTo(output); return output.ToArray(); } } }4. BSV链上数据存储4.1 构建OP_RETURN交易OP_RETURN脚本允许我们在BSV交易中嵌入最多100KB的数据非常适合存储加密日记。public class BsvTransactionHelper { public static Transaction CreateOpReturnTransaction( BitcoinSecret privateKey, string opReturnData, decimal feeRate 0.5m) { var network privateKey.Network; var address privateKey.PubKey.Hash.GetAddress(network); // 创建交易构建器 var builder network.CreateTransactionBuilder(); // 添加OP_RETURN输出 var script TxNullDataTemplate.Instance.GenerateScriptPubKey( Encoding.UTF8.GetBytes(opReturnData)); // 构建并签名交易 var tx builder .AddCoins(new Coin(new OutPoint(), new TxOut(Money.Zero, address))) .AddKeys(privateKey) .Send(script, Money.Zero) .SendFees(Money.Coins(feeRate)) .SetChange(address) .BuildTransaction(true); return tx; } }4.2 广播交易到BSV网络创建交易后我们需要将其广播到BSV网络。可以使用公共API或自建节点实现。public static async Taskstring BroadcastTransaction( Network network, Transaction tx) { var client new HttpClient(); var apiUrl network Network.Main ? https://api.whatsonchain.com/v1/bsv/main/tx/raw : https://api.whatsonchain.com/v1/bsv/test/tx/raw; var content new StringContent( JsonConvert.SerializeObject(new { txhex tx.ToHex() }), Encoding.UTF8, application/json); var response await client.PostAsync(apiUrl, content); if (response.IsSuccessStatusCode) { var result await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObjectdynamic(result).txid; } throw new Exception($广播失败: {response.StatusCode}); }5. 查询与解密历史日记5.1 查询地址历史交易通过区块链浏览器API查询特定地址的所有包含OP_RETURN输出的交易。public class TransactionHistory { public static async TaskListstring GetOpReturnTransactions(string address) { var client new HttpClient(); var apiUrl $https://api.whatsonchain.com/v1/bsv/test/address/{address}/history; var response await client.GetAsync(apiUrl); if (response.IsSuccessStatusCode) { var history JsonConvert.DeserializeObjectListTransactionInfo( await response.Content.ReadAsStringAsync()); // 获取所有交易详情并筛选包含OP_RETURN的交易 var opReturnTxs new Liststring(); foreach (var tx in history) { var txDetail await GetTransaction(tx.TxHash); if (txDetail.ContainsOpReturn) opReturnTxs.Add(tx.TxHash); } return opReturnTxs; } throw new Exception(查询历史失败); } private class TransactionInfo { public string TxHash { get; set; } } }5.2 从交易中提取并解密数据获取交易详情后提取OP_RETURN数据并解密还原为原始日记内容。public static async TaskListDiaryEntry GetDiaryEntries( string privateKeyWif, Liststring transactionHashes) { var entries new ListDiaryEntry(); var secret new BitcoinSecret(privateKeyWif); foreach (var txHash in transactionHashes) { var tx await GetTransactionDetails(txHash); var opReturnData ExtractOpReturnData(tx); try { var compressedData DataHelper.DecodeAndDecompress(opReturnData); var decrypted AesEncryption.Decrypt(compressedData, privateKeyWif); entries.Add(new DiaryEntry { TransactionId txHash, Content decrypted, Timestamp tx.BlockTime }); } catch { // 处理解密失败的情况 } } return entries.OrderBy(e e.Timestamp).ToList(); } private class DiaryEntry { public string TransactionId { get; set; } public string Content { get; set; } public DateTime Timestamp { get; set; } }6. 应用优化与扩展6.1 实现本地缓存为了提升用户体验可以实现本地缓存机制避免频繁查询区块链网络。public class DiaryCache { private static readonly string CacheFile diary_cache.json; public static void SaveToCache(ListDiaryEntry entries) { var json JsonConvert.SerializeObject(entries); File.WriteAllText(CacheFile, json); } public static ListDiaryEntry LoadFromCache() { if (!File.Exists(CacheFile)) return new ListDiaryEntry(); var json File.ReadAllText(CacheFile); return JsonConvert.DeserializeObjectListDiaryEntry(json) ?? new ListDiaryEntry(); } public static void MergeWithCache(ListDiaryEntry newEntries) { var cached LoadFromCache(); var merged cached .UnionBy(newEntries, e e.TransactionId) .OrderBy(e e.Timestamp) .ToList(); SaveToCache(merged); } }6.2 添加元数据支持扩展日记条目支持标题、标签等元数据增强实用性。public class EnhancedDiaryEntry : DiaryEntry { public string Title { get; set; } public Liststring Tags { get; set; } public Mood Mood { get; set; } public string ToEncryptedString() { return JsonConvert.SerializeObject(this); } public static EnhancedDiaryEntry FromDecrypted(string json) { return JsonConvert.DeserializeObjectEnhancedDiaryEntry(json); } } public enum Mood { Happy, Sad, Neutral, Excited, Angry }6.3 实现搜索功能基于本地缓存实现全文搜索功能方便用户查找特定日记。public static ListDiaryEntry SearchEntries( ListDiaryEntry entries, string searchTerm) { return entries .Where(e e.Content.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) || (e is EnhancedDiaryEntry ed (ed.Title.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) || ed.Tags.Any(t t.Contains(searchTerm, StringComparison.OrdinalIgnoreCase))))) .ToList(); }7. 安全最佳实践7.1 密钥安全存储提供多种密钥存储选项平衡安全性和便利性。存储方式安全性便利性适用场景内存中低高临时使用加密配置文件中中个人使用Windows凭据管理器高中单用户环境硬件钱包最高低高安全需求7.2 交易费用优化根据不同需求调整交易费用策略平衡确认速度和成本。public static decimal CalculateOptimalFee(int dataSize) { // 当前费率sat/byte可以从API获取 var currentFeeRate GetCurrentFeeRate(); // 计算交易大致体积 var txSize 100 dataSize; // 基础开销数据大小 // 转换为BSV金额1e8 satoshis 1 BSV return (decimal)(txSize * currentFeeRate) / 100000000m; }7.3 错误处理与恢复健壮的错误处理机制确保应用在各种异常情况下都能优雅降级。public static async Taskstring SafeBroadcast(Transaction tx) { try { return await BroadcastTransaction(tx); } catch (HttpRequestException ex) { // 处理网络错误 Logger.Error($网络错误: {ex.Message}); return null; } catch (JsonException ex) { // 处理JSON解析错误 Logger.Error($数据解析错误: {ex.Message}); return null; } catch (Exception ex) { // 处理其他未知错误 Logger.Error($未知错误: {ex.Message}); return null; } }