├── .gitattributes ├── .github └── workflows │ └── dotnetCI.yml ├── .gitignore ├── Doc └── Images │ └── MainPreview.jpg ├── License.md ├── README.md ├── SharpPusher.sln └── SharpPusher ├── App.axaml ├── App.axaml.cs ├── Assets └── PusherIcon.ico ├── MVVM ├── BindableCommand.cs ├── DependsOnPropertyAttribute.cs └── InpcBase.cs ├── Models ├── Networks.cs ├── Response.cs └── State.cs ├── Program.cs ├── Services ├── IApi.cs ├── P2P.cs └── PushServices │ ├── BlockCypher.cs │ ├── BlockchainInfo.cs │ └── Blockchair.cs ├── SharpPusher.csproj ├── ViewModels └── MainWindowViewModel.cs ├── Views ├── MainWindow.axaml └── MainWindow.axaml.cs └── nuget.config /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/dotnetCI.yml: -------------------------------------------------------------------------------- 1 | name: .NET-CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | Windows: 11 | runs-on: windows-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Setup .NET 15 | uses: actions/setup-dotnet@v4 16 | with: 17 | dotnet-version: 8.0.x 18 | - name: Restore dependencies 19 | run: dotnet restore 20 | - name: Build 21 | run: dotnet build -c Release --no-restore 22 | 23 | Linux: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Setup .NET 28 | uses: actions/setup-dotnet@v4 29 | with: 30 | dotnet-version: 8.0.x 31 | - name: Restore dependencies 32 | run: dotnet restore 33 | - name: Build 34 | run: dotnet build -c Release --no-restore 35 | 36 | Mac: 37 | runs-on: macos-latest 38 | steps: 39 | - uses: actions/checkout@v4 40 | - name: Setup .NET 41 | uses: actions/setup-dotnet@v4 42 | with: 43 | dotnet-version: 8.0.x 44 | - name: Restore dependencies 45 | run: dotnet restore 46 | - name: Build 47 | run: dotnet build -c Release --no-restore 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.rsuser 6 | *.suo 7 | *.user 8 | *.userosscache 9 | *.sln.docstates 10 | 11 | # Build results 12 | [Dd]ebug/ 13 | [Dd]ebugPublic/ 14 | [Rr]elease/ 15 | x64/ 16 | build/ 17 | bld/ 18 | [Bb]in/ 19 | [Oo]bj/ 20 | 21 | # Visual Studio 2015/2017 cache/options directory 22 | .vs/ 23 | # Uncomment if you have tasks that create the project's static files in wwwroot 24 | #wwwroot/ 25 | 26 | # Roslyn cache directories 27 | *.ide/ 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | #NUNIT 34 | *.VisualState.xml 35 | TestResult.xml 36 | 37 | # Build Results of an ATL Project 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | 42 | *_i.c 43 | *_p.c 44 | *_i.h 45 | *.ilk 46 | *.meta 47 | *.obj 48 | *.pch 49 | *.pdb 50 | *.pgc 51 | *.pgd 52 | *.rsp 53 | *.sbr 54 | *.tlb 55 | *.tli 56 | *.tlh 57 | *.tmp 58 | *.tmp_proj 59 | *.log 60 | *.vspscc 61 | *.vssscc 62 | .builds 63 | *.pidb 64 | *.svclog 65 | *.scc 66 | 67 | # Chutzpah Test files 68 | _Chutzpah* 69 | 70 | # Visual C++ cache files 71 | ipch/ 72 | *.aps 73 | *.ncb 74 | *.opensdf 75 | *.sdf 76 | *.cachefile 77 | 78 | # Visual Studio profiler 79 | *.psess 80 | *.vsp 81 | *.vspx 82 | 83 | # TFS 2012 Local Workspace 84 | $tf/ 85 | 86 | # Guidance Automation Toolkit 87 | *.gpState 88 | 89 | # ReSharper is a .NET coding add-in 90 | _ReSharper*/ 91 | *.[Rr]e[Ss]harper 92 | *.DotSettings.user 93 | 94 | # JustCode is a .NET coding addin-in 95 | .JustCode 96 | 97 | # TeamCity is a build add-in 98 | _TeamCity* 99 | 100 | # DotCover is a Code Coverage Tool 101 | *.dotCover 102 | 103 | # NCrunch 104 | _NCrunch_* 105 | .*crunch*.local.xml 106 | 107 | # MightyMoose 108 | *.mm.* 109 | AutoTest.Net/ 110 | 111 | # Web workbench (sass) 112 | .sass-cache/ 113 | 114 | # Installshield output folder 115 | [Ee]xpress/ 116 | 117 | # DocProject is a documentation generator add-in 118 | DocProject/buildhelp/ 119 | DocProject/Help/*.HxT 120 | DocProject/Help/*.HxC 121 | DocProject/Help/*.hhc 122 | DocProject/Help/*.hhk 123 | DocProject/Help/*.hhp 124 | DocProject/Help/Html2 125 | DocProject/Help/html 126 | 127 | # Click-Once directory 128 | publish/ 129 | 130 | # Publish Web Output 131 | *.[Pp]ublish.xml 132 | *.azurePubxml 133 | ## TODO: Comment the next line if you want to checkin your 134 | ## web deploy settings but do note that will include unencrypted 135 | ## passwords 136 | #*.pubxml 137 | 138 | # NuGet Packages Directory 139 | packages/* 140 | ## TODO: If the tool you use requires repositories.config 141 | ## uncomment the next line 142 | #!packages/repositories.config 143 | 144 | # Enable "build/" folder in the NuGet Packages folder since 145 | # NuGet packages use it for MSBuild targets. 146 | # This line needs to be after the ignore of the build folder 147 | # (and the packages folder if the line above has been uncommented) 148 | !packages/build/ 149 | 150 | # Windows Azure Build Output 151 | csx/ 152 | *.build.csdef 153 | 154 | # Windows Store app package directory 155 | AppPackages/ 156 | 157 | # Others 158 | sql/ 159 | *.Cache 160 | ClientBin/ 161 | [Ss]tyle[Cc]op.* 162 | ~$* 163 | *~ 164 | *.dbmdl 165 | *.dbproj.schemaview 166 | *.pfx 167 | *.publishsettings 168 | node_modules/ 169 | 170 | # RIA/Silverlight projects 171 | Generated_Code/ 172 | 173 | # Backup & report files from converting an old project file 174 | # to a newer Visual Studio version. Backup files are not needed, 175 | # because we have git ;-) 176 | _UpgradeReport_Files/ 177 | Backup*/ 178 | UpgradeLog*.XML 179 | UpgradeLog*.htm 180 | 181 | # SQL Server files 182 | *.mdf 183 | *.ldf 184 | 185 | # Business Intelligence projects 186 | *.rdl.data 187 | *.bim.layout 188 | *.bim_*.settings 189 | 190 | # Microsoft Fakes 191 | FakesAssemblies/ 192 | 193 | # LightSwitch generated files 194 | GeneratedArtifacts/ 195 | _Pvt_Extensions/ 196 | ModelManifest.xml 197 | *.pubxml 198 | -------------------------------------------------------------------------------- /Doc/Images/MainPreview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coding-Enthusiast/SharpPusher/6681bbda81f2acddd561b9fa49ad9cb04bb568b7/Doc/Images/MainPreview.jpg -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Coding Enthusiast 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![.NET-CI](https://github.com/Coding-Enthusiast/SharpPusher/actions/workflows/dotnetCI.yml/badge.svg)](https://github.com/Coding-Enthusiast/SharpPusher/actions/workflows/dotnetCI.yml) 2 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Coding-Enthusiast/SharpPusher/blob/master/License) 3 | [![Target](https://img.shields.io/badge/dynamic/xml?color=%23512bd4&label=target&query=%2F%2FTargetFramework%5B1%5D&url=https%3A%2F%2Fraw.githubusercontent.com%2FCoding-Enthusiast%2FSharpPusher%2Fmaster%2FSharpPusher%2FSharpPusher.csproj&logo=.net)](https://github.com/Coding-Enthusiast/SharpPusher/blob/master/Src/SharpPusher/SharpPusher.csproj) 4 | [![Downloads](https://img.shields.io/github/downloads/Coding-Enthusiast/SharpPusher/total)](https://github.com/Coding-Enthusiast/SharpPusher/releases) 5 | 6 |

