diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eba7dde --- /dev/null +++ b/.gitignore @@ -0,0 +1,191 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +x64/ +bld/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml + +# NuGet Packages Directory +packages/ +*.nupkg +NBitcoin.Mono.nuspec +NBitcoin.nuspec + +## TODO: If the tool you use requires repositories.config uncomment the next line +#!packages/repositories.config + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +# This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) +!packages/build/ + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ +/.vs/config/applicationhost.config +/NBitcoin.Portable/NBitcoin(MonoAndroid).csproj.bak +/NBitcoin.Portable/NBitcoin(Mono).csproj.bak + +# .NET Core projects +.vs/ +project.lock.json +.vscode/ +/NBitcoin.Tests/CanStoreInBlockRepository-Headers/StoreLock +/NBitcoin.Tests/addrmancache.dat +/NBitcoin.Tests/CanStoreBlocks/StoreLock +/NBitcoin.Tests/libbitcoinconsensus-0.dll +*.zip diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2d217b2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Sphere 10 Software + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/NPascalCoin.sln b/NPascalCoin.sln new file mode 100644 index 0000000..47b5694 --- /dev/null +++ b/NPascalCoin.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPascalCoin", "NPascalCoin\NPascalCoin.csproj", "{527FBE08-46AF-49C9-9479-FEFF1FA9EBF8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {527FBE08-46AF-49C9-9479-FEFF1FA9EBF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {527FBE08-46AF-49C9-9479-FEFF1FA9EBF8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {527FBE08-46AF-49C9-9479-FEFF1FA9EBF8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {527FBE08-46AF-49C9-9479-FEFF1FA9EBF8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/NPascalCoin/Constants.cs b/NPascalCoin/Constants.cs new file mode 100644 index 0000000..51cf322 --- /dev/null +++ b/NPascalCoin/Constants.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace NPascalCoin { + internal static class Constants { + public const string NPascalCoinVersion = "0.1"; + } +} diff --git a/NPascalCoin/DTO/AccountDTO.cs b/NPascalCoin/DTO/AccountDTO.cs new file mode 100644 index 0000000..c5e6add --- /dev/null +++ b/NPascalCoin/DTO/AccountDTO.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + + /// + /// An "Account object" is a JSON object with information about an account. + /// + public class AccountDTO { + + /// + /// Account number + /// + [JsonProperty("account")] + public uint Account { get; set; } + + /// + /// Encoded public key value (hexastring) + /// + [JsonProperty("enc_pubkey")] + public string EncPubKey { get; set; } + + /// + /// Account balance (PASCURRENCY) + /// + [JsonProperty("balance")] + public decimal Balance { get; set; } + + /// + /// Operations made by this account (Note: When an account receives a transaction, n_operation is not changed) + /// + [JsonProperty("n_operation")] + public uint NumOperations { get; set; } + + /// + /// Last block that updated this account. If equal to blockchain blocks count it means that it has pending operations to be included to the blockchain + /// + [JsonProperty("updated_b")] + public uint LastUpdatedBlock { get; set; } + } + +} diff --git a/NPascalCoin/DTO/BlockDTO.cs b/NPascalCoin/DTO/BlockDTO.cs new file mode 100644 index 0000000..55058b6 --- /dev/null +++ b/NPascalCoin/DTO/BlockDTO.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + + /// + /// A "Block object" is a JSON object with information about a Blockchain's block. + /// + public class BlockDTO { + + /// + /// Block number + /// + [JsonProperty("block")] + public uint Block { get; set; } + + /// + /// Last block that updated this account. If equal to blockchain blocks count it means that it has pending operations to be included to the blockchain + /// + [JsonProperty("enc_pubkey")] + public string EncPubKey { get; set; } + + /// + /// Reward of first account's block + /// + [JsonProperty("reward")] + public decimal Reward { get; set; } + + /// + /// Fee obtained by operations (PASCURRENCY) + /// + [JsonProperty("fee")] + public decimal Fee { get; set; } + + /// + /// Pascal Coin protocol used + /// + [JsonProperty("ver")] + public uint Version { get; set; } + + /// + /// Pascal Coin protocol available by the miner + /// + [JsonProperty("ver_a")] + public uint AvailableVersion { get; set; } + + /// + /// Unix timestamp + /// + [JsonProperty("timestamp")] + public uint Timestamp { get; set; } + + /// + /// Target used + /// + [JsonProperty("target")] + public uint CompactTarget { get; set; } + + /// + /// Nonce used + /// + [JsonProperty("nonce")] + public uint Nonce { get; set; } + + /// + /// Miner's payload + /// + [JsonProperty("payload")] + public string Payload { get; set; } + + /// + /// SafeBox Hash + /// + [JsonProperty("sbh")] + public string SafeBoxHash { get; set; } + + /// + /// Operations hash + /// + [JsonProperty("oph")] + public string OperationsHash { get; set; } + + /// + /// Proof of work + /// + [JsonProperty("pow")] + public string ProofOfWork { get; set; } + + /// + /// Number of operations included in this block + /// + [JsonProperty("operations")] + public uint OperationCount { get; set; } + + /// + /// Estimated network hashrate calculated by previous 50 blocks average + /// + [JsonProperty("hashratekhs")] + public uint Last50HashRateKhs { get; set; } + + /// + /// Number of blocks in the blockchain higher than this + /// + [JsonProperty("maturation")] + public uint Maturation { get; set; } + } + +} diff --git a/NPascalCoin/DTO/ConnectionDTO.cs b/NPascalCoin/DTO/ConnectionDTO.cs new file mode 100644 index 0000000..d04e30e --- /dev/null +++ b/NPascalCoin/DTO/ConnectionDTO.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + + /// + /// A "Connection object" is a JSON object with a connection to other node information. + /// + public class ConnectionDTO { + + /// + /// True if this connection is to a server node.False if this connection is a client node + /// + [JsonProperty("server")] + public bool IsServer { get; set; } + + /// + /// IP + /// + [JsonProperty("ip")] + public string IP { get; set; } + + /// + /// Port + /// + [JsonProperty("port")] + public int Port { get; set; } + + /// + /// seconds of live of this connection + /// + [JsonProperty("secs")] + public uint ConnectedDurationSec { get; set; } + + /// + /// Bytes sent + /// + [JsonProperty("sent")] + public uint BytesSent { get; set; } + + /// + /// Bytes received + /// + [JsonProperty("recv")] + public int BytesReceived { get; set; } + + /// + /// Other node App version + /// + [JsonProperty("appver")] + public string AppVersion { get; set; } + + /// + /// Net protocol of other node + /// + [JsonProperty("netvar")] + public uint RemoteVersion { get; set; } + + /// + /// Net protocol available of other node + /// + [JsonProperty("netvar_a")] + public uint RemoveAvailableVersion { get; set; } + + /// + /// Net timediff of other node (vs wallet) + /// + [JsonProperty("timediff")] + public uint TimeDiff { get; set; } + } + +} diff --git a/NPascalCoin/DTO/DecryptedPayloadDTO.cs b/NPascalCoin/DTO/DecryptedPayloadDTO.cs new file mode 100644 index 0000000..d92dee9 --- /dev/null +++ b/NPascalCoin/DTO/DecryptedPayloadDTO.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + /// + /// JSON object descibing a decrypted payload + /// + public class DecryptedPayloadDTO { + /// + /// Decryption result + /// + [JsonProperty("result")] + public bool Result { get; set; } + + /// + /// HEXASTRING - Same value than param payload sent + /// + [JsonProperty("enc_payload")] + public string OriginalPayload { get; set; } + + /// + /// Unencoded value in readable format (no HEXASTRING) + /// + [JsonProperty("unenc_payload")] + public string UnencryptedPayload { get; set; } + + /// + /// HEXASTRING - Unencoded value in hexastring + /// + [JsonProperty("unenc_hexpayload")] + public string UnencryptedPayloadHex { get; set; } + + /// + /// String - "key" or "pwd" + /// + [JsonProperty("payload_method")] + public PayloadMethod PayloadMethod { get; set; } + + /// + /// HEXASTRING - Encoded public key used to decrypt when method = "key" + /// + [JsonProperty("enc_pubkey")] + public string EncodedPubKey { get; set; } + + /// + /// String value used to decrypt when method = "pwd" + /// + [JsonProperty("pwd")] + public string DecryptPassword { get; set; } + + } +} diff --git a/NPascalCoin/DTO/ErrorCode.cs b/NPascalCoin/DTO/ErrorCode.cs new file mode 100644 index 0000000..4a6921f --- /dev/null +++ b/NPascalCoin/DTO/ErrorCode.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NPascalCoin.DTO { + public enum ErrorCode { + InternalError = 100, + MethodNotFound = 1001, + InvalidAccount = 1002, + InvalidBlock = 1003, + InvalidOperation = 1004, + InvalidPubKey = 1005, + NotFound = 1010, + WalletPasswordProtected = 1015, + InvalidData = 1016 + } +} diff --git a/NPascalCoin/DTO/ErrorResultDTO.cs b/NPascalCoin/DTO/ErrorResultDTO.cs new file mode 100644 index 0000000..c53f885 --- /dev/null +++ b/NPascalCoin/DTO/ErrorResultDTO.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + /// + /// JSON-RPC Error result + /// + public class ErrorResultDTO { + [JsonProperty("code")] + public ErrorCode ErrorCode { get; set; } + + [JsonProperty("message")] + public string Message { get; set; } + } + +} diff --git a/NPascalCoin/DTO/KeyType.cs b/NPascalCoin/DTO/KeyType.cs new file mode 100644 index 0000000..349409a --- /dev/null +++ b/NPascalCoin/DTO/KeyType.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NPascalCoin.DTO { + + /// + /// Type of key used for public/private key encryption. + /// + public enum KeyType { + /// + /// secp256k1 + /// + SECP256K1 = 714, + + /// + /// secp384r1 + /// + SECP384R1 = 715, + + /// + /// secp283k1 + /// + SECP283K1 = 729, + + /// + /// secp521r1 + /// + SECP521R1 = 716, + } + +} diff --git a/NPascalCoin/DTO/NetProtocolDTO.cs b/NPascalCoin/DTO/NetProtocolDTO.cs new file mode 100644 index 0000000..3a3a40f --- /dev/null +++ b/NPascalCoin/DTO/NetProtocolDTO.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + + /// + /// Embedded JSON object describing node protocol support + /// + public class NetProtocolDTO { + + /// + /// Net protocol version + /// + [JsonProperty("ver")] + public uint Version { get; set; } + + /// + /// Net protocol available + /// + [JsonProperty("ver_a")] + public uint AvailableVersion { get; set; } + } + +} diff --git a/NPascalCoin/DTO/NetStatsDTO.cs b/NPascalCoin/DTO/NetStatsDTO.cs new file mode 100644 index 0000000..2a2cdfe --- /dev/null +++ b/NPascalCoin/DTO/NetStatsDTO.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + + /// + /// Embedded JSON object describing node protocol support + /// + public class NetStatsDTO { + + [JsonProperty("active")] + public uint Active { get; set; } + + [JsonProperty("clients")] + public uint Clients { get; set; } + + [JsonProperty("servers")] + public uint Servers { get; set; } + + [JsonProperty("servers_t")] + public uint Servers_t { get; set; } + + [JsonProperty("total")] + public uint Total { get; set; } + + [JsonProperty("tclients")] + public uint TotalClients { get; set; } + + [JsonProperty("tservers")] + public uint TotalServers { get; set; } + + [JsonProperty("breceived")] + public uint BytesReceived { get; set; } + + [JsonProperty("bsend")] + public uint BytesSent { get; set; } + } + +} diff --git a/NPascalCoin/DTO/NodeServerDTO.cs b/NPascalCoin/DTO/NodeServerDTO.cs new file mode 100644 index 0000000..d13f3ed --- /dev/null +++ b/NPascalCoin/DTO/NodeServerDTO.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + /// + /// Embedded JSON object describing a nodes list of servers + /// + public class NodeServerDTO { + [JsonProperty("ip")] + public string IP { get; set; } + + [JsonProperty("port")] + public uint Port { get; set; } + + [JsonProperty("lastcon")] + public uint LastConnect { get; set; } + + [JsonProperty("attempts")] + public uint Attempts { get; set; } + + } +} diff --git a/NPascalCoin/DTO/NodeStatusDTO.cs b/NPascalCoin/DTO/NodeStatusDTO.cs new file mode 100644 index 0000000..3e2c0bb --- /dev/null +++ b/NPascalCoin/DTO/NodeStatusDTO.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + + /// + /// Information about node + /// + public class NodeStatusDTO { + + /// + /// Must be true, otherwise Node is not ready to execute operations + /// + [JsonProperty("ready")] + public bool Ready { get; set; } + + /// + /// Human readable information about ready or not + /// + [JsonProperty("ready_s")] + public string ReadyDescriptor { get; set; } + + + /// + /// Human readable information about node status...Running, downloading blockchain, discovering servers... + /// + [JsonProperty("status_s")] + public string StatusDescriptor { get; set; } + + /// + /// Server port + /// + [JsonProperty("port")] + public uint Port { get; set; } + + /// + /// True when this wallet is locked, false otherwise + /// + [JsonProperty("locked")] + public bool Locked { get; set; } + + /// + /// Timestamp of the Node + /// + [JsonProperty("timestamp")] + public uint Timestamp { get; set; } + + /// + /// Server version + /// + [JsonProperty("version")] + public string Version { get; set; } + + /// + /// JSON Object with protocol versions + /// + [JsonProperty("netprotocol")] + public NetProtocolDTO NetProtocol { get; set; } + + /// + /// Blockchain blocks + /// + [JsonProperty("blocks")] + public uint Blocks { get; set; } + + /// + /// JSON Object with net information + /// + [JsonProperty("netstats")] + public NetStatsDTO NetStats { get; set; } + + + /// + /// JSON Array with servers candidates + /// + [JsonProperty("nodeservers")] + public NodeServerDTO[] NodeServers { get; set; } + } +} diff --git a/NPascalCoin/DTO/OperationDTO.cs b/NPascalCoin/DTO/OperationDTO.cs new file mode 100644 index 0000000..da700de --- /dev/null +++ b/NPascalCoin/DTO/OperationDTO.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + + /// + /// An "Operation object" is a JSON object with information about an operation. + /// + public class OperationDTO { + + /// + /// If operation is invalid, value=false (optional) + /// + [JsonProperty("valid")] + [DefaultValue(null)] + public bool? Valid { get; set; } + + /// + /// If operation is invalid, an error description (optional) + /// + [JsonProperty("errors")] + [DefaultValue(null)] + public string Errors { get; set; } + + /// + /// Block number (only when valid) + /// + [JsonProperty("block")] + public uint? Block { get; set; } + + /// + /// Block timestamp (only when valid) + /// + [JsonProperty("time")] + [DefaultValue(null)] + public uint? Time { get; set; } + + /// + /// Operation index inside a block(0..operations-1). Note: If opblock = -1 means that is a blockchain reward (only when valid) + /// + [JsonProperty("opblock")] + [DefaultValue(null)] + public int? OperationBlock { get; set; } + + /// + /// Return null when operation is not included on a blockchain yet, 0 means that is included in highest block and so on... (New on Build 1.4.3) + /// + [JsonProperty("maturation")] + public uint Maturation { get; set; } + + /// + /// Operation type. + /// + [JsonProperty("optype")] + public OperationType Type { get; set; } + + /// + /// Account affected by this operation.Note: A transaction has 2 affected accounts. + /// + [JsonProperty("account")] + public int Account { get; set; } + + /// + /// Account affected by this operation.Note: A transaction has 2 affected accounts. + /// + [JsonProperty("optxt")] + public string TypeDescriptor { get; set; } + + /// + /// Amount of coins transferred from sender_account to dest_account (Only apply when optype = 1) (PASCURRENCY) + /// + [JsonProperty("amount")] + public decimal Amount { get; set; } + + /// + /// Fee of this operation (PASCURRENCY) + /// + [JsonProperty("fee")] + public decimal Fee { get; set; } + + /// + /// Balance of account after this block is introduced in the Blockchain (PASCURRENCY). + /// + /// + /// balance is a calculation based on current safebox account balance and previous operations, it's only returned on pending operations and account operations + /// + [JsonProperty("balance")] + public decimal Balance { get; set; } + + /// + /// Sender account in a transaction (optype = 1) + /// + [JsonProperty("sender_account")] + public uint SenderAccount { get; set; } + + /// + /// Destination account in a transaction (optype = 1) + /// + [JsonProperty("dest_account")] + public uint DestAccount { get; set; } + + /// + /// Encoded public key in a change key operation(optype = 2). (HEXASTRING). See decodepubkey. + /// + [JsonProperty("enc_pubkey")] + public string EncPubKey { get; set; } + + /// + /// Operation hash used to find this operation in the blockchain. (HEXASTRING). + /// + [JsonProperty("ophash")] + public string OPID { get; set; } + } +} diff --git a/NPascalCoin/DTO/OperationType.cs b/NPascalCoin/DTO/OperationType.cs new file mode 100644 index 0000000..d9ea9dd --- /dev/null +++ b/NPascalCoin/DTO/OperationType.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NPascalCoin.DTO { + public enum OperationType { + /// + /// Blockchain reward + /// + BlockchainReward = 0, + + /// + /// Transaction + /// + Transaction = 1, + + /// + /// Change key + /// + ChangeKey = 2, + + /// + /// Recover founds (lost keys) + /// + RecoverFunds = 3 + } + +} diff --git a/NPascalCoin/DTO/PayloadMethod.cs b/NPascalCoin/DTO/PayloadMethod.cs new file mode 100644 index 0000000..defca20 --- /dev/null +++ b/NPascalCoin/DTO/PayloadMethod.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NPascalCoin.DTO { + + /// + /// Type of payload encoded. + /// + public enum PayloadMethod { + + /// + /// Not encoded. Will be visible for everybody + /// + [Description("none")] + None, + + /// + /// Using Public key of "target" account. Only "target" will be able to decrypt this payload + /// + [Description("dest")] + Dest, + + /// + /// Using sender Public key. Only "sender" will be able to decrypt this payload + /// + [Description("sender")] + Sender, + + /// + /// Encrypted data using pwd param + /// + [Description("aes")] + Aes + } + +} diff --git a/NPascalCoin/DTO/PublicKeyDTO.cs b/NPascalCoin/DTO/PublicKeyDTO.cs new file mode 100644 index 0000000..e51bb2c --- /dev/null +++ b/NPascalCoin/DTO/PublicKeyDTO.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + + /// + /// A "Public Key object" is a JSON object with information about a public key. + /// + public class PublicKeyDTO { + + /// + /// Human readable name stored at the Wallet for this key + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// If false then Wallet doesn't have Private key for this public key, so, Wallet cannot execute operations with this key + /// + [JsonProperty("can_use")] + public bool CanUse { get; set; } + + /// + /// Encoded value of this public key.This HEXASTRING has no checksum, so, if using it always must be sure that value is correct + /// + [JsonProperty("enc_pubkey")] + public string EncPubKey { get; set; } + + /// + /// Encoded value of this public key in Base 58 format, also contains a checksum.This is the same value that Application Wallet exports as a public key + /// + [JsonProperty("b58_pubkey")] + public string Base58PubKey { get; set; } + + /// + /// Indicates which EC type is used (EC_NID) + /// + [JsonProperty("ec_nid")] + public KeyType KeyType { get; set; } + + /// + /// HEXASTRING with x value of public key + /// + [JsonProperty("x")] + public string X { get; set; } + + /// + /// HEXASTRING with y value of public key + /// + [JsonProperty("y")] + public string Y { get; set; } + } + + +} diff --git a/NPascalCoin/DTO/RawOperationDTO.cs b/NPascalCoin/DTO/RawOperationDTO.cs new file mode 100644 index 0000000..abf49d8 --- /dev/null +++ b/NPascalCoin/DTO/RawOperationDTO.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NPascalCoin.DTO { + + + /// + /// A "Raw operations object" is a JSON object with information about a signed operation made by "signsendto" or "signchangekey" + /// + public class RawOperationDTO { + + /// + /// Count how many operations has rawoperations param + /// + [JsonProperty("operations")] + public uint NumOperations { get; set; } + + /// + /// Total amount + /// + [JsonProperty("amount")] + public decimal TotalAmount { get; set; } + + /// + /// Total fee + /// + [JsonProperty("fee")] + public decimal TotalFee { get; set; } + + /// + /// This is the operations in raw format. + /// + [JsonProperty("rawoperations")] + public string RawOperations { get; set; } + } + +} diff --git a/NPascalCoin/NPascalCoin.csproj b/NPascalCoin/NPascalCoin.csproj new file mode 100644 index 0000000..c046577 --- /dev/null +++ b/NPascalCoin/NPascalCoin.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {527FBE08-46AF-49C9-9479-FEFF1FA9EBF8} + Library + Properties + NPascalCoin + NPascalCoin + v4.6 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NPascalCoin/Properties/AssemblyInfo.cs b/NPascalCoin/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..09fb26d --- /dev/null +++ b/NPascalCoin/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NPascalCoin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NPascalCoin")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("527fbe08-46af-49c9-9479-feff1fa9ebf8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NPascalCoin/RPC/ApiMethodName.cs b/NPascalCoin/RPC/ApiMethodName.cs new file mode 100644 index 0000000..14d3567 --- /dev/null +++ b/NPascalCoin/RPC/ApiMethodName.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NPascalCoin.DTO; + +namespace NPascalCoin { + internal enum ApiMethodName { + addnode, + getaccount, + getwalletaccounts, + getwalletaccountscount, + getwalletpubkeys, + getwalletpubkey, + getwalletcoins, + getblock, + getblocks, + getblockcount, + getblockoperation, + getblockoperations, + getaccountoperations, + getpendings, + findoperation, + sendto, + changekey, + changekeys, + signsendto, + signchangekey, + operationsinfo, + executeoperations, + nodestatus, + encodepubkey, + decodepubkey, + payloadencrypt, + payloaddecrypt, + getconnections, + addnewkey, + @lock, + unlock, + setwalletpassword, + stopnode, + startnode, + } +} diff --git a/NPascalCoin/RPC/IPascalCoinClient.cs b/NPascalCoin/RPC/IPascalCoinClient.cs new file mode 100644 index 0000000..1bede2d --- /dev/null +++ b/NPascalCoin/RPC/IPascalCoinClient.cs @@ -0,0 +1,333 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NPascalCoin.DTO; + +namespace NPascalCoin { + public interface IPascalCoinClient { + + /// + /// Adds a node to connect + /// + /// String containing 1 or multiple IP:port separated by ";" + /// Returns an integer with nodes added + int AddNode(string nodes); + + /// + /// Returns a JSON Object with account information including pending operations not included in blockchain yet, but affecting this account. + /// + /// + /// To know if there are pending operations, must look at updated_b param.It tells last block that modified this account.If this number is equal to blockchain blocks then this account is affected by pending operations (send/receive or change key) + /// + /// Cardinal containing account number + AccountDTO GetAccount(uint account); + + /// + /// Returns a JSON array with all wallet accounts. + /// + /// HEXASTRING (optional). If provided, return only accounts of this public key + /// String (optional). If provided, return only accounts of this public key. Note: If use enc_pubkey and b58_pubkey together and is not the same public key, will return an error + /// Integer (optional, default = 0). If provided, will return wallet accounts starting at this position (index starts at position 0) + /// Integer (optional, default = 100). If provided, will return max accounts. If not provided, max=100 by default + /// Each JSON array item contains an Account Object + AccountDTO[] GetWalletAccounts(string enc_pubkey = null, string b58_pubkey = null, uint? start = null, uint? max = null); + + /// + /// Get number of available wallet accounts (total or filtered by public key) + /// + /// HEXASTRING (optional). If provided, return only accounts of this public key + /// String (optional). If provided, return only accounts of this public key. Note: If use enc_pubkey and b58_pubkey together and is not the same public key, will return an error + /// Integer (optional, default = 0). If provided, will return wallet accounts starting at this position (index starts at position 0) + /// Integer (optional, default = 100). If provided, will return max accounts. If not provided, max=100 by default + /// Returns an integer with total + uint GetWalletAccountsCount(string enc_pubkey = null, string b58_pubkey = null, uint? start = null, uint? max = null); + + /// + /// Returns a JSON Object with a public key if found in the Wallet + /// + /// HEXASTRING + /// String + /// Note: If use enc_pubkey and b58_pubkey together and is not the same public key, will return an error + /// Returns a JSON Object with a "Public Key Object" + PublicKeyDTO GetWalletPubKey(string enc_pubkey = null, string b58_pubkey = null); + + + /// + /// Returns a JSON Array with all pubkeys of the Wallet (address) + /// + /// Integer (optional, default = 0). If provided, will return wallet public keys starting at this position (index starts at position 0) + /// Integer (optional, default = 100). If provided, will return max public keys. If not provided, max=100 by default + /// Returns a JSON Array with "Public Key Object" + PublicKeyDTO[] GetWalletPubKeys(uint? start = null, uint? max = null); + + /// + /// Returns coins balance. + /// + /// HEXASTRING (optional). If provided, return only this public key balance + /// String (optional). If provided, return only this public key balance If use enc_pubkey and b58_pubkey together and is not the same public key, will throw an error + /// Returns a PASCURRENCY value with maximum 4 decimals + decimal GetWalletCoins(string enc_pubkey = null, string b58_pubkey = null); + + /// + /// Returns a JSON Object with a block information + /// + /// Block number (0..blocks count-1) + /// Returns a JSON Object with a "Block Object" + BlockDTO GetBlock(uint block); + + /// + /// Returns a JSON Array with blocks information from "start" to "end" (or "last" n blocks) Blocks are returned in DESCENDING order. See + /// + /// Last n blocks in the blockchain (n>0 and n<=1000) + /// From this block + /// To this block + /// Must use last exclusively, or start and end, or error + /// + BlockDTO[] GetBlocks(uint? last = null, uint? start = null, uint? end = null); + + /// + /// Returns an Integer with blockcount of node + /// + /// Total blocks + uint GetBlockCount(); + + /// + /// Returns a JSON Object with an operation inside a block + /// + /// Block number + /// Operation (0..operations-1) of this block + /// JSON Object with a "Operation Object" + OperationDTO GetBlockOperation(uint block, uint opblock); + + /// + /// Returns a JSON Array with all operations of specified block Operations are returned in DESCENDING order + /// + /// Block number + /// Integer (optional, default = 0). If provided, will start at this position (index starts at position 0) + /// Integer (optional, default = 100). If provided, will return max registers. If not provided, max=100 by default + /// Returns a JSON Array with "Operation Object" items + OperationDTO[] GetBlockOperations(uint block, uint? start = null, uint? max = null); + + /// + /// Return a JSON Array with "Operation Object" items. Operations made over an account Operations are returned in DESCENDING order + /// + /// Account number (0..accounts count-1) + /// Integer - (Optional, default value 100) Depth to search on blocks where this account has been affected. Allowed to use deep as a param name too. + /// Integer (optional, default = 0). If provided, will start at this position (index starts at position 0). If start is -1, then will include pending operations, otherwise only operations included on the blockchain + /// Integer (optional, default = 100). If provided, will return max registers. If not provided, max=100 by default + /// Returns an array holding operations made over account in "Operation Object" format + OperationDTO[] GetAccountOperations(uint account, uint? depth = null, uint? start = null, uint? max = null); + + /// + /// Return a JSON Array with "Operation Object" items with operations pending to be included at the Blockchain. + /// + /// Returns an array holding pending operations in "Operation Object" format + OperationDTO[] GetPendings(); + + /// + /// Return a JSON Object in "Operation Object" format. + /// + /// HEXASTRING - Value ophash received on an operation + /// Returns "Operation Object" format JSON object + OperationDTO FindOperation(string ophash); + + + /// + /// Executes a transaction operation from "sender" to "target" + /// + /// Sender account + /// Destination account + /// Coins to be transferred + /// Fee of the operation + /// Payload "item" that will be included in this operation + /// Used to encrypt payload with aes as a payload_method. If none equals to empty password + /// If transaction is successfull will return a JSON Object in "Operation Object" format. Otherwise, will return a JSON-RPC error code with description + OperationDTO SendTo(uint sender, uint target, decimal amount, decimal fee, PayloadMethod? payloadMethod = null, string pwd = null); + + + /// + /// Executes a change key operation, changing "account" public key for a new one. + /// + /// Note that new one public key can be another Wallet public key, or none.When none, it's like a transaction, tranferring account owner to an external owner + /// Account number to change key + /// HEXASTRING - New public key in encoded format + /// New public key in Base 58 format (the same that Application Wallet exports) + /// PASCURRENCY - Fee of the operation + /// HEXASTRING - Payload "item" that will be included in this operation + /// Encode type of the item payload + /// Used to encrypt payload with aes as a payload_method. If none equals to empty password + /// If operation is successfull will return a JSON Object in "Operation Object" format. Otherwise, will return a JSON-RPC error code with description + OperationDTO ChangeKey(uint account, string new_enc_pubkey, string new_b58_pubkey, decimal fee, string payload, PayloadMethod? payloadMethod = null, string pwd = null); + + + /// + /// Executes a change key operation, changing "account" public key for a new one, in multiple accounts Works like changekey + /// + /// List of accounts separated by a comma + /// HEXASTRING - New public key in encoded format + /// New public key in Base 58 format (the same that Application Wallet exports) + /// PASCURRENCY - Fee of the operation + /// HEXASTRING - Payload "item" that will be included in this operation + /// Encode type of the item payload + /// Used to encrypt payload with aes as a payload_method. If none equals to empty password + /// If operation is successfull will return a JSON Array with Operation object items for each key If operation cannot be made, a JSON-RPC error message is returned + OperationDTO[] ChangeKeys(string accounts, string new_enc_pubkey, string new_b58_pubkey, decimal fee, string payload, PayloadMethod? payloadMethod = null, string pwd = null); + + + /// + /// Creates and signs a "Send to" operation without checking information and without transfering to the network. It's usefull for "cold wallets" that are off-line (not synchronized with the network) and only holds private keys + /// + /// Sender account + /// Target account + /// HEXASTRING - Public key of the sender account in encoded format + /// HEXASTRING - Public key of the sender account in base58 format + /// HEXASTRING - Public key of the target account in encoded format + /// HEXASTRING - Public key of the target account in base58 format + /// Last value of n_operation obtained with an Account object, for example when called to getaccount + /// Coins to be transferred + /// Fee of the operation + /// Payload "item" that will be included in this operation + /// Used to encrypt payload with aes as a payload_method. If none equals to empty password + /// HEXASTRING (optional) - If we want to add a sign operation with other previous operations, here we must put previous rawoperations result + /// Wallet must be unlocked and sender private key (searched with provided public key) must be in wallet. No other checks are made (no checks for valid target, valid n_operation, valid amount or fee ...) + /// Only one of sender_enc_pubkey, sender_b58_pubkey needs be provided + /// Only one of target_enc_pubkey, target_b58_pubkey needs be provided + /// Returns a Raw Operations Object + RawOperationDTO SignSendTo(uint sender, uint target, string sender_enc_pubkey, string sender_b58_pubkey, string target_enc_pubkey, string target_b58_pubkey, uint last_n_operation, decimal amount, decimal fee, PayloadMethod? payloadMethod = null, string pwd = null, string rawoperations = null); + + + /// + /// Creates and signs a "Change key" operation without checking information and without transfering to the network. It's usefull for "cold wallets" that are off-line (not synchronized with the network) and only holds private keys + /// + /// Account number to change key + /// HEXASTRING - Public key of the account in encoded format + /// HEXASTRING - Public key of the account in base58 format + /// HEXASTRING - Public key of the new key for the account in encoded format + /// HEXASTRING - Public key of the new key for the account in base58 format + /// Last value of n_operation obtained with an Account object, for example when called to getaccount + /// Fee of the operation + /// Payload "item" that will be included in this operation + /// Used to encrypt payload with aes as a payload_method. If none equals to empty password + /// HEXASTRING (optional) - If we want to add a sign operation with other previous operations, here we must put previous rawoperations result + /// Wallet must be unlocked and private key (searched with provided public key) must be in wallet. No other checks are made (no checks for valid n_operation, valid fee ...) + /// Only one of old_enc_pubkey, old_b58_pubkey needs be provided + /// Only one of new_enc_pubkey, new_b58_pubkey needs be provided + /// Wallet must be unlocked and private key (searched with provided public key) must be in wallet. No other checks are made (no checks for valid n_operation, valid fee ...) Returns a Raw Operations Object + RawOperationDTO SignChangeKey(uint account, string old_enc_pubkey, string old_b58_pubkey, string new_enc_pubkey, string new_b58_pubkey, uint last_n_operation, decimal fee, PayloadMethod? payloadMethod = null, string pwd = null, string rawoperations = null); + + /// + /// Returns information stored in a rawoperations param (obtained calling signchangekey or signsendto) + /// + /// HEXASTRING (obtained calling signchangekey or signsendto) + /// Returns a JSON Array with Operation Object items, one for each operation in rawoperations param. NOTE: Remember that rawoperations are operations that maybe are not correct + OperationDTO[] OperationsInfo(string rawoperations); + + /// + /// Executes operations included in rawopertions param and transfers to the network. Raw operations can include "Send to" oprations or "Change key" operations. + /// + /// Executes operations included in rawopertions param and transfers to the network. Raw operations can include "Send to" oprations or "Change key" operations. + /// For each Operation Object item, if there is an error, param valid will be false and param errors will show error description.Otherwise, operation is correct and will contain ophash param + /// Returns a JSON Array with Operation Object items, one for each operation in rawoperations param. + OperationDTO[] ExecuteOperations(string rawoperations); + + + /// + /// Returns information of the Node in a JSON Object + /// + /// JSON Object with information + NodeStatusDTO NodeStatus(); + + + /// + /// Encodes a public key based on params information + /// + /// key type + /// HEXASTRING with x value of public key + /// HEXASTRING with y value of public key + /// Returns a HEXASTRING with encoded public key + string EncodePubKey(KeyType ec_nid, string x, string y); + + + /// + /// Decodes an encoded public key + /// + /// HEXASTRING with encoded public key + /// String. b58_pubkey is the same value that Application Wallet exports as a public key + /// Note: If use enc_pubkey and b58_pubkey together and is not the same public key, will return an error + /// Returns a JSON Object with a "Public Key Object" + PublicKeyDTO DecodePubKey(string enc_pubkey, string b58_pubkey); + + + /// + /// Encrypt a text "paylad" using "payload_method" + /// + /// HEXASTRING - Text to encrypt in hexadecimal format + /// Payload method + /// Using a Password. Must provide pwd param + /// Returns a HEXASTRING with encrypted payload + string PayloadEncrypt(string payload, PayloadMethod payload_method, string pwd); + + + /// + /// Returns a HEXASTRING with decrypted text (a payload) using private keys in the wallet or a list of Passwords (used in "aes" encryption) + /// + /// HEXASTRING - Encrypted data + /// List of passwords to use + /// If using one of private keys is able to decrypt payload then returns value "key" in payload_method and enc_pubkey contains encoded public key in HEXASTRING + /// If using one of passwords to decrypt payload then returns value "pwd" in payload_method and pwd contains password used + /// Decryped payload + DecryptedPayloadDTO PayloadDecrypt(string payload, string[] pwds = null); + + /// + /// Returns all the current connections + /// + /// JSON Array with Connection Objects + ConnectionDTO[] GetConnections(); + + + /// + /// Creates a new Private key and sotres it on the wallet, returning an enc_pubkey value + /// + /// Type of key encryption + /// Name to alias this new private key + /// + PublicKeyDTO AddNewKey(KeyType ec_nid, string name); + + /// + /// Locks the Wallet if it has a password, otherwise wallet cannot be locked + /// + /// Returns a Boolean indicating if Wallet is locked. If false that means that Wallet has an empty password and cannot be locked + bool Lock(); + + /// + /// Unlocks a locked Wallet using "pwd" param + /// + /// + /// Returns a Boolean indicating if Wallet is unlocked after using pwd password + bool Unlock(string pwd); + + + /// + /// Changes the password of the Wallet. (Must be previously unlocked) Note: If pwd param is empty string, then wallet will be not protected by password + /// + /// New password + /// Returns a Boolean if Wallet password changed with new pwd password + bool SetWalletPassword(string pwd); + + /// + /// Stops the node and the server. Closes all connections + /// + /// + bool StopNode(); + + /// + /// Starts the node and the server. Starts connection process + /// + /// Boolean "true" + bool StartNode(); + + } +} diff --git a/NPascalCoin/RPC/PascalCoinClient.cs b/NPascalCoin/RPC/PascalCoinClient.cs new file mode 100644 index 0000000..9023fac --- /dev/null +++ b/NPascalCoin/RPC/PascalCoinClient.cs @@ -0,0 +1,395 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NPascalCoin.DTO; + +namespace NPascalCoin { + public class PascalCoinClient : IPascalCoinClient { + private readonly Uri _url; + private int _callID; + + public PascalCoinClient(string server, int port) { + _url = new Uri($"http://{server}:{port}"); + _callID = 0; + } + + public int AddNode(string nodes) { + return Invoke(ApiMethodName.addnode.ToString(), new Dictionary() { + ["nodes"] = nodes + }); + } + + public AccountDTO GetAccount(uint account) { + return Invoke(ApiMethodName.getaccount.ToString(), new Dictionary() { + ["account"] = account + }); + + } + + public AccountDTO[] GetWalletAccounts(string enc_pubkey = null, string b58_pubkey = null, uint? start = null, uint? max = null) { + return Invoke(ApiMethodName.getwalletaccounts.ToString(), new Dictionary() { + ["enc_pubkey"] = enc_pubkey, + ["b58_pubkey"] = b58_pubkey, + ["start"] = start, + ["max"] = max, + }); + } + + public uint GetWalletAccountsCount(string enc_pubkey = null, string b58_pubkey = null, uint? start = null, uint? max = null) { + return Invoke(ApiMethodName.getwalletaccountscount.ToString(), new Dictionary() { + ["enc_pubkey"] = enc_pubkey, + ["b58_pubkey"] = b58_pubkey, + ["start"] = start, + ["max"] = max, + }); + } + + public PublicKeyDTO GetWalletPubKey(string enc_pubkey = null, string b58_pubkey = null) { + return Invoke(ApiMethodName.getwalletpubkey.ToString(), new Dictionary() { + ["enc_pubkey"] = enc_pubkey, + ["b58_pubkey"] = b58_pubkey + }); + } + + public PublicKeyDTO[] GetWalletPubKeys(uint? start = null, uint? max = null) { + return Invoke(ApiMethodName.getwalletpubkeys.ToString(), new Dictionary() { + ["start"] = start, + ["max"] = max, + }); + } + + public decimal GetWalletCoins(string enc_pubkey = null, string b58_pubkey = null) { + return Invoke(ApiMethodName.getwalletcoins.ToString(), new Dictionary() { + ["enc_pubkey"] = enc_pubkey, + ["b58_pubkey"] = b58_pubkey, + }); + } + + public BlockDTO GetBlock(uint block) { + return Invoke(ApiMethodName.getblock.ToString(), new Dictionary { + ["block"] = block + }); + } + + public BlockDTO[] GetBlocks(uint? last = null, uint? start = null, uint? end = null) { + return Invoke(ApiMethodName.getblocks.ToString(), new Dictionary { + ["last"] = last, + ["start"] = start, + ["end"] = end, + }); + } + + public uint GetBlockCount() { + return Invoke(ApiMethodName.getblockcount.ToString()); + } + + public OperationDTO GetBlockOperation(uint block, uint opblock) { + return Invoke(ApiMethodName.getblockoperation.ToString(), new Dictionary() { + ["block"] = block, + ["opblock"] = opblock, + }); + } + + public OperationDTO[] GetBlockOperations(uint block, uint? start = null, uint? max = null) { + return Invoke(ApiMethodName.getblockoperations.ToString(), new Dictionary() { + ["block"] = block, + ["start"] = start, + ["max"] = max, + }); + } + + public OperationDTO[] GetAccountOperations(uint account, uint? depth = null, uint? start = null, uint? max = null) { + return Invoke(ApiMethodName.getaccountoperations.ToString(), new Dictionary() { + ["account"] = account, + ["depth"] = depth, + ["start"] = start, + ["max"] = max, + }); + } + + public OperationDTO[] GetPendings() { + return Invoke(ApiMethodName.getpendings.ToString()); + } + + public OperationDTO FindOperation(string ophash) { + return Invoke(ApiMethodName.findoperation.ToString(), new Dictionary() { + ["ophash"] = ophash + }); + } + + public OperationDTO SendTo(uint sender, uint target, decimal amount, decimal fee, PayloadMethod? payloadMethod = null, string pwd = null) { + return Invoke(ApiMethodName.sendto.ToString(), new Dictionary() { + ["sender"] = sender, + ["target"] = target, + ["amount"] = amount, + ["fee"] = fee, + ["payloadMethod"] = payloadMethod, + ["pwd"] = pwd + }); + } + + public OperationDTO ChangeKey(uint account, string new_enc_pubkey, string new_b58_pubkey, decimal fee, string payload, PayloadMethod? payloadMethod = null, string pwd = null) { + return Invoke(ApiMethodName.changekey.ToString(), new Dictionary() { + ["account"] = account, + ["new_enc_pubkey"] = new_enc_pubkey, + ["new_b58_pubkey"] = new_b58_pubkey, + ["fee"] = fee, + ["payloadMethod"] = payloadMethod, + ["pwd"] = pwd + }); + } + + public OperationDTO[] ChangeKeys(string accounts, string new_enc_pubkey, string new_b58_pubkey, decimal fee, string payload, PayloadMethod? payloadMethod = null, string pwd = null) { + return Invoke(ApiMethodName.changekeys.ToString(), new Dictionary() { + ["accounts"] = accounts, + ["new_enc_pubkey"] = new_enc_pubkey, + ["new_b58_pubkey"] = new_b58_pubkey, + ["fee"] = fee, + ["payloadMethod"] = payloadMethod, + ["pwd"] = pwd, + }); + } + + public RawOperationDTO SignSendTo(uint sender, uint target, string sender_enc_pubkey, string sender_b58_pubkey, string target_enc_pubkey, string target_b58_pubkey, uint last_n_operation, decimal amount, decimal fee, PayloadMethod? payloadMethod = null, string pwd = null, string rawoperations = null) { + return Invoke(ApiMethodName.signsendto.ToString(), new Dictionary() { + ["rawoperations"] = rawoperations, + ["sender"] = sender, + ["target"] = target, + ["sender_enc_pubkey"] = sender_enc_pubkey, + ["sender_b58_pubkey"] = sender_b58_pubkey, + ["target_enc_pubkey"] = target_enc_pubkey, + ["target_b58_pubkey"] = target_b58_pubkey, + ["last_n_operation"] = last_n_operation, + ["amount"] = amount, + ["fee"] = fee, + ["payloadMethod"] = fee, + ["pwd"] = fee, + }); + } + + public RawOperationDTO SignChangeKey(uint account, string old_enc_pubkey, string old_b58_pubkey, string new_enc_pubkey, string new_b58_pubkey, uint last_n_operation, decimal fee, PayloadMethod? payloadMethod = null, string pwd = null, string rawoperations = null) { + return Invoke(ApiMethodName.signchangekey.ToString(), new Dictionary() { + ["rawoperations"] = rawoperations, + ["account"] = account, + ["old_enc_pubkey"] = old_enc_pubkey, + ["old_b58_pubkey"] = old_b58_pubkey, + ["new_enc_pubkey"] = new_enc_pubkey, + ["new_b58_pubkey"] = new_b58_pubkey, + ["last_n_operation"] = last_n_operation, + ["fee"] = fee, + ["payloadMethod"] = fee, + ["pwd"] = fee, + }); + } + + public OperationDTO[] OperationsInfo(string rawoperations) { + return Invoke(ApiMethodName.operationsinfo.ToString(), new Dictionary() { + ["rawoperations"] = rawoperations + }); + } + + public OperationDTO[] ExecuteOperations(string rawoperations) { + return Invoke(ApiMethodName.executeoperations.ToString(), new Dictionary() { + ["rawoperations"] = rawoperations + }); + } + + public NodeStatusDTO NodeStatus() { + return Invoke(ApiMethodName.nodestatus.ToString()); + } + + public string EncodePubKey(KeyType ec_nid, string x, string y) { + return Invoke(ApiMethodName.encodepubkey.ToString(), new Dictionary() { + ["ec_nid"] = ec_nid, + ["x"] = x, + ["y"] = y, + }); + } + + public PublicKeyDTO DecodePubKey(string enc_pubkey, string b58_pubkey) { + return Invoke(ApiMethodName.decodepubkey.ToString(), new Dictionary() { + ["enc_pubkey"] = enc_pubkey, + ["b58_pubkey"] = b58_pubkey, + }); + } + + public string PayloadEncrypt(string payload, PayloadMethod payload_method, string pwd) { + return Invoke(ApiMethodName.payloadencrypt.ToString(), new Dictionary() { + ["payload"] = payload, + ["payload_method"] = payload_method, + ["pwd"] = pwd + }); + } + + public DecryptedPayloadDTO PayloadDecrypt(string payload, string[] pwds = null) { + return Invoke(ApiMethodName.payloaddecrypt.ToString(), new Dictionary() { + ["payload"] = payload, + ["pwds"] = pwds, + }); + } + + public ConnectionDTO[] GetConnections() { + return Invoke(ApiMethodName.getconnections.ToString()); + } + + public PublicKeyDTO AddNewKey(KeyType ec_nid, string name) { + return Invoke(ApiMethodName.addnewkey.ToString(), new Dictionary() { + ["ec_nid"] = ec_nid, + ["name"] = name, + }); + } + + public bool Lock() { + return Invoke(ApiMethodName.@lock.ToString()); + } + + public bool Unlock(string pwd) { + return Invoke(ApiMethodName.unlock.ToString(), new Dictionary() { + ["pwd"] = pwd + }); + } + + public bool SetWalletPassword(string pwd) { + return Invoke(ApiMethodName.setwalletpassword.ToString(), new Dictionary() { + ["pwd"] = pwd + }); + } + + public bool StopNode() { + return Invoke(ApiMethodName.stopnode.ToString()); + } + + public bool StartNode() { + return Invoke(ApiMethodName.startnode.ToString()); + } + + + protected virtual T Invoke_del(string method, IDictionary arguments = null) { + using (var callScope = new CallScope(this)) { + var webClient = new WebClient(); + webClient.Headers.Set(HttpRequestHeader.UserAgent, "NPascalCoin v0.1"); + webClient.Headers.Set(HttpRequestHeader.ContentType, "application/json-rpc"); + //webClient.Headers.Set(HttpRequestHeader.); + + var request = new JObject { + ["jsonrpc"] = "2.0", + ["id"] = callScope.CallID, + ["method"] = method + }; + + if (arguments?.Count > 0) { + var @params = new JArray(); + foreach (var param in arguments.Where(x => x.Value != null)) { + @params.Add(new JProperty(param.Key, param.Value)); + } + request.Add(new JProperty("params", @params)); + } + + var requestJson = JsonConvert.SerializeObject(request); + Trace.WriteLine($"RPC Request:{Environment.NewLine}{requestJson}", TraceCategories.RPC); + + try { + var responseJson = webClient.UploadString(_url, "POST", requestJson); + Trace.WriteLine($"RPC Response:{Environment.NewLine}{responseJson}", TraceCategories.RPC); + return JsonConvert.DeserializeObject(responseJson); + } catch (WebException error) { + if (error.Response == null) + throw; + var resp = new StreamReader(error.Response.GetResponseStream()).ReadToEnd(); + var errorDTO = JsonConvert.DeserializeObject(resp); + throw new PascalCoinRPCException(errorDTO); + } + } + } + + protected virtual T Invoke(string method, IDictionary arguments = null) { + using (var callScope = new CallScope(this)) { + var webRequest = (HttpWebRequest)WebRequest.Create(_url); + webRequest.ProtocolVersion = System.Net.HttpVersion.Version11; + webRequest.UserAgent = $"NPascalCoin {Constants.NPascalCoinVersion}"; + webRequest.ContentType = "application/json-rpc"; + webRequest.Method = "POST"; + + var request = new JObject { + ["jsonrpc"] = "2.0", + ["id"] = callScope.CallID, + ["method"] = method + }; + + if (arguments?.Count > 0) { + var @params = new JArray(); + foreach (var param in arguments.Where(x => x.Value != null)) { + @params.Add(new JProperty(param.Key, param.Value)); + } + request.Add(new JProperty("params", @params)); + } + + var requestJson = JsonConvert.SerializeObject(request); + Trace.WriteLine($"RPC Request:{Environment.NewLine}{requestJson}", TraceCategories.RPC); + + + // serialize json for the request + var requestBytes = Encoding.UTF8.GetBytes(requestJson); + webRequest.ContentLength = requestBytes.Length; + using (var requestStream = webRequest.GetRequestStream()) { + requestStream.Write(requestBytes, 0, requestBytes.Length); + } + try { + using (var webResponse = webRequest.GetResponse()) { + using (var responseStream = webResponse.GetResponseStream()) { + using (var sr = new StreamReader(responseStream)) { + var responseJson = sr.ReadToEnd(); + Trace.WriteLine($"RPC Response:{Environment.NewLine}{responseJson}", TraceCategories.RPC); + var jsonResponse = JsonConvert.DeserializeObject(responseJson); + if (jsonResponse.RPCVersion != "2.0") + throw new ApplicationException($"JSON-RPC response was an invalid version '{jsonResponse.RPCVersion ?? string.Empty}'. Expected '2.0'"); + if (jsonResponse.ID != callScope.CallID) + throw new ApplicationException($"JSON-RPC response ID had invalid value '{jsonResponse.ID}'. Expected '{callScope.CallID}'"); + return JsonConvert.DeserializeObject(jsonResponse.Result); + } + } + } + } catch (WebException error) { + if (error.Response == null) + throw; + var resp = new StreamReader(error.Response.GetResponseStream()).ReadToEnd(); + var errorDTO = JsonConvert.DeserializeObject(resp); + throw new PascalCoinRPCException(errorDTO); + } + } + } + + protected class CallScope : IDisposable { + private readonly PascalCoinClient _client; + public readonly int CallID; + public CallScope(PascalCoinClient client) { + _client = client; + CallID = Interlocked.Increment(ref _client._callID); + } + + public void Dispose() { + Interlocked.Decrement(ref _client._callID); + } + } + + private class JsonRpcResponse { + [JsonProperty("jsonrpc")] + public string RPCVersion { get; set; } + + [JsonProperty("id")] + public int ID { get; set; } + + [JsonProperty("result")] + public string Result { get; set; } + } + } +} diff --git a/NPascalCoin/RPC/PascalCoinRPCException.cs b/NPascalCoin/RPC/PascalCoinRPCException.cs new file mode 100644 index 0000000..d98d184 --- /dev/null +++ b/NPascalCoin/RPC/PascalCoinRPCException.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NPascalCoin.DTO; + +namespace NPascalCoin { + public class PascalCoinRPCException : ApplicationException { + public PascalCoinRPCException(ErrorResultDTO result) : base($"{result.ErrorCode} - {result.Message}") { + Error = result; + } + + public ErrorResultDTO Error { get; } + + } +} diff --git a/NPascalCoin/TraceCategories.cs b/NPascalCoin/TraceCategories.cs new file mode 100644 index 0000000..7220b19 --- /dev/null +++ b/NPascalCoin/TraceCategories.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace NPascalCoin { + internal static class TraceCategories { + public const string RPC = "NPascalCoin.RPC"; + } +} diff --git a/NPascalCoin/packages.config b/NPascalCoin/packages.config new file mode 100644 index 0000000..b34dd25 --- /dev/null +++ b/NPascalCoin/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc6b816 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +#NPascalCoin + +NPascalCoin is a .NET library for PascalCoin. Currently, it supports the full JSON-RPC api. In future, it will contain the full network protcol.