Skip to content

Commit

Permalink
New Denovo feature: verify any transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
Coding-Enthusiast committed Sep 6, 2021
1 parent 5abe579 commit acd3805
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 2 deletions.
18 changes: 16 additions & 2 deletions Src/Autarkysoft.Bitcoin/Blockchain/TransactionVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ namespace Autarkysoft.Bitcoin.Blockchain
/// </summary>
public class TransactionVerifier : ITransactionVerifier, IDisposable
{
/// <summary>
/// A simplified constructor used for testing. Skips setting <see cref="UtxoDb"/> and <see cref="mempool"/>.
/// </summary>
/// <param name="consensus">Consensus rules to use</param>
public TransactionVerifier(IConsensus consensus)
{
this.consensus = consensus;

calc = new EllipticCurveCalculator();
scrSer = new ScriptSerializer();
hash160 = new Ripemd160Sha256();
sha256 = new Sha256();
}

/// <summary>
/// Initializes a new instance of <see cref="TransactionVerifier"/> using given parameters.
/// </summary>
Expand Down Expand Up @@ -159,7 +173,7 @@ public bool VerifyCoinbaseOutput(ITransaction coinbase, out string error)
private bool VerifyP2pkh(ITransaction tx, int index, PushDataOp sigPush, PushDataOp pubPush,
ReadOnlySpan<byte> pubScrData, out string error)
{
var actualHash = hash160.ComputeHash(pubPush.data);
byte[] actualHash = hash160.ComputeHash(pubPush.data);
if (!pubScrData.Slice(3, 20).SequenceEqual(actualHash))
{
error = "Invalid hash.";
Expand Down Expand Up @@ -496,7 +510,7 @@ public bool Verify(ITransaction tx, out string error)
/// <inheritdoc/>
public bool Verify(ITransaction tx, IUtxo[] utxos, out string error)
{
if (utxos is null || tx.TxInList.Length != utxos.Length)
if (utxos is null || tx.TxInList.Length != utxos.Length)
{
error = "Invalid number of UTXOs.";
return false;
Expand Down
68 changes: 68 additions & 0 deletions Src/Denovo/Models/UtxoModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Denovo
// Copyright (c) 2020 Autarkysoft
// Distributed under the MIT software license, see the accompanying
// file LICENCE or http://www.opensource.org/licenses/mit-license.php.

using Autarkysoft.Bitcoin.Blockchain;
using Autarkysoft.Bitcoin.Blockchain.Scripts;
using Autarkysoft.Bitcoin.Encoders;
using Denovo.MVVM;

namespace Denovo.Models
{
public class UtxoModel : InpcBase
{
public UtxoModel() { }

public UtxoModel(byte[] txHash, uint index)
{
TxId = Base16.EncodeReverse(txHash);
Index = index;
}


private string _txId;
public string TxId
{
get => _txId;
set => SetField(ref _txId, value);
}

private uint _index;
public uint Index
{
get => _index;
set => SetField(ref _index, value);
}

private ulong _amaount;
public ulong Amount
{
get => _amaount;
set => SetField(ref _amaount, value);
}

private string _scr = string.Empty;
public string Script
{
get => _scr;
set => SetField(ref _scr, value);
}


public bool TryConvertToUtxo(out Utxo result, out string error)
{
if (!Base16.TryDecode(Script, out byte[] scrBa))
{
error = "Invalid pubkey script hex.";
result = null;
return false;
}

var scr = new PubkeyScript(scrBa);
result = new Utxo(Index, Amount, scr);
error = null;
return true;
}
}
}
6 changes: 6 additions & 0 deletions Src/Denovo/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ public async void OpenEcies()
await WinMan.ShowDialog(vm);
}

public async void OpenVerifyTx()
{
var vm = new VerifyTxViewModel();
await WinMan.ShowDialog(vm);
}

private Node _selNode;
public Node SelectedNode
{
Expand Down
124 changes: 124 additions & 0 deletions Src/Denovo/ViewModels/VerifyTxViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Denovo
// Copyright (c) 2020 Autarkysoft
// Distributed under the MIT software license, see the accompanying
// file LICENCE or http://www.opensource.org/licenses/mit-license.php.

using Autarkysoft.Bitcoin;
using Autarkysoft.Bitcoin.Blockchain;
using Autarkysoft.Bitcoin.Blockchain.Transactions;
using Autarkysoft.Bitcoin.Encoders;
using Denovo.Models;
using Denovo.MVVM;
using System;
using System.Linq;

namespace Denovo.ViewModels
{
public class VerifyTxViewModel : VmWithSizeBase
{
public VerifyTxViewModel() : base(800, 750)
{
VerifyCommand = new BindableCommand(Verify, () => IsVerifyEnable);
}



private readonly Transaction tx = new();
// TODO: add block height and network type
private readonly TransactionVerifier verifier = new(new Consensus(int.MaxValue, NetworkType.MainNet));


private string _txHex;
public string TxHex
{
get => _txHex;
set
{
if (SetField(ref _txHex, value))
{
if (string.IsNullOrWhiteSpace(value))
{
Result = "Enter a valid transaction hex.";
}
else if (!Base16.TryDecode(value, out byte[] ba))
{
UtxoList = Array.Empty<UtxoModel>();
IsVerifyEnable = false;
Result = "Invalid hexadecimal transaction.";
}
else if (!tx.TryDeserialize(new FastStreamReader(ba), out string error))
{
UtxoList = Array.Empty<UtxoModel>();
IsVerifyEnable = false;
Result = $"Error deserializing transaction: {error}";
}
else
{
// Has to recompute hashes when more than one tx is verified
tx.ComputeTransactionHashes();

Result = string.Empty;
UtxoList = tx.TxInList.Select(x => new UtxoModel(x.TxHash, x.Index)).ToArray();
IsVerifyEnable = true;
}
}
}
}

private UtxoModel[] _utxos;
public UtxoModel[] UtxoList
{
get => _utxos;
private set => SetField(ref _utxos, value);
}

private string _res;
public string Result
{
get => _res;
private set => SetField(ref _res, value);
}

private bool _isEnable;
public bool IsVerifyEnable
{
get => _isEnable;
private set
{
if (SetField(ref _isEnable, value))
{
VerifyCommand.RaiseCanExecuteChanged();
}
}
}


public BindableCommand VerifyCommand { get; private set; }
private void Verify()
{
var temp = new Utxo[UtxoList.Length];
for (int i = 0; i < UtxoList.Length; i++)
{
if (!UtxoList[i].TryConvertToUtxo(out Utxo u, out string error))
{
Result = $"Failed to set UTXO at index = {i}.{Environment.NewLine}{error}";
return;
}

temp[i] = u;
}

verifier.Init();
if (verifier.Verify(tx, temp, out string err))
{
Result = $"Transaction is valid (assuming all given UTXOs existed and were unspent at the time)." +
$"{Environment.NewLine}Transaction fee: {verifier.TotalFee} satoshi" +
$"{Environment.NewLine}Transaction SigOp count: {verifier.TotalSigOpCount}";
}
else
{
Result = $"Failed to verify the given transaction. Error: {err}";
}
}
}
}
1 change: 1 addition & 0 deletions Src/Denovo/Views/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<Button Content="Config" Command="{Binding OpenConfig}" Width="100" HorizontalAlignment="Left"/>
<Button Content="Miner" Command="{Binding OpenMiner}" Width="100" HorizontalAlignment="Left"/>
<Button Content="Encrypt/Decrypt" Command="{Binding OpenEcies}" HorizontalAlignment="Left"/>
<Button Content="Transaction verifier" Command="{Binding OpenVerifyTx}" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock Text="IP:"/>
Expand Down
68 changes: 68 additions & 0 deletions Src/Denovo/Views/VerifyTxView.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Denovo.ViewModels;assembly=Denovo"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="550"
x:Class="Denovo.Views.VerifyTxView"
FontSize="14">

<Design.DataContext>
<vm:VerifyTxViewModel/>
</Design.DataContext>

<Grid RowDefinitions="100,*,auto,70">
<TextBox Text="{Binding TxHex}"
Watermark="Transaction hex"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Grid.Row="0"/>

<ItemsControl Items="{Binding UtxoList}" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding TxId}"
TextWrapping="NoWrap"
ToolTip.Tip="Transaction ID"
Watermark="Transaction ID"
UseFloatingWatermark="False"
Width="150"/>

<TextBox Text="{Binding Index}"
ToolTip.Tip="Index"
Watermark="Index"
UseFloatingWatermark="False"
Width="50"/>

<TextBox Text="{Binding Amount}"
ToolTip.Tip="Amount (satoshi)"
Watermark="Amount (satoshi)"
UseFloatingWatermark="False"
Width="150"/>

<TextBox Text="{Binding Script}"
TextWrapping="NoWrap"
ToolTip.Tip="Script (hex)"
Watermark="Script (hex)"
UseFloatingWatermark="False"
Width="370"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

<Button Content="Verify"
Command="{Binding VerifyCommand}"
Margin="5"
Grid.Row="2"/>

<TextBox Text="{Binding Result, Mode=OneWay}"
IsReadOnly="True"
Grid.Row="3"/>
</Grid>
</UserControl>
16 changes: 16 additions & 0 deletions Src/Denovo/Views/VerifyTxView.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Denovo
// Copyright (c) 2020 Autarkysoft
// Distributed under the MIT software license, see the accompanying
// file LICENCE or http://www.opensource.org/licenses/mit-license.php.

using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace Denovo.Views
{
public partial class VerifyTxView : UserControl
{
public VerifyTxView() => InitializeComponent();
private void InitializeComponent() => AvaloniaXamlLoader.Load(this);
}
}

0 comments on commit acd3805

Please sign in to comment.