mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Have address wallet objects rather than script objects (#4417)
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="NBXplorer.Client" Version="4.2.1" />
|
<PackageReference Include="NBXplorer.Client" Version="4.2.2" />
|
||||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.18" />
|
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.18" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="'$(Altcoins)' != 'true'">
|
<ItemGroup Condition="'$(Altcoins)' != 'true'">
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace BTCPayServer.Data
|
|||||||
public const string PayjoinExposed = "pj-exposed";
|
public const string PayjoinExposed = "pj-exposed";
|
||||||
public const string Payout = "payout";
|
public const string Payout = "payout";
|
||||||
public const string PullPayment = "pull-payment";
|
public const string PullPayment = "pull-payment";
|
||||||
public const string Script = "script";
|
public const string Address = "address";
|
||||||
public const string Utxo = "utxo";
|
public const string Utxo = "utxo";
|
||||||
}
|
}
|
||||||
public string WalletId { get; set; }
|
public string WalletId { get; set; }
|
||||||
|
|||||||
@@ -678,7 +678,7 @@ namespace BTCPayServer.Tests
|
|||||||
parser = new DerivationSchemeParser(networkProvider.BTC);
|
parser = new DerivationSchemeParser(networkProvider.BTC);
|
||||||
var od = "wpkh([8bafd160/49h/0h/0h]xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw/0/*)#9x4vkw48";
|
var od = "wpkh([8bafd160/49h/0h/0h]xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw/0/*)#9x4vkw48";
|
||||||
(strategyBase, rootedKeyPath) = parser.ParseOutputDescriptor(od);
|
(strategyBase, rootedKeyPath) = parser.ParseOutputDescriptor(od);
|
||||||
Assert.Equal(1, rootedKeyPath.Length);
|
Assert.Single(rootedKeyPath);
|
||||||
Assert.IsType<DirectDerivationStrategy>(strategyBase);
|
Assert.IsType<DirectDerivationStrategy>(strategyBase);
|
||||||
Assert.True(((DirectDerivationStrategy)strategyBase).Segwit);
|
Assert.True(((DirectDerivationStrategy)strategyBase).Segwit);
|
||||||
|
|
||||||
|
|||||||
@@ -1936,7 +1936,7 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
var invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", newInvoice.Id), false);
|
var invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", newInvoice.Id), false);
|
||||||
Assert.Contains(invoiceObject.Links.Select(l => l.Type), t => t == "script");
|
Assert.Contains(invoiceObject.Links.Select(l => l.Type), t => t == "address");
|
||||||
|
|
||||||
Assert.EndsWith($"/i/{newInvoice.Id}", newInvoice.CheckoutLink);
|
Assert.EndsWith($"/i/{newInvoice.Id}", newInvoice.CheckoutLink);
|
||||||
var controller = tester.PayTester.GetController<UIInvoiceController>(user.UserId, user.StoreId);
|
var controller = tester.PayTester.GetController<UIInvoiceController>(user.UserId, user.StoreId);
|
||||||
@@ -1972,7 +1972,7 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
invoice = await client.CreateInvoice(user.StoreId, new CreateInvoiceRequest() { Amount = 1, Currency = "USD" });
|
invoice = await client.CreateInvoice(user.StoreId, new CreateInvoiceRequest() { Amount = 1, Currency = "USD" });
|
||||||
invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", invoice.Id), false);
|
invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", invoice.Id), false);
|
||||||
Assert.DoesNotContain(invoiceObject.Links.Select(l => l.Type), t => t == "script");
|
Assert.DoesNotContain(invoiceObject.Links.Select(l => l.Type), t => t == "address");
|
||||||
|
|
||||||
|
|
||||||
paymentMethods = await client.GetInvoicePaymentMethods(store.Id, invoice.Id);
|
paymentMethods = await client.GetInvoicePaymentMethods(store.Id, invoice.Id);
|
||||||
@@ -1981,7 +1981,7 @@ namespace BTCPayServer.Tests
|
|||||||
await client.ActivateInvoicePaymentMethod(user.StoreId, invoice.Id,
|
await client.ActivateInvoicePaymentMethod(user.StoreId, invoice.Id,
|
||||||
paymentMethods.First().PaymentMethod);
|
paymentMethods.First().PaymentMethod);
|
||||||
invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", invoice.Id), false);
|
invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", invoice.Id), false);
|
||||||
Assert.Contains(invoiceObject.Links.Select(l => l.Type), t => t == "script");
|
Assert.Contains(invoiceObject.Links.Select(l => l.Type), t => t == "address");
|
||||||
|
|
||||||
paymentMethods = await client.GetInvoicePaymentMethods(store.Id, invoice.Id);
|
paymentMethods = await client.GetInvoicePaymentMethods(store.Id, invoice.Id);
|
||||||
Assert.Single(paymentMethods);
|
Assert.Single(paymentMethods);
|
||||||
@@ -2046,10 +2046,10 @@ namespace BTCPayServer.Tests
|
|||||||
var pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id));
|
var pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id));
|
||||||
Assert.Single(pm.Payments);
|
Assert.Single(pm.Payments);
|
||||||
Assert.Equal(-0.0001m, pm.Due);
|
Assert.Equal(-0.0001m, pm.Due);
|
||||||
});
|
|
||||||
|
|
||||||
invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", invoice.Id), false);
|
invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", invoice.Id), false);
|
||||||
Assert.Contains(invoiceObject.Links.Select(l => l.Type), t => t == "tx");
|
Assert.Contains(invoiceObject.Links.Select(l => l.Type), t => t == "tx");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = 60 * 20 * 1000)]
|
[Fact(Timeout = 60 * 20 * 1000)]
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ services:
|
|||||||
expose:
|
expose:
|
||||||
- "4444"
|
- "4444"
|
||||||
nbxplorer:
|
nbxplorer:
|
||||||
image: nicolasdorier/nbxplorer:2.3.40
|
image: nicolasdorier/nbxplorer:2.3.54
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "32838:32838"
|
- "32838:32838"
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ services:
|
|||||||
expose:
|
expose:
|
||||||
- "4444"
|
- "4444"
|
||||||
nbxplorer:
|
nbxplorer:
|
||||||
image: nicolasdorier/nbxplorer:2.3.40
|
image: nicolasdorier/nbxplorer:2.3.54
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "32838:32838"
|
- "32838:32838"
|
||||||
|
|||||||
@@ -391,8 +391,8 @@ namespace BTCPayServer.Controllers
|
|||||||
await _walletRepository.EnsureWalletObjectLink(
|
await _walletRepository.EnsureWalletObjectLink(
|
||||||
new WalletObjectId(
|
new WalletObjectId(
|
||||||
walletId,
|
walletId,
|
||||||
WalletObjectData.Types.Script,
|
WalletObjectData.Types.Address,
|
||||||
address.ScriptPubKey.ToHex()),
|
address.ToString()),
|
||||||
new WalletObjectId(
|
new WalletObjectId(
|
||||||
walletId,
|
walletId,
|
||||||
WalletObjectData.Types.Invoice,
|
WalletObjectData.Types.Invoice,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using BTCPayServer.Services.Apps;
|
|||||||
using BTCPayServer.Services.Labels;
|
using BTCPayServer.Services.Labels;
|
||||||
using BTCPayServer.Services.PaymentRequests;
|
using BTCPayServer.Services.PaymentRequests;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
|
using NBXplorer.DerivationStrategy;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
@@ -25,9 +26,12 @@ namespace BTCPayServer.HostedServices
|
|||||||
{
|
{
|
||||||
private readonly WalletRepository _walletRepository;
|
private readonly WalletRepository _walletRepository;
|
||||||
|
|
||||||
public TransactionLabelMarkerHostedService(EventAggregator eventAggregator, WalletRepository walletRepository, Logs logs) :
|
public BTCPayNetworkProvider NetworkProvider { get; }
|
||||||
|
|
||||||
|
public TransactionLabelMarkerHostedService(BTCPayNetworkProvider networkProvider, EventAggregator eventAggregator, WalletRepository walletRepository, Logs logs) :
|
||||||
base(eventAggregator, logs)
|
base(eventAggregator, logs)
|
||||||
{
|
{
|
||||||
|
NetworkProvider = networkProvider;
|
||||||
_walletRepository = walletRepository;
|
_walletRepository = walletRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,36 +48,40 @@ namespace BTCPayServer.HostedServices
|
|||||||
// any utxo or script object matching it.
|
// any utxo or script object matching it.
|
||||||
// If we find, then we create a link between them and the tx object.
|
// If we find, then we create a link between them and the tx object.
|
||||||
case NewOnChainTransactionEvent transactionEvent:
|
case NewOnChainTransactionEvent transactionEvent:
|
||||||
{
|
|
||||||
var txHash = transactionEvent.NewTransactionEvent.TransactionData.TransactionHash.ToString();
|
|
||||||
|
|
||||||
// find all wallet objects that fit this transaction
|
|
||||||
// that means see if there are any utxo objects that match in/outs and scripts/addresses that match outs
|
|
||||||
var matchedObjects = transactionEvent.NewTransactionEvent.TransactionData.Transaction.Inputs
|
|
||||||
.Select(txIn => new ObjectTypeId(WalletObjectData.Types.Utxo, txIn.PrevOut.ToString()))
|
|
||||||
.Concat(transactionEvent.NewTransactionEvent.TransactionData.Transaction.Outputs.AsIndexedOutputs().SelectMany(txOut =>
|
|
||||||
|
|
||||||
new[]{
|
|
||||||
new ObjectTypeId(WalletObjectData.Types.Script,txOut.TxOut.ScriptPubKey.ToHex()),
|
|
||||||
new ObjectTypeId(WalletObjectData.Types.Utxo,txOut.ToCoin().Outpoint.ToString())
|
|
||||||
|
|
||||||
} )).Distinct().ToArray();
|
|
||||||
|
|
||||||
var objs = await _walletRepository.GetWalletObjects(new GetWalletObjectsQuery(){TypesIds = matchedObjects});
|
|
||||||
|
|
||||||
foreach (var walletObjectDatas in objs.GroupBy(data => data.Key.WalletId))
|
|
||||||
{
|
{
|
||||||
var txWalletObject = new WalletObjectId(walletObjectDatas.Key,
|
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(transactionEvent.CryptoCode);
|
||||||
WalletObjectData.Types.Tx, txHash);
|
var derivation = transactionEvent.NewTransactionEvent.DerivationStrategy;
|
||||||
await _walletRepository.EnsureWalletObject(txWalletObject);
|
if (network is null || derivation is null)
|
||||||
foreach (var walletObjectData in walletObjectDatas)
|
break;
|
||||||
{
|
var txHash = transactionEvent.NewTransactionEvent.TransactionData.TransactionHash.ToString();
|
||||||
await _walletRepository.EnsureWalletObjectLink(txWalletObject, walletObjectData.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
// find all wallet objects that fit this transaction
|
||||||
}
|
// that means see if there are any utxo objects that match in/outs and scripts/addresses that match outs
|
||||||
|
var matchedObjects = transactionEvent.NewTransactionEvent.TransactionData.Transaction.Inputs
|
||||||
|
.Select<TxIn, ObjectTypeId>(txIn => new ObjectTypeId(WalletObjectData.Types.Utxo, txIn.PrevOut.ToString()))
|
||||||
|
.Concat(transactionEvent.NewTransactionEvent.Outputs.SelectMany<NBXplorer.Models.MatchedOutput, ObjectTypeId>(txOut =>
|
||||||
|
|
||||||
|
new[]{
|
||||||
|
new ObjectTypeId(WalletObjectData.Types.Address, GetAddress(derivation, txOut, network).ToString()),
|
||||||
|
new ObjectTypeId(WalletObjectData.Types.Utxo, new OutPoint(transactionEvent.NewTransactionEvent.TransactionData.TransactionHash, (uint)txOut.Index).ToString())
|
||||||
|
|
||||||
|
})).Distinct().ToArray();
|
||||||
|
|
||||||
|
var objs = await _walletRepository.GetWalletObjects(new GetWalletObjectsQuery() { TypesIds = matchedObjects });
|
||||||
|
|
||||||
|
foreach (var walletObjectDatas in objs.GroupBy(data => data.Key.WalletId))
|
||||||
|
{
|
||||||
|
var txWalletObject = new WalletObjectId(walletObjectDatas.Key,
|
||||||
|
WalletObjectData.Types.Tx, txHash);
|
||||||
|
await _walletRepository.EnsureWalletObject(txWalletObject);
|
||||||
|
foreach (var walletObjectData in walletObjectDatas)
|
||||||
|
{
|
||||||
|
await _walletRepository.EnsureWalletObjectLink(txWalletObject, walletObjectData.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case InvoiceEvent {Name: InvoiceEvent.ReceivedPayment} invoiceEvent when
|
case InvoiceEvent {Name: InvoiceEvent.ReceivedPayment} invoiceEvent when
|
||||||
invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType == BitcoinPaymentType.Instance &&
|
invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType == BitcoinPaymentType.Instance &&
|
||||||
invoiceEvent.Payment.GetCryptoPaymentData() is BitcoinLikePaymentData bitcoinLikePaymentData:
|
invoiceEvent.Payment.GetCryptoPaymentData() is BitcoinLikePaymentData bitcoinLikePaymentData:
|
||||||
@@ -98,5 +106,11 @@ namespace BTCPayServer.HostedServices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BitcoinAddress GetAddress(DerivationStrategyBase derivationStrategy, NBXplorer.Models.MatchedOutput txOut, BTCPayNetwork network)
|
||||||
|
{
|
||||||
|
// Old version of NBX doesn't give address in the event, so we need to guess
|
||||||
|
return (txOut.Address ?? network.NBXplorerNetwork.CreateAddress(derivationStrategy, txOut.KeyPath, txOut.ScriptPubKey));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ namespace BTCPayServer.Services
|
|||||||
await _walletRepository.EnsureWalletObjectLink(
|
await _walletRepository.EnsureWalletObjectLink(
|
||||||
new WalletObjectId(
|
new WalletObjectId(
|
||||||
walletId,
|
walletId,
|
||||||
WalletObjectData.Types.Script,
|
WalletObjectData.Types.Address,
|
||||||
address.ScriptPubKey.ToHex()),
|
address.ToString()),
|
||||||
new WalletObjectId(
|
new WalletObjectId(
|
||||||
walletId,
|
walletId,
|
||||||
WalletObjectData.Types.Invoice,
|
WalletObjectData.Types.Invoice,
|
||||||
|
|||||||
@@ -52,16 +52,11 @@ namespace BTCPayServer.Services
|
|||||||
public string[]? Ids { get; set; }
|
public string[]? Ids { get; set; }
|
||||||
public bool IncludeNeighbours { get; set; } = true;
|
public bool IncludeNeighbours { get; set; } = true;
|
||||||
public bool UseInefficientPath { get; set; }
|
public bool UseInefficientPath { get; set; }
|
||||||
|
|
||||||
public static ObjectTypeId Get(Script script)
|
|
||||||
{
|
|
||||||
return new ObjectTypeId(WalletObjectData.Types.Script, script.ToHex());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<ObjectTypeId> Get(ReceivedCoin coin)
|
public static IEnumerable<ObjectTypeId> Get(ReceivedCoin coin)
|
||||||
{
|
{
|
||||||
yield return new ObjectTypeId(WalletObjectData.Types.Tx, coin.OutPoint.Hash.ToString());
|
yield return new ObjectTypeId(WalletObjectData.Types.Tx, coin.OutPoint.Hash.ToString());
|
||||||
yield return Get(coin.ScriptPubKey);
|
yield return new ObjectTypeId(WalletObjectData.Types.Address, coin.Address.ToString());
|
||||||
yield return new ObjectTypeId(WalletObjectData.Types.Utxo, coin.OutPoint.ToString());
|
yield return new ObjectTypeId(WalletObjectData.Types.Utxo, coin.OutPoint.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,7 +245,7 @@ namespace BTCPayServer.Services
|
|||||||
{
|
{
|
||||||
var wos = await GetWalletObjects(
|
var wos = await GetWalletObjects(
|
||||||
new GetWalletObjectsQuery(walletId, WalletObjectData.Types.Tx, transactionIds));
|
new GetWalletObjectsQuery(walletId, WalletObjectData.Types.Tx, transactionIds));
|
||||||
return await GetWalletTransactionsInfoCore(walletId, wos);
|
return GetWalletTransactionsInfoCore(walletId, wos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Dictionary<string, WalletTransactionInfo>> GetWalletTransactionsInfo(WalletId walletId,
|
public async Task<Dictionary<string, WalletTransactionInfo>> GetWalletTransactionsInfo(WalletId walletId,
|
||||||
@@ -259,10 +254,10 @@ namespace BTCPayServer.Services
|
|||||||
var wos = await GetWalletObjects(
|
var wos = await GetWalletObjects(
|
||||||
new GetWalletObjectsQuery(walletId, transactionIds));
|
new GetWalletObjectsQuery(walletId, transactionIds));
|
||||||
|
|
||||||
return await GetWalletTransactionsInfoCore(walletId, wos);
|
return GetWalletTransactionsInfoCore(walletId, wos);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, WalletTransactionInfo>> GetWalletTransactionsInfoCore(WalletId walletId,
|
private Dictionary<string, WalletTransactionInfo> GetWalletTransactionsInfoCore(WalletId walletId,
|
||||||
Dictionary<WalletObjectId, WalletObjectData> wos)
|
Dictionary<WalletObjectId, WalletObjectData> wos)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace BTCPayServer.Services.Wallets
|
|||||||
public IMoney Value { get; set; }
|
public IMoney Value { get; set; }
|
||||||
public Coin Coin { get; set; }
|
public Coin Coin { get; set; }
|
||||||
public long Confirmations { get; set; }
|
public long Confirmations { get; set; }
|
||||||
|
public BitcoinAddress Address { get; set; }
|
||||||
}
|
}
|
||||||
public class NetworkCoins
|
public class NetworkCoins
|
||||||
{
|
{
|
||||||
@@ -91,7 +92,7 @@ namespace BTCPayServer.Services.Wallets
|
|||||||
if (storeId != null)
|
if (storeId != null)
|
||||||
{
|
{
|
||||||
await WalletRepository.EnsureWalletObject(
|
await WalletRepository.EnsureWalletObject(
|
||||||
new WalletObjectId(new WalletId(storeId, Network.CryptoCode), WalletObjectData.Types.Script, pathInfo.ScriptPubKey.ToHex()),
|
new WalletObjectId(new WalletId(storeId, Network.CryptoCode), WalletObjectData.Types.Address, pathInfo.Address.ToString()),
|
||||||
new JObject() { ["generatedBy"] = generatedBy });
|
new JObject() { ["generatedBy"] = generatedBy });
|
||||||
}
|
}
|
||||||
return pathInfo;
|
return pathInfo;
|
||||||
@@ -360,7 +361,9 @@ namespace BTCPayServer.Services.Wallets
|
|||||||
OutPoint = c.Outpoint,
|
OutPoint = c.Outpoint,
|
||||||
ScriptPubKey = c.ScriptPubKey,
|
ScriptPubKey = c.ScriptPubKey,
|
||||||
Coin = c.AsCoin(derivationStrategy),
|
Coin = c.AsCoin(derivationStrategy),
|
||||||
Confirmations = c.Confirmations
|
Confirmations = c.Confirmations,
|
||||||
|
// Some old version of NBX doesn't have Address in this call
|
||||||
|
Address = c.Address ?? c.ScriptPubKey.GetDestinationAddress(Network.NBitcoinNetwork)
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user