| | | 1 | | using Microsoft.Extensions.Logging; |
| | | 2 | | using Microsoft.Extensions.Options; |
| | | 3 | | using NBitcoin; |
| | | 4 | | |
| | | 5 | | namespace NLightning.Infrastructure.Bitcoin.Wallet; |
| | | 6 | | |
| | | 7 | | using Domain.Bitcoin.Enums; |
| | | 8 | | using Domain.Bitcoin.ValueObjects; |
| | | 9 | | using Domain.Bitcoin.Wallet.Models; |
| | | 10 | | using Domain.Node.Options; |
| | | 11 | | using Domain.Persistence.Interfaces; |
| | | 12 | | using Domain.Protocol.Interfaces; |
| | | 13 | | using Interfaces; |
| | | 14 | | |
| | | 15 | | public class BitcoinWalletService : IBitcoinWalletService |
| | | 16 | | { |
| | 0 | 17 | | private readonly IBlockchainMonitor _blockchainMonitor; |
| | 0 | 18 | | private readonly ILogger<BitcoinWalletService> _logger; |
| | | 19 | | private readonly Network _network; |
| | 0 | 20 | | private readonly ISecureKeyManager _secureKeyManager; |
| | 0 | 21 | | private readonly IUnitOfWork _uow; |
| | | 22 | | |
| | 0 | 23 | | public BitcoinWalletService(IBlockchainMonitor blockchainMonitor, ILogger<BitcoinWalletService> logger, |
| | 0 | 24 | | IOptions<NodeOptions> nodeOptions, ISecureKeyManager secureKeyManager, |
| | 0 | 25 | | IUnitOfWork uow) |
| | 0 | 26 | | { |
| | 0 | 27 | | _blockchainMonitor = blockchainMonitor; |
| | 0 | 28 | | _logger = logger; |
| | 0 | 29 | | _secureKeyManager = secureKeyManager; |
| | 0 | 30 | | _uow = uow; |
| | | 31 | | |
| | 0 | 32 | | _network = Network.GetNetwork(nodeOptions.Value.BitcoinNetwork) ?? Network.Main; |
| | 0 | 33 | | _logger.LogInformation("BitcoinWalletService network: {Network} (config: {ConfigNetwork})", _network, nodeOption |
| | 0 | 34 | | } |
| | | 35 | | |
| | 0 | 36 | | public async Task<WalletAddressModel> GetUnusedAddressAsync(AddressType addressType, bool isChange) |
| | 0 | 37 | | { |
| | 0 | 38 | | if (addressType is not (AddressType.P2Wpkh or AddressType.P2Tr)) |
| | 0 | 39 | | throw new InvalidOperationException( |
| | 0 | 40 | | "You cannot use flags for this method. Please select only one address type."); |
| | 0 | 41 | | |
| | | 42 | | // Find an unused address in the DB |
| | 0 | 43 | | var addressModel = await _uow.WalletAddressesDbRepository.GetUnusedAddressAsync(addressType, isChange); |
| | 0 | 44 | | |
| | 0 | 45 | | if (addressModel is not null) |
| | 0 | 46 | | return addressModel; |
| | | 47 | | |
| | | 48 | | // If there's none, get the last used index from db |
| | 0 | 49 | | var lastUsedIndex = await _uow.WalletAddressesDbRepository.GetLastUsedAddressIndex(addressType, isChange); |
| | | 50 | | |
| | 0 | 51 | | _logger.LogInformation("Generating 10 new {addressType} {change}addresses and saving to the database.", |
| | 0 | 52 | | Enum.GetName(addressType), isChange ? "change " : string.Empty); |
| | | 53 | | |
| | 0 | 54 | | // Generate 10 new addresses |
| | 0 | 55 | | var addressList = new List<WalletAddressModel>(10); |
| | 0 | 56 | | for (var i = lastUsedIndex; i < lastUsedIndex + 10; i++) |
| | | 57 | | { |
| | 0 | 58 | | ExtPrivKey extPrivKey; |
| | 0 | 59 | | if (addressType == AddressType.P2Tr) |
| | 0 | 60 | | { |
| | 0 | 61 | | extPrivKey = _secureKeyManager.GetDepositP2TrKeyAtIndex(i, isChange); |
| | 0 | 62 | | var extKey = ExtKey.CreateFromBytes(extPrivKey); |
| | 0 | 63 | | var address = extKey.Neuter().PubKey.GetAddress(ScriptPubKeyType.TaprootBIP86, _network); |
| | | 64 | | |
| | 0 | 65 | | addressList.Add(new WalletAddressModel(addressType, i, isChange, address.ToString())); |
| | | 66 | | } |
| | | 67 | | else |
| | | 68 | | { |
| | 0 | 69 | | extPrivKey = _secureKeyManager.GetDepositP2WpkhKeyAtIndex(i, isChange); |
| | 0 | 70 | | var extKey = ExtKey.CreateFromBytes(extPrivKey); |
| | 0 | 71 | | var address = extKey.Neuter().PubKey.GetAddress(ScriptPubKeyType.Segwit, _network); |
| | 0 | 72 | | |
| | 0 | 73 | | addressList.Add(new WalletAddressModel(addressType, i, isChange, address.ToString())); |
| | 0 | 74 | | } |
| | 0 | 75 | | } |
| | | 76 | | |
| | 0 | 77 | | _uow.WalletAddressesDbRepository.AddRange(addressList); |
| | 0 | 78 | | await _uow.SaveChangesAsync(); |
| | | 79 | | |
| | | 80 | | // Register all newly generated addresses with blockchain monitor |
| | 0 | 81 | | foreach (var address in addressList) |
| | | 82 | | { |
| | 0 | 83 | | _blockchainMonitor.WatchBitcoinAddress(address); |
| | 0 | 84 | | } |
| | | 85 | | |
| | 0 | 86 | | return addressList[0]; |
| | 0 | 87 | | } |
| | 0 | 88 | | } |