public void SendTest( BitcoinBasedConfig currency, decimal available, decimal amount, decimal fee, DustUsagePolicy dustUsagePolicy) { var error = Send( currency: currency, available: available, amount: amount, fee: fee, dustUsagePolicy: dustUsagePolicy, apiSetup: apiMock => { apiMock.Setup(a => a.TryBroadcastAsync(It.IsAny <IBlockchainTransaction>(), 3, 1000, CancellationToken.None)) .Returns(Task.FromResult(new Result <string>("<txid>"))); }, repositorySetup: (repositoryMock, fromAddress) => { repositoryMock.Setup(r => r.GetWalletAddressAsync(It.IsAny <string>(), fromAddress.Address)) .Returns(Task.FromResult(fromAddress)); }); Assert.Null(error); }
public void SendDustAsFeeTest( BitcoinBasedConfig currency, decimal available, decimal amount, decimal fee, DustUsagePolicy dustUsagePolicy) { var change = available - amount - fee; var broadcastCallback = new Action <IBlockchainTransaction, int, int, CancellationToken>((tx, attempts, attemptsInterval, token) => { var btcBasedTx = (IBitcoinBasedTransaction)tx; Assert.True(btcBasedTx.Fees == currency.CoinToSatoshi(fee + change)); }); var error = Send( currency: currency, available: available, amount: amount, fee: fee, dustUsagePolicy: dustUsagePolicy, apiSetup: apiMock => { apiMock.Setup(a => a.TryBroadcastAsync(It.IsAny <IBlockchainTransaction>(), 3, 1000, CancellationToken.None)) .Callback(broadcastCallback) .Returns(Task.FromResult(new Result <string>("<txid>"))); }, repositorySetup: (repositoryMock, fromAddress) => { repositoryMock.Setup(r => r.GetWalletAddressAsync(It.IsAny <string>(), fromAddress.Address)) .Returns(Task.FromResult(fromAddress)); }); Assert.Null(error); }
public void SpentSegwitPaymentTx(BitcoinBasedConfig currency) { const int paymentQty = 1_0000_0000; var paymentTx = SignSegwitPaymentTx(currency); var paymentTxOutputs = paymentTx.Outputs .Where(o => o.Value == paymentQty) .ToArray(); var spentTx = currency.CreateP2WPkhTx( unspentOutputs: paymentTxOutputs, destinationAddress: Common.Bob .PubKey .GetSegwitAddress(currency.Network) .ToString(), changeAddress: Common.Bob .PubKey .GetSegwitAddress(currency.Network) .ToString(), amount: 9999_0000, fee: 1_0000); spentTx.Sign(Common.Bob, paymentTxOutputs, currency); Assert.True(spentTx.Verify(paymentTxOutputs, currency)); }
public IBitcoinBasedTransaction CreateSegwitPaymentTx(BitcoinBasedConfig currency) { var initTx = BitcoinBasedCommon.CreateFakeTx(currency, Common.Alice.PubKey, 1_0000_0000, 1_0000_0000); const int amount = 1_0000_0000; const int fee = 1_0000; const int change = 9999_0000; var tx = BitcoinBasedCommon.CreateSegwitPaymentTx( currency: currency, outputs: initTx.Outputs, from: Common.Alice.PubKey, to: Common.Bob.PubKey, amount: amount, fee: fee); Assert.NotNull(tx); Assert.True(tx.Check()); Assert.NotNull(tx.Outputs.FirstOrDefault(o => o.Value == amount)); Assert.NotNull(tx.Outputs.FirstOrDefault(o => o.Value == change)); Assert.Equal(tx.Outputs.First(o => o.Value == amount).DestinationAddress(currency.Network), Common.BobSegwitAddress(currency)); Assert.Equal(initTx.TotalOut - fee, tx.TotalOut); Assert.Equal(fee, tx.GetFee(initTx.Outputs)); return(tx); }
public IBitcoinBasedTransaction CreatePaymentTx(BitcoinBasedConfig currency) { var initTx = BitcoinBasedCommon.CreateFakeTx(currency, Common.Alice.PubKey, 1_0000_0000, 1_0000_0000); const int amount = 1_0000_0000; const int fee = 1_0000; const int change = 9999_0000; var tx = currency.CreatePaymentTx( unspentOutputs: initTx.Outputs, destinationAddress: Common.Bob.PubKey.GetAddress(currency), changeAddress: Common.Alice.PubKey.GetAddress(currency), amount: amount, fee: fee, lockTime: DateTimeOffset.MinValue); Assert.NotNull(tx); Assert.True(tx.Check()); Assert.NotNull(tx.Outputs.FirstOrDefault(o => o.Value == amount)); Assert.NotNull(tx.Outputs.FirstOrDefault(o => o.Value == change)); Assert.Equal(tx.Outputs.First(o => o.Value == amount).DestinationAddress(currency.Network), Common.BobAddress(currency)); Assert.Equal(tx.Outputs.First(o => o.Value == change).DestinationAddress(currency.Network), Common.AliceAddress(currency)); Assert.Equal(initTx.TotalOut - fee, tx.TotalOut); Assert.Equal(fee, tx.GetFee(initTx.Outputs)); return(tx); }
public static BitcoinBasedTransaction CreateTransaction( BitcoinBasedConfig currency, IEnumerable <ICoin> coins, Script destination, Script change, long amount, long fee, DateTimeOffset lockTime, params Script[] knownRedeems) { var tx = currency.Network.CreateTransactionBuilder() .SetDustPrevention(false) .SetOptInRBF(true) .AddCoins(coins) .Send(destination, new Money(amount)) .SendFees(new Money(fee)) .SetChange(change) .SetLockTime(lockTime != DateTimeOffset.MinValue ? new LockTime(lockTime) : NBitcoin.LockTime.Zero) .AddKnownRedeems(knownRedeems) .BuildTransaction(false); return(new BitcoinBasedTransaction( currency: currency.Name, tx: tx, blockInfo: null, fees: (long)tx.GetFee(coins.ToArray()).ToUnit(MoneyUnit.Satoshi))); }
public (IBitcoinBasedTransaction, byte[]) CreateHtlcP2PkhScriptSwapPaymentTx(BitcoinBasedConfig currency) { var initTx = BitcoinBasedCommon.CreateFakeTx(currency, Common.Alice.PubKey, 1_0000_0000, 1_0000_0000); const int amount = 1_0000_0000; const int fee = 1_0000; //const int change = 9999_0000; var tx = currency.CreateHtlcP2PkhScriptSwapPaymentTx( unspentOutputs: initTx.Outputs, aliceRefundAddress: Common.Alice.PubKey.GetAddress(currency), bobAddress: Common.Bob.PubKey.GetAddress(currency), lockTime: DateTimeOffset.UtcNow.AddHours(1), secretHash: Common.SecretHash, secretSize: Common.Secret.Length, amount: amount, fee: fee, redeemScript: out var redeemScript); Assert.NotNull(tx); Assert.NotNull(redeemScript); Assert.True(tx.Check()); Assert.Equal(initTx.TotalOut - fee, tx.TotalOut); Assert.Equal(fee, tx.GetFee(initTx.Outputs)); return(tx, redeemScript); }
public static ICurrencySwap Create( CurrencyConfig currency, IAccount account) { return(currency switch { BitcoinBasedConfig _ => new BitcoinBasedSwap( account: account.GetCurrencyAccount <BitcoinBasedAccount>(currency.Name), currencies: account.Currencies), Erc20Config _ => new Erc20Swap( account: account.GetCurrencyAccount <Erc20Account>(currency.Name), ethereumAccount: account.GetCurrencyAccount <EthereumAccount>("ETH"), currencies: account.Currencies), EthereumConfig _ => new EthereumSwap( account: account.GetCurrencyAccount <EthereumAccount>(currency.Name), currencies: account.Currencies), Fa12Config _ => new Fa12Swap( account: account.GetCurrencyAccount <Fa12Account>(currency.Name), tezosAccount: account.GetCurrencyAccount <TezosAccount>(TezosConfig.Xtz), currencies: account.Currencies), TezosConfig _ => new TezosSwap( account: account.GetCurrencyAccount <TezosAccount>(currency.Name), currencies: account.Currencies), _ => throw new NotSupportedException($"Not supported currency {currency.Name}") });
public async void GetBalanceTest( BitcoinBasedConfig currency, string address) { var api = new BlockCypherApi(currency, BlockCypherApi.BitcoinMainNet); var balanceResult = await api.GetBalanceAsync(address); Assert.False(balanceResult.HasError, balanceResult.Error?.Description ?? ""); }
public void Sign( SecureBytes privateKey, ITxOutput[] spentOutputs, BitcoinBasedConfig bitcoinBasedConfig) { using var scopedPrivateKey = privateKey.ToUnsecuredBytes(); Sign(new Key(scopedPrivateKey), spentOutputs, bitcoinBasedConfig); // todo: do not use NBitcoin.Key }
public async Task <IBitcoinBasedTransaction> CreateSwapRefundTxAsync( IBitcoinBasedTransaction paymentTx, long amount, string refundAddress, byte[] redeemScript, DateTimeOffset lockTime, BitcoinBasedConfig currencyConfig) { var swapOutput = paymentTx.Outputs .Cast <BitcoinBasedTxOutput>() .FirstOrDefault(o => o.IsPayToScriptHash(redeemScript)); if (swapOutput == null) { throw new Exception("Can't find pay to script hash output"); } var feeRate = await currencyConfig .GetFeeRateAsync() .ConfigureAwait(false); var inputSize = new BitcoinInputToSign { Output = swapOutput, Signer = new BitcoinRefundSigner() }.SizeWithSignature(); var outputSize = new BitcoinDestination { AmountInSatoshi = amount, Script = BitcoinAddress.Create(refundAddress, currencyConfig.Network).ScriptPubKey }.Size(); var(txSize, _) = BitcoinTransactionParams.CalculateTxSize( inputsCount: 1, inputsSize: inputSize, outputsCount: 1, outputsSize: outputSize, witnessCount: swapOutput.IsSegWit ? 1 : 0, changeOutputSize: 0); var feeInSatoshi = (long)(txSize * feeRate); if (amount - feeInSatoshi < 0) { throw new Exception($"Insufficient funds for fee. Available {amount}, required {feeInSatoshi}"); } return(currencyConfig.CreateP2PkhTx( unspentOutputs: new ITxOutput[] { swapOutput }, destinationAddress: refundAddress, changeAddress: refundAddress, amount: amount - feeInSatoshi, fee: feeInSatoshi, lockTime: lockTime, knownRedeems: new Script(redeemScript))); }
public void Sign( Key privateKey, ITxOutput spentOutput, BitcoinBasedConfig bitcoinBasedConfig) { var output = (BitcoinBasedTxOutput)spentOutput; Tx.Sign(new BitcoinSecret(privateKey, bitcoinBasedConfig.Network), output.Coin); }
public BitcoinBasedTransactionViewModel( IBitcoinBasedTransaction tx, BitcoinBasedConfig bitcoinBasedConfig) : base(tx, bitcoinBasedConfig, tx.Amount / bitcoinBasedConfig.DigitsMultiplier, GetFee(tx, bitcoinBasedConfig)) { Fee = tx.Fees != null ? tx.Fees.Value / bitcoinBasedConfig.DigitsMultiplier : 0; // todo: N/A }
public static IBitcoinBasedTransaction CreateFakeTx(BitcoinBasedConfig currency, PubKey to, params long[] outputs) { var tx = Transaction.Create(currency.Network); foreach (var output in outputs) { tx.Outputs.Add(new TxOut(new Money(output), to.Hash)); // p2pkh } return(new BitcoinBasedTransaction(currency.Name, tx)); }
public byte[] SignMessageByServiceKey(byte[] data, int chain, uint index) { using var masterKey = BitcoinBasedConfig .CreateExtKeyFromSeed(Seed); using var derivedKey = masterKey .Derive(new KeyPath(path: $"m/{ServicePurpose}'/0'/0'/{chain}/{index}")); return(derivedKey.SignMessage(data)); }
public void Sign( Key privateKey, ITxOutput[] spentOutputs, BitcoinBasedConfig bitcoinBasedConfig) { foreach (var output in spentOutputs) { Sign(privateKey, output, bitcoinBasedConfig); } }
public SecureBytes GetServicePublicKey(uint index) { using var masterKey = BitcoinBasedConfig .CreateExtKeyFromSeed(Seed); using var extKey = masterKey .Derive(new KeyPath(path: $"m/{ServicePurpose}'/0'/0'/0/{index}")); return(extKey.GetPublicKey()); }
private static decimal GetFee( IBitcoinBasedTransaction tx, BitcoinBasedConfig bitcoinBasedConfig) { return(tx.Fees != null ? tx.Type.HasFlag(BlockchainTransactionType.Output) ? tx.Fees.Value / bitcoinBasedConfig.DigitsMultiplier : 0 : 0); }
public static SendViewModel CreateViewModel(IAtomexApp app, CurrencyViewModel currencyViewModel) { return(currencyViewModel.Currency switch { BitcoinBasedConfig _ => (SendViewModel) new BitcoinBasedSendViewModel(app, currencyViewModel), Erc20Config _ => (SendViewModel) new Erc20SendViewModel(app, currencyViewModel), EthereumConfig _ => (SendViewModel) new EthereumSendViewModel(app, currencyViewModel), Fa12Config _ => (SendViewModel) new Fa12SendViewModel(app, currencyViewModel), TezosConfig _ => (SendViewModel) new TezosSendViewModel(app, currencyViewModel), _ => throw new NotSupportedException($"Can't create send view model for {currencyViewModel.Currency.Name}. This currency is not supported."), });
public void ExtractSecretFromHtlcP2PkhScriptSwapRedeemTx(BitcoinBasedConfig currency) { var tx = SignHtlcP2PkhScriptSwapRedeemTx(currency); var data = (tx.Inputs.First() as BitcoinBasedTxPoint) .ExtractAllPushData(); var secret = data.FirstOrDefault(d => d.SequenceEqual(Common.Secret)); Assert.NotNull(secret); }
public IBitcoinBasedTransaction SignSegwitPaymentTx(BitcoinBasedConfig currency) { var initTx = BitcoinBasedCommon.CreateFakeTx(currency, Common.Alice.PubKey, 1_0000_0000, 1_0000_0000); var tx = CreateSegwitPaymentTx(currency); tx.Sign(Common.Alice, initTx.Outputs, currency); Assert.True(tx.Verify(initTx.Outputs, currency)); return(tx); }
public (IBitcoinBasedTransaction, byte[]) SignHtlcP2PkhScriptSwapPaymentTx(BitcoinBasedConfig currency) { var initTx = BitcoinBasedCommon.CreateFakeTx(currency, Common.Alice.PubKey, 1_0000_0000, 1_0000_0000); var(tx, redeemScript) = CreateHtlcP2PkhScriptSwapPaymentTx(currency); tx.Sign(Common.Alice, initTx.Outputs, currency); Assert.True(tx.Verify(initTx.Outputs, currency)); return(tx, redeemScript); }
public async void GetUnspentOutputsTest( BitcoinBasedConfig currency, string address) { var api = new BlockCypherApi(currency, BlockCypherApi.BitcoinMainNet); var utxoResult = await api.GetUnspentOutputsAsync(address); Assert.False(utxoResult.HasError, utxoResult.Error?.Description ?? ""); var utxo = utxoResult.Value; Assert.NotNull(utxo); }
public static IBitcoinBasedTransaction CreateSegwitPaymentTx( BitcoinBasedConfig currency, IEnumerable <ITxOutput> outputs, PubKey from, PubKey to, int amount, int fee) { return(currency.CreateP2WPkhTx( unspentOutputs: outputs, destinationAddress: to.GetSegwitAddress(currency.Network).ToString(), changeAddress: from.GetSegwitAddress(currency.Network).ToString(), amount: amount, fee: fee)); }
public bool Verify( ITxOutput spentOutput, BitcoinBasedConfig bitcoinBasedConfig, bool checkScriptPubKey = true) { var result = bitcoinBasedConfig.Network.CreateTransactionBuilder() .SetTransactionPolicy(new StandardTransactionPolicy { CheckScriptPubKey = checkScriptPubKey, ScriptVerify = ScriptVerify.Standard }) .AddCoins(((BitcoinBasedTxOutput)spentOutput).Coin) .Verify(Tx, out _); return(result); }
public static Task <BitcoinTransactionParams> SelectTransactionParamsByFeeRateAsync( IEnumerable <BitcoinInputToSign> availableInputs, IEnumerable <BitcoinDestination> destinations, string changeAddress, decimal feeRate, BitcoinBasedConfig currencyConfig, CancellationToken cancellationToken = default) { return(SelectTransactionParamsByFeeRateAsync( availableInputs: availableInputs, destinations: destinations.Select(d => (d.AmountInSatoshi, d.Size())), changeAddress: changeAddress, feeRate: feeRate, currencyConfig: currencyConfig, cancellationToken: cancellationToken)); }
public void SendDustChangeFailTest( BitcoinBasedConfig currency, decimal available, decimal amount, decimal fee, DustUsagePolicy dustUsagePolicy) { var error = Send( currency: currency, available: available, amount: amount, fee: fee, dustUsagePolicy: dustUsagePolicy); Assert.NotNull(error); Assert.Equal(Errors.InsufficientAmount, error.Code); }
private Error Send( BitcoinBasedConfig currency, decimal available, decimal amount, decimal fee, DustUsagePolicy dustUsagePolicy, Action <Mock <IBlockchainApi> > apiSetup = null, Action <Mock <IAccountDataRepository>, WalletAddress> repositorySetup = null) { var apiMock = new Mock <IBlockchainApi>(); apiSetup?.Invoke(apiMock); var wallet = new HdWallet(Network.TestNet); var fromAddress = wallet.GetAddress( currency: currency, account: 0, chain: 0, index: 0, keyType: CurrencyConfig.StandardKey); var fromOutputs = GetOutputs(fromAddress.Address, NBitcoin.Network.TestNet, currency.CoinToSatoshi(available)).ToList(); var repositoryMock = new Mock <IAccountDataRepository>(); repositorySetup?.Invoke(repositoryMock, fromAddress); var currencies = Common.CurrenciesTestNet; currencies.GetByName(currency.Name).BlockchainApi = apiMock.Object; var account = new BitcoinBasedAccount( currency: currency.Name, currencies: currencies, wallet: wallet, dataRepository: repositoryMock.Object); return(account .SendAsync( from: fromOutputs, to: currency.TestAddress(), amount: amount, fee: fee, dustUsagePolicy: dustUsagePolicy) .WaitForResult()); }
public async void IsTransactionOutputSpentTest( BitcoinBasedConfig currency, string txId, uint outputNo, string spentTxId, uint spentIndex) { var api = new BlockCypherApi(currency, BlockCypherApi.BitcoinMainNet); var spentPointResult = await api.IsTransactionOutputSpent(txId, outputNo); Assert.False(spentPointResult.HasError, spentPointResult.Error?.Description ?? ""); var spentPoint = spentPointResult.Value; Assert.NotNull(spentPoint); Assert.Equal(spentTxId, spentPoint.Hash); Assert.Equal(spentIndex, spentPoint.Index); }
public async void GetInputTest( BitcoinBasedConfig currency, string txId, uint inputIndex, string prevTxId, uint prevTxOutputIndex) { var api = new BlockCypherApi(currency, BlockCypherApi.BitcoinMainNet); var inputResult = await api.GetInputAsync(txId, inputIndex); Assert.False(inputResult.HasError, inputResult.Error?.Description ?? ""); var input = inputResult.Value; Assert.NotNull(input); Assert.Equal(prevTxId, input.Hash); Assert.Equal(prevTxOutputIndex, input.Index); }