├── pre-compiled libs └── libleveldb.dll ├── MedPoints ├── Storage.Core │ ├── IScriptContainer.cs │ ├── IVerifiable.cs │ ├── Storage.Core.csproj │ ├── Witness.cs │ ├── UInt256.cs │ ├── UInt160.cs │ ├── CoreHelper.cs │ ├── BigDecimal.cs │ ├── UIntBase.cs │ ├── Fixed8.cs │ ├── Helper.cs │ └── Transaction.cs ├── Storage │ ├── Core │ │ ├── Transactions │ │ │ ├── TransactionType.cs │ │ │ ├── ITransaction.cs │ │ │ ├── DoctorVisitTransaction.cs │ │ │ └── CoinTransaction.cs │ │ ├── Chain.cs │ │ ├── Block.cs │ │ └── Wallet.cs │ ├── Mempool │ │ ├── MempoolTransaction.cs │ │ └── Mempool.cs │ ├── Storage.csproj │ ├── Database │ │ ├── BlockRepository.cs │ │ ├── CoinTransactionRepository.cs │ │ ├── MempoolRepository.cs │ │ └── AppointmentToTheDoctorRepository.cs │ └── Utils │ │ ├── HashHelper.cs │ │ └── RSAKeyExtensions.cs ├── Storage.Network │ ├── InventoryType.cs │ ├── IInventory.cs │ ├── RPC │ │ ├── RpcException.cs │ │ └── RpcServer.cs │ ├── Storage.Network.csproj │ ├── InventoryReceivingEventArgs.cs │ ├── Payloads │ │ ├── FilterAddPayload.cs │ │ ├── AddrPayload.cs │ │ ├── HeadersPayload.cs │ │ ├── GetBlocksPayload.cs │ │ ├── InvPayload.cs │ │ ├── FilterLoadPayload.cs │ │ ├── NetworkAddressWithTime.cs │ │ ├── MerkleBlockPayload.cs │ │ ├── VersionPayload.cs │ │ └── ConsensusPayload.cs │ ├── WebSocketRemoteNode.cs │ ├── TcpRemoteNode.cs │ ├── Message.cs │ └── UPnP.cs ├── Storage.Utils │ ├── ISerializable.cs │ ├── Storage.Utils.csproj │ ├── Settings.cs │ └── Helper.cs ├── Storage.Data │ ├── LevelDB │ │ ├── LevelDBException.cs │ │ ├── WriteOptions.cs │ │ ├── Snapshot.cs │ │ ├── WriteBatch.cs │ │ ├── ReadOptions.cs │ │ ├── Options.cs │ │ ├── Iterator.cs │ │ ├── DatabaseContext.cs │ │ ├── Slice.cs │ │ └── NativeWrapper.cs │ └── Storage.Data.csproj ├── Storage.Cli │ ├── Storage.Cli.csproj │ ├── Program.cs │ └── TestChain.cs └── MedPoints.sln ├── README.md └── .gitignore /pre-compiled libs/libleveldb.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MedPoints/storage/HEAD/pre-compiled libs/libleveldb.dll -------------------------------------------------------------------------------- /MedPoints/Storage.Core/IScriptContainer.cs: -------------------------------------------------------------------------------- 1 | namespace Storage.Core 2 | { 3 | public interface IScriptContainer 4 | { 5 | byte[] GetMessage(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /MedPoints/Storage/Core/Transactions/TransactionType.cs: -------------------------------------------------------------------------------- 1 | namespace Storage.Core.Transactions 2 | { 3 | public enum TransactionType 4 | { 5 | Coins, 6 | VisitToTheDoctor 7 | } 8 | } -------------------------------------------------------------------------------- /MedPoints/Storage.Network/InventoryType.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable InconsistentNaming 2 | namespace Storage.Network 3 | { 4 | public enum InventoryType : byte 5 | { 6 | TX = 0x01, 7 | Block = 0x02, 8 | Consensus = 0xe0 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # storage 2 | 3 | `Storage` is a project which is about prototype of blockchain. 4 | 5 | We are using `.NET Core` for creating/running it. 6 | 7 | # Run 8 | 9 | For running it you need to go to the folder with `Storage.csproj` and call `dotnet run`. 10 | -------------------------------------------------------------------------------- /MedPoints/Storage.Utils/ISerializable.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Storage.Utils 4 | { 5 | public interface ISerializable 6 | { 7 | int Size { get; } 8 | void Serialize(BinaryWriter writer); 9 | void Deserialize(BinaryReader reader); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/IInventory.cs: -------------------------------------------------------------------------------- 1 | using Storage.Core; 2 | 3 | namespace Storage.Network 4 | { 5 | public interface IInventory : IVerifiable 6 | { 7 | UInt256 Hash { get; } 8 | 9 | InventoryType InventoryType { get; } 10 | 11 | bool Verify(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/RPC/RpcException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo.Network.RPC 4 | { 5 | public class RpcException : Exception 6 | { 7 | public RpcException(int code, string message) : base(message) 8 | { 9 | HResult = code; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /MedPoints/Storage/Core/Transactions/ITransaction.cs: -------------------------------------------------------------------------------- 1 | namespace Storage.Core.Transactions 2 | { 3 | public interface ITransaction 4 | { 5 | string Id { get; set; } 6 | string Sender { get; set; } 7 | string Signature { get; set; } 8 | TransactionType Type { get; } 9 | } 10 | } -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/LevelDBException.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Common; 2 | 3 | namespace Storage.Data.LevelDB 4 | { 5 | internal class LevelDbException : DbException 6 | { 7 | internal LevelDbException(string message) 8 | : base(message) 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Storage.Network.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /MedPoints/Storage.Cli/Storage.Cli.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/Storage.Data.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | true 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /MedPoints/Storage/Mempool/MempoolTransaction.cs: -------------------------------------------------------------------------------- 1 | using Storage.Core.Transactions; 2 | 3 | namespace Storage.Mempool 4 | { 5 | public class MempoolTransaction 6 | { 7 | public string Id { get; set; } 8 | public string UserId { get; set; } 9 | public TransactionType Type { get; set; } 10 | public string Transaction { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /MedPoints/Storage.Network/InventoryReceivingEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace Storage.Network 4 | { 5 | public class InventoryReceivingEventArgs : CancelEventArgs 6 | { 7 | public IInventory Inventory { get; } 8 | 9 | public InventoryReceivingEventArgs(IInventory inventory) 10 | { 11 | this.Inventory = inventory; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/IVerifiable.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Storage.Utils; 3 | 4 | namespace Storage.Core 5 | { 6 | public interface IVerifiable : ISerializable, IScriptContainer 7 | { 8 | 9 | Witness[] Scripts { get; set; } 10 | void DeserializeUnsigned(BinaryReader reader); 11 | UInt160[] GetScriptHashesForVerifying(); 12 | void SerializeUnsigned(BinaryWriter writer); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MedPoints/Storage.Utils/Storage.Utils.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/Storage.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/FilterAddPayload.cs: -------------------------------------------------------------------------------- 1 | using Neo.IO; 2 | using System.IO; 3 | 4 | namespace Neo.Network.Payloads 5 | { 6 | public class FilterAddPayload : ISerializable 7 | { 8 | public byte[] Data; 9 | 10 | public int Size => Data.GetVarSize(); 11 | 12 | void ISerializable.Deserialize(BinaryReader reader) 13 | { 14 | Data = reader.ReadVarBytes(520); 15 | } 16 | 17 | void ISerializable.Serialize(BinaryWriter writer) 18 | { 19 | writer.WriteVarBytes(Data); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/WriteOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Storage.Data.LevelDB 4 | { 5 | class WriteOptions 6 | { 7 | public static readonly WriteOptions Default = new WriteOptions(); 8 | internal readonly IntPtr handle = NativeWrapper.leveldb_writeoptions_create(); 9 | 10 | public bool Sync 11 | { 12 | set => NativeWrapper.leveldb_writeoptions_set_sync(handle, value); 13 | } 14 | 15 | ~WriteOptions() 16 | { 17 | NativeWrapper.leveldb_writeoptions_destroy(handle); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/Snapshot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static Storage.Data.LevelDB.NativeWrapper; 3 | 4 | namespace Storage.Data.LevelDB 5 | { 6 | public class Snapshot 7 | { 8 | public IntPtr Db, Handle; 9 | 10 | internal Snapshot(IntPtr db) 11 | { 12 | Db = db; 13 | Handle = leveldb_create_snapshot(db); 14 | } 15 | 16 | public void Dispose() 17 | { 18 | if (Handle == IntPtr.Zero) return; 19 | 20 | leveldb_release_snapshot(Db, Handle); 21 | Handle = IntPtr.Zero; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /MedPoints/Storage/Core/Transactions/DoctorVisitTransaction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Storage.Core.Transactions 4 | { 5 | public class AppointmentToTheDoctorTransaction : ITransaction 6 | { 7 | public string Id { get; set; } 8 | public string Sender { get; set; } 9 | public string Signature { get; set; } 10 | public TransactionType Type => TransactionType.VisitToTheDoctor; 11 | 12 | public string DoctorId { get; set; } 13 | public string ClinicId { get; set; } 14 | public string ServiceId { get; set; } 15 | 16 | public string Description { get; set; } 17 | 18 | public DateTime Date { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/AddrPayload.cs: -------------------------------------------------------------------------------- 1 | using Neo.IO; 2 | using System.IO; 3 | 4 | namespace Neo.Network.Payloads 5 | { 6 | public class AddrPayload : ISerializable 7 | { 8 | public NetworkAddressWithTime[] AddressList; 9 | 10 | public int Size => AddressList.GetVarSize(); 11 | 12 | public static AddrPayload Create(params NetworkAddressWithTime[] addresses) 13 | { 14 | return new AddrPayload 15 | { 16 | AddressList = addresses 17 | }; 18 | } 19 | 20 | void ISerializable.Deserialize(BinaryReader reader) 21 | { 22 | this.AddressList = reader.ReadSerializableArray(200); 23 | } 24 | 25 | void ISerializable.Serialize(BinaryWriter writer) 26 | { 27 | writer.Write(AddressList); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/WriteBatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static Storage.Data.LevelDB.NativeWrapper; 3 | 4 | namespace Storage.Data.LevelDB 5 | { 6 | public class WriteBatch 7 | { 8 | internal readonly IntPtr Handle = leveldb_writebatch_create(); 9 | 10 | ~WriteBatch() 11 | { 12 | leveldb_writebatch_destroy(Handle); 13 | } 14 | 15 | public void Clear() 16 | { 17 | leveldb_writebatch_clear(Handle); 18 | } 19 | 20 | public void Delete(Slice key) 21 | { 22 | leveldb_writebatch_delete(Handle, key.buffer, (UIntPtr)key.buffer.Length); 23 | } 24 | 25 | public void Put(Slice key, Slice value) 26 | { 27 | leveldb_writebatch_put(Handle, key.buffer, (UIntPtr)key.buffer.Length, value.buffer, (UIntPtr)value.buffer.Length); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/ReadOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static Storage.Data.LevelDB.NativeWrapper; 3 | 4 | namespace Storage.Data.LevelDB 5 | { 6 | class ReadOptions 7 | { 8 | public static readonly ReadOptions Default = new ReadOptions(); 9 | internal readonly IntPtr handle = leveldb_readoptions_create(); 10 | 11 | public bool VerifyChecksums 12 | { 13 | set => leveldb_readoptions_set_verify_checksums(handle, value); 14 | } 15 | 16 | public bool FillCache 17 | { 18 | set => leveldb_readoptions_set_fill_cache(handle, value); 19 | } 20 | 21 | public Snapshot Snapshot 22 | { 23 | set => leveldb_readoptions_set_snapshot(handle, value.Handle); 24 | } 25 | 26 | ~ReadOptions() 27 | { 28 | leveldb_readoptions_destroy(handle); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/HeadersPayload.cs: -------------------------------------------------------------------------------- 1 | using Neo.Core; 2 | using Neo.IO; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | namespace Neo.Network.Payloads 8 | { 9 | public class HeadersPayload : ISerializable 10 | { 11 | public Header[] Headers; 12 | 13 | public int Size => Headers.GetVarSize(); 14 | 15 | public static HeadersPayload Create(IEnumerable
headers) 16 | { 17 | return new HeadersPayload 18 | { 19 | Headers = headers.ToArray() 20 | }; 21 | } 22 | 23 | void ISerializable.Deserialize(BinaryReader reader) 24 | { 25 | Headers = reader.ReadSerializableArray
(2000); 26 | } 27 | 28 | void ISerializable.Serialize(BinaryWriter writer) 29 | { 30 | writer.Write(Headers); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /MedPoints/Storage/Core/Chain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Storage.Core 6 | { 7 | public class Chain 8 | { 9 | public static List Blockchain = new List(); 10 | 11 | public static bool IsChainValid() 12 | { 13 | for (int i = 1; i < Blockchain.Count; i++) 14 | { 15 | var currentBlock = Blockchain[i]; 16 | var previousBlock = Blockchain[i - 1]; 17 | if (currentBlock.Hash != currentBlock.CalculateHash()) 18 | { 19 | Console.WriteLine("Current Hashes not equal"); 20 | return false; 21 | } 22 | 23 | if (previousBlock.Hash != currentBlock.PreviousHash) 24 | { 25 | Console.WriteLine("Previous Hashes not equal"); 26 | return false; 27 | } 28 | } 29 | return true; 30 | } 31 | 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MedPoints/Storage.Cli/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Storage.Core; 4 | using Storage.Utils; 5 | 6 | namespace Storage.Cli 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | var coinbase = new Wallet(); 13 | //Console.WriteLine(coinbase.PrivateKey.); 14 | 15 | //TestChain.Test(); 16 | 17 | /*var sender = new Wallet(); 18 | var receipent = new Wallet(); 19 | 20 | var transaction = new Transaction(sender.PublicKey, receipent.PublicKey, 20, null, null); 21 | transaction.Sign(sender); 22 | transaction.VerifySignature();*/ 23 | 24 | /*var dataBase = DatabaseContext.Open("test", new Options { CreateIfMissing = true }); 25 | dataBase.Put(WriteOptions.Default, block.Hash.ToSlice(), block.ToSlice()); 26 | var data = dataBase.Get(ReadOptions.Default, block.Hash.ToSlice()); 27 | var result = data.ToString();*/ 28 | 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/GetBlocksPayload.cs: -------------------------------------------------------------------------------- 1 | using Neo.IO; 2 | using System.IO; 3 | 4 | namespace Neo.Network.Payloads 5 | { 6 | public class GetBlocksPayload : ISerializable 7 | { 8 | public UInt256[] HashStart; 9 | public UInt256 HashStop; 10 | 11 | public int Size => HashStart.GetVarSize() + HashStop.Size; 12 | 13 | public static GetBlocksPayload Create(UInt256 hash_start, UInt256 hash_stop = null) 14 | { 15 | return new GetBlocksPayload 16 | { 17 | HashStart = new[] { hash_start }, 18 | HashStop = hash_stop ?? UInt256.Zero 19 | }; 20 | } 21 | 22 | void ISerializable.Deserialize(BinaryReader reader) 23 | { 24 | HashStart = reader.ReadSerializableArray(16); 25 | HashStop = reader.ReadSerializable(); 26 | } 27 | 28 | void ISerializable.Serialize(BinaryWriter writer) 29 | { 30 | writer.Write(HashStart); 31 | writer.Write(HashStop); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MedPoints/Storage/Storage.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.0 4 | 7.2 5 | 6 | 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/InvPayload.cs: -------------------------------------------------------------------------------- 1 | using Neo.IO; 2 | using System; 3 | using System.IO; 4 | 5 | namespace Neo.Network.Payloads 6 | { 7 | public class InvPayload : ISerializable 8 | { 9 | public InventoryType Type; 10 | public UInt256[] Hashes; 11 | 12 | public int Size => sizeof(InventoryType) + Hashes.GetVarSize(); 13 | 14 | public static InvPayload Create(InventoryType type, params UInt256[] hashes) 15 | { 16 | return new InvPayload 17 | { 18 | Type = type, 19 | Hashes = hashes 20 | }; 21 | } 22 | 23 | void ISerializable.Deserialize(BinaryReader reader) 24 | { 25 | Type = (InventoryType)reader.ReadByte(); 26 | if (!Enum.IsDefined(typeof(InventoryType), Type)) 27 | throw new FormatException(); 28 | Hashes = reader.ReadSerializableArray(); 29 | } 30 | 31 | void ISerializable.Serialize(BinaryWriter writer) 32 | { 33 | writer.Write((byte)Type); 34 | writer.Write(Hashes); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /MedPoints/Storage/Database/BlockRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using Dapper; 3 | using Npgsql; 4 | using Storage.Core; 5 | using Storage.Core.Transactions; 6 | 7 | namespace Storage.Database 8 | { 9 | public class BlockRepository 10 | { 11 | private string connectionString; 12 | private IDbConnection Connection => new NpgsqlConnection(connectionString); 13 | 14 | public string GetLastBlockHash(){ 15 | using (IDbConnection dbConnection = Connection) 16 | { 17 | dbConnection.Open(); 18 | return dbConnection.QueryFirst("SELECT hash FROM blocks ORDER BY id DESC LIMIT 1;"); 19 | } 20 | } 21 | 22 | public void Add(Block block) 23 | { 24 | using (IDbConnection dbConnection = Connection) 25 | { 26 | dbConnection.Open(); 27 | dbConnection.Execute( 28 | "INSERT INTO blocks (hash, data) VALUES(@Hash,@Data)", 29 | new {Hash = block.Hash, Data = block.Serialize()}); 30 | } 31 | } 32 | 33 | 34 | } 35 | 36 | 37 | } -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/FilterLoadPayload.cs: -------------------------------------------------------------------------------- 1 | using Neo.Cryptography; 2 | using Neo.IO; 3 | using System; 4 | using System.IO; 5 | 6 | namespace Neo.Network.Payloads 7 | { 8 | public class FilterLoadPayload : ISerializable 9 | { 10 | public byte[] Filter; 11 | public byte K; 12 | public uint Tweak; 13 | 14 | public int Size => Filter.GetVarSize() + sizeof(byte) + sizeof(uint); 15 | 16 | public static FilterLoadPayload Create(BloomFilter filter) 17 | { 18 | byte[] buffer = new byte[filter.M / 8]; 19 | filter.GetBits(buffer); 20 | return new FilterLoadPayload 21 | { 22 | Filter = buffer, 23 | K = (byte)filter.K, 24 | Tweak = filter.Tweak 25 | }; 26 | } 27 | 28 | void ISerializable.Deserialize(BinaryReader reader) 29 | { 30 | Filter = reader.ReadVarBytes(36000); 31 | K = reader.ReadByte(); 32 | if (K > 50) throw new FormatException(); 33 | Tweak = reader.ReadUInt32(); 34 | } 35 | 36 | void ISerializable.Serialize(BinaryWriter writer) 37 | { 38 | writer.WriteVarBytes(Filter); 39 | writer.Write(K); 40 | writer.Write(Tweak); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/Witness.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Newtonsoft.Json.Linq; 3 | using Storage.Utils; 4 | 5 | namespace Storage.Core 6 | { 7 | public class Witness : ISerializable 8 | { 9 | public byte[] InvocationScript; 10 | public byte[] VerificationScript; 11 | 12 | private UInt160 _scriptHash; 13 | public virtual UInt160 ScriptHash 14 | { 15 | get 16 | { 17 | if (_scriptHash == null) 18 | { 19 | _scriptHash = VerificationScript.ToScriptHash(); 20 | } 21 | return _scriptHash; 22 | } 23 | } 24 | 25 | public int Size => InvocationScript.GetVarSize() + VerificationScript.GetVarSize(); 26 | 27 | void ISerializable.Deserialize(BinaryReader reader) 28 | { 29 | InvocationScript = reader.ReadVarBytes(65536); 30 | VerificationScript = reader.ReadVarBytes(65536); 31 | } 32 | 33 | void ISerializable.Serialize(BinaryWriter writer) 34 | { 35 | writer.WriteVarBytes(InvocationScript); 36 | writer.WriteVarBytes(VerificationScript); 37 | } 38 | 39 | public JObject ToJson() 40 | { 41 | JObject json = new JObject(); 42 | json["invocation"] = InvocationScript.ToHexString(); 43 | json["verification"] = VerificationScript.ToHexString(); 44 | return json; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /MedPoints/MedPoints.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2043 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Storage", "Storage\Storage.csproj", "{8513EC5C-2BB5-4310-B10D-8924624ABDAD}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage.Cli", "Storage.Cli\Storage.Cli.csproj", "{C9348D23-403E-451C-804E-6BC3D77C6FD8}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {8513EC5C-2BB5-4310-B10D-8924624ABDAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {8513EC5C-2BB5-4310-B10D-8924624ABDAD}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {8513EC5C-2BB5-4310-B10D-8924624ABDAD}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {8513EC5C-2BB5-4310-B10D-8924624ABDAD}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {C9348D23-403E-451C-804E-6BC3D77C6FD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C9348D23-403E-451C-804E-6BC3D77C6FD8}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C9348D23-403E-451C-804E-6BC3D77C6FD8}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C9348D23-403E-451C-804E-6BC3D77C6FD8}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {73B319C4-5B5B-4AF3-A757-CA04666D525E} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/Options.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static Storage.Data.LevelDB.NativeWrapper; 3 | 4 | namespace Storage.Data.LevelDB 5 | { 6 | public class Options 7 | { 8 | public static readonly Options Default = new Options(); 9 | internal readonly IntPtr Handle = leveldb_options_create(); 10 | 11 | public bool CreateIfMissing 12 | { 13 | set => leveldb_options_set_create_if_missing(Handle, value); 14 | } 15 | 16 | public bool ErrorIfExists 17 | { 18 | set => leveldb_options_set_error_if_exists(Handle, value); 19 | } 20 | 21 | public bool ParanoidChecks 22 | { 23 | set => leveldb_options_set_paranoid_checks(Handle, value); 24 | } 25 | 26 | public int WriteBufferSize 27 | { 28 | set => leveldb_options_set_write_buffer_size(Handle, (UIntPtr)value); 29 | } 30 | 31 | public int MaxOpenFiles 32 | { 33 | set => leveldb_options_set_max_open_files(Handle, value); 34 | } 35 | 36 | public int BlockSize 37 | { 38 | set => leveldb_options_set_block_size(Handle, (UIntPtr)value); 39 | } 40 | 41 | public int BlockRestartInterval 42 | { 43 | set => leveldb_options_set_block_restart_interval(Handle, value); 44 | } 45 | 46 | public CompressionType Compression 47 | { 48 | set => leveldb_options_set_compression(Handle, value); 49 | } 50 | 51 | public IntPtr FilterPolicy 52 | { 53 | set => leveldb_options_set_filter_policy(Handle, value); 54 | } 55 | 56 | ~Options() 57 | { 58 | leveldb_options_destroy(Handle); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /MedPoints/Storage/Utils/HashHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | using Storage.Core; 6 | using Storage.Core.Transactions; 7 | 8 | namespace Storage.Utils 9 | { 10 | public static class HashHelper 11 | { 12 | public static string GetSha256Hash(this string value) 13 | { 14 | var sb = new StringBuilder(); 15 | 16 | using (var hash = SHA256.Create()) 17 | { 18 | Encoding enc = Encoding.UTF8; 19 | Byte[] result = hash.ComputeHash(enc.GetBytes(value)); 20 | 21 | foreach (Byte b in result) 22 | sb.Append(b.ToString("x2")); 23 | } 24 | 25 | return sb.ToString(); 26 | } 27 | 28 | public static string GenerateMerkleRoot(this List transactions) 29 | { 30 | int count = transactions.Count; 31 | var previousTreeLayer = new List(); 32 | foreach (var transaction in transactions) 33 | { 34 | previousTreeLayer.Add(transaction.Id); 35 | } 36 | var treeLayer = previousTreeLayer; 37 | while (count > 1) 38 | { 39 | treeLayer = new List(); 40 | for (int i = 1; i < previousTreeLayer.Count; i++) 41 | { 42 | var hash = $"{previousTreeLayer[i - 1]}{previousTreeLayer[i]}".GetSha256Hash(); 43 | treeLayer.Add(hash); 44 | } 45 | count = treeLayer.Count; 46 | previousTreeLayer = treeLayer; 47 | } 48 | 49 | return treeLayer.Count == 1 ? treeLayer[0] : ""; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/NetworkAddressWithTime.cs: -------------------------------------------------------------------------------- 1 | using Neo.IO; 2 | using System; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | 7 | namespace Neo.Network.Payloads 8 | { 9 | public class NetworkAddressWithTime : ISerializable 10 | { 11 | public const ulong NODE_NETWORK = 1; 12 | 13 | public uint Timestamp; 14 | public ulong Services; 15 | public IPEndPoint EndPoint; 16 | 17 | public int Size => sizeof(uint) + sizeof(ulong) + 16 + sizeof(ushort); 18 | 19 | public static NetworkAddressWithTime Create(IPEndPoint endpoint, ulong services, uint timestamp) 20 | { 21 | return new NetworkAddressWithTime 22 | { 23 | Timestamp = timestamp, 24 | Services = services, 25 | EndPoint = endpoint 26 | }; 27 | } 28 | 29 | void ISerializable.Deserialize(BinaryReader reader) 30 | { 31 | Timestamp = reader.ReadUInt32(); 32 | Services = reader.ReadUInt64(); 33 | byte[] data = reader.ReadBytes(16); 34 | if (data.Length != 16) throw new FormatException(); 35 | IPAddress address = new IPAddress(data); 36 | data = reader.ReadBytes(2); 37 | if (data.Length != 2) throw new FormatException(); 38 | ushort port = data.Reverse().ToArray().ToUInt16(0); 39 | EndPoint = new IPEndPoint(address, port); 40 | } 41 | 42 | void ISerializable.Serialize(BinaryWriter writer) 43 | { 44 | writer.Write(Timestamp); 45 | writer.Write(Services); 46 | writer.Write(EndPoint.Address.GetAddressBytes()); 47 | writer.Write(BitConverter.GetBytes((ushort)EndPoint.Port).Reverse().ToArray()); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/MerkleBlockPayload.cs: -------------------------------------------------------------------------------- 1 | using Neo.Core; 2 | using Neo.Cryptography; 3 | using Neo.IO; 4 | using System.Collections; 5 | using System.IO; 6 | using System.Linq; 7 | 8 | namespace Neo.Network.Payloads 9 | { 10 | public class MerkleBlockPayload : BlockBase 11 | { 12 | public int TxCount; 13 | public UInt256[] Hashes; 14 | public byte[] Flags; 15 | 16 | public override int Size => base.Size + sizeof(int) + Hashes.GetVarSize() + Flags.GetVarSize(); 17 | 18 | public static MerkleBlockPayload Create(Block block, BitArray flags) 19 | { 20 | MerkleTree tree = new MerkleTree(block.Transactions.Select(p => p.Hash).ToArray()); 21 | tree.Trim(flags); 22 | byte[] buffer = new byte[(flags.Length + 7) / 8]; 23 | flags.CopyTo(buffer, 0); 24 | return new MerkleBlockPayload 25 | { 26 | Version = block.Version, 27 | PrevHash = block.PrevHash, 28 | MerkleRoot = block.MerkleRoot, 29 | Timestamp = block.Timestamp, 30 | Index = block.Index, 31 | ConsensusData = block.ConsensusData, 32 | NextConsensus = block.NextConsensus, 33 | Script = block.Script, 34 | TxCount = block.Transactions.Length, 35 | Hashes = tree.ToHashArray(), 36 | Flags = buffer 37 | }; 38 | } 39 | 40 | public override void Deserialize(BinaryReader reader) 41 | { 42 | base.Deserialize(reader); 43 | TxCount = (int)reader.ReadVarInt(int.MaxValue); 44 | Hashes = reader.ReadSerializableArray(); 45 | Flags = reader.ReadVarBytes(); 46 | } 47 | 48 | public override void Serialize(BinaryWriter writer) 49 | { 50 | base.Serialize(writer); 51 | writer.WriteVarInt(TxCount); 52 | writer.Write(Hashes); 53 | writer.WriteVarBytes(Flags); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /MedPoints/Storage/Database/CoinTransactionRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data; 3 | using System.Linq; 4 | using Dapper; 5 | using Microsoft.Extensions.Configuration; 6 | using Npgsql; 7 | using Storage.Core; 8 | using Storage.Core.Transactions; 9 | 10 | namespace Storage.Database 11 | { 12 | public class CoinTransactionRepository 13 | { 14 | private class TxInternal 15 | { 16 | public string UserId { get; set; } 17 | public TransactionType Type { get; set; } 18 | public string Transaction { get; set; } 19 | } 20 | 21 | private string connectionString; 22 | private IDbConnection Connection => new NpgsqlConnection(connectionString); 23 | 24 | public CoinTransactionRepository(IConfiguration configuration) 25 | { 26 | connectionString = configuration.GetValue("DBInfo:ConnectionString"); 27 | } 28 | 29 | 30 | public void Add(string userId, CoinTransaction tx) 31 | { 32 | using (IDbConnection dbConnection = Connection) 33 | { 34 | dbConnection.Open(); 35 | dbConnection.Execute( 36 | "INSERT INTO transactions (user_id, type, transaction) VALUES(@UserId,@Type,@Transaction)", 37 | new TxInternal{UserId = userId, Type = tx.Type, Transaction = tx.Serialize()}); 38 | } 39 | 40 | } 41 | 42 | 43 | public List GetByUserId(string userId) 44 | { 45 | using (IDbConnection dbConnection = Connection) 46 | { 47 | dbConnection.Open(); 48 | return dbConnection 49 | .Query("SELECT * FROM transactions where user_id=@UserId and type=@Type", 50 | new TxInternal {UserId = userId, Type = TransactionType.Coins}) 51 | .Select(tx => TransactionExtensions.Deserialize(tx.Transaction)).ToList(); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /MedPoints/Storage.Utils/Settings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.Extensions.Configuration; 4 | 5 | namespace Storage.Utils 6 | { 7 | public class Settings 8 | { 9 | public uint Magic { get; } 10 | public byte AddressVersion { get; } 11 | public int MaxTransactionsPerBlock { get; } 12 | public string[] StandbyValidators { get; } 13 | public string[] SeedList { get; } 14 | //public IReadOnlyDictionary SystemFee { get; private set; } 15 | public uint SecondsPerBlock { get; } 16 | 17 | public static Settings Default { get; } 18 | 19 | static Settings() 20 | { 21 | IConfigurationSection section = new ConfigurationBuilder().AddJsonFile("protocol.json").Build().GetSection("ProtocolConfiguration"); 22 | Default = new Settings(section); 23 | } 24 | 25 | public Settings(IConfigurationSection section) 26 | { 27 | this.Magic = uint.Parse(section.GetSection("Magic").Value); 28 | this.AddressVersion = byte.Parse(section.GetSection("AddressVersion").Value); 29 | this.MaxTransactionsPerBlock = GetValueOrDefault(section.GetSection("MaxTransactionsPerBlock"), 500, p => int.Parse(p)); 30 | this.StandbyValidators = section.GetSection("StandbyValidators").GetChildren().Select(p => p.Value).ToArray(); 31 | this.SeedList = section.GetSection("SeedList").GetChildren().Select(p => p.Value).ToArray(); 32 | //this.SystemFee = section.GetSection("SystemFee").GetChildren().ToDictionary(p => (TransactionType)Enum.Parse(typeof(TransactionType), p.Key, true), p => Fixed8.Parse(p.Value)); 33 | this.SecondsPerBlock = GetValueOrDefault(section.GetSection("SecondsPerBlock"), 15u, p => uint.Parse(p)); 34 | } 35 | 36 | public T GetValueOrDefault(IConfigurationSection section, T defaultValue, Func selector) 37 | { 38 | if (section.Value == null) return defaultValue; 39 | return selector(section.Value); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/Iterator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static Storage.Data.LevelDB.NativeWrapper; 3 | 4 | namespace Storage.Data.LevelDB 5 | { 6 | public class Iterator : IDisposable 7 | { 8 | private IntPtr handle; 9 | 10 | internal Iterator(IntPtr handle) 11 | { 12 | this.handle = handle; 13 | } 14 | 15 | private void CheckError() 16 | { 17 | IntPtr error; 18 | leveldb_iter_get_error(handle, out error); 19 | NativeHelper.CheckError(error); 20 | } 21 | 22 | public void Dispose() 23 | { 24 | if (handle != IntPtr.Zero) 25 | { 26 | leveldb_iter_destroy(handle); 27 | handle = IntPtr.Zero; 28 | } 29 | } 30 | 31 | public Slice Key() 32 | { 33 | UIntPtr length; 34 | IntPtr key = leveldb_iter_key(handle, out length); 35 | CheckError(); 36 | return new Slice(key, length); 37 | } 38 | 39 | public void Next() 40 | { 41 | leveldb_iter_next(handle); 42 | CheckError(); 43 | } 44 | 45 | public void Prev() 46 | { 47 | leveldb_iter_prev(handle); 48 | CheckError(); 49 | } 50 | 51 | public void Seek(Slice target) 52 | { 53 | leveldb_iter_seek(handle, target.buffer, (UIntPtr)target.buffer.Length); 54 | } 55 | 56 | public void SeekToFirst() 57 | { 58 | leveldb_iter_seek_to_first(handle); 59 | } 60 | 61 | public void SeekToLast() 62 | { 63 | leveldb_iter_seek_to_last(handle); 64 | } 65 | 66 | public bool Valid() 67 | { 68 | return leveldb_iter_valid(handle); 69 | } 70 | 71 | public Slice Value() 72 | { 73 | UIntPtr length; 74 | IntPtr value = leveldb_iter_value(handle, out length); 75 | CheckError(); 76 | return new Slice(value, length); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /MedPoints/Storage/Core/Block.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Storage.Core.Transactions; 5 | using Storage.Utils; 6 | 7 | namespace Storage.Core 8 | { 9 | public class Block 10 | { 11 | public string Hash { get; private set; } 12 | public string PreviousHash { get; private set; } 13 | public DateTime Time { get; private set; } 14 | public int Nonce { get; private set; } 15 | public string MerkleRoot { get; private set; } 16 | public List CoinTransactions { get;} = new List(); 17 | public List OtherTransactions { get;} = new List(); 18 | 19 | public Block( string previousHash) 20 | { 21 | PreviousHash = previousHash; 22 | Time = DateTime.UtcNow; 23 | Hash = CalculateHash(); 24 | } 25 | 26 | public string CalculateHash() 27 | { 28 | var hash = $"{PreviousHash}{Time}{Nonce}{MerkleRoot}".GetSha256Hash(); 29 | return hash; 30 | } 31 | 32 | public void MineBlock(int difficulty) 33 | { 34 | MerkleRoot = CoinTransactions.GenerateMerkleRoot(); 35 | var target = string.Empty.PadLeft(difficulty, '0'); 36 | while (Hash.Substring(0, difficulty) != target) 37 | { 38 | Nonce++; 39 | Hash = CalculateHash(); 40 | } 41 | Console.WriteLine($"Block Mined - {Hash}"); 42 | } 43 | 44 | public void AddTransaction(Dictionary utxos, CoinTransaction coinTransaction) 45 | { 46 | if (PreviousHash != "0") 47 | { 48 | if(!coinTransaction.ProcessTransaction(utxos)) 49 | throw new Exception(); 50 | } 51 | CoinTransactions.Add(coinTransaction); 52 | } 53 | 54 | public void AddTransaction(ITransaction transaction) 55 | { 56 | OtherTransactions.Add(transaction); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /MedPoints/Storage/Database/MempoolRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using Dapper; 5 | using Microsoft.Extensions.Configuration; 6 | using Npgsql; 7 | using Storage.Mempool; 8 | 9 | namespace Storage.Database 10 | { 11 | public class MempoolRepository 12 | { 13 | private string connectionString; 14 | private IDbConnection Connection => new NpgsqlConnection(connectionString); 15 | 16 | public MempoolRepository(IConfiguration configuration) 17 | { 18 | connectionString = configuration.GetValue("DBInfo:ConnectionString"); 19 | } 20 | 21 | 22 | public void Add(MempoolTransaction tx) 23 | { 24 | tx.Id = Guid.NewGuid().ToString(); 25 | using (IDbConnection dbConnection = Connection) 26 | { 27 | dbConnection.Open(); 28 | dbConnection.Execute( 29 | "INSERT INTO mempool (id, user_id, type, transaction) VALUES(@Id, @UserId, @Type, @Transaction)", 30 | tx); 31 | } 32 | } 33 | 34 | public MempoolTransaction GetNext() 35 | { 36 | using (IDbConnection dbConnection = Connection) 37 | { 38 | dbConnection.Open(); 39 | return dbConnection 40 | .QueryFirst("SELECT * FROM mempool LIMIT 1"); 41 | } 42 | } 43 | 44 | public List GetNextList() 45 | { 46 | using (IDbConnection dbConnection = Connection) 47 | { 48 | dbConnection.Open(); 49 | return dbConnection 50 | .QueryFirst("SELECT * FROM mempool LIMIT 10"); 51 | } 52 | } 53 | 54 | 55 | public void Delete(string txId) 56 | { 57 | using (IDbConnection dbConnection = Connection) 58 | { 59 | dbConnection.Open(); 60 | dbConnection.Execute("DELETE FROM mempool WHERE id = @TxId", new {TxId = txId}); 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/VersionPayload.cs: -------------------------------------------------------------------------------- 1 | using Neo.Core; 2 | using Neo.IO; 3 | using System; 4 | using System.IO; 5 | 6 | namespace Neo.Network.Payloads 7 | { 8 | public class VersionPayload : ISerializable 9 | { 10 | public uint Version; 11 | public ulong Services; 12 | public uint Timestamp; 13 | public ushort Port; 14 | public uint Nonce; 15 | public string UserAgent; 16 | public uint StartHeight; 17 | public bool Relay; 18 | 19 | public int Size => sizeof(uint) + sizeof(ulong) + sizeof(uint) + sizeof(ushort) + sizeof(uint) + UserAgent.GetVarSize() + sizeof(uint) + sizeof(bool); 20 | 21 | public static VersionPayload Create(int port, uint nonce, string userAgent) 22 | { 23 | return new VersionPayload 24 | { 25 | Version = LocalNode.ProtocolVersion, 26 | Services = NetworkAddressWithTime.NODE_NETWORK, 27 | Timestamp = DateTime.Now.ToTimestamp(), 28 | Port = (ushort)port, 29 | Nonce = nonce, 30 | UserAgent = userAgent, 31 | StartHeight = Blockchain.Default?.Height ?? 0, 32 | Relay = true 33 | }; 34 | } 35 | 36 | void ISerializable.Deserialize(BinaryReader reader) 37 | { 38 | Version = reader.ReadUInt32(); 39 | Services = reader.ReadUInt64(); 40 | Timestamp = reader.ReadUInt32(); 41 | Port = reader.ReadUInt16(); 42 | Nonce = reader.ReadUInt32(); 43 | UserAgent = reader.ReadVarString(1024); 44 | StartHeight = reader.ReadUInt32(); 45 | Relay = reader.ReadBoolean(); 46 | } 47 | 48 | void ISerializable.Serialize(BinaryWriter writer) 49 | { 50 | writer.Write(Version); 51 | writer.Write(Services); 52 | writer.Write(Timestamp); 53 | writer.Write(Port); 54 | writer.Write(Nonce); 55 | writer.WriteVarString(UserAgent); 56 | writer.Write(StartHeight); 57 | writer.Write(Relay); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /MedPoints/Storage.Cli/TestChain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Storage.Core; 5 | using Storage.Core.Transactions; 6 | 7 | namespace Storage.Cli 8 | { 9 | public class TestChain 10 | { 11 | private static List Blocks = new List(); 12 | private static Dictionary UTXOs = new Dictionary(); 13 | 14 | private static int Difficulty = 3; 15 | private static Wallet WalletA; 16 | private static Wallet WalletB; 17 | private static CoinTransaction _genesisCoinTransaction; 18 | 19 | public static void Test() 20 | { 21 | WalletA = new Wallet(); 22 | WalletB = new Wallet(); 23 | var coinBase = new Wallet(); 24 | 25 | _genesisCoinTransaction = new CoinTransaction(coinBase.PublicKey, WalletA.PublicKey, 100, null); 26 | _genesisCoinTransaction.Sign(coinBase); 27 | _genesisCoinTransaction.Id = "0"; 28 | _genesisCoinTransaction.Outputs.Add( 29 | new TransactionOutput(_genesisCoinTransaction.Reciepient, _genesisCoinTransaction.Amount, _genesisCoinTransaction.Id) 30 | ); 31 | UTXOs[_genesisCoinTransaction.Outputs[0].Id] = _genesisCoinTransaction.Outputs[0]; 32 | 33 | var genesis = new Block("0"); 34 | genesis.AddTransaction(UTXOs, _genesisCoinTransaction); 35 | AddBlock(genesis); 36 | 37 | var balance = WalletA.GetBalance(UTXOs); 38 | var block1 = new Block(genesis.Hash); 39 | block1.AddTransaction(UTXOs, WalletA.Send(UTXOs, WalletB.PublicKey, 40)); 40 | AddBlock(block1); 41 | 42 | balance = WalletA.GetBalance(UTXOs); 43 | var block2 = new Block(block1.Hash); 44 | block2.AddTransaction(UTXOs, WalletA.Send(UTXOs, WalletB.PublicKey, 1000)); 45 | AddBlock(block2); 46 | 47 | balance = WalletA.GetBalance(UTXOs); 48 | var block3 = new Block(block2.Hash); 49 | block3.AddTransaction(UTXOs, WalletB.Send(UTXOs, WalletA.PublicKey,20)); 50 | AddBlock(block3); 51 | 52 | balance = WalletA.GetBalance(UTXOs); 53 | } 54 | 55 | public static void AddBlock(Block newBlock) 56 | { 57 | newBlock.MineBlock(Difficulty); 58 | Blocks.Add(newBlock); 59 | } 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /MedPoints/Storage/Database/AppointmentToTheDoctorRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data; 3 | using System.Linq; 4 | using Dapper; 5 | using Microsoft.Extensions.Configuration; 6 | using Npgsql; 7 | using Storage.Core.Transactions; 8 | 9 | namespace Storage.Database 10 | { 11 | public class AppointmentToTheDoctorRepository 12 | { 13 | private class TxInternal 14 | { 15 | public string UserId { get; set; } 16 | public TransactionType Type { get; set; } 17 | public string Transaction { get; set; } 18 | } 19 | 20 | private string connectionString; 21 | private IDbConnection Connection => new NpgsqlConnection(connectionString); 22 | 23 | public AppointmentToTheDoctorRepository(IConfiguration configuration) 24 | { 25 | connectionString = configuration.GetValue("DBInfo:ConnectionString"); 26 | } 27 | 28 | 29 | public void Add(string userId, AppointmentToTheDoctorTransaction tx) 30 | { 31 | using (IDbConnection dbConnection = Connection) 32 | { 33 | dbConnection.Open(); 34 | dbConnection.Execute( 35 | "INSERT INTO transactions (user_id, type, transaction) VALUES(@UserId,@Type,@Transaction)", 36 | new TxInternal{UserId = userId, Type = tx.Type, Transaction = tx.Serialize()}); 37 | } 38 | 39 | } 40 | 41 | public List GetByDoctorId(string doctorId) 42 | { 43 | using (IDbConnection dbConnection = Connection) 44 | { 45 | dbConnection.Open(); 46 | return dbConnection 47 | .Query("SELECT * FROM transactions where type == @Type and transaction->>'doctorId' = '@DoctorId';", 48 | new {DoctorId = doctorId, Type = TransactionType.VisitToTheDoctor}) 49 | .Select(tx => TransactionExtensions.Deserialize(tx.Transaction)).ToList(); 50 | } 51 | } 52 | 53 | 54 | public List GetByUserId(string userId) 55 | { 56 | using (IDbConnection dbConnection = Connection) 57 | { 58 | dbConnection.Open(); 59 | return dbConnection 60 | .Query("SELECT * FROM transactions where user_id=@UserId and type=@Type", 61 | new TxInternal {UserId = userId, Type = TransactionType.VisitToTheDoctor}) 62 | .Select(tx => TransactionExtensions.Deserialize(tx.Transaction)).ToList(); 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /MedPoints/Storage.Network/WebSocketRemoteNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Net.WebSockets; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Neo.Network; 8 | using Storage.Utils; 9 | 10 | namespace Storage.Network 11 | { 12 | internal class WebSocketRemoteNode : RemoteNode 13 | { 14 | private readonly WebSocket _socket; 15 | private readonly bool _connected; 16 | private int _disposed; 17 | 18 | public WebSocketRemoteNode(LocalNode localNode, WebSocket socket, IPEndPoint remoteEndpoint) 19 | : base(localNode) 20 | { 21 | _socket = socket; 22 | RemoteEndpoint = new IPEndPoint(remoteEndpoint.Address.MapToIPv6(), remoteEndpoint.Port); 23 | _connected = true; 24 | } 25 | 26 | public override void Disconnect(bool error) 27 | { 28 | if (Interlocked.Exchange(ref _disposed, 1) == 0) 29 | { 30 | _socket.Dispose(); 31 | base.Disconnect(error); 32 | } 33 | } 34 | 35 | protected override async Task ReceiveMessageAsync(TimeSpan timeout) 36 | { 37 | CancellationTokenSource source = new CancellationTokenSource(timeout); 38 | try 39 | { 40 | return await Message.DeserializeFromAsync(_socket, source.Token); 41 | } 42 | catch (ArgumentException) { } 43 | catch (ObjectDisposedException) { } 44 | catch (Exception ex) when (ex is FormatException || ex is IOException || ex is WebSocketException || ex is OperationCanceledException) 45 | { 46 | Disconnect(false); 47 | } 48 | finally 49 | { 50 | source.Dispose(); 51 | } 52 | return null; 53 | } 54 | 55 | protected override async Task SendMessageAsync(Message message) 56 | { 57 | if (!_connected) throw new InvalidOperationException(); 58 | if (_disposed > 0) return false; 59 | ArraySegment segment = new ArraySegment(message.ToArray()); 60 | CancellationTokenSource source = new CancellationTokenSource(10000); 61 | try 62 | { 63 | await _socket.SendAsync(segment, WebSocketMessageType.Binary, true, source.Token); 64 | return true; 65 | } 66 | catch (ObjectDisposedException) { } 67 | catch (Exception ex) when (ex is WebSocketException || ex is OperationCanceledException) 68 | { 69 | Disconnect(false); 70 | } 71 | finally 72 | { 73 | source.Dispose(); 74 | } 75 | return false; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/UInt256.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | 5 | namespace Storage.Core 6 | { 7 | public class UInt256 : UIntBase, IComparable, IEquatable 8 | { 9 | public static readonly UInt256 Zero = new UInt256(); 10 | 11 | public UInt256() 12 | : this(null) 13 | { 14 | } 15 | 16 | public UInt256(byte[] value) 17 | : base(32, value) 18 | { 19 | } 20 | 21 | public int CompareTo(UInt256 other) 22 | { 23 | byte[] x = ToArray(); 24 | byte[] y = other.ToArray(); 25 | for (int i = x.Length - 1; i >= 0; i--) 26 | { 27 | if (x[i] > y[i]) 28 | return 1; 29 | if (x[i] < y[i]) 30 | return -1; 31 | } 32 | return 0; 33 | } 34 | 35 | bool IEquatable.Equals(UInt256 other) 36 | { 37 | return Equals(other); 38 | } 39 | 40 | public static new UInt256 Parse(string s) 41 | { 42 | if (s == null) 43 | throw new ArgumentNullException(); 44 | if (s.StartsWith("0x")) 45 | s = s.Substring(2); 46 | if (s.Length != 64) 47 | throw new FormatException(); 48 | return new UInt256(s.HexToBytes().Reverse().ToArray()); 49 | } 50 | 51 | public static bool TryParse(string s, out UInt256 result) 52 | { 53 | if (s == null) 54 | { 55 | result = null; 56 | return false; 57 | } 58 | if (s.StartsWith("0x")) 59 | s = s.Substring(2); 60 | if (s.Length != 64) 61 | { 62 | result = null; 63 | return false; 64 | } 65 | byte[] data = new byte[32]; 66 | for (int i = 0; i < 32; i++) 67 | if (!byte.TryParse(s.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out data[i])) 68 | { 69 | result = null; 70 | return false; 71 | } 72 | result = new UInt256(data.Reverse().ToArray()); 73 | return true; 74 | } 75 | 76 | public static bool operator >(UInt256 left, UInt256 right) 77 | { 78 | return left.CompareTo(right) > 0; 79 | } 80 | 81 | public static bool operator >=(UInt256 left, UInt256 right) 82 | { 83 | return left.CompareTo(right) >= 0; 84 | } 85 | 86 | public static bool operator <(UInt256 left, UInt256 right) 87 | { 88 | return left.CompareTo(right) < 0; 89 | } 90 | 91 | public static bool operator <=(UInt256 left, UInt256 right) 92 | { 93 | return left.CompareTo(right) <= 0; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/UInt160.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | 5 | namespace Storage.Core 6 | { 7 | public class UInt160 : UIntBase, IComparable, IEquatable 8 | { 9 | public static readonly UInt160 Zero = new UInt160(); 10 | 11 | public UInt160() 12 | : this(null) 13 | { 14 | } 15 | 16 | public UInt160(byte[] value) 17 | : base(20, value) 18 | { 19 | } 20 | 21 | public int CompareTo(UInt160 other) 22 | { 23 | byte[] x = ToArray(); 24 | byte[] y = other.ToArray(); 25 | for (int i = x.Length - 1; i >= 0; i--) 26 | { 27 | if (x[i] > y[i]) 28 | return 1; 29 | if (x[i] < y[i]) 30 | return -1; 31 | } 32 | return 0; 33 | } 34 | 35 | bool IEquatable.Equals(UInt160 other) 36 | { 37 | return Equals(other); 38 | } 39 | 40 | public static new UInt160 Parse(string value) 41 | { 42 | if (value == null) 43 | throw new ArgumentNullException(); 44 | if (value.StartsWith("0x")) 45 | value = value.Substring(2); 46 | if (value.Length != 40) 47 | throw new FormatException(); 48 | return new UInt160(value.HexToBytes().Reverse().ToArray()); 49 | } 50 | 51 | public static bool TryParse(string s, out UInt160 result) 52 | { 53 | if (s == null) 54 | { 55 | result = null; 56 | return false; 57 | } 58 | if (s.StartsWith("0x")) 59 | s = s.Substring(2); 60 | if (s.Length != 40) 61 | { 62 | result = null; 63 | return false; 64 | } 65 | byte[] data = new byte[20]; 66 | for (int i = 0; i < 20; i++) 67 | if (!byte.TryParse(s.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out data[i])) 68 | { 69 | result = null; 70 | return false; 71 | } 72 | result = new UInt160(data.Reverse().ToArray()); 73 | return true; 74 | } 75 | 76 | public static bool operator >(UInt160 left, UInt160 right) 77 | { 78 | return left.CompareTo(right) > 0; 79 | } 80 | 81 | public static bool operator >=(UInt160 left, UInt160 right) 82 | { 83 | return left.CompareTo(right) >= 0; 84 | } 85 | 86 | public static bool operator <(UInt160 left, UInt160 right) 87 | { 88 | return left.CompareTo(right) < 0; 89 | } 90 | 91 | public static bool operator <=(UInt160 left, UInt160 right) 92 | { 93 | return left.CompareTo(right) <= 0; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/CoreHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace Storage.Core 7 | { 8 | public static class CoreHelper 9 | { 10 | public static byte[] GetHashData(this IVerifiable verifiable) 11 | { 12 | using (MemoryStream ms = new MemoryStream()) 13 | using (BinaryWriter writer = new BinaryWriter(ms)) 14 | { 15 | verifiable.SerializeUnsigned(writer); 16 | writer.Flush(); 17 | return ms.ToArray(); 18 | } 19 | } 20 | 21 | //public static byte[] Sign(this IVerifiable verifiable, KeyPair key) 22 | //{ 23 | // throw new NotImplementedException(); 24 | // //using (key.Decrypt()) 25 | // //{ 26 | // // return Crypto.Default.Sign(verifiable.GetHashData(), key.PrivateKey, key.PublicKey.EncodePoint(false).Skip(1).ToArray()); 27 | // //} 28 | //} 29 | 30 | public static UInt160 ToScriptHash(this byte[] script) 31 | { 32 | throw new NotImplementedException(); 33 | //return new UInt160(Crypto.Default.Hash160(script)); 34 | } 35 | 36 | internal static bool VerifyScripts(this IVerifiable verifiable) 37 | { 38 | throw new NotImplementedException(); 39 | //UInt160[] hashes; 40 | //try 41 | //{ 42 | // hashes = verifiable.GetScriptHashesForVerifying(); 43 | //} 44 | //catch (InvalidOperationException) 45 | //{ 46 | // return false; 47 | //} 48 | //if (hashes.Length != verifiable.Scripts.Length) return false; 49 | //for (int i = 0; i < hashes.Length; i++) 50 | //{ 51 | // byte[] verification = verifiable.Scripts[i].VerificationScript; 52 | // if (verification.Length == 0) 53 | // { 54 | // using (ScriptBuilder sb = new ScriptBuilder()) 55 | // { 56 | // sb.EmitAppCall(hashes[i].ToArray()); 57 | // verification = sb.ToArray(); 58 | // } 59 | // } 60 | // else 61 | // { 62 | // if (hashes[i] != verifiable.Scripts[i].ScriptHash) return false; 63 | // } 64 | // using (StateReader service = new StateReader()) 65 | // { 66 | // ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, Blockchain.Default, service, Fixed8.Zero); 67 | // engine.LoadScript(verification, false); 68 | // engine.LoadScript(verifiable.Scripts[i].InvocationScript, true); 69 | // if (!engine.Execute()) return false; 70 | // if (engine.EvaluationStack.Count != 1 || !engine.EvaluationStack.Pop().GetBoolean()) return false; 71 | // } 72 | //} 73 | //return true; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /MedPoints/Storage/Mempool/Mempool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Storage.Core; 4 | using Storage.Core.Transactions; 5 | using Storage.Database; 6 | 7 | namespace Storage.Mempool 8 | { 9 | public class Mempool 10 | { 11 | private readonly MempoolRepository _mempoolRepository; 12 | private readonly BlockRepository _blockRepository; 13 | private static Dictionary UTXOs = new Dictionary(); 14 | 15 | public Mempool(MempoolRepository mempoolRepository, BlockRepository blockRepository) 16 | { 17 | _mempoolRepository = mempoolRepository; 18 | _blockRepository = blockRepository; 19 | } 20 | 21 | 22 | public void AddToMempool(string data, string userId, TransactionType type) 23 | { 24 | _mempoolRepository.Add(new MempoolTransaction() 25 | { 26 | Transaction = data, 27 | UserId = userId, 28 | Type = type 29 | }); 30 | } 31 | 32 | public void Mine(Wallet wallet) 33 | { 34 | var txs = _mempoolRepository.GetNextList(); 35 | var lastBlockHash = _blockRepository.GetLastBlockHash(); 36 | if (lastBlockHash == null) 37 | lastBlockHash = InsertGenesis(wallet); 38 | 39 | var newBlock = new Block(lastBlockHash); 40 | foreach (var tx in txs) 41 | { 42 | switch (tx.Type) 43 | { 44 | case TransactionType.Coins: 45 | break; 46 | case TransactionType.VisitToTheDoctor: 47 | break; 48 | default: 49 | throw new ArgumentOutOfRangeException(); 50 | } 51 | } 52 | 53 | 54 | var coinBase = new Wallet(); 55 | var mainedBlockMoney = new CoinTransaction(coinBase.PublicKey, wallet.PublicKey, 100, null); 56 | mainedBlockMoney.Sign(coinBase); 57 | mainedBlockMoney.Id = Guid.NewGuid().ToString(); 58 | mainedBlockMoney.Outputs.Add( 59 | new TransactionOutput(mainedBlockMoney.Reciepient, mainedBlockMoney.Amount, mainedBlockMoney.Id) 60 | ); 61 | UTXOs[mainedBlockMoney.Outputs[0].Id] = mainedBlockMoney.Outputs[0]; 62 | 63 | newBlock.AddTransaction(UTXOs, mainedBlockMoney); 64 | 65 | newBlock.MineBlock(new Random().Next(0, 5)); 66 | 67 | 68 | _blockRepository.Add(newBlock); 69 | } 70 | 71 | private string InsertGenesis(Wallet wallet) 72 | { 73 | var coinBase = new Wallet(); 74 | var genesisCoinTransaction = new CoinTransaction(coinBase.PublicKey, wallet.PublicKey, 100, null); 75 | genesisCoinTransaction.Sign(coinBase); 76 | genesisCoinTransaction.Id = "0"; 77 | genesisCoinTransaction.Outputs.Add( 78 | new TransactionOutput(genesisCoinTransaction.Reciepient, genesisCoinTransaction.Amount, genesisCoinTransaction.Id) 79 | ); 80 | UTXOs[genesisCoinTransaction.Outputs[0].Id] = genesisCoinTransaction.Outputs[0]; 81 | 82 | var genesis = new Block("0"); 83 | genesis.AddTransaction(UTXOs, genesisCoinTransaction); 84 | 85 | _blockRepository.Add(genesis); 86 | return _blockRepository.GetLastBlockHash(); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /MedPoints/Storage.Core/BigDecimal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | 4 | namespace Storage.Core 5 | { 6 | public struct BigDecimal 7 | { 8 | private readonly BigInteger _value; 9 | private readonly byte _decimals; 10 | 11 | public BigInteger Value => _value; 12 | public byte Decimals => _decimals; 13 | public int Sign => _value.Sign; 14 | 15 | public BigDecimal(BigInteger value, byte decimals) 16 | { 17 | this._value = value; 18 | this._decimals = decimals; 19 | } 20 | 21 | public BigDecimal ChangeDecimals(byte decimals) 22 | { 23 | if (_decimals == decimals) return this; 24 | BigInteger value; 25 | if (_decimals < decimals) 26 | { 27 | value = this._value * BigInteger.Pow(10, decimals - this._decimals); 28 | } 29 | else 30 | { 31 | BigInteger divisor = BigInteger.Pow(10, this._decimals - decimals); 32 | value = BigInteger.DivRem(this._value, divisor, out BigInteger remainder); 33 | if (remainder > BigInteger.Zero) 34 | throw new ArgumentOutOfRangeException(); 35 | } 36 | return new BigDecimal(value, decimals); 37 | } 38 | 39 | public static BigDecimal Parse(string s, byte decimals) 40 | { 41 | if (!TryParse(s, decimals, out BigDecimal result)) 42 | throw new FormatException(); 43 | return result; 44 | } 45 | 46 | public Fixed8 ToFixed8() 47 | { 48 | try 49 | { 50 | return new Fixed8((long)ChangeDecimals(8)._value); 51 | } 52 | catch (Exception ex) 53 | { 54 | throw new InvalidCastException(ex.Message, ex); 55 | } 56 | } 57 | 58 | public override string ToString() 59 | { 60 | BigInteger divisor = BigInteger.Pow(10, _decimals); 61 | BigInteger result = BigInteger.DivRem(_value, divisor, out BigInteger remainder); 62 | if (remainder == 0) return result.ToString(); 63 | return $"{result}.{remainder.ToString("d" + _decimals)}".TrimEnd('0'); 64 | } 65 | 66 | public static bool TryParse(string s, byte decimals, out BigDecimal result) 67 | { 68 | int e = 0; 69 | int index = s.IndexOfAny(new[] { 'e', 'E' }); 70 | if (index >= 0) 71 | { 72 | if (!sbyte.TryParse(s.Substring(index + 1), out sbyte e_temp)) 73 | { 74 | result = default(BigDecimal); 75 | return false; 76 | } 77 | e = e_temp; 78 | s = s.Substring(0, index); 79 | } 80 | index = s.IndexOf('.'); 81 | if (index >= 0) 82 | { 83 | s = s.TrimEnd('0'); 84 | e -= s.Length - index - 1; 85 | s = s.Remove(index, 1); 86 | } 87 | int ds = e + decimals; 88 | if (ds < 0) 89 | { 90 | result = default(BigDecimal); 91 | return false; 92 | } 93 | if (ds > 0) 94 | s += new string('0', ds); 95 | if (!BigInteger.TryParse(s, out BigInteger value)) 96 | { 97 | result = default(BigDecimal); 98 | return false; 99 | } 100 | result = new BigDecimal(value, decimals); 101 | return true; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /MedPoints/Storage/Utils/RSAKeyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | using System.Xml; 6 | using Newtonsoft.Json; 7 | 8 | namespace Storage.Utils 9 | { 10 | public class RSAParametersJson 11 | { 12 | public string Modulus { get; set; } 13 | public string Exponent { get; set; } 14 | public string P { get; set; } 15 | public string Q { get; set; } 16 | public string DP { get; set; } 17 | public string DQ { get; set; } 18 | public string InverseQ { get; set; } 19 | public string D { get; set; } 20 | } 21 | 22 | 23 | public static class RsaKeyExtensions 24 | { 25 | public static void FromJsonString(this RSA rsa, string jsonString) 26 | { 27 | try 28 | { 29 | var paramsJson = JsonConvert.DeserializeObject(jsonString); 30 | 31 | var parameters = new RSAParameters 32 | { 33 | Modulus = paramsJson.Modulus != null ? Convert.FromBase64String(paramsJson.Modulus) : null, 34 | Exponent = paramsJson.Exponent != null ? Convert.FromBase64String(paramsJson.Exponent) : null, 35 | P = paramsJson.P != null ? Convert.FromBase64String(paramsJson.P) : null, 36 | Q = paramsJson.Q != null ? Convert.FromBase64String(paramsJson.Q) : null, 37 | DP = paramsJson.DP != null ? Convert.FromBase64String(paramsJson.DP) : null, 38 | DQ = paramsJson.DQ != null ? Convert.FromBase64String(paramsJson.DQ) : null, 39 | InverseQ = paramsJson.InverseQ != null ? Convert.FromBase64String(paramsJson.InverseQ) : null, 40 | D = paramsJson.D != null ? Convert.FromBase64String(paramsJson.D) : null 41 | }; 42 | 43 | rsa.ImportParameters(parameters); 44 | } 45 | catch 46 | { 47 | throw new Exception("Invalid JSON RSA key."); 48 | } 49 | } 50 | 51 | public static string ToJsonString(this RSA rsa, bool includePrivateParameters) 52 | { 53 | var parameters = rsa.ExportParameters(includePrivateParameters); 54 | 55 | var parasJson = new RSAParametersJson() 56 | { 57 | Modulus = parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null, 58 | Exponent = parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null, 59 | P = parameters.P != null ? Convert.ToBase64String(parameters.P) : null, 60 | Q = parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null, 61 | DP = parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null, 62 | DQ = parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null, 63 | InverseQ = parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null, 64 | D = parameters.D != null ? Convert.ToBase64String(parameters.D) : null 65 | }; 66 | 67 | return JsonConvert.SerializeObject(parasJson); 68 | } 69 | 70 | public static string Base64Encode(this string plainText) { 71 | var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); 72 | return Convert.ToBase64String(plainTextBytes); 73 | } 74 | 75 | public static string Base64Decode(this string base64EncodedData) { 76 | var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData); 77 | return Encoding.UTF8.GetString(base64EncodedBytes); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Payloads/ConsensusPayload.cs: -------------------------------------------------------------------------------- 1 | using Neo.Core; 2 | using Neo.Cryptography; 3 | using Neo.Cryptography.ECC; 4 | using Neo.IO; 5 | using Neo.SmartContract; 6 | using Neo.VM; 7 | using System; 8 | using System.IO; 9 | using Storage.Network; 10 | 11 | namespace Neo.Network.Payloads 12 | { 13 | public class ConsensusPayload : IInventory 14 | { 15 | public uint Version; 16 | public UInt256 PrevHash; 17 | public uint BlockIndex; 18 | public ushort ValidatorIndex; 19 | public uint Timestamp; 20 | public byte[] Data; 21 | public Witness Script; 22 | 23 | private UInt256 _hash = null; 24 | UInt256 IInventory.Hash 25 | { 26 | get 27 | { 28 | if (_hash == null) 29 | { 30 | _hash = new UInt256(Crypto.Default.Hash256(this.GetHashData())); 31 | } 32 | return _hash; 33 | } 34 | } 35 | 36 | InventoryType IInventory.InventoryType => InventoryType.Consensus; 37 | 38 | Witness[] IVerifiable.Scripts 39 | { 40 | get 41 | { 42 | return new[] { Script }; 43 | } 44 | set 45 | { 46 | if (value.Length != 1) throw new ArgumentException(); 47 | Script = value[0]; 48 | } 49 | } 50 | 51 | public int Size => sizeof(uint) + PrevHash.Size + sizeof(uint) + sizeof(ushort) + sizeof(uint) + Data.GetVarSize() + 1 + Script.Size; 52 | 53 | void ISerializable.Deserialize(BinaryReader reader) 54 | { 55 | ((IVerifiable)this).DeserializeUnsigned(reader); 56 | if (reader.ReadByte() != 1) throw new FormatException(); 57 | Script = reader.ReadSerializable(); 58 | } 59 | 60 | void IVerifiable.DeserializeUnsigned(BinaryReader reader) 61 | { 62 | Version = reader.ReadUInt32(); 63 | PrevHash = reader.ReadSerializable(); 64 | BlockIndex = reader.ReadUInt32(); 65 | ValidatorIndex = reader.ReadUInt16(); 66 | Timestamp = reader.ReadUInt32(); 67 | Data = reader.ReadVarBytes(); 68 | } 69 | 70 | byte[] IScriptContainer.GetMessage() 71 | { 72 | return this.GetHashData(); 73 | } 74 | 75 | UInt160[] IVerifiable.GetScriptHashesForVerifying() 76 | { 77 | if (Blockchain.Default == null) 78 | throw new InvalidOperationException(); 79 | ECPoint[] validators = Blockchain.Default.GetValidators(); 80 | if (validators.Length <= ValidatorIndex) 81 | throw new InvalidOperationException(); 82 | return new[] { Contract.CreateSignatureRedeemScript(validators[ValidatorIndex]).ToScriptHash() }; 83 | } 84 | 85 | void ISerializable.Serialize(BinaryWriter writer) 86 | { 87 | ((IVerifiable)this).SerializeUnsigned(writer); 88 | writer.Write((byte)1); writer.Write(Script); 89 | } 90 | 91 | void IVerifiable.SerializeUnsigned(BinaryWriter writer) 92 | { 93 | writer.Write(Version); 94 | writer.Write(PrevHash); 95 | writer.Write(BlockIndex); 96 | writer.Write(ValidatorIndex); 97 | writer.Write(Timestamp); 98 | writer.WriteVarBytes(Data); 99 | } 100 | 101 | public bool Verify() 102 | { 103 | if (Blockchain.Default == null) return false; 104 | if (BlockIndex <= Blockchain.Default.Height) 105 | return false; 106 | return this.VerifyScripts(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/UIntBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using Storage.Utils; 5 | 6 | namespace Storage.Core 7 | { 8 | public abstract class UIntBase : IEquatable, ISerializable 9 | { 10 | private byte[] data_bytes; 11 | 12 | public int Size => data_bytes.Length; 13 | 14 | protected UIntBase(int bytes, byte[] value) 15 | { 16 | if (value == null) 17 | { 18 | this.data_bytes = new byte[bytes]; 19 | return; 20 | } 21 | if (value.Length != bytes) 22 | throw new ArgumentException(); 23 | this.data_bytes = value; 24 | } 25 | 26 | void ISerializable.Deserialize(BinaryReader reader) 27 | { 28 | reader.Read(data_bytes, 0, data_bytes.Length); 29 | } 30 | 31 | public bool Equals(UIntBase other) 32 | { 33 | if (ReferenceEquals(other, null)) 34 | return false; 35 | if (ReferenceEquals(this, other)) 36 | return true; 37 | if (data_bytes.Length != other.data_bytes.Length) 38 | return false; 39 | return data_bytes.SequenceEqual(other.data_bytes); 40 | } 41 | 42 | public override bool Equals(object obj) 43 | { 44 | if (ReferenceEquals(obj, null)) 45 | return false; 46 | if (!(obj is UIntBase)) 47 | return false; 48 | return this.Equals((UIntBase)obj); 49 | } 50 | 51 | public override int GetHashCode() 52 | { 53 | return data_bytes.ToInt32(0); 54 | } 55 | 56 | public static UIntBase Parse(string s) 57 | { 58 | if (s.Length == 40 || s.Length == 42) 59 | return UInt160.Parse(s); 60 | else if (s.Length == 64 || s.Length == 66) 61 | return UInt256.Parse(s); 62 | else 63 | throw new FormatException(); 64 | } 65 | 66 | void ISerializable.Serialize(BinaryWriter writer) 67 | { 68 | writer.Write(data_bytes); 69 | } 70 | 71 | public byte[] ToArray() 72 | { 73 | return data_bytes; 74 | } 75 | 76 | public override string ToString() 77 | { 78 | return "0x" + data_bytes.Reverse().ToHexString(); 79 | } 80 | 81 | public static bool TryParse(string s, out T result) where T : UIntBase 82 | { 83 | int size; 84 | if (typeof(T) == typeof(UInt160)) 85 | size = 20; 86 | else if (typeof(T) == typeof(UInt256)) 87 | size = 32; 88 | else if (s.Length == 40 || s.Length == 42) 89 | size = 20; 90 | else if (s.Length == 64 || s.Length == 66) 91 | size = 32; 92 | else 93 | size = 0; 94 | if (size == 20) 95 | { 96 | if (UInt160.TryParse(s, out UInt160 r)) 97 | { 98 | result = (T)(UIntBase)r; 99 | return true; 100 | } 101 | } 102 | else if (size == 32) 103 | { 104 | if (UInt256.TryParse(s, out UInt256 r)) 105 | { 106 | result = (T)(UIntBase)r; 107 | return true; 108 | } 109 | } 110 | result = null; 111 | return false; 112 | } 113 | 114 | public static bool operator ==(UIntBase left, UIntBase right) 115 | { 116 | if (ReferenceEquals(left, right)) 117 | return true; 118 | if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) 119 | return false; 120 | return left.Equals(right); 121 | } 122 | 123 | public static bool operator !=(UIntBase left, UIntBase right) 124 | { 125 | return !(left == right); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/TcpRemoteNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Storage.Network; 8 | using Storage.Utils; 9 | 10 | namespace Neo.Network 11 | { 12 | internal class TcpRemoteNode : RemoteNode 13 | { 14 | private readonly Socket _socket; 15 | private NetworkStream _stream; 16 | private bool _connected; 17 | private int _disposed; 18 | 19 | public TcpRemoteNode(LocalNode localNode, IPEndPoint remoteEndpoint) 20 | : base(localNode) 21 | { 22 | this._socket = new Socket(remoteEndpoint.Address.IsIPv4MappedToIPv6 ? AddressFamily.InterNetwork : remoteEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 23 | this.ListenerEndpoint = remoteEndpoint; 24 | } 25 | 26 | public TcpRemoteNode(LocalNode localNode, Socket socket) 27 | : base(localNode) 28 | { 29 | _socket = socket; 30 | OnConnected(); 31 | } 32 | 33 | public async Task ConnectAsync() 34 | { 35 | IPAddress address = ListenerEndpoint.Address; 36 | if (address.IsIPv4MappedToIPv6) 37 | address = address.MapToIPv4(); 38 | try 39 | { 40 | await _socket.ConnectAsync(address, ListenerEndpoint.Port); 41 | OnConnected(); 42 | } 43 | catch (SocketException) 44 | { 45 | Disconnect(false); 46 | return false; 47 | } 48 | return true; 49 | } 50 | 51 | public override void Disconnect(bool error) 52 | { 53 | if (Interlocked.Exchange(ref _disposed, 1) == 0) 54 | { 55 | if (_stream != null) _stream.Dispose(); 56 | _socket.Dispose(); 57 | base.Disconnect(error); 58 | } 59 | } 60 | 61 | private void OnConnected() 62 | { 63 | IPEndPoint remoteEndpoint = (IPEndPoint)_socket.RemoteEndPoint; 64 | RemoteEndpoint = new IPEndPoint(remoteEndpoint.Address.MapToIPv6(), remoteEndpoint.Port); 65 | _stream = new NetworkStream(_socket); 66 | _connected = true; 67 | } 68 | 69 | protected override async Task ReceiveMessageAsync(TimeSpan timeout) 70 | { 71 | CancellationTokenSource source = new CancellationTokenSource(timeout); 72 | //Stream.ReadAsync doesn't support CancellationToken 73 | //see: https://stackoverflow.com/questions/20131434/cancel-networkstream-readasync-using-tcplistener 74 | source.Token.Register(() => Disconnect(false)); 75 | try 76 | { 77 | return await Message.DeserializeFromAsync(_stream, source.Token); 78 | } 79 | catch (ArgumentException) { } 80 | catch (ObjectDisposedException) { } 81 | catch (Exception ex) when (ex is FormatException || ex is IOException || ex is OperationCanceledException) 82 | { 83 | Disconnect(false); 84 | } 85 | finally 86 | { 87 | source.Dispose(); 88 | } 89 | return null; 90 | } 91 | 92 | protected override async Task SendMessageAsync(Message message) 93 | { 94 | if (!_connected) throw new InvalidOperationException(); 95 | if (_disposed > 0) return false; 96 | byte[] buffer = message.ToArray(); 97 | CancellationTokenSource source = new CancellationTokenSource(30000); 98 | //Stream.WriteAsync doesn't support CancellationToken 99 | //see: https://stackoverflow.com/questions/20131434/cancel-networkstream-readasync-using-tcplistener 100 | source.Token.Register(() => Disconnect(false)); 101 | try 102 | { 103 | await _stream.WriteAsync(buffer, 0, buffer.Length, source.Token); 104 | return true; 105 | } 106 | catch (ObjectDisposedException) { } 107 | catch (Exception ex) when (ex is IOException || ex is OperationCanceledException) 108 | { 109 | Disconnect(false); 110 | } 111 | finally 112 | { 113 | source.Dispose(); 114 | } 115 | return false; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/DatabaseContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using static Storage.Data.LevelDB.NativeWrapper; 5 | 6 | namespace Storage.Data.LevelDB 7 | { 8 | class DatabaseContext : IDisposable 9 | { 10 | private IntPtr handle; 11 | 12 | /// 13 | /// Return true if haven't got valid handle 14 | /// 15 | public bool IsDisposed => handle == IntPtr.Zero; 16 | 17 | private DatabaseContext(IntPtr handle) 18 | { 19 | this.handle = handle; 20 | } 21 | 22 | public void Dispose() 23 | { 24 | if (handle != IntPtr.Zero) 25 | { 26 | leveldb_close(handle); 27 | handle = IntPtr.Zero; 28 | } 29 | } 30 | 31 | public void Delete(WriteOptions options, Slice key) 32 | { 33 | IntPtr error; 34 | leveldb_delete(handle, options.handle, key.buffer, (UIntPtr)key.buffer.Length, out error); 35 | NativeHelper.CheckError(error); 36 | } 37 | 38 | public Slice Get(ReadOptions options, Slice key) 39 | { 40 | UIntPtr length; 41 | IntPtr error; 42 | IntPtr value = leveldb_get(handle, options.handle, key.buffer, (UIntPtr)key.buffer.Length, out length, out error); 43 | try 44 | { 45 | NativeHelper.CheckError(error); 46 | if (value == IntPtr.Zero) 47 | throw new LevelDbException("not found"); 48 | return new Slice(value, length); 49 | } 50 | finally 51 | { 52 | if (value != IntPtr.Zero) leveldb_free(value); 53 | } 54 | } 55 | 56 | public Snapshot GetSnapshot() 57 | { 58 | return new Snapshot(handle); 59 | } 60 | 61 | public Iterator NewIterator(ReadOptions options) 62 | { 63 | return new Iterator(leveldb_create_iterator(handle, options.handle)); 64 | } 65 | 66 | public static DatabaseContext Open(string name) 67 | { 68 | return Open(name, Options.Default); 69 | } 70 | 71 | public static DatabaseContext Open(string name, Options options) 72 | { 73 | IntPtr error; 74 | IntPtr handle = leveldb_open(options.Handle, name, out error); 75 | NativeHelper.CheckError(error); 76 | return new DatabaseContext(handle); 77 | } 78 | 79 | public void Put(WriteOptions options, Slice key, Slice value) 80 | { 81 | IntPtr error; 82 | leveldb_put(handle, options.handle, key.buffer, (UIntPtr)key.buffer.Length, value.buffer, (UIntPtr)value.buffer.Length, out error); 83 | NativeHelper.CheckError(error); 84 | } 85 | 86 | public bool TryGet(ReadOptions options, Slice key, out Slice value) 87 | { 88 | UIntPtr length; 89 | IntPtr error; 90 | IntPtr v = leveldb_get(handle, options.handle, key.buffer, (UIntPtr)key.buffer.Length, out length, out error); 91 | if (error != IntPtr.Zero) 92 | { 93 | leveldb_free(error); 94 | value = default(Slice); 95 | return false; 96 | } 97 | if (v == IntPtr.Zero) 98 | { 99 | value = default(Slice); 100 | return false; 101 | } 102 | value = new Slice(v, length); 103 | leveldb_free(v); 104 | return true; 105 | } 106 | 107 | public void Write(WriteOptions options, WriteBatch write_batch) 108 | { 109 | // There's a bug in .Net Core. 110 | // When calling DB.Write(), it will throw LevelDbException sometimes. 111 | // But when you try to catch the exception, the bug disappears. 112 | // We shall remove the "try...catch" clause when Microsoft fix the bug. 113 | byte retry = 0; 114 | while (true) 115 | { 116 | try 117 | { 118 | IntPtr error; 119 | leveldb_write(handle, options.handle, write_batch.Handle, out error); 120 | NativeHelper.CheckError(error); 121 | break; 122 | } 123 | catch (LevelDbException ex) 124 | { 125 | if (++retry >= 4) throw; 126 | System.IO.File.AppendAllText("leveldb.log", ex.Message + "\r\n"); 127 | } 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /MedPoints/Storage/Core/Transactions/CoinTransaction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Serialization; 5 | using Storage.Utils; 6 | 7 | namespace Storage.Core.Transactions 8 | { 9 | public class CoinTransaction : ITransaction 10 | { 11 | public string Reciepient { get; private set; } 12 | public decimal Amount { get; private set; } 13 | public string Id { get; set; } 14 | public string Sender { get; set; } 15 | public string Signature { get; set; } 16 | public TransactionType Type => TransactionType.Coins; 17 | 18 | private List Inputs { get; } 19 | public List Outputs { get; } = new List(); 20 | 21 | private static int Sequence { get; set; } 22 | 23 | public CoinTransaction( 24 | string from, 25 | string to, 26 | decimal amount, 27 | List inputs 28 | ) 29 | { 30 | Sender = from; 31 | Reciepient = to; 32 | Amount = amount; 33 | Inputs = inputs; 34 | } 35 | 36 | private string CalculateHash() 37 | { 38 | Sequence++; 39 | return $"{Sender}{Reciepient}{Amount}{Sequence}".GetSha256Hash(); 40 | } 41 | 42 | public void Sign(Wallet wallet) 43 | { 44 | var data = $"{Sender}{Reciepient}{Amount}"; 45 | Signature = wallet.SignMessage(data); 46 | } 47 | 48 | public bool VerifySignature() 49 | { 50 | var data = $"{Sender}{Reciepient}{Amount}"; 51 | return Wallet.VerifyMessage(data, Signature, Sender); 52 | } 53 | 54 | public bool ProcessTransaction(Dictionary utxos) 55 | { 56 | if (!VerifySignature()) 57 | return false; 58 | 59 | foreach (var input in Inputs) 60 | { 61 | input.UTXO = utxos[input.TransactionOutputId]; 62 | } 63 | 64 | var leftOver = GetInputsValue() - Amount; 65 | Id = CalculateHash(); 66 | 67 | Outputs.Add(new TransactionOutput(reciepient: Reciepient, amount: Amount, parentTransactionId: Id)); 68 | Outputs.Add(new TransactionOutput(reciepient: Sender, amount: leftOver, parentTransactionId: Id)); 69 | 70 | foreach (var transactionOutput in Outputs) 71 | { 72 | utxos[transactionOutput.Id] = transactionOutput; 73 | } 74 | 75 | 76 | foreach (var transactionInput in Inputs) 77 | { 78 | if(transactionInput.UTXO == null)continue; 79 | utxos.Remove(transactionInput.UTXO.Id); 80 | } 81 | 82 | return true; 83 | } 84 | 85 | private decimal GetInputsValue() 86 | { 87 | decimal sum = 0; 88 | foreach (var transactionInput in Inputs) 89 | { 90 | if (transactionInput.UTXO == null) continue; 91 | sum += transactionInput.UTXO.Amount; 92 | } 93 | return sum; 94 | } 95 | 96 | public decimal GetOutputsValue() 97 | { 98 | decimal total = 0; 99 | foreach (var output in Outputs) 100 | { 101 | total += output.Amount; 102 | } 103 | return total; 104 | } 105 | 106 | 107 | } 108 | 109 | public class TransactionOutput 110 | { 111 | public string Id { get; } 112 | public string Reciepient { get; } 113 | public decimal Amount { get; } 114 | public string ParentTransactionId { get; } 115 | 116 | public TransactionOutput(string reciepient, decimal amount, string parentTransactionId) 117 | { 118 | Reciepient = reciepient; 119 | Amount = amount; 120 | ParentTransactionId = parentTransactionId; 121 | Id = $"{Reciepient}{Amount}{ParentTransactionId}".GetSha256Hash(); 122 | } 123 | 124 | 125 | public bool IsMine(string publicKey) 126 | { 127 | return publicKey == Reciepient; 128 | } 129 | } 130 | 131 | public class TransactionInput 132 | { 133 | public string TransactionOutputId { get; set; } 134 | 135 | // ReSharper disable once InconsistentNaming 136 | public TransactionOutput UTXO { get; set; } 137 | } 138 | 139 | public static class TransactionExtensions 140 | { 141 | public static string Serialize(this Block block) 142 | { 143 | var serializerSettings = 144 | new JsonSerializerSettings {ContractResolver = new CamelCasePropertyNamesContractResolver()}; 145 | return JsonConvert.SerializeObject(block, serializerSettings); 146 | } 147 | 148 | public static string Serialize(this ITransaction tx) 149 | { 150 | var serializerSettings = 151 | new JsonSerializerSettings {ContractResolver = new CamelCasePropertyNamesContractResolver()}; 152 | return JsonConvert.SerializeObject(tx, serializerSettings); 153 | } 154 | 155 | public static T Deserialize(string data) 156 | { 157 | return JsonConvert.DeserializeObject(data); 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs -------------------------------------------------------------------------------- /MedPoints/Storage/Core/Wallet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using Storage.Core.Transactions; 7 | using Storage.Utils; 8 | 9 | namespace Storage.Core 10 | { 11 | public class Wallet 12 | { 13 | public string PrivateKey { get; } 14 | public string PublicKey { get; } 15 | 16 | private readonly UnicodeEncoding _encoder = new UnicodeEncoding(); 17 | // ReSharper disable once InconsistentNaming 18 | public Dictionary UTXOs { get; set; } = new Dictionary(); 19 | 20 | public Wallet() 21 | { 22 | using (var rsa = RSA.Create()) 23 | { 24 | PrivateKey = rsa.ToJsonString(true); 25 | PublicKey = rsa.ToJsonString(false); 26 | } 27 | } 28 | 29 | public Wallet(string privateKey) 30 | { 31 | PrivateKey = privateKey; 32 | var rsa = new RSACryptoServiceProvider(); 33 | rsa.FromJsonString(privateKey); 34 | PublicKey = rsa.ToJsonString(false); 35 | rsa.Dispose(); 36 | } 37 | 38 | public decimal GetBalance(Dictionary chainUtxos) 39 | { 40 | decimal sum = 0; 41 | foreach (var utxo in chainUtxos) 42 | { 43 | var item = utxo.Value; 44 | if (item.IsMine(PublicKey)) 45 | { 46 | UTXOs[item.Id] = item; 47 | sum += item.Amount; 48 | } 49 | } 50 | return sum; 51 | } 52 | 53 | public CoinTransaction Send(Dictionary chainUtxos, string recipient, decimal amount) 54 | { 55 | if (GetBalance(chainUtxos) < amount) 56 | return null; 57 | 58 | var inputs = new List(); 59 | decimal sum = 0; 60 | foreach (var utxoItem in UTXOs) 61 | { 62 | var utxo = utxoItem.Value; 63 | sum += utxo.Amount; 64 | inputs.Add(new TransactionInput(){TransactionOutputId = utxo.Id}); 65 | if(sum > amount) break; 66 | } 67 | 68 | var tx = new CoinTransaction(PublicKey, recipient, amount, inputs); 69 | tx.Sign(this); 70 | 71 | foreach (var transactionInput in inputs) 72 | { 73 | UTXOs.Remove(transactionInput.TransactionOutputId); 74 | } 75 | 76 | return tx; 77 | } 78 | 79 | public string Decrypt(string data) 80 | { 81 | var rsa = new RSACryptoServiceProvider(); 82 | var dataArray = data.Split(new char[] { ',' }); 83 | byte[] dataByte = new byte[dataArray.Length]; 84 | for (int i = 0; i < dataArray.Length; i++) 85 | { 86 | dataByte[i] = Convert.ToByte(dataArray[i]); 87 | } 88 | 89 | rsa.FromJsonString(PrivateKey); 90 | var decryptedByte = rsa.Decrypt(dataByte, false); 91 | return _encoder.GetString(decryptedByte); 92 | } 93 | 94 | public string Encrypt(string data) 95 | { 96 | var rsa = new RSACryptoServiceProvider(); 97 | rsa.FromJsonString(PublicKey); 98 | var dataToEncrypt = _encoder.GetBytes(data); 99 | var encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray(); 100 | var length = encryptedByteArray.Count(); 101 | var item = 0; 102 | var sb = new StringBuilder(); 103 | foreach (var x in encryptedByteArray) 104 | { 105 | item++; 106 | sb.Append(x); 107 | 108 | if (item < length) 109 | sb.Append(","); 110 | } 111 | 112 | return sb.ToString(); 113 | } 114 | 115 | public string SignMessage(string message) 116 | { 117 | string signedMessage; 118 | try 119 | { 120 | RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048); 121 | //Initiate a new instanse with 2048 bit key size 122 | 123 | rsa.FromJsonString(PrivateKey); 124 | // Load private key 125 | 126 | signedMessage = Convert.ToBase64String(rsa.SignData(Encoding.UTF8.GetBytes(message), CryptoConfig.MapNameToOID("SHA512"))); 127 | //rsa.SignData( buffer, hash algorithm) - For signed data. Here I used SHA512 for hash. 128 | //Encoding.UTF8.GetBytes(string) - convert string to byte messafe 129 | //Convert.ToBase64String(string) - convert back to a string. 130 | } 131 | catch (Exception) 132 | { 133 | signedMessage = String.Empty; 134 | } 135 | 136 | return signedMessage; 137 | } 138 | 139 | public byte[] SignMessageInBytes(string message) 140 | { 141 | byte[] signedMessage; 142 | try 143 | { 144 | RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048); 145 | rsa.FromJsonString(PrivateKey); 146 | signedMessage = rsa.SignData(Encoding.UTF8.GetBytes(message), CryptoConfig.MapNameToOID("SHA512")); 147 | } 148 | catch (Exception) 149 | { 150 | signedMessage = default(byte[]); 151 | } 152 | 153 | return signedMessage; 154 | } 155 | 156 | public static bool VerifyMessage(string originalMessage, string signedMessage, string publicKey) 157 | { 158 | bool verified; 159 | try 160 | { 161 | RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048); 162 | rsa.FromJsonString(publicKey); 163 | // load public key 164 | verified = rsa.VerifyData(Encoding.UTF8.GetBytes(originalMessage), CryptoConfig.MapNameToOID("SHA512"), Convert.FromBase64String(signedMessage)); 165 | } 166 | catch (Exception) 167 | { 168 | verified = false; 169 | } 170 | 171 | return verified; 172 | } 173 | 174 | 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/Message.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.WebSockets; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Storage.Utils; 8 | 9 | namespace Storage.Network 10 | { 11 | public class Message : ISerializable 12 | { 13 | private const int PayloadMaxSize = 0x02000000; 14 | 15 | public static readonly uint Magic = Settings.Default.Magic; 16 | public string Command; 17 | public uint Checksum; 18 | public byte[] Payload; 19 | 20 | public int Size => sizeof(uint) + 12 + sizeof(int) + sizeof(uint) + Payload.Length; 21 | 22 | public static Message Create(string command, ISerializable payload = null) 23 | { 24 | return Create(command, payload == null ? new byte[0] : payload.ToArray()); 25 | } 26 | 27 | public static Message Create(string command, byte[] payload) 28 | { 29 | return new Message 30 | { 31 | Command = command, 32 | Checksum = GetChecksum(payload), 33 | Payload = payload 34 | }; 35 | } 36 | 37 | void ISerializable.Deserialize(BinaryReader reader) 38 | { 39 | if (reader.ReadUInt32() != Magic) 40 | throw new FormatException(); 41 | this.Command = reader.ReadFixedString(12); 42 | uint length = reader.ReadUInt32(); 43 | if (length > PayloadMaxSize) 44 | throw new FormatException(); 45 | this.Checksum = reader.ReadUInt32(); 46 | this.Payload = reader.ReadBytes((int)length); 47 | if (GetChecksum(Payload) != Checksum) 48 | throw new FormatException(); 49 | } 50 | 51 | public static async Task DeserializeFromAsync(Stream stream, CancellationToken cancellationToken) 52 | { 53 | uint payloadLength; 54 | byte[] buffer = await FillBufferAsync(stream, 24, cancellationToken); 55 | Message message = new Message(); 56 | using (MemoryStream ms = new MemoryStream(buffer, false)) 57 | using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) 58 | { 59 | if (reader.ReadUInt32() != Magic) 60 | throw new FormatException(); 61 | message.Command = reader.ReadFixedString(12); 62 | payloadLength = reader.ReadUInt32(); 63 | if (payloadLength > PayloadMaxSize) 64 | throw new FormatException(); 65 | message.Checksum = reader.ReadUInt32(); 66 | } 67 | if (payloadLength > 0) 68 | message.Payload = await FillBufferAsync(stream, (int)payloadLength, cancellationToken); 69 | else 70 | message.Payload = new byte[0]; 71 | if (GetChecksum(message.Payload) != message.Checksum) 72 | throw new FormatException(); 73 | return message; 74 | } 75 | 76 | public static async Task DeserializeFromAsync(WebSocket socket, CancellationToken cancellationToken) 77 | { 78 | uint payloadLength; 79 | byte[] buffer = await FillBufferAsync(socket, 24, cancellationToken); 80 | Message message = new Message(); 81 | using (MemoryStream ms = new MemoryStream(buffer, false)) 82 | using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) 83 | { 84 | if (reader.ReadUInt32() != Magic) 85 | throw new FormatException(); 86 | message.Command = reader.ReadFixedString(12); 87 | payloadLength = reader.ReadUInt32(); 88 | if (payloadLength > PayloadMaxSize) 89 | throw new FormatException(); 90 | message.Checksum = reader.ReadUInt32(); 91 | } 92 | if (payloadLength > 0) 93 | message.Payload = await FillBufferAsync(socket, (int)payloadLength, cancellationToken); 94 | else 95 | message.Payload = new byte[0]; 96 | if (GetChecksum(message.Payload) != message.Checksum) 97 | throw new FormatException(); 98 | return message; 99 | } 100 | 101 | private static async Task FillBufferAsync(Stream stream, int buffer_size, CancellationToken cancellationToken) 102 | { 103 | var maxSize = 1024; 104 | byte[] buffer = new byte[buffer_size < maxSize ? buffer_size : maxSize]; 105 | using (MemoryStream ms = new MemoryStream()) 106 | { 107 | while (buffer_size > 0) 108 | { 109 | int count = buffer_size < maxSize ? buffer_size : maxSize; 110 | count = await stream.ReadAsync(buffer, 0, count, cancellationToken); 111 | if (count <= 0) throw new IOException(); 112 | ms.Write(buffer, 0, count); 113 | buffer_size -= count; 114 | } 115 | return ms.ToArray(); 116 | } 117 | } 118 | 119 | private static async Task FillBufferAsync(WebSocket socket, int buffer_size, CancellationToken cancellationToken) 120 | { 121 | var maxSize = 1024; 122 | byte[] buffer = new byte[buffer_size < maxSize ? buffer_size : maxSize]; 123 | using (MemoryStream ms = new MemoryStream()) 124 | { 125 | while (buffer_size > 0) 126 | { 127 | int count = buffer_size < maxSize ? buffer_size : maxSize; 128 | ArraySegment segment = new ArraySegment(buffer, 0, count); 129 | WebSocketReceiveResult result = await socket.ReceiveAsync(segment, cancellationToken); 130 | if (result.Count <= 0 || result.MessageType != WebSocketMessageType.Binary) 131 | throw new IOException(); 132 | ms.Write(buffer, 0, result.Count); 133 | buffer_size -= result.Count; 134 | } 135 | return ms.ToArray(); 136 | } 137 | } 138 | 139 | private static uint GetChecksum(byte[] value) 140 | { 141 | //return Crypto.Default.Hash256(value).ToUInt32(0); 142 | throw new NotImplementedException(); 143 | } 144 | 145 | void ISerializable.Serialize(BinaryWriter writer) 146 | { 147 | writer.Write(Magic); 148 | writer.WriteFixedString(Command, 12); 149 | writer.Write(Payload.Length); 150 | writer.Write(Checksum); 151 | writer.Write(Payload); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/UPnP.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml; 9 | 10 | namespace Storage.Network 11 | { 12 | // ReSharper disable once InconsistentNaming 13 | public class UPnP 14 | { 15 | private static string _serviceUrl; 16 | 17 | public static TimeSpan TimeOut { get; set; } = TimeSpan.FromSeconds(3); 18 | 19 | public static async Task DiscoverAsync() 20 | { 21 | Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 22 | s.ReceiveTimeout = (int)TimeOut.TotalMilliseconds; 23 | s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); 24 | string req = "M-SEARCH * HTTP/1.1\r\n" + 25 | "HOST: 239.255.255.250:1900\r\n" + 26 | "ST:upnp:rootdevice\r\n" + 27 | "MAN:\"ssdp:discover\"\r\n" + 28 | "MX:3\r\n\r\n"; 29 | byte[] data = Encoding.ASCII.GetBytes(req); 30 | IPEndPoint ipe = new IPEndPoint(IPAddress.Broadcast, 1900); 31 | 32 | DateTime start = DateTime.Now; 33 | 34 | s.SendTo(data, ipe); 35 | s.SendTo(data, ipe); 36 | s.SendTo(data, ipe); 37 | 38 | byte[] buffer = new byte[0x1000]; 39 | do 40 | { 41 | int length; 42 | try 43 | { 44 | length = s.Receive(buffer); 45 | } 46 | catch (SocketException) 47 | { 48 | continue; 49 | } 50 | string resp = Encoding.ASCII.GetString(buffer, 0, length).ToLower(); 51 | if (resp.Contains("upnp:rootdevice")) 52 | { 53 | resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9); 54 | resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); 55 | if (!string.IsNullOrEmpty(_serviceUrl = await GetServiceUrlAsync(resp))) 56 | { 57 | return true; 58 | } 59 | } 60 | } while (DateTime.Now - start < TimeOut); 61 | return false; 62 | } 63 | 64 | private static async Task GetServiceUrlAsync(string resp) 65 | { 66 | try 67 | { 68 | XmlDocument desc = new XmlDocument(); 69 | HttpWebRequest request = WebRequest.CreateHttp(resp); 70 | WebResponse response = await request.GetResponseAsync(); 71 | desc.Load(response.GetResponseStream()); 72 | XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable); 73 | nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); 74 | XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr); 75 | if (!typen.Value.Contains("InternetGatewayDevice")) 76 | return null; 77 | XmlNode node = desc.SelectSingleNode("//tns:service[contains(tns:serviceType,\"WANIPConnection\")]/tns:controlURL/text()", nsMgr); 78 | if (node == null) 79 | return null; 80 | XmlNode eventnode = desc.SelectSingleNode("//tns:service[contains(tns:serviceType,\"WANIPConnection\")]/tns:eventSubURL/text()", nsMgr); 81 | return CombineUrls(resp, node.Value); 82 | } 83 | catch { return null; } 84 | } 85 | 86 | private static string CombineUrls(string resp, string p) 87 | { 88 | int n = resp.IndexOf("://"); 89 | n = resp.IndexOf('/', n + 3); 90 | return resp.Substring(0, n) + p; 91 | } 92 | 93 | public static async Task ForwardPortAsync(int port, ProtocolType protocol, string description) 94 | { 95 | if (string.IsNullOrEmpty(_serviceUrl)) 96 | throw new Exception("No UPnP service available or Discover() has not been called"); 97 | XmlDocument xdoc = await SOAPRequestAsync(_serviceUrl, "" + 98 | "" + port.ToString() + "" + protocol.ToString().ToUpper() + "" + 99 | "" + port.ToString() + "" + (await Dns.GetHostAddressesAsync(Dns.GetHostName())).First(p => p.AddressFamily == AddressFamily.InterNetwork).ToString() + 100 | "1" + description + 101 | "0", "AddPortMapping"); 102 | } 103 | 104 | public static async Task DeleteForwardingRuleAsync(int port, ProtocolType protocol) 105 | { 106 | if (string.IsNullOrEmpty(_serviceUrl)) 107 | throw new Exception("No UPnP service available or Discover() has not been called"); 108 | XmlDocument xdoc = await SOAPRequestAsync(_serviceUrl, 109 | "" + 110 | "" + 111 | "" + 112 | "" + port + "" + 113 | "" + protocol.ToString().ToUpper() + "" + 114 | "", "DeletePortMapping"); 115 | } 116 | 117 | public static async Task GetExternalIPAsync() 118 | { 119 | if (string.IsNullOrEmpty(_serviceUrl)) 120 | throw new Exception("No UPnP service available or Discover() has not been called"); 121 | XmlDocument xdoc = await SOAPRequestAsync(_serviceUrl, "" + 122 | "", "GetExternalIPAddress"); 123 | XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); 124 | nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); 125 | string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value; 126 | return IPAddress.Parse(IP); 127 | } 128 | 129 | private static async Task SOAPRequestAsync(string url, string soap, string function) 130 | { 131 | string req = "" + 132 | "" + 133 | "" + 134 | soap + 135 | "" + 136 | ""; 137 | HttpWebRequest r = WebRequest.CreateHttp(url); 138 | r.Method = "POST"; 139 | byte[] b = Encoding.UTF8.GetBytes(req); 140 | r.Headers["SOAPACTION"] = "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\""; 141 | r.ContentType = "text/xml; charset=\"utf-8\""; 142 | Stream reqs = await r.GetRequestStreamAsync(); 143 | reqs.Write(b, 0, b.Length); 144 | XmlDocument resp = new XmlDocument(); 145 | WebResponse wres = await r.GetResponseAsync(); 146 | Stream ress = wres.GetResponseStream(); 147 | resp.Load(ress); 148 | return resp; 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/Slice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Storage.Data.LevelDB 7 | { 8 | public struct Slice : IComparable, IEquatable 9 | { 10 | internal byte[] buffer; 11 | 12 | internal Slice(IntPtr data, UIntPtr length) 13 | { 14 | buffer = new byte[(int)length]; 15 | Marshal.Copy(data, buffer, 0, (int)length); 16 | } 17 | 18 | public int CompareTo(Slice other) 19 | { 20 | for (int i = 0; i < buffer.Length && i < other.buffer.Length; i++) 21 | { 22 | int r = buffer[i].CompareTo(other.buffer[i]); 23 | if (r != 0) return r; 24 | } 25 | return buffer.Length.CompareTo(other.buffer.Length); 26 | } 27 | 28 | public bool Equals(Slice other) 29 | { 30 | if (buffer.Length != other.buffer.Length) return false; 31 | return buffer.SequenceEqual(other.buffer); 32 | } 33 | 34 | public override bool Equals(object obj) 35 | { 36 | if (ReferenceEquals(null, obj)) return false; 37 | if (!(obj is Slice)) return false; 38 | return Equals((Slice)obj); 39 | } 40 | 41 | public override int GetHashCode() 42 | { 43 | throw new NotImplementedException(); 44 | } 45 | 46 | public byte[] ToArray() 47 | { 48 | return buffer ?? new byte[0]; 49 | } 50 | 51 | public unsafe bool ToBoolean() 52 | { 53 | if (buffer.Length != sizeof(bool)) 54 | throw new InvalidCastException(); 55 | fixed (byte* pbyte = &buffer[0]) 56 | { 57 | return *((bool*)pbyte); 58 | } 59 | } 60 | 61 | public byte ToByte() 62 | { 63 | if (buffer.Length != sizeof(byte)) 64 | throw new InvalidCastException(); 65 | return buffer[0]; 66 | } 67 | 68 | public unsafe double ToDouble() 69 | { 70 | if (buffer.Length != sizeof(double)) 71 | throw new InvalidCastException(); 72 | fixed (byte* pbyte = &buffer[0]) 73 | { 74 | return *((double*)pbyte); 75 | } 76 | } 77 | 78 | public unsafe short ToInt16() 79 | { 80 | if (buffer.Length != sizeof(short)) 81 | throw new InvalidCastException(); 82 | fixed (byte* pbyte = &buffer[0]) 83 | { 84 | return *((short*)pbyte); 85 | } 86 | } 87 | 88 | public unsafe int ToInt32() 89 | { 90 | if (buffer.Length != sizeof(int)) 91 | throw new InvalidCastException(); 92 | fixed (byte* pbyte = &buffer[0]) 93 | { 94 | return *((int*)pbyte); 95 | } 96 | } 97 | 98 | public unsafe long ToInt64() 99 | { 100 | if (buffer.Length != sizeof(long)) 101 | throw new InvalidCastException(); 102 | fixed (byte* pbyte = &buffer[0]) 103 | { 104 | return *((long*)pbyte); 105 | } 106 | } 107 | 108 | public unsafe float ToSingle() 109 | { 110 | if (buffer.Length != sizeof(float)) 111 | throw new InvalidCastException(); 112 | fixed (byte* pbyte = &buffer[0]) 113 | { 114 | return *((float*)pbyte); 115 | } 116 | } 117 | 118 | public override string ToString() 119 | { 120 | return Encoding.UTF8.GetString(buffer); 121 | } 122 | 123 | public unsafe ushort ToUInt16() 124 | { 125 | if (buffer.Length != sizeof(ushort)) 126 | throw new InvalidCastException(); 127 | fixed (byte* pbyte = &buffer[0]) 128 | { 129 | return *((ushort*)pbyte); 130 | } 131 | } 132 | 133 | public unsafe uint ToUInt32(int index = 0) 134 | { 135 | if (buffer.Length != sizeof(uint) + index) 136 | throw new InvalidCastException(); 137 | fixed (byte* pbyte = &buffer[index]) 138 | { 139 | return *((uint*)pbyte); 140 | } 141 | } 142 | 143 | public unsafe 144 | ulong ToUInt64() 145 | { 146 | if (buffer.Length != sizeof(ulong)) 147 | throw new InvalidCastException(); 148 | fixed (byte* pbyte = &buffer[0]) 149 | { 150 | return *((ulong*)pbyte); 151 | } 152 | } 153 | 154 | public static implicit operator Slice(byte[] data) 155 | { 156 | return new Slice { buffer = data }; 157 | } 158 | 159 | public static implicit operator Slice(bool data) 160 | { 161 | return new Slice { buffer = BitConverter.GetBytes(data) }; 162 | } 163 | 164 | public static implicit operator Slice(byte data) 165 | { 166 | return new Slice { buffer = new[] { data } }; 167 | } 168 | 169 | public static implicit operator Slice(double data) 170 | { 171 | return new Slice { buffer = BitConverter.GetBytes(data) }; 172 | } 173 | 174 | public static implicit operator Slice(short data) 175 | { 176 | return new Slice { buffer = BitConverter.GetBytes(data) }; 177 | } 178 | 179 | public static implicit operator Slice(int data) 180 | { 181 | return new Slice { buffer = BitConverter.GetBytes(data) }; 182 | } 183 | 184 | public static implicit operator Slice(long data) 185 | { 186 | return new Slice { buffer = BitConverter.GetBytes(data) }; 187 | } 188 | 189 | public static implicit operator Slice(float data) 190 | { 191 | return new Slice { buffer = BitConverter.GetBytes(data) }; 192 | } 193 | 194 | public static implicit operator Slice(string data) 195 | { 196 | return new Slice { buffer = Encoding.UTF8.GetBytes(data) }; 197 | } 198 | 199 | public static implicit operator Slice(ushort data) 200 | { 201 | return new Slice { buffer = BitConverter.GetBytes(data) }; 202 | } 203 | 204 | public static implicit operator Slice(uint data) 205 | { 206 | return new Slice { buffer = BitConverter.GetBytes(data) }; 207 | } 208 | 209 | public static implicit operator Slice(ulong data) 210 | { 211 | return new Slice { buffer = BitConverter.GetBytes(data) }; 212 | } 213 | 214 | public static bool operator <(Slice x, Slice y) 215 | { 216 | return x.CompareTo(y) < 0; 217 | } 218 | 219 | public static bool operator <=(Slice x, Slice y) 220 | { 221 | return x.CompareTo(y) <= 0; 222 | } 223 | 224 | public static bool operator >(Slice x, Slice y) 225 | { 226 | return x.CompareTo(y) > 0; 227 | } 228 | 229 | public static bool operator >=(Slice x, Slice y) 230 | { 231 | return x.CompareTo(y) >= 0; 232 | } 233 | 234 | public static bool operator ==(Slice x, Slice y) 235 | { 236 | return x.Equals(y); 237 | } 238 | 239 | public static bool operator !=(Slice x, Slice y) 240 | { 241 | return !x.Equals(y); 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/Fixed8.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.IO; 4 | using Storage.Utils; 5 | 6 | namespace Storage.Core 7 | { 8 | /// 9 | /// Accurate to 10^-8 64-bit fixed-point numbers minimize rounding errors. 10 | /// By controlling the accuracy of the multiplier, rounding errors can be completely eliminated. 11 | /// 12 | public struct Fixed8 : IComparable, IEquatable, IFormattable, ISerializable 13 | { 14 | private const long D = 100_000_000; 15 | internal long Value; 16 | 17 | public static readonly Fixed8 MaxValue = new Fixed8 { Value = long.MaxValue }; 18 | 19 | public static readonly Fixed8 MinValue = new Fixed8 { Value = long.MinValue }; 20 | 21 | public static readonly Fixed8 One = new Fixed8 { Value = D }; 22 | 23 | public static readonly Fixed8 Satoshi = new Fixed8 { Value = 1 }; 24 | 25 | public static readonly Fixed8 Zero = default(Fixed8); 26 | 27 | public int Size => sizeof(long); 28 | 29 | public Fixed8(long data) 30 | { 31 | this.Value = data; 32 | } 33 | 34 | public Fixed8 Abs() 35 | { 36 | if (Value >= 0) return this; 37 | return new Fixed8 38 | { 39 | Value = -Value 40 | }; 41 | } 42 | 43 | public Fixed8 Ceiling() 44 | { 45 | long remainder = Value % D; 46 | if (remainder == 0) return this; 47 | if (remainder > 0) 48 | return new Fixed8 49 | { 50 | Value = Value - remainder + D 51 | }; 52 | else 53 | return new Fixed8 54 | { 55 | Value = Value - remainder 56 | }; 57 | } 58 | 59 | public int CompareTo(Fixed8 other) 60 | { 61 | return Value.CompareTo(other.Value); 62 | } 63 | 64 | void ISerializable.Deserialize(BinaryReader reader) 65 | { 66 | Value = reader.ReadInt64(); 67 | } 68 | 69 | public bool Equals(Fixed8 other) 70 | { 71 | return Value.Equals(other.Value); 72 | } 73 | 74 | public override bool Equals(object obj) 75 | { 76 | if (!(obj is Fixed8)) return false; 77 | return Equals((Fixed8)obj); 78 | } 79 | 80 | public static Fixed8 FromDecimal(decimal value) 81 | { 82 | value *= D; 83 | if (value < long.MinValue || value > long.MaxValue) 84 | throw new OverflowException(); 85 | return new Fixed8 86 | { 87 | Value = (long)value 88 | }; 89 | } 90 | 91 | public long GetData() => Value; 92 | 93 | public override int GetHashCode() 94 | { 95 | return Value.GetHashCode(); 96 | } 97 | 98 | public static Fixed8 Max(Fixed8 first, params Fixed8[] others) 99 | { 100 | foreach (Fixed8 other in others) 101 | { 102 | if (first.CompareTo(other) < 0) 103 | first = other; 104 | } 105 | return first; 106 | } 107 | 108 | public static Fixed8 Min(Fixed8 first, params Fixed8[] others) 109 | { 110 | foreach (Fixed8 other in others) 111 | { 112 | if (first.CompareTo(other) > 0) 113 | first = other; 114 | } 115 | return first; 116 | } 117 | 118 | public static Fixed8 Parse(string s) 119 | { 120 | return FromDecimal(decimal.Parse(s, NumberStyles.Float, CultureInfo.InvariantCulture)); 121 | } 122 | 123 | void ISerializable.Serialize(BinaryWriter writer) 124 | { 125 | writer.Write(Value); 126 | } 127 | 128 | public override string ToString() 129 | { 130 | return ((decimal)this).ToString(CultureInfo.InvariantCulture); 131 | } 132 | 133 | public string ToString(string format) 134 | { 135 | return ((decimal)this).ToString(format); 136 | } 137 | 138 | public string ToString(string format, IFormatProvider formatProvider) 139 | { 140 | return ((decimal)this).ToString(format, formatProvider); 141 | } 142 | 143 | public static bool TryParse(string s, out Fixed8 result) 144 | { 145 | decimal d; 146 | if (!decimal.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out d)) 147 | { 148 | result = default(Fixed8); 149 | return false; 150 | } 151 | d *= D; 152 | if (d < long.MinValue || d > long.MaxValue) 153 | { 154 | result = default(Fixed8); 155 | return false; 156 | } 157 | result = new Fixed8 158 | { 159 | Value = (long)d 160 | }; 161 | return true; 162 | } 163 | 164 | public static explicit operator decimal(Fixed8 value) 165 | { 166 | return value.Value / (decimal)D; 167 | } 168 | 169 | public static explicit operator long(Fixed8 value) 170 | { 171 | return value.Value / D; 172 | } 173 | 174 | public static bool operator ==(Fixed8 x, Fixed8 y) 175 | { 176 | return x.Equals(y); 177 | } 178 | 179 | public static bool operator !=(Fixed8 x, Fixed8 y) 180 | { 181 | return !x.Equals(y); 182 | } 183 | 184 | public static bool operator >(Fixed8 x, Fixed8 y) 185 | { 186 | return x.CompareTo(y) > 0; 187 | } 188 | 189 | public static bool operator <(Fixed8 x, Fixed8 y) 190 | { 191 | return x.CompareTo(y) < 0; 192 | } 193 | 194 | public static bool operator >=(Fixed8 x, Fixed8 y) 195 | { 196 | return x.CompareTo(y) >= 0; 197 | } 198 | 199 | public static bool operator <=(Fixed8 x, Fixed8 y) 200 | { 201 | return x.CompareTo(y) <= 0; 202 | } 203 | 204 | public static Fixed8 operator *(Fixed8 x, Fixed8 y) 205 | { 206 | const ulong QUO = (1ul << 63) / (D >> 1); 207 | const ulong REM = ((1ul << 63) % (D >> 1)) << 1; 208 | int sign = Math.Sign(x.Value) * Math.Sign(y.Value); 209 | ulong ux = (ulong)Math.Abs(x.Value); 210 | ulong uy = (ulong)Math.Abs(y.Value); 211 | ulong xh = ux >> 32; 212 | ulong xl = ux & 0x00000000fffffffful; 213 | ulong yh = uy >> 32; 214 | ulong yl = uy & 0x00000000fffffffful; 215 | ulong rh = xh * yh; 216 | ulong rm = xh * yl + xl * yh; 217 | ulong rl = xl * yl; 218 | ulong rmh = rm >> 32; 219 | ulong rml = rm << 32; 220 | rh += rmh; 221 | rl += rml; 222 | if (rl < rml) 223 | ++rh; 224 | if (rh >= D) 225 | throw new OverflowException(); 226 | ulong rd = rh * REM + rl; 227 | if (rd < rl) 228 | ++rh; 229 | ulong r = rh * QUO + rd / D; 230 | x.Value = (long)r * sign; 231 | return x; 232 | } 233 | 234 | public static Fixed8 operator *(Fixed8 x, long y) 235 | { 236 | x.Value *= y; 237 | return x; 238 | } 239 | 240 | public static Fixed8 operator /(Fixed8 x, long y) 241 | { 242 | x.Value /= y; 243 | return x; 244 | } 245 | 246 | public static Fixed8 operator +(Fixed8 x, Fixed8 y) 247 | { 248 | x.Value = checked(x.Value + y.Value); 249 | return x; 250 | } 251 | 252 | public static Fixed8 operator -(Fixed8 x, Fixed8 y) 253 | { 254 | x.Value = checked(x.Value - y.Value); 255 | return x; 256 | } 257 | 258 | public static Fixed8 operator -(Fixed8 value) 259 | { 260 | value.Value = -value.Value; 261 | return value; 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /MedPoints/Storage.Utils/Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | using System.Runtime.Serialization; 8 | using System.Text; 9 | 10 | namespace Storage.Utils 11 | { 12 | public static class Helper 13 | { 14 | public static T AsSerializable(this byte[] value, int start = 0) where T : ISerializable, new() 15 | { 16 | using (MemoryStream ms = new MemoryStream(value, start, value.Length - start, false)) 17 | using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) 18 | { 19 | return reader.ReadSerializable(); 20 | } 21 | } 22 | 23 | public static ISerializable AsSerializable(this byte[] value, Type type) 24 | { 25 | if (!typeof(ISerializable).GetTypeInfo().IsAssignableFrom(type)) 26 | throw new InvalidCastException(); 27 | ISerializable serializable = (ISerializable)Activator.CreateInstance(type); 28 | using (MemoryStream ms = new MemoryStream(value, false)) 29 | using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) 30 | { 31 | serializable.Deserialize(reader); 32 | } 33 | return serializable; 34 | } 35 | 36 | public static T[] AsSerializableArray(this byte[] value, int max = 0x10000000) where T : ISerializable, new() 37 | { 38 | using (MemoryStream ms = new MemoryStream(value, false)) 39 | using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) 40 | { 41 | return reader.ReadSerializableArray(max); 42 | } 43 | } 44 | 45 | public static int GetVarSize(int value) 46 | { 47 | if (value < 0xFD) 48 | return sizeof(byte); 49 | else if (value <= 0xFFFF) 50 | return sizeof(byte) + sizeof(ushort); 51 | else 52 | return sizeof(byte) + sizeof(uint); 53 | } 54 | 55 | public static int GetVarSize(this T[] value) 56 | { 57 | int value_size; 58 | Type t = typeof(T); 59 | if (typeof(ISerializable).IsAssignableFrom(t)) 60 | { 61 | value_size = value.OfType().Sum(p => p.Size); 62 | } 63 | else if (t.GetTypeInfo().IsEnum) 64 | { 65 | int element_size; 66 | Type u = t.GetTypeInfo().GetEnumUnderlyingType(); 67 | if (u == typeof(sbyte) || u == typeof(byte)) 68 | element_size = 1; 69 | else if (u == typeof(short) || u == typeof(ushort)) 70 | element_size = 2; 71 | else if (u == typeof(int) || u == typeof(uint)) 72 | element_size = 4; 73 | else //if (u == typeof(long) || u == typeof(ulong)) 74 | element_size = 8; 75 | value_size = value.Length * element_size; 76 | } 77 | else 78 | { 79 | value_size = value.Length * Marshal.SizeOf(); 80 | } 81 | return GetVarSize(value.Length) + value_size; 82 | } 83 | 84 | internal static int GetVarSize(this string value) 85 | { 86 | int size = Encoding.UTF8.GetByteCount(value); 87 | return GetVarSize(size) + size; 88 | } 89 | 90 | public static byte[] ReadBytesWithGrouping(this BinaryReader reader) 91 | { 92 | const int GROUP_SIZE = 16; 93 | using (MemoryStream ms = new MemoryStream()) 94 | { 95 | int padding = 0; 96 | do 97 | { 98 | byte[] group = reader.ReadBytes(GROUP_SIZE); 99 | padding = reader.ReadByte(); 100 | if (padding > GROUP_SIZE) 101 | throw new FormatException(); 102 | int count = GROUP_SIZE - padding; 103 | if (count > 0) 104 | ms.Write(group, 0, count); 105 | } while (padding == 0); 106 | return ms.ToArray(); 107 | } 108 | } 109 | 110 | public static string ReadFixedString(this BinaryReader reader, int length) 111 | { 112 | byte[] data = reader.ReadBytes(length); 113 | return Encoding.UTF8.GetString(data.TakeWhile(p => p != 0).ToArray()); 114 | } 115 | 116 | public static T ReadSerializable(this BinaryReader reader) where T : ISerializable, new() 117 | { 118 | T obj = new T(); 119 | obj.Deserialize(reader); 120 | return obj; 121 | } 122 | 123 | public static T[] ReadSerializableArray(this BinaryReader reader, int max = 0x10000000) where T : ISerializable, new() 124 | { 125 | T[] array = new T[reader.ReadVarInt((ulong)max)]; 126 | for (int i = 0; i < array.Length; i++) 127 | { 128 | array[i] = new T(); 129 | array[i].Deserialize(reader); 130 | } 131 | return array; 132 | } 133 | 134 | public static byte[] ReadVarBytes(this BinaryReader reader, int max = 0X7fffffc7) 135 | { 136 | return reader.ReadBytes((int)reader.ReadVarInt((ulong)max)); 137 | } 138 | 139 | public static ulong ReadVarInt(this BinaryReader reader, ulong max = ulong.MaxValue) 140 | { 141 | byte fb = reader.ReadByte(); 142 | ulong value; 143 | if (fb == 0xFD) 144 | value = reader.ReadUInt16(); 145 | else if (fb == 0xFE) 146 | value = reader.ReadUInt32(); 147 | else if (fb == 0xFF) 148 | value = reader.ReadUInt64(); 149 | else 150 | value = fb; 151 | if (value > max) throw new FormatException(); 152 | return value; 153 | } 154 | 155 | public static string ReadVarString(this BinaryReader reader, int max = 0X7fffffc7) 156 | { 157 | return Encoding.UTF8.GetString(reader.ReadVarBytes(max)); 158 | } 159 | 160 | public static byte[] ToArray(this ISerializable value) 161 | { 162 | using (MemoryStream ms = new MemoryStream()) 163 | using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) 164 | { 165 | value.Serialize(writer); 166 | writer.Flush(); 167 | return ms.ToArray(); 168 | } 169 | } 170 | 171 | public static byte[] ToByteArray(this T[] value) where T : ISerializable 172 | { 173 | using (MemoryStream ms = new MemoryStream()) 174 | using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) 175 | { 176 | writer.Write(value); 177 | writer.Flush(); 178 | return ms.ToArray(); 179 | } 180 | } 181 | 182 | public static void Write(this BinaryWriter writer, ISerializable value) 183 | { 184 | value.Serialize(writer); 185 | } 186 | 187 | public static void Write(this BinaryWriter writer, T[] value) where T : ISerializable 188 | { 189 | writer.WriteVarInt(value.Length); 190 | for (int i = 0; i < value.Length; i++) 191 | { 192 | value[i].Serialize(writer); 193 | } 194 | } 195 | 196 | public static void WriteBytesWithGrouping(this BinaryWriter writer, byte[] value) 197 | { 198 | const int GROUP_SIZE = 16; 199 | int index = 0; 200 | int remain = value.Length; 201 | while (remain >= GROUP_SIZE) 202 | { 203 | writer.Write(value, index, GROUP_SIZE); 204 | writer.Write((byte)0); 205 | index += GROUP_SIZE; 206 | remain -= GROUP_SIZE; 207 | } 208 | if (remain > 0) 209 | writer.Write(value, index, remain); 210 | int padding = GROUP_SIZE - remain; 211 | for (int i = 0; i < padding; i++) 212 | writer.Write((byte)0); 213 | writer.Write((byte)padding); 214 | } 215 | 216 | public static void WriteFixedString(this BinaryWriter writer, string value, int length) 217 | { 218 | if (value == null) 219 | throw new ArgumentNullException(nameof(value)); 220 | if (value.Length > length) 221 | throw new ArgumentException(); 222 | byte[] bytes = Encoding.UTF8.GetBytes(value); 223 | if (bytes.Length > length) 224 | throw new ArgumentException(); 225 | writer.Write(bytes); 226 | if (bytes.Length < length) 227 | writer.Write(new byte[length - bytes.Length]); 228 | } 229 | 230 | public static void WriteVarBytes(this BinaryWriter writer, byte[] value) 231 | { 232 | writer.WriteVarInt(value.Length); 233 | writer.Write(value); 234 | } 235 | 236 | public static void WriteVarInt(this BinaryWriter writer, long value) 237 | { 238 | if (value < 0) 239 | throw new ArgumentOutOfRangeException(); 240 | if (value < 0xFD) 241 | { 242 | writer.Write((byte)value); 243 | } 244 | else if (value <= 0xFFFF) 245 | { 246 | writer.Write((byte)0xFD); 247 | writer.Write((ushort)value); 248 | } 249 | else if (value <= 0xFFFFFFFF) 250 | { 251 | writer.Write((byte)0xFE); 252 | writer.Write((uint)value); 253 | } 254 | else 255 | { 256 | writer.Write((byte)0xFF); 257 | writer.Write(value); 258 | } 259 | } 260 | 261 | public static void WriteVarString(this BinaryWriter writer, string value) 262 | { 263 | writer.WriteVarBytes(Encoding.UTF8.GetBytes(value)); 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Numerics; 6 | using System.Runtime.CompilerServices; 7 | using System.Security.Cryptography; 8 | using System.Text; 9 | 10 | namespace Storage.Core 11 | { 12 | public static class Helper 13 | { 14 | private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 15 | 16 | private static int BitLen(int w) 17 | { 18 | return (w < 1 << 15 ? (w < 1 << 7 19 | ? (w < 1 << 3 ? (w < 1 << 1 20 | ? (w < 1 << 0 ? (w < 0 ? 32 : 0) : 1) 21 | : (w < 1 << 2 ? 2 : 3)) : (w < 1 << 5 22 | ? (w < 1 << 4 ? 4 : 5) 23 | : (w < 1 << 6 ? 6 : 7))) 24 | : (w < 1 << 11 25 | ? (w < 1 << 9 ? (w < 1 << 8 ? 8 : 9) : (w < 1 << 10 ? 10 : 11)) 26 | : (w < 1 << 13 ? (w < 1 << 12 ? 12 : 13) : (w < 1 << 14 ? 14 : 15)))) : (w < 1 << 23 ? (w < 1 << 19 27 | ? (w < 1 << 17 ? (w < 1 << 16 ? 16 : 17) : (w < 1 << 18 ? 18 : 19)) 28 | : (w < 1 << 21 ? (w < 1 << 20 ? 20 : 21) : (w < 1 << 22 ? 22 : 23))) : (w < 1 << 27 29 | ? (w < 1 << 25 ? (w < 1 << 24 ? 24 : 25) : (w < 1 << 26 ? 26 : 27)) 30 | : (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31))))); 31 | } 32 | 33 | internal static int GetBitLength(this BigInteger i) 34 | { 35 | byte[] b = i.ToByteArray(); 36 | return (b.Length - 1) * 8 + BitLen(i.Sign > 0 ? b[b.Length - 1] : 255 - b[b.Length - 1]); 37 | } 38 | 39 | internal static int GetLowestSetBit(this BigInteger i) 40 | { 41 | if (i.Sign == 0) 42 | return -1; 43 | byte[] b = i.ToByteArray(); 44 | int w = 0; 45 | while (b[w] == 0) 46 | w++; 47 | for (int x = 0; x < 8; x++) 48 | if ((b[w] & 1 << x) > 0) 49 | return x + w * 8; 50 | throw new Exception(); 51 | } 52 | 53 | public static byte[] HexToBytes(this string value) 54 | { 55 | if (string.IsNullOrEmpty(value)) 56 | return new byte[0]; 57 | if (value.Length % 2 == 1) 58 | throw new FormatException(); 59 | byte[] result = new byte[value.Length / 2]; 60 | for (int i = 0; i < result.Length; i++) 61 | result[i] = byte.Parse(value.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier); 62 | return result; 63 | } 64 | 65 | internal static BigInteger Mod(this BigInteger x, BigInteger y) 66 | { 67 | x %= y; 68 | if (x.Sign < 0) 69 | x += y; 70 | return x; 71 | } 72 | 73 | internal static BigInteger ModInverse(this BigInteger a, BigInteger n) 74 | { 75 | BigInteger i = n, v = 0, d = 1; 76 | while (a > 0) 77 | { 78 | BigInteger t = i / a, x = a; 79 | a = i % x; 80 | i = x; 81 | x = d; 82 | d = v - t * x; 83 | v = x; 84 | } 85 | v %= n; 86 | if (v < 0) v = (v + n) % n; 87 | return v; 88 | } 89 | 90 | internal static BigInteger NextBigInteger(this Random rand, int sizeInBits) 91 | { 92 | if (sizeInBits < 0) 93 | throw new ArgumentException("sizeInBits must be non-negative"); 94 | if (sizeInBits == 0) 95 | return 0; 96 | byte[] b = new byte[sizeInBits / 8 + 1]; 97 | rand.NextBytes(b); 98 | if (sizeInBits % 8 == 0) 99 | b[b.Length - 1] = 0; 100 | else 101 | b[b.Length - 1] &= (byte)((1 << sizeInBits % 8) - 1); 102 | return new BigInteger(b); 103 | } 104 | 105 | internal static BigInteger NextBigInteger(this RandomNumberGenerator rng, int sizeInBits) 106 | { 107 | if (sizeInBits < 0) 108 | throw new ArgumentException("sizeInBits must be non-negative"); 109 | if (sizeInBits == 0) 110 | return 0; 111 | byte[] b = new byte[sizeInBits / 8 + 1]; 112 | rng.GetBytes(b); 113 | if (sizeInBits % 8 == 0) 114 | b[b.Length - 1] = 0; 115 | else 116 | b[b.Length - 1] &= (byte)((1 << sizeInBits % 8) - 1); 117 | return new BigInteger(b); 118 | } 119 | 120 | public static Fixed8 Sum(this IEnumerable source) 121 | { 122 | long sum = 0; 123 | checked 124 | { 125 | foreach (Fixed8 item in source) 126 | { 127 | sum += item.Value; 128 | } 129 | } 130 | return new Fixed8(sum); 131 | } 132 | 133 | public static Fixed8 Sum(this IEnumerable source, Func selector) 134 | { 135 | return source.Select(selector).Sum(); 136 | } 137 | 138 | internal static bool TestBit(this BigInteger i, int index) 139 | { 140 | return (i & (BigInteger.One << index)) > BigInteger.Zero; 141 | } 142 | 143 | public static DateTime ToDateTime(this uint timestamp) 144 | { 145 | return UnixEpoch.AddSeconds(timestamp).ToLocalTime(); 146 | } 147 | 148 | public static DateTime ToDateTime(this ulong timestamp) 149 | { 150 | return UnixEpoch.AddSeconds(timestamp).ToLocalTime(); 151 | } 152 | 153 | public static string ToHexString(this IEnumerable value) 154 | { 155 | StringBuilder sb = new StringBuilder(); 156 | foreach (byte b in value) 157 | sb.AppendFormat("{0:x2}", b); 158 | return sb.ToString(); 159 | } 160 | 161 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 162 | internal static unsafe int ToInt32(this byte[] value, int startIndex) 163 | { 164 | fixed (byte* pbyte = &value[startIndex]) 165 | { 166 | return *((int*)pbyte); 167 | } 168 | } 169 | 170 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 171 | internal static unsafe long ToInt64(this byte[] value, int startIndex) 172 | { 173 | fixed (byte* pbyte = &value[startIndex]) 174 | { 175 | return *((long*)pbyte); 176 | } 177 | } 178 | 179 | public static uint ToTimestamp(this DateTime time) 180 | { 181 | return (uint)(time.ToUniversalTime() - UnixEpoch).TotalSeconds; 182 | } 183 | 184 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 185 | unsafe internal static ushort ToUInt16(this byte[] value, int startIndex) 186 | { 187 | fixed (byte* pbyte = &value[startIndex]) 188 | { 189 | return *((ushort*)pbyte); 190 | } 191 | } 192 | 193 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 194 | internal static unsafe uint ToUInt32(this byte[] value, int startIndex) 195 | { 196 | fixed (byte* pbyte = &value[startIndex]) 197 | { 198 | return *((uint*)pbyte); 199 | } 200 | } 201 | 202 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 203 | internal static unsafe ulong ToUInt64(this byte[] value, int startIndex) 204 | { 205 | fixed (byte* pbyte = &value[startIndex]) 206 | { 207 | return *((ulong*)pbyte); 208 | } 209 | } 210 | 211 | internal static long WeightedAverage(this IEnumerable source, Func valueSelector, Func weightSelector) 212 | { 213 | long sum_weight = 0; 214 | long sum_value = 0; 215 | foreach (T item in source) 216 | { 217 | long weight = weightSelector(item); 218 | sum_weight += weight; 219 | sum_value += valueSelector(item) * weight; 220 | } 221 | if (sum_value == 0) return 0; 222 | return sum_value / sum_weight; 223 | } 224 | 225 | internal static IEnumerable WeightedFilter(this IList source, double start, double end, Func weightSelector, Func resultSelector) 226 | { 227 | if (source == null) throw new ArgumentNullException(nameof(source)); 228 | if (start < 0 || start > 1) throw new ArgumentOutOfRangeException(nameof(start)); 229 | if (end < start || start + end > 1) throw new ArgumentOutOfRangeException(nameof(end)); 230 | if (weightSelector == null) throw new ArgumentNullException(nameof(weightSelector)); 231 | if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); 232 | if (source.Count == 0 || start == end) yield break; 233 | double amount = source.Sum(weightSelector); 234 | long sum = 0; 235 | double current = 0; 236 | foreach (T item in source) 237 | { 238 | if (current >= end) break; 239 | long weight = weightSelector(item); 240 | sum += weight; 241 | double old = current; 242 | current = sum / amount; 243 | if (current <= start) continue; 244 | if (old < start) 245 | { 246 | if (current > end) 247 | { 248 | weight = (long)((end - start) * amount); 249 | } 250 | else 251 | { 252 | weight = (long)((current - start) * amount); 253 | } 254 | } 255 | else if (current > end) 256 | { 257 | weight = (long)((end - old) * amount); 258 | } 259 | yield return resultSelector(item, weight); 260 | } 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /MedPoints/Storage.Data/LevelDB/NativeWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Storage.Data.LevelDB 5 | { 6 | public enum CompressionType : byte 7 | { 8 | kNoCompression = 0x0, 9 | kSnappyCompression = 0x1 10 | } 11 | 12 | public static class NativeWrapper 13 | { 14 | #region Logger 15 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 16 | public static extern IntPtr leveldb_logger_create(IntPtr /* Action */ logger); 17 | 18 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 19 | public static extern void leveldb_logger_destroy(IntPtr /* logger*/ option); 20 | #endregion 21 | 22 | #region DB 23 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 24 | public static extern IntPtr leveldb_open(IntPtr /* Options*/ options, string name, out IntPtr error); 25 | 26 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 27 | public static extern void leveldb_close(IntPtr /*DB */ db); 28 | 29 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 30 | public static extern void leveldb_put(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen, out IntPtr errptr); 31 | 32 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 33 | public static extern void leveldb_delete(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, UIntPtr keylen, out IntPtr errptr); 34 | 35 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 36 | public static extern void leveldb_write(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, IntPtr /* WriteBatch */ batch, out IntPtr errptr); 37 | 38 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 39 | public static extern IntPtr leveldb_get(IntPtr /* DB */ db, IntPtr /* ReadOptions*/ options, byte[] key, UIntPtr keylen, out UIntPtr vallen, out IntPtr errptr); 40 | 41 | //[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 42 | //static extern void leveldb_approximate_sizes(IntPtr /* DB */ db, int num_ranges, byte[] range_start_key, long range_start_key_len, byte[] range_limit_key, long range_limit_key_len, out long sizes); 43 | 44 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 45 | public static extern IntPtr leveldb_create_iterator(IntPtr /* DB */ db, IntPtr /* ReadOption */ options); 46 | 47 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 48 | public static extern IntPtr leveldb_create_snapshot(IntPtr /* DB */ db); 49 | 50 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 51 | public static extern void leveldb_release_snapshot(IntPtr /* DB */ db, IntPtr /* SnapShot*/ snapshot); 52 | 53 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 54 | public static extern IntPtr leveldb_property_value(IntPtr /* DB */ db, string propname); 55 | 56 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 57 | public static extern void leveldb_repair_db(IntPtr /* Options*/ options, string name, out IntPtr error); 58 | 59 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 60 | public static extern void leveldb_destroy_db(IntPtr /* Options*/ options, string name, out IntPtr error); 61 | 62 | #region extensions 63 | 64 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 65 | public static extern void leveldb_free(IntPtr /* void */ ptr); 66 | 67 | #endregion 68 | 69 | 70 | #endregion 71 | 72 | #region Env 73 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 74 | public static extern IntPtr leveldb_create_default_env(); 75 | 76 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 77 | public static extern void leveldb_env_destroy(IntPtr /*Env*/ cache); 78 | #endregion 79 | 80 | #region Iterator 81 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 82 | public static extern void leveldb_iter_destroy(IntPtr /*Iterator*/ iterator); 83 | 84 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 85 | [return: MarshalAs(UnmanagedType.U1)] 86 | public static extern bool leveldb_iter_valid(IntPtr /*Iterator*/ iterator); 87 | 88 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 89 | public static extern void leveldb_iter_seek_to_first(IntPtr /*Iterator*/ iterator); 90 | 91 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 92 | public static extern void leveldb_iter_seek_to_last(IntPtr /*Iterator*/ iterator); 93 | 94 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 95 | public static extern void leveldb_iter_seek(IntPtr /*Iterator*/ iterator, byte[] key, UIntPtr length); 96 | 97 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 98 | public static extern void leveldb_iter_next(IntPtr /*Iterator*/ iterator); 99 | 100 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 101 | public static extern void leveldb_iter_prev(IntPtr /*Iterator*/ iterator); 102 | 103 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 104 | public static extern IntPtr leveldb_iter_key(IntPtr /*Iterator*/ iterator, out UIntPtr length); 105 | 106 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 107 | public static extern IntPtr leveldb_iter_value(IntPtr /*Iterator*/ iterator, out UIntPtr length); 108 | 109 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 110 | public static extern void leveldb_iter_get_error(IntPtr /*Iterator*/ iterator, out IntPtr error); 111 | #endregion 112 | 113 | #region Options 114 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 115 | public static extern IntPtr leveldb_options_create(); 116 | 117 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 118 | public static extern void leveldb_options_destroy(IntPtr /*Options*/ options); 119 | 120 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 121 | public static extern void leveldb_options_set_create_if_missing(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); 122 | 123 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 124 | public static extern void leveldb_options_set_error_if_exists(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); 125 | 126 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 127 | public static extern void leveldb_options_set_info_log(IntPtr /*Options*/ options, IntPtr /* Logger */ logger); 128 | 129 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 130 | public static extern void leveldb_options_set_paranoid_checks(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); 131 | 132 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 133 | public static extern void leveldb_options_set_env(IntPtr /*Options*/ options, IntPtr /*Env*/ env); 134 | 135 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 136 | public static extern void leveldb_options_set_write_buffer_size(IntPtr /*Options*/ options, UIntPtr size); 137 | 138 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 139 | public static extern void leveldb_options_set_max_open_files(IntPtr /*Options*/ options, int max); 140 | 141 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 142 | public static extern void leveldb_options_set_cache(IntPtr /*Options*/ options, IntPtr /*Cache*/ cache); 143 | 144 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 145 | public static extern void leveldb_options_set_block_size(IntPtr /*Options*/ options, UIntPtr size); 146 | 147 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 148 | public static extern void leveldb_options_set_block_restart_interval(IntPtr /*Options*/ options, int interval); 149 | 150 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 151 | public static extern void leveldb_options_set_compression(IntPtr /*Options*/ options, CompressionType level); 152 | 153 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 154 | public static extern void leveldb_options_set_comparator(IntPtr /*Options*/ options, IntPtr /*Comparator*/ comparer); 155 | 156 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 157 | public static extern void leveldb_options_set_filter_policy(IntPtr /*Options*/ options, IntPtr /*FilterPolicy*/ policy); 158 | #endregion 159 | 160 | #region ReadOptions 161 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 162 | public static extern IntPtr leveldb_readoptions_create(); 163 | 164 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 165 | public static extern void leveldb_readoptions_destroy(IntPtr /*ReadOptions*/ options); 166 | 167 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 168 | public static extern void leveldb_readoptions_set_verify_checksums(IntPtr /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); 169 | 170 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 171 | public static extern void leveldb_readoptions_set_fill_cache(IntPtr /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); 172 | 173 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 174 | public static extern void leveldb_readoptions_set_snapshot(IntPtr /*ReadOptions*/ options, IntPtr /*SnapShot*/ snapshot); 175 | #endregion 176 | 177 | #region WriteBatch 178 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 179 | public static extern IntPtr leveldb_writebatch_create(); 180 | 181 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 182 | public static extern void leveldb_writebatch_destroy(IntPtr /* WriteBatch */ batch); 183 | 184 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 185 | public static extern void leveldb_writebatch_clear(IntPtr /* WriteBatch */ batch); 186 | 187 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 188 | public static extern void leveldb_writebatch_put(IntPtr /* WriteBatch */ batch, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen); 189 | 190 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 191 | public static extern void leveldb_writebatch_delete(IntPtr /* WriteBatch */ batch, byte[] key, UIntPtr keylen); 192 | 193 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 194 | public static extern void leveldb_writebatch_iterate(IntPtr /* WriteBatch */ batch, object state, Action put, Action deleted); 195 | #endregion 196 | 197 | #region WriteOptions 198 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 199 | public static extern IntPtr leveldb_writeoptions_create(); 200 | 201 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 202 | public static extern void leveldb_writeoptions_destroy(IntPtr /*WriteOptions*/ options); 203 | 204 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 205 | public static extern void leveldb_writeoptions_set_sync(IntPtr /*WriteOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); 206 | #endregion 207 | 208 | #region Cache 209 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 210 | public static extern IntPtr leveldb_cache_create_lru(int capacity); 211 | 212 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 213 | public static extern void leveldb_cache_destroy(IntPtr /*Cache*/ cache); 214 | #endregion 215 | 216 | #region Comparator 217 | 218 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 219 | public static extern IntPtr /* leveldb_comparator_t* */ 220 | leveldb_comparator_create( 221 | IntPtr /* void* */ state, 222 | IntPtr /* void (*)(void*) */ destructor, 223 | IntPtr 224 | /* int (*compare)(void*, 225 | const char* a, size_t alen, 226 | const char* b, size_t blen) */ 227 | compare, 228 | IntPtr /* const char* (*)(void*) */ name); 229 | 230 | [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 231 | public static extern void leveldb_comparator_destroy(IntPtr /* leveldb_comparator_t* */ cmp); 232 | 233 | #endregion 234 | } 235 | 236 | internal static class NativeHelper 237 | { 238 | public static void CheckError(IntPtr error) 239 | { 240 | if (error != IntPtr.Zero) 241 | { 242 | string message = Marshal.PtrToStringAnsi(error); 243 | NativeWrapper.leveldb_free(error); 244 | throw new LevelDbException(message); 245 | } 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /MedPoints/Storage.Core/Transaction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Storage.Core 6 | { 7 | public abstract class Transaction : IEquatable, IInventory 8 | { 9 | /// 10 | /// Maximum number of attributes that can be contained within a transaction 11 | /// 12 | private const int MaxTransactionAttributes = 16; 13 | 14 | /// 15 | /// Reflection cache for TransactionType 16 | /// 17 | private static ReflectionCache ReflectionCache = ReflectionCache.CreateFromEnum(); 18 | 19 | /// 20 | /// 交易类型 21 | /// 22 | public readonly TransactionType Type; 23 | /// 24 | /// 版本 25 | /// 26 | public byte Version; 27 | /// 28 | /// 该交易所具备的额外特性 29 | /// 30 | public TransactionAttribute[] Attributes; 31 | /// 32 | /// 输入列表 33 | /// 34 | public CoinReference[] Inputs; 35 | /// 36 | /// 输出列表 37 | /// 38 | public TransactionOutput[] Outputs; 39 | /// 40 | /// 用于验证该交易的脚本列表 41 | /// 42 | public Witness[] Scripts { get; set; } 43 | 44 | private UInt256 _hash = null; 45 | public UInt256 Hash 46 | { 47 | get 48 | { 49 | if (_hash == null) 50 | { 51 | _hash = new UInt256(Crypto.Default.Hash256(this.GetHashData())); 52 | } 53 | return _hash; 54 | } 55 | } 56 | 57 | /// 58 | /// 清单类型 59 | /// 60 | InventoryType IInventory.InventoryType => InventoryType.TX; 61 | 62 | private Fixed8 _network_fee = -Fixed8.Satoshi; 63 | public virtual Fixed8 NetworkFee 64 | { 65 | get 66 | { 67 | if (_network_fee == -Fixed8.Satoshi) 68 | { 69 | Fixed8 input = References.Values.Where(p => p.AssetId.Equals(Blockchain.UtilityToken.Hash)).Sum(p => p.Value); 70 | Fixed8 output = Outputs.Where(p => p.AssetId.Equals(Blockchain.UtilityToken.Hash)).Sum(p => p.Value); 71 | _network_fee = input - output - SystemFee; 72 | } 73 | return _network_fee; 74 | } 75 | } 76 | 77 | private IReadOnlyDictionary _references; 78 | /// 79 | /// 每一个交易输入所引用的交易输出 80 | /// 81 | public IReadOnlyDictionary References 82 | { 83 | get 84 | { 85 | if (_references == null) 86 | { 87 | Dictionary dictionary = new Dictionary(); 88 | foreach (var group in Inputs.GroupBy(p => p.PrevHash)) 89 | { 90 | Transaction tx = Blockchain.Default.GetTransaction(group.Key); 91 | if (tx == null) return null; 92 | foreach (var reference in group.Select(p => new 93 | { 94 | Input = p, 95 | Output = tx.Outputs[p.PrevIndex] 96 | })) 97 | { 98 | dictionary.Add(reference.Input, reference.Output); 99 | } 100 | } 101 | _references = dictionary; 102 | } 103 | return _references; 104 | } 105 | } 106 | 107 | public virtual int Size => sizeof(TransactionType) + sizeof(byte) + Attributes.GetVarSize() + Inputs.GetVarSize() + Outputs.GetVarSize() + Scripts.GetVarSize(); 108 | 109 | /// 110 | /// 系统费用 111 | /// 112 | public virtual Fixed8 SystemFee => Settings.Default.SystemFee.TryGetValue(Type, out Fixed8 fee) ? fee : Fixed8.Zero; 113 | 114 | /// 115 | /// 用指定的类型初始化Transaction对象 116 | /// 117 | /// 交易类型 118 | protected Transaction(TransactionType type) 119 | { 120 | this.Type = type; 121 | } 122 | 123 | /// 124 | /// 反序列化 125 | /// 126 | /// 数据来源 127 | void ISerializable.Deserialize(BinaryReader reader) 128 | { 129 | ((IVerifiable)this).DeserializeUnsigned(reader); 130 | Scripts = reader.ReadSerializableArray(); 131 | OnDeserialized(); 132 | } 133 | 134 | /// 135 | /// 反序列化交易中的额外数据 136 | /// 137 | /// 数据来源 138 | protected virtual void DeserializeExclusiveData(BinaryReader reader) 139 | { 140 | } 141 | 142 | /// 143 | /// 从指定的字节数组反序列化一笔交易 144 | /// 145 | /// 字节数组 146 | /// 偏移量,反序列化从该偏移量处开始 147 | /// 返回反序列化后的结果 148 | public static Transaction DeserializeFrom(byte[] value, int offset = 0) 149 | { 150 | using (MemoryStream ms = new MemoryStream(value, offset, value.Length - offset, false)) 151 | using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) 152 | { 153 | return DeserializeFrom(reader); 154 | } 155 | } 156 | 157 | /// 158 | /// 反序列化 159 | /// 160 | /// 数据来源 161 | /// 返回反序列化后的结果 162 | internal static Transaction DeserializeFrom(BinaryReader reader) 163 | { 164 | // Looking for type in reflection cache 165 | Transaction transaction = ReflectionCache.CreateInstance(reader.ReadByte()); 166 | if (transaction == null) throw new FormatException(); 167 | 168 | transaction.DeserializeUnsignedWithoutType(reader); 169 | transaction.Scripts = reader.ReadSerializableArray(); 170 | transaction.OnDeserialized(); 171 | return transaction; 172 | } 173 | 174 | void IVerifiable.DeserializeUnsigned(BinaryReader reader) 175 | { 176 | if ((TransactionType)reader.ReadByte() != Type) 177 | throw new FormatException(); 178 | DeserializeUnsignedWithoutType(reader); 179 | } 180 | 181 | private void DeserializeUnsignedWithoutType(BinaryReader reader) 182 | { 183 | Version = reader.ReadByte(); 184 | DeserializeExclusiveData(reader); 185 | Attributes = reader.ReadSerializableArray(MaxTransactionAttributes); 186 | Inputs = reader.ReadSerializableArray(); 187 | Outputs = reader.ReadSerializableArray(ushort.MaxValue + 1); 188 | } 189 | 190 | public bool Equals(Transaction other) 191 | { 192 | if (ReferenceEquals(null, other)) return false; 193 | if (ReferenceEquals(this, other)) return true; 194 | return Hash.Equals(other.Hash); 195 | } 196 | 197 | public override bool Equals(object obj) 198 | { 199 | return Equals(obj as Transaction); 200 | } 201 | 202 | public override int GetHashCode() 203 | { 204 | return Hash.GetHashCode(); 205 | } 206 | 207 | byte[] IScriptContainer.GetMessage() 208 | { 209 | return this.GetHashData(); 210 | } 211 | 212 | /// 213 | /// 获取需要校验的脚本散列值 214 | /// 215 | /// 返回需要校验的脚本散列值 216 | public virtual UInt160[] GetScriptHashesForVerifying() 217 | { 218 | if (References == null) throw new InvalidOperationException(); 219 | HashSet hashes = new HashSet(Inputs.Select(p => References[p].ScriptHash)); 220 | hashes.UnionWith(Attributes.Where(p => p.Usage == TransactionAttributeUsage.Script).Select(p => new UInt160(p.Data))); 221 | foreach (var group in Outputs.GroupBy(p => p.AssetId)) 222 | { 223 | AssetState asset = Blockchain.Default.GetAssetState(group.Key); 224 | if (asset == null) throw new InvalidOperationException(); 225 | if (asset.AssetType.HasFlag(AssetType.DutyFlag)) 226 | { 227 | hashes.UnionWith(group.Select(p => p.ScriptHash)); 228 | } 229 | } 230 | return hashes.OrderBy(p => p).ToArray(); 231 | } 232 | 233 | /// 234 | /// 获取交易后各资产的变化量 235 | /// 236 | /// 返回交易后各资产的变化量 237 | public IEnumerable GetTransactionResults() 238 | { 239 | if (References == null) return null; 240 | return References.Values.Select(p => new 241 | { 242 | AssetId = p.AssetId, 243 | Value = p.Value 244 | }).Concat(Outputs.Select(p => new 245 | { 246 | AssetId = p.AssetId, 247 | Value = -p.Value 248 | })).GroupBy(p => p.AssetId, (k, g) => new TransactionResult 249 | { 250 | AssetId = k, 251 | Amount = g.Sum(p => p.Value) 252 | }).Where(p => p.Amount != Fixed8.Zero); 253 | } 254 | 255 | /// 256 | /// 通知子类反序列化完毕 257 | /// 258 | protected virtual void OnDeserialized() 259 | { 260 | } 261 | 262 | /// 263 | /// 序列化 264 | /// 265 | /// 存放序列化后的结果 266 | void ISerializable.Serialize(BinaryWriter writer) 267 | { 268 | ((IVerifiable)this).SerializeUnsigned(writer); 269 | writer.Write(Scripts); 270 | } 271 | 272 | /// 273 | /// 序列化交易中的额外数据 274 | /// 275 | /// 存放序列化后的结果 276 | protected virtual void SerializeExclusiveData(BinaryWriter writer) 277 | { 278 | } 279 | 280 | void IVerifiable.SerializeUnsigned(BinaryWriter writer) 281 | { 282 | writer.Write((byte)Type); 283 | writer.Write(Version); 284 | SerializeExclusiveData(writer); 285 | writer.Write(Attributes); 286 | writer.Write(Inputs); 287 | writer.Write(Outputs); 288 | } 289 | 290 | /// 291 | /// 变成json对象 292 | /// 293 | /// 返回json对象 294 | public virtual JObject ToJson() 295 | { 296 | JObject json = new JObject(); 297 | json["txid"] = Hash.ToString(); 298 | json["size"] = Size; 299 | json["type"] = Type; 300 | json["version"] = Version; 301 | json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); 302 | json["vin"] = Inputs.Select(p => p.ToJson()).ToArray(); 303 | json["vout"] = Outputs.Select((p, i) => p.ToJson((ushort)i)).ToArray(); 304 | json["sys_fee"] = SystemFee.ToString(); 305 | json["net_fee"] = NetworkFee.ToString(); 306 | json["scripts"] = Scripts.Select(p => p.ToJson()).ToArray(); 307 | return json; 308 | } 309 | 310 | bool IInventory.Verify() 311 | { 312 | return Verify(Enumerable.Empty()); 313 | } 314 | 315 | /// 316 | /// 验证交易 317 | /// 318 | /// 返回验证的结果 319 | public virtual bool Verify(IEnumerable mempool) 320 | { 321 | for (int i = 1; i < Inputs.Length; i++) 322 | for (int j = 0; j < i; j++) 323 | if (Inputs[i].PrevHash == Inputs[j].PrevHash && Inputs[i].PrevIndex == Inputs[j].PrevIndex) 324 | return false; 325 | if (mempool.Where(p => p != this).SelectMany(p => p.Inputs).Intersect(Inputs).Count() > 0) 326 | return false; 327 | if (Blockchain.Default.IsDoubleSpend(this)) 328 | return false; 329 | foreach (var group in Outputs.GroupBy(p => p.AssetId)) 330 | { 331 | AssetState asset = Blockchain.Default.GetAssetState(group.Key); 332 | if (asset == null) return false; 333 | if (asset.Expiration <= Blockchain.Default.Height + 1 && asset.AssetType != AssetType.GoverningToken && asset.AssetType != AssetType.UtilityToken) 334 | return false; 335 | foreach (TransactionOutput output in group) 336 | if (output.Value.GetData() % (long)Math.Pow(10, 8 - asset.Precision) != 0) 337 | return false; 338 | } 339 | TransactionResult[] results = GetTransactionResults()?.ToArray(); 340 | if (results == null) return false; 341 | TransactionResult[] results_destroy = results.Where(p => p.Amount > Fixed8.Zero).ToArray(); 342 | if (results_destroy.Length > 1) return false; 343 | if (results_destroy.Length == 1 && results_destroy[0].AssetId != Blockchain.UtilityToken.Hash) 344 | return false; 345 | if (SystemFee > Fixed8.Zero && (results_destroy.Length == 0 || results_destroy[0].Amount < SystemFee)) 346 | return false; 347 | TransactionResult[] results_issue = results.Where(p => p.Amount < Fixed8.Zero).ToArray(); 348 | switch (Type) 349 | { 350 | case TransactionType.MinerTransaction: 351 | case TransactionType.ClaimTransaction: 352 | if (results_issue.Any(p => p.AssetId != Blockchain.UtilityToken.Hash)) 353 | return false; 354 | break; 355 | case TransactionType.IssueTransaction: 356 | if (results_issue.Any(p => p.AssetId == Blockchain.UtilityToken.Hash)) 357 | return false; 358 | break; 359 | default: 360 | if (results_issue.Length > 0) 361 | return false; 362 | break; 363 | } 364 | if (Attributes.Count(p => p.Usage == TransactionAttributeUsage.ECDH02 || p.Usage == TransactionAttributeUsage.ECDH03) > 1) 365 | return false; 366 | if (!VerifyReceivingScripts()) return false; 367 | return this.VerifyScripts(); 368 | } 369 | 370 | private bool VerifyReceivingScripts() 371 | { 372 | foreach (UInt160 hash in Outputs.Select(p => p.ScriptHash).Distinct()) 373 | { 374 | ContractState contract = Blockchain.Default.GetContract(hash); 375 | if (contract == null) continue; 376 | if (!contract.Payable) return false; 377 | using (StateReader service = new StateReader()) 378 | { 379 | ApplicationEngine engine = new ApplicationEngine(TriggerType.VerificationR, this, Blockchain.Default, service, Fixed8.Zero); 380 | engine.LoadScript(contract.Script, false); 381 | using (ScriptBuilder sb = new ScriptBuilder()) 382 | { 383 | sb.EmitPush(0); 384 | sb.Emit(OpCode.PACK); 385 | sb.EmitPush("receiving"); 386 | engine.LoadScript(sb.ToArray(), false); 387 | } 388 | if (!engine.Execute()) return false; 389 | if (engine.EvaluationStack.Count != 1 || !engine.EvaluationStack.Pop().GetBoolean()) return false; 390 | } 391 | } 392 | return true; 393 | } 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /MedPoints/Storage.Network/RPC/RpcServer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.ResponseCompression; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Neo.Core; 7 | using Neo.IO; 8 | using Neo.IO.Json; 9 | using Neo.Plugins; 10 | using Neo.SmartContract; 11 | using Neo.VM; 12 | using Neo.Wallets; 13 | using System; 14 | using System.IO; 15 | using System.IO.Compression; 16 | using System.Linq; 17 | using System.Net; 18 | using System.Text; 19 | using System.Threading.Tasks; 20 | 21 | namespace Neo.Network.RPC 22 | { 23 | public class RpcServer : IDisposable 24 | { 25 | protected readonly LocalNode LocalNode; 26 | private IWebHost host; 27 | 28 | public RpcServer(LocalNode localNode) 29 | { 30 | this.LocalNode = localNode; 31 | } 32 | 33 | private static JObject CreateErrorResponse(JObject id, int code, string message, JObject data = null) 34 | { 35 | JObject response = CreateResponse(id); 36 | response["error"] = new JObject(); 37 | response["error"]["code"] = code; 38 | response["error"]["message"] = message; 39 | if (data != null) 40 | response["error"]["data"] = data; 41 | return response; 42 | } 43 | 44 | private static JObject CreateResponse(JObject id) 45 | { 46 | JObject response = new JObject(); 47 | response["jsonrpc"] = "2.0"; 48 | response["id"] = id; 49 | return response; 50 | } 51 | 52 | public void Dispose() 53 | { 54 | if (host != null) 55 | { 56 | host.Dispose(); 57 | host = null; 58 | } 59 | } 60 | 61 | private static JObject GetInvokeResult(byte[] script) 62 | { 63 | ApplicationEngine engine = ApplicationEngine.Run(script); 64 | JObject json = new JObject(); 65 | json["script"] = script.ToHexString(); 66 | json["state"] = engine.State; 67 | json["gas_consumed"] = engine.GasConsumed.ToString(); 68 | json["stack"] = new JArray(engine.EvaluationStack.Select(p => p.ToParameter().ToJson())); 69 | return json; 70 | } 71 | 72 | protected virtual JObject Process(string method, JArray _params) 73 | { 74 | switch (method) 75 | { 76 | case "getaccountstate": 77 | { 78 | UInt160 script_hash = Wallet.ToScriptHash(_params[0].AsString()); 79 | AccountState account = Blockchain.Default.GetAccountState(script_hash) ?? new AccountState(script_hash); 80 | return account.ToJson(); 81 | } 82 | case "getassetstate": 83 | { 84 | UInt256 asset_id = UInt256.Parse(_params[0].AsString()); 85 | AssetState asset = Blockchain.Default.GetAssetState(asset_id); 86 | return asset?.ToJson() ?? throw new RpcException(-100, "Unknown asset"); 87 | } 88 | case "getbestblockhash": 89 | return Blockchain.Default.CurrentBlockHash.ToString(); 90 | case "getblock": 91 | { 92 | Block block; 93 | if (_params[0] is JNumber) 94 | { 95 | uint index = (uint)_params[0].AsNumber(); 96 | block = Blockchain.Default.GetBlock(index); 97 | } 98 | else 99 | { 100 | UInt256 hash = UInt256.Parse(_params[0].AsString()); 101 | block = Blockchain.Default.GetBlock(hash); 102 | } 103 | if (block == null) 104 | throw new RpcException(-100, "Unknown block"); 105 | bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false); 106 | if (verbose) 107 | { 108 | JObject json = block.ToJson(); 109 | json["confirmations"] = Blockchain.Default.Height - block.Index + 1; 110 | UInt256 hash = Blockchain.Default.GetNextBlockHash(block.Hash); 111 | if (hash != null) 112 | json["nextblockhash"] = hash.ToString(); 113 | return json; 114 | } 115 | else 116 | { 117 | return block.ToArray().ToHexString(); 118 | } 119 | } 120 | case "getblockcount": 121 | return Blockchain.Default.Height + 1; 122 | case "getblockhash": 123 | { 124 | uint height = (uint)_params[0].AsNumber(); 125 | if (height >= 0 && height <= Blockchain.Default.Height) 126 | { 127 | return Blockchain.Default.GetBlockHash(height).ToString(); 128 | } 129 | else 130 | { 131 | throw new RpcException(-100, "Invalid Height"); 132 | } 133 | } 134 | case "getblocksysfee": 135 | { 136 | uint height = (uint)_params[0].AsNumber(); 137 | if (height >= 0 && height <= Blockchain.Default.Height) 138 | { 139 | return Blockchain.Default.GetSysFeeAmount(height).ToString(); 140 | } 141 | else 142 | { 143 | throw new RpcException(-100, "Invalid Height"); 144 | } 145 | } 146 | case "getconnectioncount": 147 | return LocalNode.RemoteNodeCount; 148 | case "getcontractstate": 149 | { 150 | UInt160 script_hash = UInt160.Parse(_params[0].AsString()); 151 | ContractState contract = Blockchain.Default.GetContract(script_hash); 152 | return contract?.ToJson() ?? throw new RpcException(-100, "Unknown contract"); 153 | } 154 | case "getrawmempool": 155 | return new JArray(LocalNode.GetMemoryPool().Select(p => (JObject)p.Hash.ToString())); 156 | case "getrawtransaction": 157 | { 158 | UInt256 hash = UInt256.Parse(_params[0].AsString()); 159 | bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false); 160 | int height = -1; 161 | Transaction tx = LocalNode.GetTransaction(hash); 162 | if (tx == null) 163 | tx = Blockchain.Default.GetTransaction(hash, out height); 164 | if (tx == null) 165 | throw new RpcException(-100, "Unknown transaction"); 166 | if (verbose) 167 | { 168 | JObject json = tx.ToJson(); 169 | if (height >= 0) 170 | { 171 | Header header = Blockchain.Default.GetHeader((uint)height); 172 | json["blockhash"] = header.Hash.ToString(); 173 | json["confirmations"] = Blockchain.Default.Height - header.Index + 1; 174 | json["blocktime"] = header.Timestamp; 175 | } 176 | return json; 177 | } 178 | else 179 | { 180 | return tx.ToArray().ToHexString(); 181 | } 182 | } 183 | case "getstorage": 184 | { 185 | UInt160 script_hash = UInt160.Parse(_params[0].AsString()); 186 | byte[] key = _params[1].AsString().HexToBytes(); 187 | StorageItem item = Blockchain.Default.GetStorageItem(new StorageKey 188 | { 189 | ScriptHash = script_hash, 190 | Key = key 191 | }) ?? new StorageItem(); 192 | return item.Value?.ToHexString(); 193 | } 194 | case "gettxout": 195 | { 196 | UInt256 hash = UInt256.Parse(_params[0].AsString()); 197 | ushort index = (ushort)_params[1].AsNumber(); 198 | return Blockchain.Default.GetUnspent(hash, index)?.ToJson(index); 199 | } 200 | case "invoke": 201 | { 202 | UInt160 script_hash = UInt160.Parse(_params[0].AsString()); 203 | ContractParameter[] parameters = ((JArray)_params[1]).Select(p => ContractParameter.FromJson(p)).ToArray(); 204 | byte[] script; 205 | using (ScriptBuilder sb = new ScriptBuilder()) 206 | { 207 | script = sb.EmitAppCall(script_hash, parameters).ToArray(); 208 | } 209 | return GetInvokeResult(script); 210 | } 211 | case "invokefunction": 212 | { 213 | UInt160 script_hash = UInt160.Parse(_params[0].AsString()); 214 | string operation = _params[1].AsString(); 215 | ContractParameter[] args = _params.Count >= 3 ? ((JArray)_params[2]).Select(p => ContractParameter.FromJson(p)).ToArray() : new ContractParameter[0]; 216 | byte[] script; 217 | using (ScriptBuilder sb = new ScriptBuilder()) 218 | { 219 | script = sb.EmitAppCall(script_hash, operation, args).ToArray(); 220 | } 221 | return GetInvokeResult(script); 222 | } 223 | case "invokescript": 224 | { 225 | byte[] script = _params[0].AsString().HexToBytes(); 226 | return GetInvokeResult(script); 227 | } 228 | case "sendrawtransaction": 229 | { 230 | Transaction tx = Transaction.DeserializeFrom(_params[0].AsString().HexToBytes()); 231 | return LocalNode.Relay(tx); 232 | } 233 | case "submitblock": 234 | { 235 | Block block = _params[0].AsString().HexToBytes().AsSerializable(); 236 | return LocalNode.Relay(block); 237 | } 238 | case "validateaddress": 239 | { 240 | JObject json = new JObject(); 241 | UInt160 scriptHash; 242 | try 243 | { 244 | scriptHash = Wallet.ToScriptHash(_params[0].AsString()); 245 | } 246 | catch 247 | { 248 | scriptHash = null; 249 | } 250 | json["address"] = _params[0]; 251 | json["isvalid"] = scriptHash != null; 252 | return json; 253 | } 254 | case "getpeers": 255 | { 256 | JObject json = new JObject(); 257 | 258 | { 259 | JArray unconnectedPeers = new JArray(); 260 | foreach (IPEndPoint peer in LocalNode.GetUnconnectedPeers()) 261 | { 262 | JObject peerJson = new JObject(); 263 | peerJson["address"] = peer.Address.ToString(); 264 | peerJson["port"] = peer.Port; 265 | unconnectedPeers.Add(peerJson); 266 | } 267 | json["unconnected"] = unconnectedPeers; 268 | } 269 | 270 | { 271 | JArray badPeers = new JArray(); 272 | foreach (IPEndPoint peer in LocalNode.GetBadPeers()) 273 | { 274 | JObject peerJson = new JObject(); 275 | peerJson["address"] = peer.Address.ToString(); 276 | peerJson["port"] = peer.Port; 277 | badPeers.Add(peerJson); 278 | } 279 | json["bad"] = badPeers; 280 | } 281 | 282 | { 283 | JArray connectedPeers = new JArray(); 284 | foreach (RemoteNode node in LocalNode.GetRemoteNodes()) 285 | { 286 | JObject peerJson = new JObject(); 287 | peerJson["address"] = node.RemoteEndpoint.Address.ToString(); 288 | peerJson["port"] = node.ListenerEndpoint?.Port ?? 0; 289 | connectedPeers.Add(peerJson); 290 | } 291 | json["connected"] = connectedPeers; 292 | } 293 | 294 | return json; 295 | } 296 | case "getversion": 297 | { 298 | JObject json = new JObject(); 299 | json["port"] = LocalNode.Port; 300 | json["nonce"] = LocalNode.Nonce; 301 | json["useragent"] = LocalNode.UserAgent; 302 | return json; 303 | } 304 | default: 305 | throw new RpcException(-32601, "Method not found"); 306 | } 307 | } 308 | 309 | private async Task ProcessAsync(HttpContext context) 310 | { 311 | context.Response.Headers["Access-Control-Allow-Origin"] = "*"; 312 | context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST"; 313 | context.Response.Headers["Access-Control-Allow-Headers"] = "Content-Type"; 314 | context.Response.Headers["Access-Control-Max-Age"] = "31536000"; 315 | if (context.Request.Method != "GET" && context.Request.Method != "POST") return; 316 | JObject request = null; 317 | if (context.Request.Method == "GET") 318 | { 319 | string jsonrpc = context.Request.Query["jsonrpc"]; 320 | string id = context.Request.Query["id"]; 321 | string method = context.Request.Query["method"]; 322 | string _params = context.Request.Query["params"]; 323 | if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(method) && !string.IsNullOrEmpty(_params)) 324 | { 325 | try 326 | { 327 | _params = Encoding.UTF8.GetString(Convert.FromBase64String(_params)); 328 | } 329 | catch (FormatException) { } 330 | request = new JObject(); 331 | if (!string.IsNullOrEmpty(jsonrpc)) 332 | request["jsonrpc"] = jsonrpc; 333 | request["id"] = double.Parse(id); 334 | request["method"] = method; 335 | request["params"] = JObject.Parse(_params); 336 | } 337 | } 338 | else if (context.Request.Method == "POST") 339 | { 340 | using (StreamReader reader = new StreamReader(context.Request.Body)) 341 | { 342 | try 343 | { 344 | request = JObject.Parse(reader); 345 | } 346 | catch (FormatException) { } 347 | } 348 | } 349 | JObject response; 350 | if (request == null) 351 | { 352 | response = CreateErrorResponse(null, -32700, "Parse error"); 353 | } 354 | else if (request is JArray array) 355 | { 356 | if (array.Count == 0) 357 | { 358 | response = CreateErrorResponse(request["id"], -32600, "Invalid Request"); 359 | } 360 | else 361 | { 362 | response = array.Select(p => ProcessRequest(context, p)).Where(p => p != null).ToArray(); 363 | } 364 | } 365 | else 366 | { 367 | response = ProcessRequest(context, request); 368 | } 369 | if (response == null || (response as JArray)?.Count == 0) return; 370 | context.Response.ContentType = "application/json-rpc"; 371 | await context.Response.WriteAsync(response.ToString(), Encoding.UTF8); 372 | } 373 | 374 | private JObject ProcessRequest(HttpContext context, JObject request) 375 | { 376 | if (!request.ContainsProperty("id")) return null; 377 | if (!request.ContainsProperty("method") || !request.ContainsProperty("params") || !(request["params"] is JArray)) 378 | { 379 | return CreateErrorResponse(request["id"], -32600, "Invalid Request"); 380 | } 381 | JObject result = null; 382 | try 383 | { 384 | string method = request["method"].AsString(); 385 | JArray _params = (JArray)request["params"]; 386 | foreach (RpcPlugin plugin in RpcPlugin.Instances) 387 | { 388 | result = plugin.OnProcess(context, method, _params); 389 | if (result != null) break; 390 | } 391 | if (result == null) 392 | result = Process(method, _params); 393 | } 394 | catch (Exception ex) 395 | { 396 | #if DEBUG 397 | return CreateErrorResponse(request["id"], ex.HResult, ex.Message, ex.StackTrace); 398 | #else 399 | return CreateErrorResponse(request["id"], ex.HResult, ex.Message); 400 | #endif 401 | } 402 | JObject response = CreateResponse(request["id"]); 403 | response["result"] = result; 404 | return response; 405 | } 406 | 407 | public void Start(int port, string sslCert = null, string password = null) 408 | { 409 | host = new WebHostBuilder().UseKestrel(options => options.Listen(IPAddress.Any, port, listenOptions => 410 | { 411 | if (!string.IsNullOrEmpty(sslCert)) 412 | listenOptions.UseHttps(sslCert, password); 413 | })) 414 | .Configure(app => 415 | { 416 | app.UseResponseCompression(); 417 | app.Run(ProcessAsync); 418 | }) 419 | .ConfigureServices(services => 420 | { 421 | services.AddResponseCompression(options => 422 | { 423 | // options.EnableForHttps = false; 424 | options.Providers.Add(); 425 | options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/json-rpc" }); 426 | }); 427 | 428 | services.Configure(options => 429 | { 430 | options.Level = CompressionLevel.Fastest; 431 | }); 432 | }) 433 | .Build(); 434 | 435 | host.Start(); 436 | } 437 | } 438 | } --------------------------------------------------------------------------------