diff --git a/src/Angor/Client/Pages/Settings.razor b/src/Angor/Client/Pages/Settings.razor index bdb762e6..7f952990 100644 --- a/src/Angor/Client/Pages/Settings.razor +++ b/src/Angor/Client/Pages/Settings.razor @@ -131,19 +131,7 @@ protected override Task OnInitializedAsync() { - settingsInfo = _clientStorage.GetSettingsInfo(); - - if(!settingsInfo.Indexers.Any()) - { - settingsInfo.Indexers.AddRange(_networkConfiguration.GetDefaultIndexerUrls()); - _clientStorage.SetSettingsInfo(settingsInfo); - } - - if(!settingsInfo.Relays.Any()) - { - settingsInfo.Relays.AddRange(_networkConfiguration.GetDefaultRelayUrls()); - _clientStorage.SetSettingsInfo(settingsInfo); - } + _networkService.AddSettingsIfNotExist(); networkType = _networkConfiguration.GetNetwork().Name; diff --git a/src/Angor/Client/Pages/Wallet.razor b/src/Angor/Client/Pages/Wallet.razor index 8385a308..9c10538f 100644 --- a/src/Angor/Client/Pages/Wallet.razor +++ b/src/Angor/Client/Pages/Wallet.razor @@ -7,6 +7,7 @@ @using Angor.Shared.Models @using Angor.Client.Components +@inject HttpClient _httpClient; @inject IClientStorage storage; @inject IWalletStorage _walletStorage; @inject ILogger Logger; @@ -163,7 +164,12 @@

Receive Address

@nextReceiveAddress

- + + + @if (network.NetworkType == NetworkType.Testnet) + { + + }
@@ -660,4 +666,33 @@ coinControlModal = true; StateHasChanged(); } + + private async Task GetTestCoins() + { + if (Money.Satoshis(localAccountInfo.TotalBalance).ToUnit(MoneyUnit.BTC) > 10) + { + notificationComponent.ShowNotificationMessage("you already have coins!"); + return; + } + + var operationResult = await notificationComponent.LongOperation(async () => + { + var res = await _httpClient.GetAsync($"/api/faucet/send/{localAccountInfo.GetNextReceiveAddress()}"); + + if (res.IsSuccessStatusCode) + { + var trxhex = await res.Content.ReadAsStringAsync(); + var trx = network.CreateTransaction(trxhex); + localAccountInfo.TotalUnConfirmedBalance = trx.Outputs.FirstOrDefault()?.Value.Satoshi ?? 50; + return new OperationResult { Success = true }; + } + + return new OperationResult { Success = false, Message = await res.Content.ReadAsStringAsync()}; + }); + + if (operationResult is { Success: true }) + { + notificationComponent.ShowNotificationMessage("Success!"); + } + } } \ No newline at end of file diff --git a/src/Angor/Client/Shared/NavMenu.razor b/src/Angor/Client/Shared/NavMenu.razor index 3177500f..b5e6dbe5 100644 --- a/src/Angor/Client/Shared/NavMenu.razor +++ b/src/Angor/Client/Shared/NavMenu.razor @@ -107,6 +107,8 @@ protected override async Task OnInitializedAsync() { + _networkService.AddSettingsIfNotExist(); + await _networkService.CheckServices(); await base.OnInitializedAsync(); diff --git a/src/Angor/Server/FaucetController.cs b/src/Angor/Server/FaucetController.cs new file mode 100644 index 00000000..304108d6 --- /dev/null +++ b/src/Angor/Server/FaucetController.cs @@ -0,0 +1,147 @@ +using Angor.Server; +using Angor.Shared; +using Angor.Shared.Models; +using Angor.Shared.ProtocolNew; +using Blockcore.Consensus.TransactionInfo; +using Blockcore.NBitcoin.BIP39; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using Angor.Client.Services; +using Angor.Shared.Services; +using Blockcore.NBitcoin; +using Blockcore.NBitcoin.BIP32; +using Blockcore.Networks; +using ExtPubKey = Blockcore.NBitcoin.BIP32.ExtPubKey; +using Network = Blockcore.Networks.Network; +using static System.Runtime.InteropServices.JavaScript.JSType; +using TransactionBuilder = Blockcore.Consensus.TransactionInfo.TransactionBuilder; +using BitcoinWitPubKeyAddress = Blockcore.NBitcoin.BitcoinWitPubKeyAddress; +using Money = Blockcore.NBitcoin.Money; +using Transaction = Blockcore.Consensus.TransactionInfo.Transaction; + +namespace Blockcore.AtomicSwaps.Server.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class FaucetController : ControllerBase + { + private readonly IWalletOperations _walletOperations; + private readonly IIndexerService _indexerService; + private readonly IHdOperations _hdOperations; + private readonly INetworkConfiguration _networkConfiguration; + + private List PendingUtxo = new (); + + public FaucetController(IWalletOperations walletOperations, IIndexerService indexerService, IHdOperations hdOperations, INetworkConfiguration networkConfiguration) + { + _walletOperations = walletOperations; + _indexerService = indexerService; + _hdOperations = hdOperations; + _networkConfiguration = networkConfiguration; + } + + [HttpGet] + [Route("send/{address}/{amount?}")] + public async Task Send(string address, long? amount) + { + var network = _networkConfiguration.GetNetwork(); + + //var mnemonic = new Mnemonic(wrods); + var words = new WalletWords { Words = "margin radio diamond leg loud street announce guitar video shiver speed eyebrow" }; + + var accountInfo = _walletOperations.BuildAccountInfoForWalletWords(words); + + // the miners address with all the utxos + var addressInfo = GenerateAddressFromPubKey(0, _networkConfiguration.GetNetwork(), false, ExtPubKey.Parse(accountInfo.ExtPubKey, _networkConfiguration.GetNetwork())); + + List list = new(); + + if (!PendingUtxo.Any()) + { + // we assume a miner wallet so for now just ignore amounts and send a utxo to the request address + var utxos = await _indexerService.FetchUtxoAsync(addressInfo.Address, 510, 5); + + lock (PendingUtxo) + { + if (!PendingUtxo.Any()) + { + PendingUtxo.AddRange(utxos); + } + } + } + + lock (PendingUtxo) + { + list = new() { new UtxoDataWithPath { HdPath = addressInfo.HdPath, UtxoData = PendingUtxo.First() } }; + PendingUtxo.Remove(PendingUtxo.First()); + } + + var (coins, keys) = _walletOperations.GetUnspentOutputsForTransaction(words, list); + + Transaction trx = network.CreateTransaction(); + trx.AddOutput(Money.Satoshis(list.First().UtxoData.value) - Money.Satoshis(10000), BitcoinWitPubKeyAddress.Create(address, network)); + trx.AddInput(new TxIn { PrevOut = list.First().UtxoData.outpoint.ToOutPoint() }); + + var signedTransaction = new TransactionBuilder(network) + .AddCoins(coins) + .AddKeys(keys.ToArray()) + .SignTransaction(trx); + + var res = await _walletOperations.PublishTransactionAsync(network, signedTransaction); + + if (res.Success) + { + return Ok(signedTransaction.ToHex(_networkConfiguration.GetNetwork().Consensus.ConsensusFactory)); + } + + return BadRequest(res.Message); + } + + private AddressInfo GenerateAddressFromPubKey(int scanIndex, Network network, bool isChange, ExtPubKey accountExtPubKey) + { + var pubKey = _hdOperations.GeneratePublicKey(accountExtPubKey, scanIndex, isChange); + var path = _hdOperations.CreateHdPath(84, network.Consensus.CoinType, 0, isChange, scanIndex); + var address = pubKey.GetSegwitAddress(network).ToString(); + + return new AddressInfo { Address = address, HdPath = path }; + } + } + + public class NetworkServiceMock : INetworkService + { + public Task CheckServices(bool force = false) + { + throw new NotImplementedException(); + } + + public void AddSettingsIfNotExist() + { + throw new NotImplementedException(); + } + + public SettingsUrl GetPrimaryIndexer() + { + return new SettingsUrl { Url = "https://tbtc.indexer.angor.io" }; + } + + public SettingsUrl GetPrimaryRelay() + { + throw new NotImplementedException(); + } + + public List GetRelays() + { + throw new NotImplementedException(); + } + + public void CheckAndHandleError(HttpResponseMessage httpResponseMessage) + { + + } + + public void HandleException(Exception exception) + { + throw exception; + } + } +} \ No newline at end of file diff --git a/src/Angor/Server/Program.cs b/src/Angor/Server/Program.cs index a5952e37..6632dbf8 100644 --- a/src/Angor/Server/Program.cs +++ b/src/Angor/Server/Program.cs @@ -6,6 +6,8 @@ using Angor.Client; using Angor.Shared; using Blockcore.AtomicSwaps.Server.Controllers; +using Angor.Client.Services; +using Angor.Shared.Services; var builder = WebApplication.CreateBuilder(args); @@ -20,7 +22,11 @@ // types needed to build investor sigs builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/Angor/Shared/Services/INetworkService.cs b/src/Angor/Shared/Services/INetworkService.cs index 8420cca5..58b92e4e 100644 --- a/src/Angor/Shared/Services/INetworkService.cs +++ b/src/Angor/Shared/Services/INetworkService.cs @@ -5,12 +5,10 @@ namespace Angor.Shared.Services; public interface INetworkService { Task CheckServices(bool force = false); - + void AddSettingsIfNotExist(); SettingsUrl GetPrimaryIndexer(); SettingsUrl GetPrimaryRelay(); List GetRelays(); - void CheckAndHandleError(HttpResponseMessage httpResponseMessage); - void HandleException(Exception exception); } \ No newline at end of file diff --git a/src/Angor/Shared/Services/NetworkService.cs b/src/Angor/Shared/Services/NetworkService.cs index 68c82efd..5a2590ec 100644 --- a/src/Angor/Shared/Services/NetworkService.cs +++ b/src/Angor/Shared/Services/NetworkService.cs @@ -11,12 +11,31 @@ public class NetworkService : INetworkService private readonly INetworkStorage _networkStorage; private readonly HttpClient _httpClient; private readonly ILogger _logger; + private readonly INetworkConfiguration _networkConfiguration; - public NetworkService(INetworkStorage networkStorage, HttpClient httpClient, ILogger logger) + public NetworkService(INetworkStorage networkStorage, HttpClient httpClient, ILogger logger, INetworkConfiguration networkConfiguration) { _networkStorage = networkStorage; _httpClient = httpClient; _logger = logger; + _networkConfiguration = networkConfiguration; + } + + public void AddSettingsIfNotExist() + { + var settings = _networkStorage.GetSettings(); + + if (!settings.Indexers.Any()) + { + settings.Indexers.AddRange(_networkConfiguration.GetDefaultIndexerUrls()); + _networkStorage.SetSettings(settings); + } + + if (!settings.Relays.Any()) + { + settings.Relays.AddRange(_networkConfiguration.GetDefaultRelayUrls()); + _networkStorage.SetSettings(settings); + } } public async Task CheckServices(bool force = false)