mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-18 21:32:27 +01:00
Have address wallet objects rather than script objects (#4417)
This commit is contained in:
parent
3673230fdf
commit
0c3f819200
@ -4,7 +4,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Altcoins)' != 'true'">
|
||||
|
@ -21,7 +21,7 @@ namespace BTCPayServer.Data
|
||||
public const string PayjoinExposed = "pj-exposed";
|
||||
public const string Payout = "payout";
|
||||
public const string PullPayment = "pull-payment";
|
||||
public const string Script = "script";
|
||||
public const string Address = "address";
|
||||
public const string Utxo = "utxo";
|
||||
}
|
||||
public string WalletId { get; set; }
|
||||
|
@ -678,7 +678,7 @@ namespace BTCPayServer.Tests
|
||||
parser = new DerivationSchemeParser(networkProvider.BTC);
|
||||
var od = "wpkh([8bafd160/49h/0h/0h]xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw/0/*)#9x4vkw48";
|
||||
(strategyBase, rootedKeyPath) = parser.ParseOutputDescriptor(od);
|
||||
Assert.Equal(1, rootedKeyPath.Length);
|
||||
Assert.Single(rootedKeyPath);
|
||||
Assert.IsType<DirectDerivationStrategy>(strategyBase);
|
||||
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);
|
||||
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);
|
||||
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" });
|
||||
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);
|
||||
@ -1981,7 +1981,7 @@ namespace BTCPayServer.Tests
|
||||
await client.ActivateInvoicePaymentMethod(user.StoreId, invoice.Id,
|
||||
paymentMethods.First().PaymentMethod);
|
||||
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);
|
||||
Assert.Single(paymentMethods);
|
||||
@ -2046,10 +2046,10 @@ namespace BTCPayServer.Tests
|
||||
var pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id));
|
||||
Assert.Single(pm.Payments);
|
||||
Assert.Equal(-0.0001m, pm.Due);
|
||||
});
|
||||
|
||||
invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", invoice.Id), false);
|
||||
Assert.Contains(invoiceObject.Links.Select(l => l.Type), t => t == "tx");
|
||||
invoiceObject = await client.GetOnChainWalletObject(user.StoreId, "BTC", new OnChainWalletObjectId("invoice", invoice.Id), false);
|
||||
Assert.Contains(invoiceObject.Links.Select(l => l.Type), t => t == "tx");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact(Timeout = 60 * 20 * 1000)]
|
||||
|
@ -90,7 +90,7 @@ services:
|
||||
expose:
|
||||
- "4444"
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.3.40
|
||||
image: nicolasdorier/nbxplorer:2.3.54
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
|
@ -87,7 +87,7 @@ services:
|
||||
expose:
|
||||
- "4444"
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.3.40
|
||||
image: nicolasdorier/nbxplorer:2.3.54
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
|
@ -391,8 +391,8 @@ namespace BTCPayServer.Controllers
|
||||
await _walletRepository.EnsureWalletObjectLink(
|
||||
new WalletObjectId(
|
||||
walletId,
|
||||
WalletObjectData.Types.Script,
|
||||
address.ScriptPubKey.ToHex()),
|
||||
WalletObjectData.Types.Address,
|
||||
address.ToString()),
|
||||
new WalletObjectId(
|
||||
walletId,
|
||||
WalletObjectData.Types.Invoice,
|
||||
|
@ -16,6 +16,7 @@ using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Labels;
|
||||
using BTCPayServer.Services.PaymentRequests;
|
||||
using NBitcoin;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
@ -25,9 +26,12 @@ namespace BTCPayServer.HostedServices
|
||||
{
|
||||
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)
|
||||
{
|
||||
NetworkProvider = networkProvider;
|
||||
_walletRepository = walletRepository;
|
||||
}
|
||||
|
||||
@ -44,36 +48,40 @@ namespace BTCPayServer.HostedServices
|
||||
// any utxo or script object matching it.
|
||||
// If we find, then we create a link between them and the tx object.
|
||||
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,
|
||||
WalletObjectData.Types.Tx, txHash);
|
||||
await _walletRepository.EnsureWalletObject(txWalletObject);
|
||||
foreach (var walletObjectData in walletObjectDatas)
|
||||
{
|
||||
await _walletRepository.EnsureWalletObjectLink(txWalletObject, walletObjectData.Key);
|
||||
}
|
||||
}
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(transactionEvent.CryptoCode);
|
||||
var derivation = transactionEvent.NewTransactionEvent.DerivationStrategy;
|
||||
if (network is null || derivation is null)
|
||||
break;
|
||||
var txHash = transactionEvent.NewTransactionEvent.TransactionData.TransactionHash.ToString();
|
||||
|
||||
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
|
||||
invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType == BitcoinPaymentType.Instance &&
|
||||
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(
|
||||
new WalletObjectId(
|
||||
walletId,
|
||||
WalletObjectData.Types.Script,
|
||||
address.ScriptPubKey.ToHex()),
|
||||
WalletObjectData.Types.Address,
|
||||
address.ToString()),
|
||||
new WalletObjectId(
|
||||
walletId,
|
||||
WalletObjectData.Types.Invoice,
|
||||
|
@ -52,16 +52,11 @@ namespace BTCPayServer.Services
|
||||
public string[]? Ids { get; set; }
|
||||
public bool IncludeNeighbours { get; set; } = true;
|
||||
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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -250,7 +245,7 @@ namespace BTCPayServer.Services
|
||||
{
|
||||
var wos = await GetWalletObjects(
|
||||
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,
|
||||
@ -259,10 +254,10 @@ namespace BTCPayServer.Services
|
||||
var wos = await GetWalletObjects(
|
||||
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)
|
||||
{
|
||||
|
||||
|
@ -27,6 +27,7 @@ namespace BTCPayServer.Services.Wallets
|
||||
public IMoney Value { get; set; }
|
||||
public Coin Coin { get; set; }
|
||||
public long Confirmations { get; set; }
|
||||
public BitcoinAddress Address { get; set; }
|
||||
}
|
||||
public class NetworkCoins
|
||||
{
|
||||
@ -91,7 +92,7 @@ namespace BTCPayServer.Services.Wallets
|
||||
if (storeId != null)
|
||||
{
|
||||
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 });
|
||||
}
|
||||
return pathInfo;
|
||||
@ -360,7 +361,9 @@ namespace BTCPayServer.Services.Wallets
|
||||
OutPoint = c.Outpoint,
|
||||
ScriptPubKey = c.ScriptPubKey,
|
||||
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();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user