mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 07:34:24 +01:00
cleanup
This commit is contained in:
25
BTCPayServer.Plugins.Tests/BTCPayServer.Plugins.Tests.csproj
Normal file
25
BTCPayServer.Plugins.Tests/BTCPayServer.Plugins.Tests.csproj
Normal file
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Plugins\BTCPayServer.Plugins.Wabisabi\BTCPayServer.Plugins.Wabisabi.csproj" />
|
||||
<ProjectReference Include="..\submodules\walletwasabi\WalletWasabi\WalletWasabi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -45,6 +45,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.PluginPacker",
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.NIP05", "Plugins\BTCPayServer.Plugins.NIP05\BTCPayServer.Plugins.NIP05.csproj", "{362D2175-9632-418E-95B1-5D638C52ECA6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Tests", "submodules\btcpayserver\BTCPayServer.Tests\BTCPayServer.Tests.csproj", "{4146B6DF-7BEE-4BD0-B6B1-77E7630A1B81}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Tests", "BTCPayServer.Plugins.Tests\BTCPayServer.Plugins.Tests.csproj", "{C6E671F6-5417-4F2F-A5A2-0A7D307BA1F7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -213,6 +217,22 @@ Global
|
||||
{362D2175-9632-418E-95B1-5D638C52ECA6}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{362D2175-9632-418E-95B1-5D638C52ECA6}.Altcoins-Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{362D2175-9632-418E-95B1-5D638C52ECA6}.Altcoins-Release|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4146B6DF-7BEE-4BD0-B6B1-77E7630A1B81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4146B6DF-7BEE-4BD0-B6B1-77E7630A1B81}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4146B6DF-7BEE-4BD0-B6B1-77E7630A1B81}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4146B6DF-7BEE-4BD0-B6B1-77E7630A1B81}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4146B6DF-7BEE-4BD0-B6B1-77E7630A1B81}.Altcoins-Debug|Any CPU.ActiveCfg = Altcoins-Debug|Any CPU
|
||||
{4146B6DF-7BEE-4BD0-B6B1-77E7630A1B81}.Altcoins-Debug|Any CPU.Build.0 = Altcoins-Debug|Any CPU
|
||||
{4146B6DF-7BEE-4BD0-B6B1-77E7630A1B81}.Altcoins-Release|Any CPU.ActiveCfg = Altcoins-Release|Any CPU
|
||||
{4146B6DF-7BEE-4BD0-B6B1-77E7630A1B81}.Altcoins-Release|Any CPU.Build.0 = Altcoins-Release|Any CPU
|
||||
{C6E671F6-5417-4F2F-A5A2-0A7D307BA1F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C6E671F6-5417-4F2F-A5A2-0A7D307BA1F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C6E671F6-5417-4F2F-A5A2-0A7D307BA1F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C6E671F6-5417-4F2F-A5A2-0A7D307BA1F7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C6E671F6-5417-4F2F-A5A2-0A7D307BA1F7}.Altcoins-Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C6E671F6-5417-4F2F-A5A2-0A7D307BA1F7}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C6E671F6-5417-4F2F-A5A2-0A7D307BA1F7}.Altcoins-Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C6E671F6-5417-4F2F-A5A2-0A7D307BA1F7}.Altcoins-Release|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{B19C9F52-DC47-466D-8B5C-2D202B7B003F} = {9E04ECE9-E304-4FF2-9CBC-83256E6C6962}
|
||||
|
||||
@@ -15,8 +15,10 @@ using Microsoft.Extensions.Options;
|
||||
using NBitcoin.DataEncoders;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using WalletWasabi.Affiliation;
|
||||
using WalletWasabi.Affiliation.Models;
|
||||
using WalletWasabi.Affiliation.Models.CoinjoinRequest;
|
||||
using WalletWasabi.Affiliation.Models.CoinJoinNotification;
|
||||
using WalletWasabi.Affiliation.Serialization;
|
||||
|
||||
namespace BTCPayServer.Plugins.Wabisabi.AffiliateServer;
|
||||
|
||||
@@ -84,8 +86,8 @@ public class AffiliateServerController:Controller
|
||||
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("get_coinjoin_request")]
|
||||
public async Task<IActionResult> GetCoinjoinRequest([FromBody] GetCoinjoinRequestRequest request)
|
||||
[HttpPost("notify_coinjoin")]
|
||||
public async Task<IActionResult> GetCoinjoinRequest()
|
||||
{
|
||||
|
||||
var settings = await _settingsRepository.GetSettingAsync<WabisabiAffiliateSettings>();
|
||||
@@ -93,11 +95,13 @@ public class AffiliateServerController:Controller
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var keyB = Encoders.Hex.DecodeData(settings.SigningKey);
|
||||
ecdsa.ImportSubjectPublicKeyInfo(keyB.AsSpan(), out _);
|
||||
|
||||
Payload payload = new(new Header(), request.Body);
|
||||
using var reader = new StreamReader(Request.Body);
|
||||
var request =
|
||||
AffiliateServerHttpApiClient.Deserialize<CoinJoinNotificationRequest>(await reader.ReadToEndAsync());
|
||||
Payload payload = new(Header.Instance, request.Body);
|
||||
try
|
||||
{
|
||||
|
||||
@@ -109,7 +113,8 @@ public class AffiliateServerController:Controller
|
||||
|
||||
|
||||
await System.IO.File.AppendAllLinesAsync(path, new[] {JObject.FromObject(request).ToString(Formatting.None).Replace(Environment.NewLine, "")}, Encoding.UTF8);
|
||||
return Ok(new GetCoinjoinRequestResponse(Array.Empty<byte>()));
|
||||
var response = new CoinJoinNotificationResponse(Array.Empty<byte>());
|
||||
return Json(response, AffiliationJsonSerializationOptions.Settings);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using NBitcoin;
|
||||
using WalletWasabi.WabiSabi.Models;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Models;
|
||||
|
||||
public record AffiliateInformation
|
||||
(
|
||||
ImmutableArray<AffiliationFlag> RunningAffiliateServers,
|
||||
ImmutableDictionary<string, ImmutableDictionary<AffiliationFlag, byte[]>> CoinjoinRequests
|
||||
)
|
||||
{
|
||||
public static readonly AffiliateInformation Empty = new(ImmutableArray<AffiliationFlag>.Empty, ImmutableDictionary<string, ImmutableDictionary<AffiliationFlag, byte[]>>.Empty);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using WalletWasabi.Affiliation.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Models.CoinjoinRequest;
|
||||
|
||||
public record Body
|
||||
{
|
||||
public Body(IEnumerable<Input> inputs, IEnumerable<Output> outputs, long slip44CoinType, decimal feeRate, long noFeeThreshold, long minRegistrableAmount, long timestamp)
|
||||
{
|
||||
Inputs = inputs;
|
||||
Outputs = outputs;
|
||||
Slip44CoinType = slip44CoinType;
|
||||
FeeRate = feeRate;
|
||||
NoFeeThreshold = noFeeThreshold;
|
||||
MinRegistrableAmount = minRegistrableAmount;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "inputs")]
|
||||
public IEnumerable<Input> Inputs { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "outputs")]
|
||||
public IEnumerable<Output> Outputs { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "slip44_coin_type")]
|
||||
public long Slip44CoinType { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "fee_rate")]
|
||||
[JsonConverter(typeof(AffiliationFeeRateJsonConverter))]
|
||||
public decimal FeeRate { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "no_fee_threshold")]
|
||||
public long NoFeeThreshold { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "min_registrable_amount")]
|
||||
public long MinRegistrableAmount { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "timestamp")]
|
||||
public long Timestamp { get; }
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Models.CoinjoinRequest;
|
||||
|
||||
public record Header
|
||||
{
|
||||
[JsonProperty(PropertyName = "title")]
|
||||
public static readonly string Title = "payment request";
|
||||
|
||||
[JsonProperty(PropertyName = "version")]
|
||||
public static readonly int Version = 1;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using WalletWasabi.Affiliation.Serialization;
|
||||
using WalletWasabi.WabiSabi.Models;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Models.CoinjoinRequest;
|
||||
|
||||
public record Input
|
||||
{
|
||||
public Input(Outpoint prevout, byte[] scriptPubkey, bool isAffiliated, bool isNoFee)
|
||||
{
|
||||
Prevout = prevout;
|
||||
ScriptPubkey = scriptPubkey;
|
||||
IsAffiliated = isAffiliated;
|
||||
IsNoFee = isNoFee;
|
||||
|
||||
if (isNoFee && isAffiliated)
|
||||
{
|
||||
Logging.Logger.LogWarning($"Detected input with redundant affiliation flag: {Convert.ToHexString(prevout.Hash)}, {prevout.Index}");
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "prevout")]
|
||||
public Outpoint Prevout { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "script_pubkey")]
|
||||
[JsonConverter(typeof(AffiliationByteArrayJsonConverter))]
|
||||
public byte[] ScriptPubkey { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "is_affiliated")]
|
||||
public bool IsAffiliated { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "is_no_fee")]
|
||||
public bool IsNoFee { get; }
|
||||
|
||||
public static Input FromAffiliateInput(AffiliateInput affiliateInput, AffiliationFlag affiliationFlag)
|
||||
{
|
||||
return new Input(Outpoint.FromOutPoint(affiliateInput.Prevout), affiliateInput.ScriptPubKey.ToBytes(), affiliateInput.AffiliationFlag == affiliationFlag, affiliateInput.IsNoFee);
|
||||
}
|
||||
}
|
||||
|
||||
public record AffiliateInput
|
||||
{
|
||||
public AffiliateInput(OutPoint prevout, Script scriptPubKey, AffiliationFlag affiliationFlag, bool isNoFee)
|
||||
{
|
||||
Prevout = prevout;
|
||||
ScriptPubKey = scriptPubKey;
|
||||
AffiliationFlag = affiliationFlag;
|
||||
IsNoFee = isNoFee;
|
||||
}
|
||||
|
||||
public AffiliateInput(Coin coin, AffiliationFlag affiliationFlag, bool isNoFee)
|
||||
: this(coin.Outpoint, coin.ScriptPubKey, affiliationFlag, isNoFee)
|
||||
{
|
||||
}
|
||||
|
||||
public OutPoint Prevout { get; }
|
||||
public Script ScriptPubKey { get; }
|
||||
public AffiliationFlag AffiliationFlag { get; }
|
||||
public bool IsNoFee { get; }
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using WalletWasabi.Affiliation.Serialization;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Models.CoinjoinRequest;
|
||||
|
||||
public record Outpoint
|
||||
{
|
||||
public Outpoint(byte[] hash, long index)
|
||||
{
|
||||
Hash = hash;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "hash")]
|
||||
[JsonConverter(typeof(AffiliationByteArrayJsonConverter))]
|
||||
public byte[] Hash { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "index")]
|
||||
public long Index { get; }
|
||||
|
||||
public static Outpoint FromOutPoint(OutPoint outPoint)
|
||||
{
|
||||
return new Outpoint(outPoint.Hash.ToBytes(lendian: true), outPoint.N);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using WalletWasabi.Affiliation.Serialization;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Models.CoinjoinRequest;
|
||||
|
||||
public record Output
|
||||
{
|
||||
public Output(long amount, byte[] script_pubkey)
|
||||
{
|
||||
Amount = amount;
|
||||
ScriptPubkey = script_pubkey;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "amount")]
|
||||
public long Amount { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "script_pubkey")]
|
||||
[JsonConverter(typeof(AffiliationByteArrayJsonConverter))]
|
||||
public byte[] ScriptPubkey { get; }
|
||||
|
||||
public static Output FromTxOut(TxOut txOut)
|
||||
{
|
||||
return new Output(txOut.Value, txOut.ScriptPubKey.ToBytes());
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using WalletWasabi.Affiliation.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Models.CoinjoinRequest;
|
||||
|
||||
public record Payload
|
||||
{
|
||||
public Payload(Header header, Body body)
|
||||
{
|
||||
Header = header;
|
||||
Body = body;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "header")]
|
||||
public Header Header { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "body")]
|
||||
public Body Body { get; }
|
||||
|
||||
public byte[] GetCanonicalSerialization()
|
||||
{
|
||||
return Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(this, CanonicalJsonSerializationOptions.Settings));
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using WalletWasabi.Affiliation.Serialization;
|
||||
using WalletWasabi.Affiliation.Models.CoinjoinRequest;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Models;
|
||||
|
||||
public record GetCoinjoinRequestRequest
|
||||
{
|
||||
|
||||
[JsonProperty(PropertyName = "signature")]
|
||||
[JsonConverter(typeof(AffiliationByteArrayJsonConverter))]
|
||||
public byte[] Signature { get; }
|
||||
|
||||
[JsonProperty(PropertyName = "body")]
|
||||
public Body Body { get; }
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Models;
|
||||
|
||||
public class GetCoinjoinRequestResponse
|
||||
{
|
||||
[JsonProperty(PropertyName = "coinjoin_request")]
|
||||
public byte[] CoinjoinRequest;
|
||||
|
||||
public GetCoinjoinRequestResponse(byte[] coinjoinRequest)
|
||||
{
|
||||
CoinjoinRequest = coinjoinRequest;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace WalletWasabi.Affiliation.Models;
|
||||
|
||||
public record StatusRequest();
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace WalletWasabi.Affiliation.Models;
|
||||
|
||||
public record StatusResponse();
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using NBitcoin.DataEncoders;
|
||||
using Newtonsoft.Json;
|
||||
using WalletWasabi.Helpers;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Serialization;
|
||||
|
||||
public class AffiliationByteArrayJsonConverter : JsonConverter<byte[]>
|
||||
{
|
||||
public override byte[]? ReadJson(JsonReader reader, Type objectType, byte[]? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.Value is string serialized)
|
||||
{
|
||||
return Convert.FromHexString(serialized);
|
||||
}
|
||||
throw new JsonSerializationException("Cannot deserialize object.");
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, byte[]? value, JsonSerializer serializer)
|
||||
{
|
||||
Guard.NotNull(nameof(value), value);
|
||||
writer.WriteValue(Convert.ToHexString(value).ToLower());
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Serialization;
|
||||
|
||||
public class AffiliationFeeRateJsonConverter : JsonConverter<decimal>
|
||||
{
|
||||
public static readonly decimal Base = 1e-8m;
|
||||
|
||||
private static long EncodeDecimal(decimal value)
|
||||
{
|
||||
return (long)decimal.Round(value / Base);
|
||||
}
|
||||
|
||||
private static decimal DecodeDecimal(long value)
|
||||
{
|
||||
return value * Base;
|
||||
}
|
||||
|
||||
private static bool IsEncodable(decimal value)
|
||||
{
|
||||
return DecodeDecimal(EncodeDecimal(value)) == value;
|
||||
}
|
||||
|
||||
public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.Value is long number)
|
||||
{
|
||||
return DecodeDecimal(number);
|
||||
}
|
||||
throw new JsonSerializationException("Cannot deserialize object.");
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
|
||||
{
|
||||
if (!IsEncodable(value))
|
||||
{
|
||||
throw new ArgumentException("Decimal cannot be unambiguously encodable.", nameof(value));
|
||||
}
|
||||
writer.WriteValue(EncodeDecimal(value));
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using WalletWasabi.WabiSabi.Models;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Serialization;
|
||||
|
||||
public class AffiliationFlagConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||
{
|
||||
if (value is string)
|
||||
{
|
||||
return new AffiliationFlag((string)value);
|
||||
}
|
||||
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
if (value is AffiliationFlag)
|
||||
{
|
||||
return ((AffiliationFlag)value).Name;
|
||||
}
|
||||
}
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Serialization;
|
||||
|
||||
public static class AffiliationJsonSerializationOptions
|
||||
{
|
||||
public static readonly List<JsonConverter> Converters = new()
|
||||
{
|
||||
new AffiliationByteArrayJsonConverter(),
|
||||
new AffiliationFeeRateJsonConverter()
|
||||
};
|
||||
|
||||
public static readonly JsonSerializerSettings Settings = new() { Converters = Converters };
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WalletWasabi.Affiliation.Serialization;
|
||||
|
||||
public static class CanonicalJsonSerializationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// JSON settings that enforces JSON's objects' properties' to be serialized in alphabetical order.
|
||||
/// </summary>
|
||||
public static readonly JsonSerializerSettings Settings = new()
|
||||
{
|
||||
ContractResolver = new OrderedContractResolver(),
|
||||
Converters = AffiliationJsonSerializationOptions.Converters,
|
||||
|
||||
// Intentionally enforced default value.
|
||||
Formatting = Formatting.None
|
||||
};
|
||||
|
||||
/// <seealso href="https://stackoverflow.com/a/11309106/3744182"/>
|
||||
private class OrderedContractResolver : DefaultContractResolver
|
||||
{
|
||||
private static bool IsValidCharacter(char c)
|
||||
{
|
||||
return char.IsAscii(c) && ((char.IsLetter(c) && char.IsLower(c)) || char.IsDigit(c) || c == '_');
|
||||
}
|
||||
|
||||
private static bool IsValidPropertyName(string name)
|
||||
{
|
||||
return name.All(IsValidCharacter);
|
||||
}
|
||||
|
||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||
{
|
||||
IEnumerable<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||
|
||||
foreach (JsonProperty property in properties)
|
||||
{
|
||||
if (property.PropertyName is null)
|
||||
{
|
||||
throw new JsonSerializationException("Property name is not set");
|
||||
}
|
||||
|
||||
if (!IsValidPropertyName(property.PropertyName))
|
||||
{
|
||||
throw new JsonSerializationException("Object property contains an invalid character.");
|
||||
}
|
||||
}
|
||||
|
||||
return properties.OrderBy(p => p.PropertyName, StringComparer.Ordinal).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NBitcoin;
|
||||
using WalletWasabi.Blockchain.TransactionOutputs;
|
||||
@@ -83,29 +81,6 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
||||
new Dictionary<AnonsetType, int>() {{AnonsetType.Red, 1}, {AnonsetType.Orange, 1}, {AnonsetType.Green, 1}},
|
||||
_wallet.ConsolidationMode, liquidityClue);
|
||||
_logger.LogInformation(solution.ToString());
|
||||
// SubsetSolution bestSolution = null;
|
||||
// for (int i = 0; i < 100; i++)
|
||||
// {
|
||||
// var minCoins = new Dictionary<AnonsetType, int>();
|
||||
// if (_wallet.RedCoinIsolation)
|
||||
// {
|
||||
// minCoins.Add(AnonsetType.Red, 1);
|
||||
// }
|
||||
// var solution = SelectCoinsInternal(utxoSelectionParameters, coinCandidates, payments, Random.Shared.Next(10,31),
|
||||
// minCoins, new Dictionary<AnonsetType, int>()
|
||||
// {
|
||||
//
|
||||
// {AnonsetType.Red, 1},
|
||||
// {AnonsetType.Orange, 1},
|
||||
// {AnonsetType.Green, 1}
|
||||
// },_wallet.ConsolidationMode, liquidityClue);
|
||||
// if (bestSolution is null || solution.Score() > bestSolution.Score())
|
||||
// {
|
||||
// bestSolution = solution;
|
||||
// }
|
||||
// }
|
||||
// _logger.LogInformation(bestSolution.ToString());
|
||||
// return bestSolution.Coins.ToImmutableList();
|
||||
return solution.Coins.ToImmutableList();
|
||||
}
|
||||
|
||||
@@ -115,7 +90,6 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
||||
Dictionary<AnonsetType, int> maxPerType, Dictionary<AnonsetType, int> idealMinimumPerType,
|
||||
bool consolidationMode, Money liquidityClue)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
// Sort the coins by their anon score and then by descending order their value, and then slightly randomize in 2 ways:
|
||||
//attempt to shift coins that comes from the same tx AND also attempt to shift coins based on percentage probability
|
||||
var remainingCoins = SlightlyShiftOrder(RandomizeCoins(
|
||||
@@ -130,25 +104,6 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
||||
if (remainingCoins.All(coin => coin.CoinColor(_wallet.AnonScoreTarget) == AnonsetType.Green) &&
|
||||
!remainingPendingPayments.Any())
|
||||
{
|
||||
// var decidedAmt = Random.Shared.Next(10, maxCoins);
|
||||
// // all the coins are mixed and we have no payments to do..
|
||||
// //if we are trying to reduce our utxoset, and we
|
||||
// if (consolidationMode && remainingCoins.Count >= decidedAmt)
|
||||
// {
|
||||
//
|
||||
// for (int i = 0; i < decidedAmt; i++)
|
||||
// {
|
||||
//
|
||||
// var anonsetOrderedCoin =
|
||||
// remainingCoins.OrderBy(coin => coin.AnonymitySet).BiasedRandomElement(70);
|
||||
// solution.Coins.Add(anonsetOrderedCoin);
|
||||
// remainingCoins.Remove(anonsetOrderedCoin);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
//still good to have a chance to proceed with a join to reduce timing analysis
|
||||
|
||||
var rand = Random.Shared.Next(1, 1001);
|
||||
if (rand > _wallet.WabisabiStoreSettings.ExtraJoinProbability)
|
||||
{
|
||||
@@ -158,7 +113,6 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
||||
|
||||
_logger.LogInformation(
|
||||
"All coins are private and we have no pending payments but will join just to reduce timing analysis");
|
||||
//}
|
||||
}
|
||||
|
||||
while (remainingCoins.Any())
|
||||
@@ -246,9 +200,6 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
solution.TimeElapsed = stopwatch.Elapsed;
|
||||
return solution;
|
||||
}
|
||||
|
||||
@@ -316,7 +267,7 @@ public enum AnonsetType
|
||||
Green
|
||||
}
|
||||
|
||||
public class SubsetSolution : IEquatable<SubsetSolution>
|
||||
public class SubsetSolution
|
||||
{
|
||||
private readonly UtxoSelectionParameters _utxoSelectionParameters;
|
||||
|
||||
@@ -326,8 +277,6 @@ public class SubsetSolution : IEquatable<SubsetSolution>
|
||||
TotalPaymentsGross = totalPaymentsGross;
|
||||
AnonsetTarget = anonsetTarget;
|
||||
}
|
||||
|
||||
public TimeSpan TimeElapsed { get; set; }
|
||||
public List<SmartCoin> Coins { get; set; } = new();
|
||||
public List<PendingPayment> HandledPayments { get; set; } = new();
|
||||
|
||||
@@ -346,49 +295,6 @@ public class SubsetSolution : IEquatable<SubsetSolution>
|
||||
|
||||
public decimal LeftoverValue => TotalValue - TotalPaymentCost;
|
||||
|
||||
public decimal Score()
|
||||
{
|
||||
var score = 0m;
|
||||
|
||||
decimal ComputeCoinScore(List<SmartCoin> coins)
|
||||
{
|
||||
var w = 0m;
|
||||
foreach (var smartCoin in coins)
|
||||
{
|
||||
var val = smartCoin.EffectiveValue(_utxoSelectionParameters.MiningFeeRate,
|
||||
_utxoSelectionParameters.CoordinationFeeRate).ToDecimal(MoneyUnit.BTC);
|
||||
if (smartCoin.AnonymitySet <= 0)
|
||||
{
|
||||
w += val;
|
||||
}
|
||||
else
|
||||
{
|
||||
w += val / (decimal)smartCoin.AnonymitySet;
|
||||
}
|
||||
}
|
||||
|
||||
return w; // / (coins.Count == 0 ? 1 : coins.Count);
|
||||
}
|
||||
|
||||
decimal ComputePaymentScore(List<PendingPayment> pendingPayments)
|
||||
{
|
||||
return TotalPaymentsGross == 0 ? 100 : (pendingPayments.Count / (decimal)TotalPaymentsGross) * 100;
|
||||
}
|
||||
|
||||
score += ComputeCoinScore(Coins);
|
||||
score += ComputePaymentScore(HandledPayments);
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
public string GetId()
|
||||
{
|
||||
return string.Join("-",
|
||||
Coins.OrderBy(coin => coin.Outpoint).Select(coin => coin.Outpoint.ToString())
|
||||
.Concat(HandledPayments.OrderBy(arg => arg.Value).Select(p => p.Value.ToString())));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
@@ -402,7 +308,7 @@ public class SubsetSolution : IEquatable<SubsetSolution>
|
||||
sc.TryGetValue(AnonsetType.Orange, out var ocoins);
|
||||
sc.TryGetValue(AnonsetType.Red, out var rcoins);
|
||||
sb.AppendLine(
|
||||
$"Solution total coins:{Coins.Count} R:{rcoins?.Length ?? 0} O:{ocoins?.Length ?? 0} G:{gcoins?.Length ?? 0} AL:{GetAnonLoss(Coins)} total value: {TotalValue} total payments:{TotalPaymentCost}/{TotalPaymentsGross} leftover: {LeftoverValue} score: {Score()} Compute time: {TimeElapsed} ");
|
||||
$"Solution total coins:{Coins.Count} R:{rcoins?.Length ?? 0} O:{ocoins?.Length ?? 0} G:{gcoins?.Length ?? 0} AL:{GetAnonLoss(Coins)} total value: {TotalValue} total payments:{TotalPaymentCost}/{TotalPaymentsGross} leftover: {LeftoverValue}");
|
||||
sb.AppendLine(
|
||||
$"Used coins: {string.Join(", ", Coins.Select(coin => coin.Outpoint + " " + coin.Amount.ToString() + " A" + coin.AnonymitySet))}");
|
||||
if (HandledPayments.Any())
|
||||
@@ -410,18 +316,6 @@ public class SubsetSolution : IEquatable<SubsetSolution>
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public bool Equals(SubsetSolution? other)
|
||||
{
|
||||
return GetId() == other?.GetId();
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((SubsetSolution)obj);
|
||||
}
|
||||
|
||||
private static decimal GetAnonLoss<TCoin>(IEnumerable<TCoin> coins)
|
||||
where TCoin : SmartCoin
|
||||
|
||||
@@ -643,12 +643,11 @@ public async Task<IEnumerable<IDestination>> GetNextDestinationsAsync(int count,
|
||||
PaymentFailed = PaymentFailed(data.Id),
|
||||
PaymentSucceeded = PaymentSucceeded(data.Id),
|
||||
};
|
||||
}).Where(payment => payment is not null).ToArray();
|
||||
return await Task.WhenAll(payouts);
|
||||
}).ToArray();
|
||||
return (await Task.WhenAll(payouts)).Where(payment => payment is not null).ToArray();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
return Array.Empty<PendingPayment>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using BTCPayServer.Abstractions.Services;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WalletWasabi.Affiliation;
|
||||
using WalletWasabi.WabiSabi.Models.Serialization;
|
||||
|
||||
namespace WalletWasabi.Backend.Controllers;
|
||||
@@ -16,8 +17,12 @@ public static class CoordinatorExtensions
|
||||
services.AddTransient(provider =>
|
||||
{
|
||||
var s = provider.GetRequiredService<WabisabiCoordinatorService>();
|
||||
if (!s.Started)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new WabiSabiController(s.IdempotencyRequestCache, s.WabiSabiCoordinator.Arena,
|
||||
s.WabiSabiCoordinator.CoinJoinFeeRateStatStore);
|
||||
s.WabiSabiCoordinator.CoinJoinFeeRateStatStore, s.WabiSabiCoordinator.AffiliationManager);
|
||||
});
|
||||
services.AddHostedService((sp) => sp.GetRequiredService<WabisabiCoordinatorService>());
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using WalletWasabi.Affiliation;
|
||||
using WalletWasabi.Backend.Filters;
|
||||
using WalletWasabi.Cache;
|
||||
using WalletWasabi.WabiSabi.Backend.PostRequests;
|
||||
@@ -23,17 +24,19 @@ namespace WalletWasabi.Backend.Controllers;
|
||||
[Produces("application/json")]
|
||||
public class WabiSabiController : ControllerBase, IWabiSabiApiRequestHandler
|
||||
{
|
||||
public WabiSabiController(IdempotencyRequestCache idempotencyRequestCache, Arena arena, CoinJoinFeeRateStatStore coinJoinFeeRateStatStore)
|
||||
public WabiSabiController(IdempotencyRequestCache idempotencyRequestCache, Arena arena, CoinJoinFeeRateStatStore coinJoinFeeRateStatStore, AffiliationManager affiliationManager)
|
||||
{
|
||||
IdempotencyRequestCache = idempotencyRequestCache;
|
||||
Arena = arena;
|
||||
CoinJoinFeeRateStatStore = coinJoinFeeRateStatStore;
|
||||
AffiliationManager = affiliationManager;
|
||||
}
|
||||
|
||||
private static TimeSpan RequestTimeout { get; } = TimeSpan.FromMinutes(5);
|
||||
private IdempotencyRequestCache IdempotencyRequestCache { get; }
|
||||
private Arena Arena { get; }
|
||||
private CoinJoinFeeRateStatStore CoinJoinFeeRateStatStore { get; }
|
||||
private AffiliationManager AffiliationManager { get; }
|
||||
|
||||
[HttpPost("status")]
|
||||
public async Task<RoundStateResponse> GetStatusAsync(RoundStateRequest request, CancellationToken cancellationToken)
|
||||
@@ -41,7 +44,8 @@ public class WabiSabiController : ControllerBase, IWabiSabiApiRequestHandler
|
||||
var before = DateTimeOffset.UtcNow;
|
||||
var response = await Arena.GetStatusAsync(request, cancellationToken);
|
||||
var medians = CoinJoinFeeRateStatStore.GetDefaultMedians();
|
||||
var ret = new RoundStateResponse(response.RoundStates, medians);
|
||||
var affiliateInformation = AffiliationManager.GetAffiliateInformation();
|
||||
var ret = new RoundStateResponse(response.RoundStates, medians, affiliateInformation);
|
||||
|
||||
var duration = DateTimeOffset.UtcNow - before;
|
||||
RequestTimeStatista.Instance.Add("status", duration);
|
||||
|
||||
@@ -26,6 +26,7 @@ using NBXplorer;
|
||||
using NBXplorer.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NNostr.Client;
|
||||
using WalletWasabi.Affiliation;
|
||||
using WalletWasabi.Bases;
|
||||
using WalletWasabi.BitcoinCore.Rpc;
|
||||
using WalletWasabi.Cache;
|
||||
@@ -47,6 +48,7 @@ public class WabisabiCoordinatorService : PeriodicRunner
|
||||
|
||||
public readonly IdempotencyRequestCache IdempotencyRequestCache;
|
||||
|
||||
public bool Started => HostedServices.IsStartAllAsyncStarted;
|
||||
private HostedServices HostedServices { get; } = new();
|
||||
public WabiSabiCoordinator WabiSabiCoordinator { get; private set; }
|
||||
|
||||
@@ -64,6 +66,7 @@ public class WabisabiCoordinatorService : PeriodicRunner
|
||||
IdempotencyRequestCache = new(memoryCache);
|
||||
}
|
||||
|
||||
|
||||
private WabisabiCoordinatorSettings cachedSettings;
|
||||
|
||||
public async Task<WabisabiCoordinatorSettings> GetSettings()
|
||||
|
||||
@@ -64,11 +64,13 @@
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<h2 class="">Coinjoin configuration</h2>
|
||||
<a href="https://github.com/Kukks/BTCPayServerPlugins/blob/master/Plugins/BTCPayServer.Plugins.Wabisabi/readme.md" class="ms-1" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span></div>
|
||||
<div class="d-flex">
|
||||
<h2 class="">Coinjoin configuration</h2>
|
||||
<a href="https://github.com/Kukks/BTCPayServerPlugins/blob/master/Plugins/BTCPayServer.Plugins.Wabisabi/readme.md" class="ms-1" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post">
|
||||
@{
|
||||
var wallet = await WalletProvider.GetWalletAsync(storeId);
|
||||
@@ -121,8 +123,8 @@
|
||||
<div id="advanced" class="@(Model.PlebMode ? "d-none" : "")">
|
||||
<div class="form-group">
|
||||
|
||||
<label asp-for="AnonymitySetTarget" class="form-label">Use Anon score model</label>
|
||||
<input type="number" class="form-control" asp-for="AnonymitySetTarget" placeholder="target anon score">
|
||||
<label asp-for="AnonymitySetTarget" class="form-label">Anon score target</label>
|
||||
<input type="number" class="form-control" asp-for="AnonymitySetTarget" placeholder="target anon score" min="0">
|
||||
|
||||
<p class="text-muted">Scores your coinjoined utxos based on how many other utxos in the coinjoin (and other previous coinjoin rounds) had the same value.<br/> Anonset score computation is not an exact science, and when using coordinators with massive liquidity, is not that important as all rounds (past, present, future) contribute to your privacy.</p>
|
||||
</div>
|
||||
@@ -350,7 +352,7 @@
|
||||
<a asp-controller="WabisabiCoordinatorConfig" asp-action="UpdateWabisabiSettings" class="btn btn-secondary mt-2" permission="@Policies.CanModifyServerSettings">Coordinator runner</a>
|
||||
<a asp-controller="WabisabiStore" asp-action="ListCoinjoins" class="btn btn-secondary mt-2" asp-route-storeId="@storeId">Coinjoins</a>
|
||||
<button name="command" type="submit" value="discover" class="btn btn-secondary mt-2" permission="@Policies.CanModifyServerSettings">Discover coordinators over Nostr</button>
|
||||
<a class="btn btn-secondary mt-2" href="https://gist.github.com/nopara73/bb17e89d7dc9af536ca41f50f705d329" rel="noreferrer noopener" target="_blank">Enable Discrete payments - Coming soon</a>
|
||||
<a class="btn btn-secondary mt-2" href="https://gist.github.com/nopara73/bb17e89d7dc9af536ca41f50f705d329" rel="noreferrer noopener" target="_blank">Enable Discreet payments - Coming soon</a>
|
||||
|
||||
|
||||
</form>
|
||||
@@ -364,6 +366,7 @@
|
||||
}
|
||||
|
||||
<partial name="Wabisabi/AddManualCoordinator" model="@(new DiscoveredCoordinator())"/>
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial"/>
|
||||
}
|
||||
|
||||
@@ -4,3 +4,6 @@
|
||||
@addTagHelper *, BTCPayServer.TagHelpers
|
||||
@addTagHelper *, BTCPayServer.Views.TagHelpers
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper *, BTCPayServer
|
||||
@@ -1,41 +1,25 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Abstractions.Services;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Common;
|
||||
using BTCPayServer.Data.Data;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.PayJoin;
|
||||
using BTCPayServer.PayoutProcessors;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using WalletWasabi.Backend.Controllers;
|
||||
using WalletWasabi.Logging;
|
||||
using WalletWasabi.WabiSabi.Client;
|
||||
using WalletWasabi.WabiSabi.Models.Serialization;
|
||||
using LogLevel = WalletWasabi.Logging.LogLevel;
|
||||
|
||||
namespace BTCPayServer.Plugins.Wabisabi;
|
||||
@@ -131,7 +115,7 @@ public class WabisabiPlugin : BaseBTCPayServerPlugin
|
||||
return new[] {new PaymentMethodId("BTC", PaymentTypes.BTCLike)};
|
||||
}
|
||||
|
||||
public Task<IHostedService> ConstructProcessor(PayoutProcessorData settings)
|
||||
public Task<IHostedService> ConstructProcessor(Data.PayoutProcessorData settings)
|
||||
{
|
||||
return Task.FromResult<IHostedService>(new ShellSerice());
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data.Data;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.PayoutProcessors;
|
||||
using BTCPayServer.Services;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using WalletWasabi.WabiSabi.Client;
|
||||
|
||||
namespace BTCPayServer.Plugins.Wabisabi
|
||||
{
|
||||
|
||||
@@ -91,6 +91,7 @@ namespace BTCPayServer.Plugins.Wabisabi
|
||||
var actualCommand = pieces[0];
|
||||
var commandIndex = pieces.Length > 1 ? pieces[1] : null;
|
||||
var coordinator = pieces.Length > 2 ? pieces[2] : null;
|
||||
vm.AnonymitySetTarget = Math.Max(2, vm.AnonymitySetTarget);
|
||||
var coord = vm.Settings.SingleOrDefault(settings => settings.Coordinator == coordinator);
|
||||
ModelState.Clear();
|
||||
|
||||
|
||||
Submodule submodules/btcpayserver updated: 99299ba06f...e6a157a101
Submodule submodules/walletwasabi updated: 77021fc2ca...1123357709
Reference in New Issue
Block a user