7 | logo 8 |

9 | 10 | # SharpPusher 11 | A simple GUI to broadcast bitcoin and various altcoin transactions to the network. 12 | 13 | ![Preview](/Doc/Images/MainPreview.jpg) 14 | 15 | 16 | Following block explorer API services are used to push bitcoin raw transactions to the network 17 | * https://blockcypher.com 18 | * https://blockchair.com 19 | * https://blockchain.com 20 | 21 | Blockchair API is used for altcoin transactions. 22 | 23 | SharpPusher also supports directly connecting to the Bitcoin P2P network (random full nodes) and pushing 24 | the transaction to the network in a more decentralized way. 25 | 26 | ## Announcement Link 27 | Bitcointalk topic https://bitcointalk.org/index.php?topic=1944501.0 28 | 29 | Contributions are welcome. 30 | 31 | 32 | ## Donations 33 | 34 | If You found this tool helpful consider making a donation: 35 | Legacy address: 1Q9swRQuwhTtjZZ2yguFWk7m7pszknkWyk 36 | SegWit address: bc1q3n5t9gv40ayq68nwf0yth49dt5c799wpld376s 37 | -------------------------------------------------------------------------------- /SharpPusher.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31112.23 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpPusher", "SharpPusher\SharpPusher.csproj", "{8954305B-58E4-4978-B19F-7C2922F8BAE7}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {8954305B-58E4-4978-B19F-7C2922F8BAE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {8954305B-58E4-4978-B19F-7C2922F8BAE7}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {8954305B-58E4-4978-B19F-7C2922F8BAE7}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {8954305B-58E4-4978-B19F-7C2922F8BAE7}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {15E9ABE3-DD58-460B-9F99-1B5790085510} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /SharpPusher/App.axaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 18 | 19 | 27 | 30 | 31 | 34 | 35 | 38 | 51 | 52 | 65 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /SharpPusher/App.axaml.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using Avalonia; 7 | using Avalonia.Controls.ApplicationLifetimes; 8 | using Avalonia.Markup.Xaml; 9 | using SharpPusher.ViewModels; 10 | using SharpPusher.Views; 11 | 12 | namespace SharpPusher 13 | { 14 | public class App : Application 15 | { 16 | public override void Initialize() 17 | { 18 | AvaloniaXamlLoader.Load(this); 19 | } 20 | 21 | public override void OnFrameworkInitializationCompleted() 22 | { 23 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 24 | { 25 | desktop.MainWindow = new MainWindow() 26 | { 27 | DataContext = new MainWindowViewModel() 28 | }; 29 | } 30 | 31 | base.OnFrameworkInitializationCompleted(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SharpPusher/Assets/PusherIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coding-Enthusiast/SharpPusher/6681bbda81f2acddd561b9fa49ad9cb04bb568b7/SharpPusher/Assets/PusherIcon.ico -------------------------------------------------------------------------------- /SharpPusher/MVVM/BindableCommand.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using System; 7 | using System.Windows.Input; 8 | 9 | namespace SharpPusher.MVVM 10 | { 11 | public class BindableCommand : ICommand 12 | { 13 | public BindableCommand(Action executeMethod) : this(executeMethod, null) 14 | { 15 | } 16 | 17 | public BindableCommand(Action executeMethod, Func? canExecuteMethod) 18 | { 19 | ExecuteMethod = executeMethod; 20 | CanExecuteMethod = canExecuteMethod; 21 | } 22 | 23 | 24 | private readonly Action ExecuteMethod; 25 | private readonly Func? CanExecuteMethod; 26 | 27 | public event EventHandler? CanExecuteChanged; 28 | 29 | 30 | public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty); 31 | 32 | public bool CanExecute(object? parameter) => CanExecuteMethod is null || CanExecuteMethod(); 33 | 34 | public void Execute(object? parameter) => ExecuteMethod?.Invoke(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SharpPusher/MVVM/DependsOnPropertyAttribute.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using System; 7 | 8 | namespace SharpPusher.MVVM 9 | { 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public sealed class DependsOnPropertyAttribute : Attribute 12 | { 13 | /// 14 | /// Initializes a new instance of using depending properties names. 15 | /// 16 | /// Names of the properties that the property with this attribute depends on. 17 | public DependsOnPropertyAttribute(params string[] dependingPropertyNames) 18 | { 19 | DependentProps = dependingPropertyNames; 20 | } 21 | 22 | /// 23 | /// Array of all the properties that the property depends on! 24 | /// 25 | public readonly string[] DependentProps; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SharpPusher/MVVM/InpcBase.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Reflection; 9 | using System.Runtime.CompilerServices; 10 | 11 | namespace SharpPusher.MVVM 12 | { 13 | /// 14 | /// Base (abstract) class implementing . 15 | /// Could be used for both ViewModels and Models. 16 | /// 17 | public abstract class InpcBase : INotifyPropertyChanged 18 | { 19 | public InpcBase() 20 | { 21 | PropertyDependencyMap = new Dictionary>(); 22 | 23 | foreach (PropertyInfo property in GetType().GetProperties()) 24 | { 25 | foreach (DependsOnPropertyAttribute dependsAttr in property.GetCustomAttributes()) 26 | { 27 | if (dependsAttr is not null) 28 | { 29 | foreach (string dependence in dependsAttr.DependentProps) 30 | { 31 | if (!PropertyDependencyMap.TryGetValue(dependence, out List? value)) 32 | { 33 | value = new List(); 34 | PropertyDependencyMap.Add(dependence, value); 35 | } 36 | 37 | value.Add(property.Name); 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | 45 | 46 | private readonly Dictionary> PropertyDependencyMap; 47 | 48 | /// 49 | /// The PropertyChanged Event to raise to any UI object 50 | /// 51 | public event PropertyChangedEventHandler? PropertyChanged; 52 | 53 | 54 | /// 55 | /// Raises the event using the given property name. 56 | /// The event is only invoked if data binding is used 57 | /// 58 | /// The Name of the property that is changing. 59 | protected void RaisePropertyChanged(string propertyName) 60 | { 61 | if (PropertyChanged is not null) 62 | { 63 | PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); 64 | 65 | // Also raise the PropertyChanged event for dependant properties. 66 | if (PropertyDependencyMap.TryGetValue(propertyName, out List? value)) 67 | { 68 | foreach (string p in value) 69 | { 70 | PropertyChanged.Invoke(this, new PropertyChangedEventArgs(p)); 71 | } 72 | } 73 | } 74 | } 75 | 76 | 77 | /// 78 | /// Sets the value of a property and raises the event. 79 | /// 80 | /// Type of the property 81 | /// Property's backing field to change 82 | /// New value to set the to 83 | /// 84 | /// [Default value = null] 85 | /// The Name of the property that is changing. If it was null, the name is resolved at runtime automatically. 86 | /// 87 | /// Retruns true if the value was changed, false if otherwise. 88 | protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) 89 | { 90 | if (EqualityComparer.Default.Equals(field, value) || propertyName is null) 91 | { 92 | return false; 93 | } 94 | else 95 | { 96 | field = value; 97 | RaisePropertyChanged(propertyName); 98 | return true; 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /SharpPusher/Models/Networks.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | namespace SharpPusher.Models 7 | { 8 | public enum Networks 9 | { 10 | Bitcoin, 11 | BitcoinTestnet, 12 | BitcoinCash, 13 | BitcoinSV, 14 | Dogecoin, 15 | Litecoin, 16 | Monero, 17 | Zcash, 18 | Ethereum, 19 | EthereumTestnet, 20 | Dash, 21 | Ripple, 22 | Groestlcoin, 23 | Stellar, 24 | Cardano, 25 | Mixin, 26 | Tezos, 27 | EOS, 28 | BitcoinABC 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SharpPusher/Models/Response.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | namespace SharpPusher.Models 7 | { 8 | public class Response 9 | { 10 | public bool IsSuccess { get; private set; } = true; 11 | public string Message { get; private set; } = string.Empty; 12 | 13 | private void SetMsg(string msg, bool success) 14 | { 15 | IsSuccess = success; 16 | Message = msg; 17 | } 18 | public void SetMessage(string msg) => SetMsg(msg, true); 19 | public void SetError(string msg) => SetMsg(msg, false); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SharpPusher/Models/State.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | namespace SharpPusher.Models 7 | { 8 | public enum State 9 | { 10 | Ready, 11 | Broadcasting, 12 | Success, 13 | Failed 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SharpPusher/Program.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using Avalonia; 7 | 8 | namespace SharpPusher 9 | { 10 | class Program 11 | { 12 | // Initialization code. Don't use any Avalonia, third-party APIs or any 13 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 14 | // yet and stuff might break. 15 | public static void Main(string[] args) => BuildAvaloniaApp() 16 | .StartWithClassicDesktopLifetime(args); 17 | 18 | // Avalonia configuration, don't remove; also used by visual designer. 19 | public static AppBuilder BuildAvaloniaApp() 20 | => AppBuilder.Configure() 21 | .UsePlatformDetect() 22 | .LogToTrace(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SharpPusher/Services/IApi.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using SharpPusher.Models; 7 | using System.Threading.Tasks; 8 | 9 | namespace SharpPusher.Services 10 | { 11 | public interface IApi 12 | { 13 | string ApiName { get; } 14 | Task PushTx(string txHex); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SharpPusher/Services/P2P.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using Autarkysoft.Bitcoin; 7 | using Autarkysoft.Bitcoin.Blockchain.Transactions; 8 | using Autarkysoft.Bitcoin.Clients; 9 | using Autarkysoft.Bitcoin.P2PNetwork.Messages; 10 | using Autarkysoft.Bitcoin.P2PNetwork.Messages.MessagePayloads; 11 | using SharpPusher.Models; 12 | using System.Threading.Tasks; 13 | 14 | namespace SharpPusher.Services 15 | { 16 | public class P2P : IApi 17 | { 18 | public P2P(bool isMainNet) 19 | { 20 | netType = isMainNet ? NetworkType.MainNet : NetworkType.TestNet; 21 | } 22 | 23 | 24 | private readonly NetworkType netType; 25 | 26 | public static readonly string[] DnsMain = new string[] 27 | { 28 | "seed.bitcoin.sipa.be", // Pieter Wuille, only supports x1, x5, x9, and xd 29 | "dnsseed.bluematt.me", // Matt Corallo, only supports x9 30 | "dnsseed.bitcoin.dashjr.org", // Luke Dashjr 31 | "seed.bitcoinstats.com", // Christian Decker, supports x1 - xf 32 | "seed.bitcoin.jonasschnelli.ch", // Jonas Schnelli, only supports x1, x5, x9, and xd 33 | "seed.btc.petertodd.org", // Peter Todd, only supports x1, x5, x9, and xd 34 | "seed.bitcoin.sprovoost.nl", // Sjors Provoost 35 | "dnsseed.emzy.de", // Stephan Oeste 36 | "seed.bitcoin.wiz.biz", // Jason Maurice 37 | }; 38 | 39 | public static readonly string[] DnsTest = new string[] 40 | { 41 | "testnet-seed.bitcoin.jonasschnelli.ch", 42 | "seed.tbtc.petertodd.org", 43 | "seed.testnet.bitcoin.sprovoost.nl", 44 | "testnet-seed.bluematt.me", 45 | }; 46 | 47 | 48 | public string ApiName => "P2P"; 49 | 50 | public async Task PushTx(string txHex) 51 | { 52 | Response resp = new(); 53 | 54 | Transaction tx = new(txHex); 55 | Message msg = new(new TxPayload(tx), netType); 56 | 57 | MinimalClientSettings settings = new(netType, 4, null) 58 | { 59 | DnsSeeds = netType == NetworkType.MainNet ? DnsMain : DnsTest 60 | }; 61 | using MinimalClient client = new(settings); 62 | client.Start(); 63 | await Task.Delay(TimeConstants.MilliSeconds.FiveSec); 64 | client.Send(msg); 65 | 66 | resp.SetMessage($"Transaction was sent to {settings.MaxConnectionCount} nodes. Transaction ID: {tx.GetTransactionId()}"); 67 | return resp; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /SharpPusher/Services/PushServices/BlockCypher.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using Newtonsoft.Json.Linq; 7 | using SharpPusher.Models; 8 | using System.Net.Http; 9 | using System; 10 | using System.Threading.Tasks; 11 | 12 | namespace SharpPusher.Services.PushServices 13 | { 14 | public sealed class BlockCypher : IApi 15 | { 16 | public string ApiName => "BlockCypher"; 17 | 18 | 19 | public async Task PushTx(string txHex) 20 | { 21 | Response resp = new(); 22 | using HttpClient client = new(); 23 | 24 | try 25 | { 26 | JObject tx = new() 27 | { 28 | {"tx", txHex} 29 | }; 30 | 31 | string url = "https://api.blockcypher.com/v1/bcy/test/txs/push"; 32 | HttpResponseMessage httpResp = await client.PostAsync(url, new StringContent(tx.ToString())); 33 | if (!httpResp.IsSuccessStatusCode) 34 | { 35 | resp.SetError("API response doesn't indicate success."); 36 | return resp; 37 | } 38 | 39 | string t = await httpResp.Content.ReadAsStringAsync(); 40 | JObject jResult = JObject.Parse(t); 41 | if (jResult["error"] != null) 42 | { 43 | resp.SetError(jResult["error"]?.ToString() ?? ""); 44 | } 45 | else 46 | { 47 | resp.SetMessage($"Successfully done. Tx ID: {jResult["hash"]}"); 48 | } 49 | } 50 | catch (Exception ex) 51 | { 52 | string errMsg = (ex.InnerException == null) ? ex.Message : ex.Message + " " + ex.InnerException; 53 | resp.SetError(errMsg); 54 | } 55 | 56 | return resp; 57 | } 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /SharpPusher/Services/PushServices/BlockchainInfo.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Linq; 8 | using SharpPusher.Models; 9 | using System; 10 | using System.Net.Http; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace SharpPusher.Services.PushServices 15 | { 16 | public sealed class BlockchainInfo : IApi 17 | { 18 | public string ApiName => "Blockchain.Info"; 19 | 20 | public async Task PushTx(string txHex) 21 | { 22 | using HttpClient client = new(); 23 | Response resp = new(); 24 | 25 | try 26 | { 27 | client.BaseAddress = new Uri("https://blockchain.info"); 28 | 29 | string json = JsonConvert.SerializeObject(txHex); 30 | string contentType = "application/x-www-form-urlencoded"; 31 | HttpContent httpContent = new MultipartFormDataContent 32 | { 33 | new StringContent(json, Encoding.UTF8, contentType) 34 | }; 35 | 36 | HttpResponseMessage result = await client.PostAsync("pushtx", httpContent); 37 | string sResult = await result.Content.ReadAsStringAsync(); 38 | if (result.IsSuccessStatusCode) 39 | { 40 | if (sResult != null && sResult.StartsWith("{\"error\":")) 41 | { 42 | JObject jObject = JObject.Parse(sResult); 43 | resp.SetError(jObject["error"]?.ToString() ?? ""); 44 | } 45 | else 46 | { 47 | resp.SetMessage(sResult ?? ""); 48 | } 49 | } 50 | else 51 | { 52 | resp.SetError(sResult); 53 | } 54 | } 55 | catch (Exception ex) 56 | { 57 | string errMsg = (ex.InnerException == null) ? ex.Message : ex.Message + " " + ex.InnerException; 58 | resp.SetError(errMsg); 59 | } 60 | 61 | return resp; 62 | } 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /SharpPusher/Services/PushServices/Blockchair.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using Newtonsoft.Json.Linq; 7 | using SharpPusher.Models; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Net; 11 | using System.Net.Http; 12 | using System.Threading.Tasks; 13 | 14 | namespace SharpPusher.Services.PushServices 15 | { 16 | public sealed class Blockchair : IApi 17 | { 18 | public Blockchair(Chain chain) 19 | { 20 | this.chain = chain; 21 | } 22 | 23 | public enum Chain 24 | { 25 | BTC, 26 | BCH, 27 | DOGE, 28 | LTC, 29 | XMR, 30 | TBTC, 31 | BSV, 32 | ZEC, 33 | XRP, 34 | ΤETH, 35 | ETH, 36 | EOS, 37 | XTZ, 38 | XIN, 39 | ADA, 40 | XLM, 41 | GRS, 42 | DASH, 43 | ABC 44 | } 45 | 46 | private readonly Chain chain; 47 | 48 | public string ApiName => "Blockchair"; 49 | 50 | public async Task PushTx(string txHex) 51 | { 52 | using HttpClient client = new(); 53 | Response resp = new(); 54 | 55 | try 56 | { 57 | string chainName = chain switch 58 | { 59 | Chain.BTC => "bitcoin", 60 | Chain.TBTC => "bitcoin/testnet", 61 | Chain.BCH => "bitcoin-cash", 62 | Chain.DOGE => "dogecoin", 63 | Chain.LTC => "litecoin", 64 | Chain.XMR => "monero", 65 | Chain.ADA => "cardano", 66 | Chain.BSV => "bitcoin-sv", 67 | Chain.EOS => "eos", 68 | Chain.ETH => "ethereum", 69 | Chain.ΤETH => "ethereum/testnet", 70 | Chain.XIN => "mixin", 71 | Chain.XLM => "stellar", 72 | Chain.XRP => "ripple", 73 | Chain.XTZ => "tezos", 74 | Chain.DASH => "dash", 75 | Chain.GRS => "groestlcoin", 76 | Chain.ABC => "bitcoin-abc", 77 | Chain.ZEC => "zcash", 78 | _ => throw new ArgumentException("Undefined Chain") 79 | }; 80 | 81 | 82 | string url = $"https://api.blockchair.com/{chainName}/push/transaction"; 83 | 84 | var content = new FormUrlEncodedContent( 85 | [ 86 | new KeyValuePair("data", txHex) 87 | ]); 88 | 89 | HttpResponseMessage httpResp = await client.PostAsync(url, content); 90 | 91 | string result = await httpResp.Content.ReadAsStringAsync(); 92 | if (httpResp.IsSuccessStatusCode) 93 | { 94 | JObject jResult = JObject.Parse(result); 95 | resp.SetMessage($"Successfully done. Tx ID: {jResult["data"]?["transaction_hash"]}"); 96 | } 97 | else if (httpResp.StatusCode == HttpStatusCode.BadRequest) 98 | { 99 | JObject jResult = JObject.Parse(result); 100 | resp.SetError($"Bad request: {jResult["context"]?["error"]}"); 101 | } 102 | else 103 | { 104 | resp.SetError(result); 105 | } 106 | } 107 | catch (Exception ex) 108 | { 109 | string errMsg = (ex.InnerException == null) ? ex.Message : ex.Message + " " + ex.InnerException; 110 | resp.SetError(errMsg); 111 | } 112 | 113 | return resp; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /SharpPusher/SharpPusher.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net8.0 5 | enable 6 | Copyright (c) 2017 Coding-Enthusiast 7 | Coding-Enthusiast 8 | Autarkysoft 9 | 0.13.0 10 | Assets\PusherIcon.ico 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /SharpPusher/ViewModels/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | // SharpPusher 2 | // Copyright (c) 2017 Coding Enthusiast 3 | // Distributed under the MIT software license, see the accompanying 4 | // file LICENCE or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | using Autarkysoft.Bitcoin; 7 | using Autarkysoft.Bitcoin.Blockchain.Scripts; 8 | using Autarkysoft.Bitcoin.Blockchain.Transactions; 9 | using Autarkysoft.Bitcoin.Encoders; 10 | using SharpPusher.Models; 11 | using SharpPusher.MVVM; 12 | using SharpPusher.Services; 13 | using SharpPusher.Services.PushServices; 14 | using System; 15 | using System.Collections.ObjectModel; 16 | using System.Diagnostics; 17 | using System.Reflection; 18 | 19 | namespace SharpPusher.ViewModels 20 | { 21 | public class MainWindowViewModel : InpcBase 22 | { 23 | public MainWindowViewModel() 24 | { 25 | NetworkList = new ObservableCollection((Networks[])Enum.GetValues(typeof(Networks))); 26 | _selNet = NetworkList[0]; 27 | SetApiList(); 28 | _selApi = ApiList[0]; 29 | 30 | BroadcastTxCommand = new BindableCommand(BroadcastTx, CanBroadcast); 31 | 32 | Version ver = Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0); 33 | VersionString = ver.ToString(3); 34 | } 35 | 36 | public string VersionString { get; } 37 | 38 | 39 | private string _rawTx = string.Empty; 40 | public string RawTx 41 | { 42 | get => _rawTx; 43 | set 44 | { 45 | if (SetField(ref _rawTx, value)) 46 | { 47 | BroadcastTxCommand.RaiseCanExecuteChanged(); 48 | } 49 | } 50 | } 51 | 52 | 53 | public ObservableCollection NetworkList { get; set; } 54 | 55 | private Networks _selNet; 56 | public Networks SelectedNetwork 57 | { 58 | get => _selNet; 59 | set 60 | { 61 | if (SetField(ref _selNet, value)) 62 | { 63 | SetApiList(); 64 | } 65 | } 66 | } 67 | private void SetApiList() 68 | { 69 | switch (SelectedNetwork) 70 | { 71 | case Networks.Bitcoin: 72 | ApiList = 73 | [ 74 | new Blockchair(Blockchair.Chain.BTC), 75 | new BlockCypher(), 76 | new P2P(true) 77 | ]; 78 | break; 79 | case Networks.BitcoinCash: 80 | ApiList = 81 | [ 82 | new Blockchair(Blockchair.Chain.BCH), 83 | ]; 84 | break; 85 | case Networks.Dogecoin: 86 | ApiList = 87 | [ 88 | new Blockchair(Blockchair.Chain.DOGE), 89 | ]; 90 | break; 91 | case Networks.Litecoin: 92 | ApiList = 93 | [ 94 | new Blockchair(Blockchair.Chain.LTC), 95 | ]; 96 | break; 97 | case Networks.Monero: 98 | ApiList = 99 | [ 100 | new Blockchair(Blockchair.Chain.XMR), 101 | ]; 102 | break; 103 | case Networks.BitcoinTestnet: 104 | ApiList = 105 | [ 106 | new Blockchair(Blockchair.Chain.TBTC), 107 | new P2P(false) 108 | ]; 109 | break; 110 | case Networks.BitcoinSV: 111 | ApiList = 112 | [ 113 | new Blockchair(Blockchair.Chain.BSV), 114 | ]; 115 | break; 116 | case Networks.Zcash: 117 | ApiList = 118 | [ 119 | new Blockchair(Blockchair.Chain.ZEC), 120 | ]; 121 | break; 122 | case Networks.Ripple: 123 | ApiList = 124 | [ 125 | new Blockchair(Blockchair.Chain.XRP), 126 | ]; 127 | break; 128 | case Networks.Stellar: 129 | ApiList = 130 | [ 131 | new Blockchair(Blockchair.Chain.XLM), 132 | ]; 133 | break; 134 | case Networks.Cardano: 135 | ApiList = 136 | [ 137 | new Blockchair(Blockchair.Chain.ADA), 138 | ]; 139 | break; 140 | case Networks.Mixin: 141 | ApiList = 142 | [ 143 | new Blockchair(Blockchair.Chain.XIN), 144 | ]; 145 | break; 146 | case Networks.Tezos: 147 | ApiList = 148 | [ 149 | new Blockchair(Blockchair.Chain.XTZ), 150 | ]; 151 | break; 152 | case Networks.EOS: 153 | ApiList = 154 | [ 155 | new Blockchair(Blockchair.Chain.EOS), 156 | ]; 157 | break; 158 | case Networks.Ethereum: 159 | ApiList = 160 | [ 161 | new Blockchair(Blockchair.Chain.ETH), 162 | ]; 163 | break; 164 | case Networks.EthereumTestnet: 165 | ApiList = 166 | [ 167 | new Blockchair(Blockchair.Chain.ΤETH), 168 | ]; 169 | break; 170 | case Networks.Groestlcoin: 171 | ApiList = 172 | [ 173 | new Blockchair(Blockchair.Chain.GRS), 174 | ]; 175 | break; 176 | case Networks.Dash: 177 | ApiList = 178 | [ 179 | new Blockchair(Blockchair.Chain.DASH), 180 | ]; 181 | break; 182 | case Networks.BitcoinABC: 183 | ApiList = 184 | [ 185 | new Blockchair(Blockchair.Chain.ABC), 186 | ]; 187 | break; 188 | } 189 | } 190 | 191 | 192 | private ObservableCollection _apiList = new(); 193 | public ObservableCollection ApiList 194 | { 195 | get => _apiList; 196 | set => SetField(ref _apiList, value); 197 | } 198 | 199 | 200 | private IApi _selApi; 201 | public IApi SelectedApi 202 | { 203 | get => _selApi; 204 | set 205 | { 206 | if (SetField(ref _selApi, value)) 207 | { 208 | BroadcastTxCommand.RaiseCanExecuteChanged(); 209 | } 210 | } 211 | } 212 | 213 | private string _msg = string.Empty; 214 | public string Message 215 | { 216 | get => _msg; 217 | set => SetField(ref _msg, value); 218 | } 219 | 220 | private State _state = State.Ready; 221 | public State CurrentState 222 | { 223 | get => _state; 224 | set => SetField(ref _state, value); 225 | } 226 | 227 | 228 | [DependsOnProperty(nameof(SelectedNetwork))] 229 | public bool IsCheckTxVisible => SelectedNetwork is Networks.Bitcoin 230 | or Networks.BitcoinTestnet 231 | or Networks.BitcoinCash 232 | or Networks.BitcoinABC 233 | or Networks.BitcoinSV 234 | or Networks.Litecoin 235 | or Networks.Dogecoin; 236 | 237 | private bool _checkTx = true; 238 | public bool CheckTx 239 | { 240 | get => _checkTx; 241 | set => SetField(ref _checkTx, value); 242 | } 243 | 244 | public static string CheckTxToolTip => "Enable to check the transaction hex using Bitcoin.Net library by deserializing " + 245 | "and evaluating all its scripts."; 246 | 247 | 248 | private bool _isSending; 249 | public bool IsSending 250 | { 251 | get => _isSending; 252 | set 253 | { 254 | if (SetField(ref _isSending, value)) 255 | { 256 | BroadcastTxCommand.RaiseCanExecuteChanged(); 257 | } 258 | } 259 | } 260 | 261 | 262 | 263 | public BindableCommand BroadcastTxCommand { get; private set; } 264 | private async void BroadcastTx() 265 | { 266 | Message = string.Empty; 267 | CurrentState = State.Ready; 268 | 269 | if (!Base16.TryDecode(RawTx, out byte[] result)) 270 | { 271 | Message = "Invalid hex."; 272 | return; 273 | } 274 | 275 | if (IsCheckTxVisible && CheckTx) 276 | { 277 | Debug.Assert(result != null); 278 | FastStreamReader stream = new(result); 279 | Transaction tx = new(); 280 | if (!tx.TryDeserialize(stream, out Errors error)) 281 | { 282 | Message = $"Could not deserialize transaction. Error message:{Environment.NewLine}{error.Convert()}"; 283 | return; 284 | } 285 | 286 | for (int i = 0; i < tx.TxInList.Length; i++) 287 | { 288 | if (!tx.TxInList[i].SigScript.TryEvaluate(ScriptEvalMode.Legacy, out _, out _, out error)) 289 | { 290 | Message = $"Could not evaluate {(i + 1).ToOrdinal()} input's signature script. " + 291 | $"Error message:{Environment.NewLine}{error}"; 292 | return; 293 | } 294 | } 295 | for (int i = 0; i < tx.TxOutList.Length; i++) 296 | { 297 | if (!tx.TxOutList[i].PubScript.TryEvaluate(ScriptEvalMode.Legacy, out _, out _, out error)) 298 | { 299 | Message = $"Could not evaluate {(i + 1).ToOrdinal()} output's pubkey script. " + 300 | $"Error message: {error}"; 301 | return; 302 | } 303 | } 304 | } 305 | 306 | IsSending = true; 307 | CurrentState = State.Broadcasting; 308 | 309 | Response resp = await SelectedApi.PushTx(RawTx); 310 | Message = resp.Message; 311 | CurrentState = resp.IsSuccess ? State.Success : State.Failed; 312 | 313 | IsSending = false; 314 | } 315 | private bool CanBroadcast() 316 | { 317 | return !string.IsNullOrWhiteSpace(RawTx) && !IsSending && SelectedApi != null; 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /SharpPusher/Views/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 29 | 33 | 34 | 35 | 36 | 38 | 39 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 |