diff --git a/AntShares.sln b/neo.sln similarity index 63% rename from AntShares.sln rename to neo.sln index 01b20b5039..d32bf9a01f 100644 --- a/AntShares.sln +++ b/neo.sln @@ -1,27 +1,22 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.6 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{83696BE0-E444-44DD-9C0D-A61CACFC95E1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntShares", "src\AntShares\AntShares.csproj", "{36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC} = {83696BE0-E444-44DD-9C0D-A61CACFC95E1} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.15 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "neo", "neo\neo.csproj", "{36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/AntShares/Consensus/ChangeView.cs b/neo/Consensus/ChangeView.cs similarity index 95% rename from src/AntShares/Consensus/ChangeView.cs rename to neo/Consensus/ChangeView.cs index 2916c312d0..a76979f1ba 100644 --- a/src/AntShares/Consensus/ChangeView.cs +++ b/neo/Consensus/ChangeView.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace AntShares.Consensus +namespace Neo.Consensus { internal class ChangeView : ConsensusMessage { diff --git a/src/AntShares/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs similarity index 95% rename from src/AntShares/Consensus/ConsensusContext.cs rename to neo/Consensus/ConsensusContext.cs index ec4b086268..69b8e7d6bf 100644 --- a/src/AntShares/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -1,13 +1,13 @@ -using AntShares.Core; -using AntShares.Cryptography; -using AntShares.Cryptography.ECC; -using AntShares.IO; -using AntShares.Network.Payloads; -using AntShares.Wallets; +using Neo.Core; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Network.Payloads; +using Neo.Wallets; using System.Collections.Generic; using System.Linq; -namespace AntShares.Consensus +namespace Neo.Consensus { internal class ConsensusContext { diff --git a/src/AntShares/Consensus/ConsensusMessage.cs b/neo/Consensus/ConsensusMessage.cs similarity index 96% rename from src/AntShares/Consensus/ConsensusMessage.cs rename to neo/Consensus/ConsensusMessage.cs index e7ee2db7da..1b80a02678 100644 --- a/src/AntShares/Consensus/ConsensusMessage.cs +++ b/neo/Consensus/ConsensusMessage.cs @@ -1,9 +1,9 @@ -using AntShares.IO; +using Neo.IO; using System; using System.IO; using System.Reflection; -namespace AntShares.Consensus +namespace Neo.Consensus { internal abstract class ConsensusMessage : ISerializable { diff --git a/src/AntShares/Consensus/ConsensusMessageType.cs b/neo/Consensus/ConsensusMessageType.cs similarity index 82% rename from src/AntShares/Consensus/ConsensusMessageType.cs rename to neo/Consensus/ConsensusMessageType.cs index 91b60a249a..a3a10ba826 100644 --- a/src/AntShares/Consensus/ConsensusMessageType.cs +++ b/neo/Consensus/ConsensusMessageType.cs @@ -1,4 +1,4 @@ -namespace AntShares.Consensus +namespace Neo.Consensus { internal enum ConsensusMessageType : byte { diff --git a/src/AntShares/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs similarity index 96% rename from src/AntShares/Consensus/ConsensusService.cs rename to neo/Consensus/ConsensusService.cs index 63e66da4d1..3eb6307813 100644 --- a/src/AntShares/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -1,348 +1,349 @@ -using AntShares.Core; -using AntShares.Cryptography; -using AntShares.IO; -using AntShares.Network; -using AntShares.Network.Payloads; -using AntShares.Wallets; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; - -namespace AntShares.Consensus -{ - public class ConsensusService : IDisposable - { - public const int MaxTransactionsPerBlock = 15000; - - private ConsensusContext context = new ConsensusContext(); - private LocalNode localNode; - private Wallet wallet; - private Timer timer; - private uint timer_height; - private byte timer_view; - private DateTime block_received_time; - private bool started = false; - - public ConsensusService(LocalNode localNode, Wallet wallet) - { - this.localNode = localNode; - this.wallet = wallet; - this.timer = new Timer(OnTimeout, null, Timeout.Infinite, Timeout.Infinite); - } - - private bool AddTransaction(Transaction tx, bool verify) - { - if (Blockchain.Default.ContainsTransaction(tx.Hash) || - (verify && !tx.Verify(context.Transactions.Values)) || - !CheckPolicy(tx)) - { - Log($"reject tx: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}"); - RequestChangeView(); - return false; - } - context.Transactions[tx.Hash] = tx; - if (context.TransactionHashes.Length == context.Transactions.Count) - { - if (Blockchain.GetConsensusAddress(Blockchain.Default.GetValidators(context.Transactions.Values).ToArray()).Equals(context.NextConsensus)) - { - Log($"send perpare response"); - context.State |= ConsensusState.SignatureSent; - context.Signatures[context.MyIndex] = context.MakeHeader().Sign(wallet.GetKey(context.Validators[context.MyIndex])); - SignAndRelay(context.MakePerpareResponse(context.Signatures[context.MyIndex])); - CheckSignatures(); - } - else - { - RequestChangeView(); - return false; - } - } - return true; - } - - private void Blockchain_PersistCompleted(object sender, Block block) - { - Log($"persist block: {block.Hash}"); - block_received_time = DateTime.Now; - InitializeConsensus(0); - } - - private void CheckExpectedView(byte view_number) - { - if (context.ViewNumber == view_number) return; - if (context.ExpectedView.Count(p => p == view_number) >= context.M) - { - InitializeConsensus(view_number); - } - } - - protected virtual bool CheckPolicy(Transaction tx) - { - return true; - } - - private void CheckSignatures() - { - if (context.Signatures.Count(p => p != null) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) - { - Contract contract = Contract.CreateMultiSigContract(context.Validators[context.MyIndex].EncodePoint(true).ToScriptHash(), context.M, context.Validators); - Block block = context.MakeHeader(); - SignatureContext sc = new SignatureContext(block); - for (int i = 0, j = 0; i < context.Validators.Length && j < context.M; i++) - if (context.Signatures[i] != null) - { - sc.AddSignature(contract, context.Validators[i], context.Signatures[i]); - j++; - } - sc.Verifiable.Scripts = sc.GetScripts(); - block.Transactions = context.TransactionHashes.Select(p => context.Transactions[p]).ToArray(); - Log($"relay block: {block.Hash}"); - if (!localNode.Relay(block)) - Log($"reject block: {block.Hash}"); - context.State |= ConsensusState.BlockSent; - } - } - - private MinerTransaction CreateMinerTransaction(IEnumerable transactions, uint height, ulong nonce) - { - Fixed8 amount_netfee = Block.CalculateNetFee(transactions); - TransactionOutput[] outputs = amount_netfee == Fixed8.Zero ? new TransactionOutput[0] : new[] { new TransactionOutput - { - AssetId = Blockchain.SystemCoin.Hash, - Value = amount_netfee, - ScriptHash = wallet.GetContracts().First().ScriptHash - } }; - return new MinerTransaction - { - Nonce = (uint)(nonce % (uint.MaxValue + 1ul)), - Attributes = new TransactionAttribute[0], - Inputs = new CoinReference[0], - Outputs = outputs, - Scripts = new Witness[0] - }; - } - - public void Dispose() - { - Log("OnStop"); - if (timer != null) timer.Dispose(); - if (started) - { - Blockchain.PersistCompleted -= Blockchain_PersistCompleted; - LocalNode.InventoryReceiving -= LocalNode_InventoryReceiving; - LocalNode.InventoryReceived -= LocalNode_InventoryReceived; - } - } - - private static ulong GetNonce() - { - byte[] nonce = new byte[sizeof(ulong)]; - Random rand = new Random(); - rand.NextBytes(nonce); - return nonce.ToUInt64(0); - } - - private void InitializeConsensus(byte view_number) - { - lock (context) - { - if (view_number == 0) - context.Reset(wallet); - else - context.ChangeView(view_number); - if (context.MyIndex < 0) return; - Log($"initialize: height={context.BlockIndex} view={view_number} index={context.MyIndex} role={(context.MyIndex == context.PrimaryIndex ? ConsensusState.Primary : ConsensusState.Backup)}"); - if (context.MyIndex == context.PrimaryIndex) - { - context.State |= ConsensusState.Primary; - timer_height = context.BlockIndex; - timer_view = view_number; - TimeSpan span = DateTime.Now - block_received_time; - if (span >= Blockchain.TimePerBlock) - timer.Change(0, Timeout.Infinite); - else - timer.Change(Blockchain.TimePerBlock - span, Timeout.InfiniteTimeSpan); - } - else - { - context.State = ConsensusState.Backup; - timer_height = context.BlockIndex; - timer_view = view_number; - timer.Change(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (view_number + 1)), Timeout.InfiniteTimeSpan); - } - } - } - - private void LocalNode_InventoryReceived(object sender, IInventory inventory) - { - ConsensusPayload payload = inventory as ConsensusPayload; - if (payload != null) - { - lock (context) - { - if (payload.ValidatorIndex == context.MyIndex) return; - if (payload.Version != ConsensusContext.Version || payload.PrevHash != context.PrevHash || payload.BlockIndex != context.BlockIndex) - return; - if (payload.ValidatorIndex >= context.Validators.Length) return; - ConsensusMessage message = ConsensusMessage.DeserializeFrom(payload.Data); - if (message.ViewNumber != context.ViewNumber && message.Type != ConsensusMessageType.ChangeView) - return; - switch (message.Type) - { - case ConsensusMessageType.ChangeView: - OnChangeViewReceived(payload, (ChangeView)message); - break; - case ConsensusMessageType.PerpareRequest: - OnPerpareRequestReceived(payload, (PerpareRequest)message); - break; - case ConsensusMessageType.PerpareResponse: - OnPerpareResponseReceived(payload, (PerpareResponse)message); - break; - } - } - } - } - - private void LocalNode_InventoryReceiving(object sender, InventoryReceivingEventArgs e) - { - Transaction tx = e.Inventory as Transaction; - if (tx != null) - { - lock (context) - { - if (!context.State.HasFlag(ConsensusState.Backup) || !context.State.HasFlag(ConsensusState.RequestReceived) || context.State.HasFlag(ConsensusState.SignatureSent) || context.State.HasFlag(ConsensusState.ViewChanging)) - return; - if (context.Transactions.ContainsKey(tx.Hash)) return; - if (!context.TransactionHashes.Contains(tx.Hash)) return; - AddTransaction(tx, true); - e.Cancel = true; - } - } - } - - protected virtual void Log(string message) - { - } - - private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) - { - Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber}"); - if (message.NewViewNumber <= context.ExpectedView[payload.ValidatorIndex]) - return; - context.ExpectedView[payload.ValidatorIndex] = message.NewViewNumber; - CheckExpectedView(message.NewViewNumber); - } - - private void OnPerpareRequestReceived(ConsensusPayload payload, PerpareRequest message) - { - Log($"{nameof(OnPerpareRequestReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} tx={message.TransactionHashes.Length}"); - if (!context.State.HasFlag(ConsensusState.Backup) || context.State.HasFlag(ConsensusState.RequestReceived)) - return; - if (payload.ValidatorIndex != context.PrimaryIndex) return; - if (payload.Timestamp <= Blockchain.Default.GetHeader(context.PrevHash).Timestamp || payload.Timestamp > DateTime.Now.AddMinutes(10).ToTimestamp()) - { - Log($"Timestamp incorrect: {payload.Timestamp}"); - return; - } - context.State |= ConsensusState.RequestReceived; - context.Timestamp = payload.Timestamp; - context.Nonce = message.Nonce; - context.NextConsensus = message.NextConsensus; - context.TransactionHashes = message.TransactionHashes; - context.Transactions = new Dictionary(); - if (!Crypto.Default.VerifySignature(context.MakeHeader().GetHashData(), message.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) return; - context.Signatures = new byte[context.Validators.Length][]; - context.Signatures[payload.ValidatorIndex] = message.Signature; - Dictionary mempool = LocalNode.GetMemoryPool().ToDictionary(p => p.Hash); - foreach (UInt256 hash in context.TransactionHashes.Skip(1)) - if (mempool.ContainsKey(hash)) - if (!AddTransaction(mempool[hash], false)) - return; - if (!AddTransaction(message.MinerTransaction, true)) return; - LocalNode.AllowHashes(context.TransactionHashes.Except(context.Transactions.Keys)); - if (context.Transactions.Count < context.TransactionHashes.Length) - localNode.SynchronizeMemoryPool(); - } - - private void OnPerpareResponseReceived(ConsensusPayload payload, PerpareResponse message) - { - Log($"{nameof(OnPerpareResponseReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); - if (context.State.HasFlag(ConsensusState.BlockSent)) return; - if (context.Signatures[payload.ValidatorIndex] != null) return; - Block header = context.MakeHeader(); - if (header == null || !Crypto.Default.VerifySignature(header.GetHashData(), message.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) return; - context.Signatures[payload.ValidatorIndex] = message.Signature; - CheckSignatures(); - } - - private void OnTimeout(object state) - { - lock (context) - { - if (timer_height != context.BlockIndex || timer_view != context.ViewNumber) return; - Log($"timeout: height={timer_height} view={timer_view} state={context.State}"); - if (context.State.HasFlag(ConsensusState.Primary) && !context.State.HasFlag(ConsensusState.RequestSent)) - { - Log($"send perpare request: height={timer_height} view={timer_view}"); - context.State |= ConsensusState.RequestSent; - if (!context.State.HasFlag(ConsensusState.SignatureSent)) - { - context.Timestamp = Math.Max(DateTime.Now.ToTimestamp(), Blockchain.Default.GetHeader(context.PrevHash).Timestamp + 1); - context.Nonce = GetNonce(); - List transactions = LocalNode.GetMemoryPool().Where(p => CheckPolicy(p)).ToList(); - if (transactions.Count >= MaxTransactionsPerBlock) - transactions = transactions.OrderByDescending(p => p.NetworkFee / p.Size).Take(MaxTransactionsPerBlock - 1).ToList(); - transactions.Insert(0, CreateMinerTransaction(transactions, context.BlockIndex, context.Nonce)); - context.TransactionHashes = transactions.Select(p => p.Hash).ToArray(); - context.Transactions = transactions.ToDictionary(p => p.Hash); - context.NextConsensus = Blockchain.GetConsensusAddress(Blockchain.Default.GetValidators(transactions).ToArray()); - context.Signatures[context.MyIndex] = context.MakeHeader().Sign(wallet.GetKey(context.Validators[context.MyIndex])); - } - SignAndRelay(context.MakePerpareRequest()); - timer.Change(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (timer_view + 1)), Timeout.InfiniteTimeSpan); - } - else if ((context.State.HasFlag(ConsensusState.Primary) && context.State.HasFlag(ConsensusState.RequestSent)) || context.State.HasFlag(ConsensusState.Backup)) - { - RequestChangeView(); - } - } - } - - private void RequestChangeView() - { - context.State |= ConsensusState.ViewChanging; - context.ExpectedView[context.MyIndex]++; - Log($"request change view: height={context.BlockIndex} view={context.ViewNumber} nv={context.ExpectedView[context.MyIndex]} state={context.State}"); - timer.Change(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (context.ExpectedView[context.MyIndex] + 1)), Timeout.InfiniteTimeSpan); - SignAndRelay(context.MakeChangeView()); - CheckExpectedView(context.ExpectedView[context.MyIndex]); - } - - private void SignAndRelay(ConsensusPayload payload) - { - SignatureContext sc; - try - { - sc = new SignatureContext(payload); - } - catch (InvalidOperationException) - { - return; - } - wallet.Sign(sc); - sc.Verifiable.Scripts = sc.GetScripts(); - localNode.RelayDirectly(payload); - } - - public void Start() - { - Log("OnStart"); - started = true; - Blockchain.PersistCompleted += Blockchain_PersistCompleted; - LocalNode.InventoryReceiving += LocalNode_InventoryReceiving; - LocalNode.InventoryReceived += LocalNode_InventoryReceived; - InitializeConsensus(0); - } - } -} +using Neo.Core; +using Neo.Cryptography; +using Neo.IO; +using Neo.Network; +using Neo.Network.Payloads; +using Neo.Wallets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace Neo.Consensus +{ + public class ConsensusService : IDisposable + { + public const int MaxTransactionsPerBlock = 500; + + private ConsensusContext context = new ConsensusContext(); + private LocalNode localNode; + private Wallet wallet; + private Timer timer; + private uint timer_height; + private byte timer_view; + private DateTime block_received_time; + private bool started = false; + + public ConsensusService(LocalNode localNode, Wallet wallet) + { + this.localNode = localNode; + this.wallet = wallet; + this.timer = new Timer(OnTimeout, null, Timeout.Infinite, Timeout.Infinite); + } + + private bool AddTransaction(Transaction tx, bool verify) + { + if (Blockchain.Default.ContainsTransaction(tx.Hash) || + (verify && !tx.Verify(context.Transactions.Values)) || + !CheckPolicy(tx)) + { + Log($"reject tx: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}"); + RequestChangeView(); + return false; + } + context.Transactions[tx.Hash] = tx; + if (context.TransactionHashes.Length == context.Transactions.Count) + { + if (Blockchain.GetConsensusAddress(Blockchain.Default.GetValidators(context.Transactions.Values).ToArray()).Equals(context.NextConsensus)) + { + Log($"send perpare response"); + context.State |= ConsensusState.SignatureSent; + context.Signatures[context.MyIndex] = context.MakeHeader().Sign(wallet.GetKey(context.Validators[context.MyIndex])); + SignAndRelay(context.MakePerpareResponse(context.Signatures[context.MyIndex])); + CheckSignatures(); + } + else + { + RequestChangeView(); + return false; + } + } + return true; + } + + private void Blockchain_PersistCompleted(object sender, Block block) + { + Log($"persist block: {block.Hash}"); + block_received_time = DateTime.Now; + InitializeConsensus(0); + } + + private void CheckExpectedView(byte view_number) + { + if (context.ViewNumber == view_number) return; + if (context.ExpectedView.Count(p => p == view_number) >= context.M) + { + InitializeConsensus(view_number); + } + } + + protected virtual bool CheckPolicy(Transaction tx) + { + return true; + } + + private void CheckSignatures() + { + if (context.Signatures.Count(p => p != null) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) + { + Contract contract = Contract.CreateMultiSigContract(context.Validators[context.MyIndex].EncodePoint(true).ToScriptHash(), context.M, context.Validators); + Block block = context.MakeHeader(); + SignatureContext sc = new SignatureContext(block); + for (int i = 0, j = 0; i < context.Validators.Length && j < context.M; i++) + if (context.Signatures[i] != null) + { + sc.AddSignature(contract, context.Validators[i], context.Signatures[i]); + j++; + } + sc.Verifiable.Scripts = sc.GetScripts(); + block.Transactions = context.TransactionHashes.Select(p => context.Transactions[p]).ToArray(); + Log($"relay block: {block.Hash}"); + if (!localNode.Relay(block)) + Log($"reject block: {block.Hash}"); + context.State |= ConsensusState.BlockSent; + } + } + + private MinerTransaction CreateMinerTransaction(IEnumerable transactions, uint height, ulong nonce) + { + Fixed8 amount_netfee = Block.CalculateNetFee(transactions); + TransactionOutput[] outputs = amount_netfee == Fixed8.Zero ? new TransactionOutput[0] : new[] { new TransactionOutput + { + AssetId = Blockchain.SystemCoin.Hash, + Value = amount_netfee, + ScriptHash = wallet.GetContracts().First().ScriptHash + } }; + return new MinerTransaction + { + Nonce = (uint)(nonce % (uint.MaxValue + 1ul)), + Attributes = new TransactionAttribute[0], + Inputs = new CoinReference[0], + Outputs = outputs, + Scripts = new Witness[0] + }; + } + + public void Dispose() + { + Log("OnStop"); + if (timer != null) timer.Dispose(); + if (started) + { + Blockchain.PersistCompleted -= Blockchain_PersistCompleted; + LocalNode.InventoryReceiving -= LocalNode_InventoryReceiving; + LocalNode.InventoryReceived -= LocalNode_InventoryReceived; + } + } + + private static ulong GetNonce() + { + byte[] nonce = new byte[sizeof(ulong)]; + Random rand = new Random(); + rand.NextBytes(nonce); + return nonce.ToUInt64(0); + } + + private void InitializeConsensus(byte view_number) + { + lock (context) + { + if (view_number == 0) + context.Reset(wallet); + else + context.ChangeView(view_number); + if (context.MyIndex < 0) return; + Log($"initialize: height={context.BlockIndex} view={view_number} index={context.MyIndex} role={(context.MyIndex == context.PrimaryIndex ? ConsensusState.Primary : ConsensusState.Backup)}"); + if (context.MyIndex == context.PrimaryIndex) + { + context.State |= ConsensusState.Primary; + timer_height = context.BlockIndex; + timer_view = view_number; + TimeSpan span = DateTime.Now - block_received_time; + if (span >= Blockchain.TimePerBlock) + timer.Change(0, Timeout.Infinite); + else + timer.Change(Blockchain.TimePerBlock - span, Timeout.InfiniteTimeSpan); + } + else + { + context.State = ConsensusState.Backup; + timer_height = context.BlockIndex; + timer_view = view_number; + timer.Change(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (view_number + 1)), Timeout.InfiniteTimeSpan); + } + } + } + + private void LocalNode_InventoryReceived(object sender, IInventory inventory) + { + ConsensusPayload payload = inventory as ConsensusPayload; + if (payload != null) + { + lock (context) + { + if (payload.ValidatorIndex == context.MyIndex) return; + if (payload.Version != ConsensusContext.Version || payload.PrevHash != context.PrevHash || payload.BlockIndex != context.BlockIndex) + return; + if (payload.ValidatorIndex >= context.Validators.Length) return; + ConsensusMessage message = ConsensusMessage.DeserializeFrom(payload.Data); + if (message.ViewNumber != context.ViewNumber && message.Type != ConsensusMessageType.ChangeView) + return; + switch (message.Type) + { + case ConsensusMessageType.ChangeView: + OnChangeViewReceived(payload, (ChangeView)message); + break; + case ConsensusMessageType.PerpareRequest: + OnPerpareRequestReceived(payload, (PerpareRequest)message); + break; + case ConsensusMessageType.PerpareResponse: + OnPerpareResponseReceived(payload, (PerpareResponse)message); + break; + } + } + } + } + + private void LocalNode_InventoryReceiving(object sender, InventoryReceivingEventArgs e) + { + Transaction tx = e.Inventory as Transaction; + if (tx != null) + { + lock (context) + { + if (!context.State.HasFlag(ConsensusState.Backup) || !context.State.HasFlag(ConsensusState.RequestReceived) || context.State.HasFlag(ConsensusState.SignatureSent) || context.State.HasFlag(ConsensusState.ViewChanging)) + return; + if (context.Transactions.ContainsKey(tx.Hash)) return; + if (!context.TransactionHashes.Contains(tx.Hash)) return; + AddTransaction(tx, true); + e.Cancel = true; + } + } + } + + protected virtual void Log(string message) + { + } + + private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) + { + Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber}"); + if (message.NewViewNumber <= context.ExpectedView[payload.ValidatorIndex]) + return; + context.ExpectedView[payload.ValidatorIndex] = message.NewViewNumber; + CheckExpectedView(message.NewViewNumber); + } + + private void OnPerpareRequestReceived(ConsensusPayload payload, PerpareRequest message) + { + Log($"{nameof(OnPerpareRequestReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} tx={message.TransactionHashes.Length}"); + if (!context.State.HasFlag(ConsensusState.Backup) || context.State.HasFlag(ConsensusState.RequestReceived)) + return; + if (payload.ValidatorIndex != context.PrimaryIndex) return; + if (payload.Timestamp <= Blockchain.Default.GetHeader(context.PrevHash).Timestamp || payload.Timestamp > DateTime.Now.AddMinutes(10).ToTimestamp()) + { + Log($"Timestamp incorrect: {payload.Timestamp}"); + return; + } + context.State |= ConsensusState.RequestReceived; + context.Timestamp = payload.Timestamp; + context.Nonce = message.Nonce; + context.NextConsensus = message.NextConsensus; + context.TransactionHashes = message.TransactionHashes; + if (context.TransactionHashes.Length > MaxTransactionsPerBlock) return; + context.Transactions = new Dictionary(); + if (!Crypto.Default.VerifySignature(context.MakeHeader().GetHashData(), message.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) return; + context.Signatures = new byte[context.Validators.Length][]; + context.Signatures[payload.ValidatorIndex] = message.Signature; + Dictionary mempool = LocalNode.GetMemoryPool().ToDictionary(p => p.Hash); + foreach (UInt256 hash in context.TransactionHashes.Skip(1)) + if (mempool.ContainsKey(hash)) + if (!AddTransaction(mempool[hash], false)) + return; + if (!AddTransaction(message.MinerTransaction, true)) return; + LocalNode.AllowHashes(context.TransactionHashes.Except(context.Transactions.Keys)); + if (context.Transactions.Count < context.TransactionHashes.Length) + localNode.SynchronizeMemoryPool(); + } + + private void OnPerpareResponseReceived(ConsensusPayload payload, PerpareResponse message) + { + Log($"{nameof(OnPerpareResponseReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); + if (context.State.HasFlag(ConsensusState.BlockSent)) return; + if (context.Signatures[payload.ValidatorIndex] != null) return; + Block header = context.MakeHeader(); + if (header == null || !Crypto.Default.VerifySignature(header.GetHashData(), message.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) return; + context.Signatures[payload.ValidatorIndex] = message.Signature; + CheckSignatures(); + } + + private void OnTimeout(object state) + { + lock (context) + { + if (timer_height != context.BlockIndex || timer_view != context.ViewNumber) return; + Log($"timeout: height={timer_height} view={timer_view} state={context.State}"); + if (context.State.HasFlag(ConsensusState.Primary) && !context.State.HasFlag(ConsensusState.RequestSent)) + { + Log($"send perpare request: height={timer_height} view={timer_view}"); + context.State |= ConsensusState.RequestSent; + if (!context.State.HasFlag(ConsensusState.SignatureSent)) + { + context.Timestamp = Math.Max(DateTime.Now.ToTimestamp(), Blockchain.Default.GetHeader(context.PrevHash).Timestamp + 1); + context.Nonce = GetNonce(); + List transactions = LocalNode.GetMemoryPool().Where(p => CheckPolicy(p)).ToList(); + if (transactions.Count >= MaxTransactionsPerBlock) + transactions = transactions.OrderByDescending(p => p.NetworkFee / p.Size).Take(MaxTransactionsPerBlock - 1).ToList(); + transactions.Insert(0, CreateMinerTransaction(transactions, context.BlockIndex, context.Nonce)); + context.TransactionHashes = transactions.Select(p => p.Hash).ToArray(); + context.Transactions = transactions.ToDictionary(p => p.Hash); + context.NextConsensus = Blockchain.GetConsensusAddress(Blockchain.Default.GetValidators(transactions).ToArray()); + context.Signatures[context.MyIndex] = context.MakeHeader().Sign(wallet.GetKey(context.Validators[context.MyIndex])); + } + SignAndRelay(context.MakePerpareRequest()); + timer.Change(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (timer_view + 1)), Timeout.InfiniteTimeSpan); + } + else if ((context.State.HasFlag(ConsensusState.Primary) && context.State.HasFlag(ConsensusState.RequestSent)) || context.State.HasFlag(ConsensusState.Backup)) + { + RequestChangeView(); + } + } + } + + private void RequestChangeView() + { + context.State |= ConsensusState.ViewChanging; + context.ExpectedView[context.MyIndex]++; + Log($"request change view: height={context.BlockIndex} view={context.ViewNumber} nv={context.ExpectedView[context.MyIndex]} state={context.State}"); + timer.Change(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (context.ExpectedView[context.MyIndex] + 1)), Timeout.InfiniteTimeSpan); + SignAndRelay(context.MakeChangeView()); + CheckExpectedView(context.ExpectedView[context.MyIndex]); + } + + private void SignAndRelay(ConsensusPayload payload) + { + SignatureContext sc; + try + { + sc = new SignatureContext(payload); + } + catch (InvalidOperationException) + { + return; + } + wallet.Sign(sc); + sc.Verifiable.Scripts = sc.GetScripts(); + localNode.RelayDirectly(payload); + } + + public void Start() + { + Log("OnStart"); + started = true; + Blockchain.PersistCompleted += Blockchain_PersistCompleted; + LocalNode.InventoryReceiving += LocalNode_InventoryReceiving; + LocalNode.InventoryReceived += LocalNode_InventoryReceived; + InitializeConsensus(0); + } + } +} diff --git a/src/AntShares/Consensus/ConsensusState.cs b/neo/Consensus/ConsensusState.cs similarity index 90% rename from src/AntShares/Consensus/ConsensusState.cs rename to neo/Consensus/ConsensusState.cs index 3698ef75d7..15b570d08b 100644 --- a/src/AntShares/Consensus/ConsensusState.cs +++ b/neo/Consensus/ConsensusState.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Consensus +namespace Neo.Consensus { [Flags] internal enum ConsensusState : byte diff --git a/src/AntShares/Consensus/PerpareRequest.cs b/neo/Consensus/PerpareRequest.cs similarity index 94% rename from src/AntShares/Consensus/PerpareRequest.cs rename to neo/Consensus/PerpareRequest.cs index 1be3c17333..f038f4a7e2 100644 --- a/src/AntShares/Consensus/PerpareRequest.cs +++ b/neo/Consensus/PerpareRequest.cs @@ -1,10 +1,10 @@ -using AntShares.Core; -using AntShares.IO; +using Neo.Core; +using Neo.IO; using System; using System.IO; using System.Linq; -namespace AntShares.Consensus +namespace Neo.Consensus { internal class PerpareRequest : ConsensusMessage { diff --git a/src/AntShares/Consensus/PerpareResponse.cs b/neo/Consensus/PerpareResponse.cs similarity index 94% rename from src/AntShares/Consensus/PerpareResponse.cs rename to neo/Consensus/PerpareResponse.cs index 1afb3a0054..a74f21f352 100644 --- a/src/AntShares/Consensus/PerpareResponse.cs +++ b/neo/Consensus/PerpareResponse.cs @@ -1,6 +1,6 @@ using System.IO; -namespace AntShares.Consensus +namespace Neo.Consensus { internal class PerpareResponse : ConsensusMessage { diff --git a/src/AntShares/Core/AccountState.cs b/neo/Core/AccountState.cs similarity index 95% rename from src/AntShares/Core/AccountState.cs rename to neo/Core/AccountState.cs index 1ccb06d311..3745a03c1b 100644 --- a/src/AntShares/Core/AccountState.cs +++ b/neo/Core/AccountState.cs @@ -1,11 +1,11 @@ -using AntShares.Cryptography.ECC; -using AntShares.IO; -using AntShares.VM; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.VM; using System.Collections.Generic; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { public class AccountState : StateBase, ICloneable { diff --git a/src/AntShares/Core/AssetState.cs b/neo/Core/AssetState.cs similarity index 97% rename from src/AntShares/Core/AssetState.cs rename to neo/Core/AssetState.cs index a6af92f817..0e2a2eb5ec 100644 --- a/src/AntShares/Core/AssetState.cs +++ b/neo/Core/AssetState.cs @@ -1,14 +1,14 @@ -using AntShares.Cryptography.ECC; -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.VM; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.IO.Json; +using Neo.VM; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { public class AssetState : StateBase, ICloneable { diff --git a/src/AntShares/Core/AssetType.cs b/neo/Core/AssetType.cs similarity index 92% rename from src/AntShares/Core/AssetType.cs rename to neo/Core/AssetType.cs index b1d1cf3bcd..80696642a3 100644 --- a/src/AntShares/Core/AssetType.cs +++ b/neo/Core/AssetType.cs @@ -1,4 +1,4 @@ -namespace AntShares.Core +namespace Neo.Core { /// /// 资产类别 diff --git a/src/AntShares/Core/Block.cs b/neo/Core/Block.cs similarity index 98% rename from src/AntShares/Core/Block.cs rename to neo/Core/Block.cs index 2dfbc66488..4071287bda 100644 --- a/src/AntShares/Core/Block.cs +++ b/neo/Core/Block.cs @@ -1,13 +1,13 @@ -using AntShares.Cryptography; -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.Network; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; +using Neo.Network; using System; using System.Collections.Generic; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { /// /// 区块或区块头 diff --git a/src/AntShares/Core/BlockBase.cs b/neo/Core/BlockBase.cs similarity index 94% rename from src/AntShares/Core/BlockBase.cs rename to neo/Core/BlockBase.cs index 4aff27d946..a6e8424798 100644 --- a/src/AntShares/Core/BlockBase.cs +++ b/neo/Core/BlockBase.cs @@ -1,12 +1,12 @@ -using AntShares.Cryptography; -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.VM; -using AntShares.Wallets; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; +using Neo.VM; +using Neo.Wallets; using System; using System.IO; -namespace AntShares.Core +namespace Neo.Core { public abstract class BlockBase : IVerifiable { @@ -117,11 +117,6 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.Write(NextConsensus); } - byte[] IInteropInterface.ToArray() - { - return this.ToArray(); - } - public virtual JObject ToJson() { JObject json = new JObject(); diff --git a/src/AntShares/Core/Blockchain.cs b/neo/Core/Blockchain.cs similarity index 96% rename from src/AntShares/Core/Blockchain.cs rename to neo/Core/Blockchain.cs index f67b639efe..daa36059f0 100644 --- a/src/AntShares/Core/Blockchain.cs +++ b/neo/Core/Blockchain.cs @@ -1,13 +1,13 @@ -using AntShares.Cryptography; -using AntShares.Cryptography.ECC; -using AntShares.IO; -using AntShares.VM; -using AntShares.Wallets; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.VM; +using Neo.Wallets; using System; using System.Collections.Generic; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { /// /// 实现区块链功能的基类 @@ -23,13 +23,7 @@ public abstract class Blockchain : IDisposable, IScriptTable /// 产生每个区块的时间间隔,已秒为单位 /// public const uint SecondsPerBlock = 15; - /// - /// 小蚁币产量递减的时间间隔,以区块数量为单位 - /// public const uint DecrementInterval = 2000000; - /// - /// 每个区块产生的小蚁币的数量 - /// public static readonly uint[] GenerationAmount = { 8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; /// /// 产生每个区块的时间间隔 @@ -40,9 +34,7 @@ public abstract class Blockchain : IDisposable, IScriptTable /// public static readonly ECPoint[] StandbyValidators = Settings.Default.StandbyValidators.OfType().Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); - /// - /// 小蚁股 - /// +#pragma warning disable CS0612 public static readonly RegisterTransaction SystemShare = new RegisterTransaction { AssetType = AssetType.SystemShare, @@ -57,9 +49,6 @@ public abstract class Blockchain : IDisposable, IScriptTable Scripts = new Witness[0] }; - /// - /// 小蚁币 - /// public static readonly RegisterTransaction SystemCoin = new RegisterTransaction { AssetType = AssetType.SystemCoin, @@ -73,6 +62,7 @@ public abstract class Blockchain : IDisposable, IScriptTable Outputs = new TransactionOutput[0], Scripts = new Witness[0] }; +#pragma warning restore CS0612 /// /// 创世区块 @@ -364,7 +354,7 @@ public virtual IEnumerable GetValidators(IEnumerable other validators[pubkey] += vote.Count; } } - return validators.OrderByDescending(p => p.Value).ThenBy(p => p.Key).Select(p => p.Key).Concat(StandbyValidators).Take(validators_count); + return validators.OrderByDescending(p => p.Value).ThenBy(p => p.Key).Select(p => p.Key).Take(validators_count); } /// diff --git a/src/AntShares/Core/ClaimTransaction.cs b/neo/Core/ClaimTransaction.cs similarity index 93% rename from src/AntShares/Core/ClaimTransaction.cs rename to neo/Core/ClaimTransaction.cs index f1fc68950c..79a9572758 100644 --- a/src/AntShares/Core/ClaimTransaction.cs +++ b/neo/Core/ClaimTransaction.cs @@ -1,20 +1,14 @@ -using AntShares.IO; -using AntShares.IO.Json; +using Neo.IO; +using Neo.IO.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { - /// - /// 用于分配小蚁币的特殊交易 - /// public class ClaimTransaction : Transaction { - /// - /// 将要用于分配小蚁币的小蚁股 - /// public CoinReference[] Claims; public override Fixed8 NetworkFee => Fixed8.Zero; @@ -32,6 +26,7 @@ public ClaimTransaction() /// 数据来源 protected override void DeserializeExclusiveData(BinaryReader reader) { + if (Version != 0) throw new FormatException(); Claims = reader.ReadSerializableArray(); if (Claims.Length == 0) throw new FormatException(); } diff --git a/src/AntShares/Core/CoinReference.cs b/neo/Core/CoinReference.cs similarity index 92% rename from src/AntShares/Core/CoinReference.cs rename to neo/Core/CoinReference.cs index 5172d1f7d9..4145fbe20e 100644 --- a/src/AntShares/Core/CoinReference.cs +++ b/neo/Core/CoinReference.cs @@ -1,10 +1,10 @@ -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.VM; +using Neo.IO; +using Neo.IO.Json; +using Neo.VM; using System; using System.IO; -namespace AntShares.Core +namespace Neo.Core { /// /// 交易输入 @@ -68,11 +68,6 @@ void ISerializable.Serialize(BinaryWriter writer) writer.Write(PrevIndex); } - byte[] IInteropInterface.ToArray() - { - return this.ToArray(); - } - /// /// 将交易输入转变为json对象 /// diff --git a/src/AntShares/Core/CoinState.cs b/neo/Core/CoinState.cs similarity index 91% rename from src/AntShares/Core/CoinState.cs rename to neo/Core/CoinState.cs index 690cfac27e..fe9fdf70b8 100644 --- a/src/AntShares/Core/CoinState.cs +++ b/neo/Core/CoinState.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Core +namespace Neo.Core { [Flags] public enum CoinState : byte diff --git a/src/AntShares/Core/ContractParameterType.cs b/neo/Core/ContractParameterType.cs similarity index 93% rename from src/AntShares/Core/ContractParameterType.cs rename to neo/Core/ContractParameterType.cs index d2b22232ca..d5b10d28d5 100644 --- a/src/AntShares/Core/ContractParameterType.cs +++ b/neo/Core/ContractParameterType.cs @@ -1,4 +1,4 @@ -namespace AntShares.Core +namespace Neo.Core { /// /// 表示智能合约的参数类型 @@ -28,6 +28,8 @@ public enum ContractParameterType : byte ByteArray = 5, PublicKey = 6, + Array = 16, + Void = 0xff } } diff --git a/src/AntShares/Core/ContractState.cs b/neo/Core/ContractState.cs similarity index 97% rename from src/AntShares/Core/ContractState.cs rename to neo/Core/ContractState.cs index d303e54725..e24d1066e6 100644 --- a/src/AntShares/Core/ContractState.cs +++ b/neo/Core/ContractState.cs @@ -1,7 +1,7 @@ -using AntShares.IO; +using Neo.IO; using System.IO; -namespace AntShares.Core +namespace Neo.Core { public class ContractState : StateBase, ICloneable { diff --git a/src/AntShares/Core/ContractTransaction.cs b/neo/Core/ContractTransaction.cs similarity index 56% rename from src/AntShares/Core/ContractTransaction.cs rename to neo/Core/ContractTransaction.cs index 68528ddc32..252976fd69 100644 --- a/src/AntShares/Core/ContractTransaction.cs +++ b/neo/Core/ContractTransaction.cs @@ -1,4 +1,7 @@ -namespace AntShares.Core +using System; +using System.IO; + +namespace Neo.Core { /// /// 合约交易,这是最常用的一种交易 @@ -9,5 +12,10 @@ public ContractTransaction() : base(TransactionType.ContractTransaction) { } + + protected override void DeserializeExclusiveData(BinaryReader reader) + { + if (Version != 0) throw new FormatException(); + } } } diff --git a/src/AntShares/Core/EnrollmentTransaction.cs b/neo/Core/EnrollmentTransaction.cs similarity index 85% rename from src/AntShares/Core/EnrollmentTransaction.cs rename to neo/Core/EnrollmentTransaction.cs index 0aa842a623..b4dd8a4ee0 100644 --- a/src/AntShares/Core/EnrollmentTransaction.cs +++ b/neo/Core/EnrollmentTransaction.cs @@ -1,15 +1,15 @@ -using AntShares.Cryptography.ECC; -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.Wallets; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.IO.Json; +using Neo.Wallets; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { - /// - /// 用于报名成为记账候选人的特殊交易 - /// + [Obsolete] public class EnrollmentTransaction : Transaction { /// @@ -18,9 +18,6 @@ public class EnrollmentTransaction : Transaction public ECPoint PublicKey; private UInt160 _script_hash = null; - /// - /// 记账人的抵押地址 - /// private UInt160 ScriptHash { get @@ -46,6 +43,7 @@ public EnrollmentTransaction() /// 数据来源 protected override void DeserializeExclusiveData(BinaryReader reader) { + if (Version != 0) throw new FormatException(); PublicKey = ECPoint.DeserializeFrom(reader, ECCurve.Secp256r1); } @@ -77,5 +75,10 @@ public override JObject ToJson() json["pubkey"] = PublicKey.ToString(); return json; } + + public override bool Verify(IEnumerable mempool) + { + return false; + } } } diff --git a/src/AntShares/Core/FunctionCode.cs b/neo/Core/FunctionCode.cs similarity index 96% rename from src/AntShares/Core/FunctionCode.cs rename to neo/Core/FunctionCode.cs index 5b0f9a153e..d8ad16dc7d 100644 --- a/src/AntShares/Core/FunctionCode.cs +++ b/neo/Core/FunctionCode.cs @@ -1,8 +1,8 @@ -using AntShares.IO; +using Neo.IO; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { public class FunctionCode : ICode, ISerializable { diff --git a/src/AntShares/Core/Header.cs b/neo/Core/Header.cs similarity index 95% rename from src/AntShares/Core/Header.cs rename to neo/Core/Header.cs index 00b1e5465f..096707fd82 100644 --- a/src/AntShares/Core/Header.cs +++ b/neo/Core/Header.cs @@ -1,9 +1,9 @@ -using AntShares.IO; -using AntShares.VM; +using Neo.IO; +using Neo.VM; using System; using System.IO; -namespace AntShares.Core +namespace Neo.Core { public class Header : BlockBase, IEquatable
{ diff --git a/src/AntShares/Core/Helper.cs b/neo/Core/Helper.cs similarity index 65% rename from src/AntShares/Core/Helper.cs rename to neo/Core/Helper.cs index 16b6295eae..57bf130815 100644 --- a/src/AntShares/Core/Helper.cs +++ b/neo/Core/Helper.cs @@ -1,100 +1,82 @@ -using AntShares.Cryptography; -using AntShares.SmartContract; -using AntShares.VM; -using AntShares.Wallets; -using System; -using System.IO; -using System.Linq; - -namespace AntShares.Core -{ - /// - /// 包含一系列签名与验证的扩展方法 - /// - public static class Helper - { - public static byte[] GetHashData(this IVerifiable verifiable) - { - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms)) - { - verifiable.SerializeUnsigned(writer); - writer.Flush(); - return ms.ToArray(); - } - } - - /// - /// 根据传入的账户信息,对可签名的对象进行签名 - /// - /// 要签名的数据 - /// 用于签名的账户 - /// 返回签名后的结果 - public static byte[] Sign(this IVerifiable verifiable, KeyPair key) - { - using (key.Decrypt()) - { - return Crypto.Default.Sign(verifiable.GetHashData(), key.PrivateKey, key.PublicKey.EncodePoint(false).Skip(1).ToArray()); - } - } - - public static UInt160 ToScriptHash(this byte[] script) - { - return new UInt160(Crypto.Default.Hash160(script)); - } - - internal static bool VerifyScripts(this IVerifiable verifiable) - { - const int max_steps = 3000; - UInt160[] hashes; - try - { - hashes = verifiable.GetScriptHashesForVerifying(); - } - catch (InvalidOperationException) - { - return false; - } - if (hashes.Length != verifiable.Scripts.Length) return false; - for (int i = 0; i < hashes.Length; i++) - { - byte[] verification = verifiable.Scripts[i].VerificationScript; - if (verification.Length == 0) - { - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitAppCall(hashes[i].ToArray()); - verification = sb.ToArray(); - } - } - else - { - if (hashes[i] != verification.ToScriptHash()) return false; - } - int nOpCount = 0; - ExecutionEngine engine = new ExecutionEngine(verifiable, Crypto.Default, Blockchain.Default, StateReader.Default); - engine.LoadScript(verification, false); - engine.LoadScript(verifiable.Scripts[i].InvocationScript, true); - while (!engine.State.HasFlag(VMState.HALT) && !engine.State.HasFlag(VMState.FAULT)) - { - if (engine.CurrentContext.InstructionPointer < engine.CurrentContext.Script.Length) - { - if (++nOpCount > max_steps) return false; - if (engine.CurrentContext.NextInstruction == OpCode.CHECKMULTISIG) - { - if (engine.EvaluationStack.Count == 0) return false; - int n = (int)engine.EvaluationStack.Peek().GetBigInteger(); - if (n < 1) return false; - nOpCount += n; - if (nOpCount > max_steps) return false; - } - } - engine.StepInto(); - } - if (engine.State.HasFlag(VMState.FAULT)) return false; - if (engine.EvaluationStack.Count != 1 || !engine.EvaluationStack.Pop().GetBoolean()) return false; - } - return true; - } - } -} +using Neo.Cryptography; +using Neo.SmartContract; +using Neo.VM; +using Neo.Wallets; +using System; +using System.IO; +using System.Linq; + +namespace Neo.Core +{ + /// + /// 包含一系列签名与验证的扩展方法 + /// + public static class Helper + { + public static byte[] GetHashData(this IVerifiable verifiable) + { + using (MemoryStream ms = new MemoryStream()) + using (BinaryWriter writer = new BinaryWriter(ms)) + { + verifiable.SerializeUnsigned(writer); + writer.Flush(); + return ms.ToArray(); + } + } + + /// + /// 根据传入的账户信息,对可签名的对象进行签名 + /// + /// 要签名的数据 + /// 用于签名的账户 + /// 返回签名后的结果 + public static byte[] Sign(this IVerifiable verifiable, KeyPair key) + { + using (key.Decrypt()) + { + return Crypto.Default.Sign(verifiable.GetHashData(), key.PrivateKey, key.PublicKey.EncodePoint(false).Skip(1).ToArray()); + } + } + + public static UInt160 ToScriptHash(this byte[] script) + { + return new UInt160(Crypto.Default.Hash160(script)); + } + + internal static bool VerifyScripts(this IVerifiable verifiable) + { + UInt160[] hashes; + try + { + hashes = verifiable.GetScriptHashesForVerifying(); + } + catch (InvalidOperationException) + { + return false; + } + if (hashes.Length != verifiable.Scripts.Length) return false; + for (int i = 0; i < hashes.Length; i++) + { + byte[] verification = verifiable.Scripts[i].VerificationScript; + if (verification.Length == 0) + { + using (ScriptBuilder sb = new ScriptBuilder()) + { + sb.EmitAppCall(hashes[i].ToArray()); + verification = sb.ToArray(); + } + } + else + { + if (hashes[i] != verification.ToScriptHash()) return false; + } + ApplicationEngine engine = new ApplicationEngine(verifiable, Blockchain.Default, StateReader.Default, Fixed8.Zero); + engine.LoadScript(verification, false); + engine.LoadScript(verifiable.Scripts[i].InvocationScript, true); + if (!engine.Execute()) return false; + if (engine.EvaluationStack.Count != 1 || !engine.EvaluationStack.Pop().GetBoolean()) return false; + } + return true; + } + } +} diff --git a/src/AntShares/Core/ICloneable.cs b/neo/Core/ICloneable.cs similarity index 79% rename from src/AntShares/Core/ICloneable.cs rename to neo/Core/ICloneable.cs index cbff8e0a06..d642ffeda5 100644 --- a/src/AntShares/Core/ICloneable.cs +++ b/neo/Core/ICloneable.cs @@ -1,4 +1,4 @@ -namespace AntShares.Core +namespace Neo.Core { public interface ICloneable { diff --git a/src/AntShares/Core/ICode.cs b/neo/Core/ICode.cs similarity index 88% rename from src/AntShares/Core/ICode.cs rename to neo/Core/ICode.cs index 51d6957202..e8cd129542 100644 --- a/src/AntShares/Core/ICode.cs +++ b/neo/Core/ICode.cs @@ -1,4 +1,4 @@ -namespace AntShares.Core +namespace Neo.Core { public interface ICode { diff --git a/src/AntShares/Core/IVerifiable.cs b/neo/Core/IVerifiable.cs similarity index 93% rename from src/AntShares/Core/IVerifiable.cs rename to neo/Core/IVerifiable.cs index 0b2ad9a7ce..dcf9fc72b4 100644 --- a/src/AntShares/Core/IVerifiable.cs +++ b/neo/Core/IVerifiable.cs @@ -1,8 +1,8 @@ -using AntShares.IO; -using AntShares.VM; +using Neo.IO; +using Neo.VM; using System.IO; -namespace AntShares.Core +namespace Neo.Core { /// /// 为需要签名的数据提供一个接口 diff --git a/src/AntShares/Core/InvocationTransaction.cs b/neo/Core/InvocationTransaction.cs similarity index 83% rename from src/AntShares/Core/InvocationTransaction.cs rename to neo/Core/InvocationTransaction.cs index 869fcbc3f1..e172e774c0 100644 --- a/src/AntShares/Core/InvocationTransaction.cs +++ b/neo/Core/InvocationTransaction.cs @@ -1,10 +1,10 @@ -using AntShares.IO; -using AntShares.IO.Json; +using Neo.IO; +using Neo.IO.Json; using System; using System.Collections.Generic; using System.IO; -namespace AntShares.Core +namespace Neo.Core { public class InvocationTransaction : Transaction { @@ -13,7 +13,7 @@ public class InvocationTransaction : Transaction public override int Size => base.Size + Script.GetVarSize(); - public override Fixed8 NetworkFee => Gas; + public override Fixed8 SystemFee => Gas; public InvocationTransaction() : base(TransactionType.InvocationTransaction) @@ -22,7 +22,9 @@ public InvocationTransaction() protected override void DeserializeExclusiveData(BinaryReader reader) { + if (Version > 1) throw new FormatException(); Script = reader.ReadVarBytes(65536); + if (Script.Length == 0) throw new FormatException(); if (Version >= 1) { Gas = reader.ReadSerializable(); @@ -53,7 +55,7 @@ public override bool Verify(IEnumerable mempool) { // Not available in MAINNET until tested if (Settings.Default.Magic != 1953787457) return false; - if (Version == 0) return false; + if (Gas.GetData() % 100000000 != 0) return false; return base.Verify(mempool); } } diff --git a/src/AntShares/Core/IssueTransaction.cs b/neo/Core/IssueTransaction.cs similarity index 92% rename from src/AntShares/Core/IssueTransaction.cs rename to neo/Core/IssueTransaction.cs index 241f14544b..f4dfeab353 100644 --- a/src/AntShares/Core/IssueTransaction.cs +++ b/neo/Core/IssueTransaction.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { /// /// 用于分发资产的特殊交易 @@ -27,6 +28,11 @@ public IssueTransaction() { } + protected override void DeserializeExclusiveData(BinaryReader reader) + { + if (Version != 0) throw new FormatException(); + } + /// /// 获取需要校验的脚本散列值 /// diff --git a/src/AntShares/Core/MinerTransaction.cs b/neo/Core/MinerTransaction.cs similarity index 94% rename from src/AntShares/Core/MinerTransaction.cs rename to neo/Core/MinerTransaction.cs index 4853f79628..158b246f41 100644 --- a/src/AntShares/Core/MinerTransaction.cs +++ b/neo/Core/MinerTransaction.cs @@ -1,9 +1,9 @@ -using AntShares.IO.Json; +using Neo.IO.Json; using System; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { /// /// 用于分配字节费的特殊交易 @@ -30,6 +30,7 @@ public MinerTransaction() /// 数据来源 protected override void DeserializeExclusiveData(BinaryReader reader) { + if (Version != 0) throw new FormatException(); this.Nonce = reader.ReadUInt32(); } diff --git a/src/AntShares/Core/PublishTransaction.cs b/neo/Core/PublishTransaction.cs similarity index 87% rename from src/AntShares/Core/PublishTransaction.cs rename to neo/Core/PublishTransaction.cs index bd275575a5..eaceb808b3 100644 --- a/src/AntShares/Core/PublishTransaction.cs +++ b/neo/Core/PublishTransaction.cs @@ -1,10 +1,13 @@ -using AntShares.IO; -using AntShares.IO.Json; +using Neo.IO; +using Neo.IO.Json; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { + [Obsolete] public class PublishTransaction : Transaction { public FunctionCode Code; @@ -24,11 +27,12 @@ public PublishTransaction() protected override void DeserializeExclusiveData(BinaryReader reader) { + if (Version > 1) throw new FormatException(); Code = reader.ReadSerializable(); - if (Version == 0) - NeedStorage = false; - else + if (Version >= 1) NeedStorage = reader.ReadBoolean(); + else + NeedStorage = false; Name = reader.ReadVarString(252); CodeVersion = reader.ReadVarString(252); Author = reader.ReadVarString(252); @@ -63,5 +67,10 @@ public override JObject ToJson() json["contract"]["description"] = Description; return json; } + + public override bool Verify(IEnumerable mempool) + { + return false; + } } } diff --git a/src/AntShares/Core/RegisterTransaction.cs b/neo/Core/RegisterTransaction.cs similarity index 83% rename from src/AntShares/Core/RegisterTransaction.cs rename to neo/Core/RegisterTransaction.cs index d2cc15915d..507c2ffc22 100644 --- a/src/AntShares/Core/RegisterTransaction.cs +++ b/neo/Core/RegisterTransaction.cs @@ -1,17 +1,15 @@ -using AntShares.Cryptography.ECC; -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.Wallets; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.IO.Json; +using Neo.Wallets; using System; using System.Collections.Generic; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { - /// - /// 用于资产登记的特殊交易 - /// + [Obsolete] public class RegisterTransaction : Transaction { /// @@ -64,6 +62,7 @@ public RegisterTransaction() /// 数据来源 protected override void DeserializeExclusiveData(BinaryReader reader) { + if (Version != 0) throw new FormatException(); AssetType = (AssetType)reader.ReadByte(); Name = reader.ReadVarString(1024); Amount = reader.ReadSerializable(); @@ -133,18 +132,7 @@ public override JObject ToJson() public override bool Verify(IEnumerable mempool) { - if (!base.Verify(mempool)) return false; - if (!Enum.IsDefined(typeof(AssetType), AssetType) || AssetType == AssetType.CreditFlag || AssetType == AssetType.DutyFlag) - return false; - if (Amount == Fixed8.Zero || Amount < -Fixed8.Satoshi) return false; - if (AssetType == AssetType.Invoice && Amount != -Fixed8.Satoshi) - return false; - if (Precision > 8) return false; - if (AssetType == AssetType.Share && Precision != 0) - return false; - if (Amount != -Fixed8.Satoshi && Amount.GetData() % (long)Math.Pow(10, 8 - Precision) != 0) - return false; - return true; + return false; } } } diff --git a/src/AntShares/Core/SpentCoin.cs b/neo/Core/SpentCoin.cs similarity index 87% rename from src/AntShares/Core/SpentCoin.cs rename to neo/Core/SpentCoin.cs index f01adb982d..ba6a900aa7 100644 --- a/src/AntShares/Core/SpentCoin.cs +++ b/neo/Core/SpentCoin.cs @@ -1,4 +1,4 @@ -namespace AntShares.Core +namespace Neo.Core { public class SpentCoin { diff --git a/src/AntShares/Core/SpentCoinState.cs b/neo/Core/SpentCoinState.cs similarity index 96% rename from src/AntShares/Core/SpentCoinState.cs rename to neo/Core/SpentCoinState.cs index 09a641e34b..940a7ea568 100644 --- a/src/AntShares/Core/SpentCoinState.cs +++ b/neo/Core/SpentCoinState.cs @@ -1,8 +1,8 @@ -using AntShares.IO; +using Neo.IO; using System.Collections.Generic; using System.IO; -namespace AntShares.Core +namespace Neo.Core { public class SpentCoinState : StateBase { diff --git a/src/AntShares/Core/StateBase.cs b/neo/Core/StateBase.cs similarity index 72% rename from src/AntShares/Core/StateBase.cs rename to neo/Core/StateBase.cs index fd2fa19097..f50ae73df2 100644 --- a/src/AntShares/Core/StateBase.cs +++ b/neo/Core/StateBase.cs @@ -1,10 +1,9 @@ -using AntShares.IO; -using AntShares.VM; +using Neo.IO; +using Neo.VM; using System; using System.IO; -using System.Linq; -namespace AntShares.Core +namespace Neo.Core { public abstract class StateBase : IInteropInterface, ISerializable { @@ -21,10 +20,5 @@ public virtual void Serialize(BinaryWriter writer) { writer.Write(StateVersion); } - - byte[] IInteropInterface.ToArray() - { - return this.ToArray(); - } } } diff --git a/src/AntShares/Core/StorageItem.cs b/neo/Core/StorageItem.cs similarity index 94% rename from src/AntShares/Core/StorageItem.cs rename to neo/Core/StorageItem.cs index 94e24c5f7b..257dbf936b 100644 --- a/src/AntShares/Core/StorageItem.cs +++ b/neo/Core/StorageItem.cs @@ -1,7 +1,7 @@ -using AntShares.IO; +using Neo.IO; using System.IO; -namespace AntShares.Core +namespace Neo.Core { public class StorageItem : StateBase, ICloneable { diff --git a/src/AntShares/Core/StorageKey.cs b/neo/Core/StorageKey.cs similarity index 94% rename from src/AntShares/Core/StorageKey.cs rename to neo/Core/StorageKey.cs index 88abd01b96..3babef23c3 100644 --- a/src/AntShares/Core/StorageKey.cs +++ b/neo/Core/StorageKey.cs @@ -1,10 +1,10 @@ -using AntShares.Cryptography; -using AntShares.IO; +using Neo.Cryptography; +using Neo.IO; using System; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { public class StorageKey : IEquatable, ISerializable { diff --git a/src/AntShares/Core/Transaction.cs b/neo/Core/Transaction.cs similarity index 95% rename from src/AntShares/Core/Transaction.cs rename to neo/Core/Transaction.cs index b13199dc7c..ae5d19c55a 100644 --- a/src/AntShares/Core/Transaction.cs +++ b/neo/Core/Transaction.cs @@ -1,382 +1,379 @@ -using AntShares.Cryptography; -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.Network; -using AntShares.VM; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace AntShares.Core -{ - /// - /// 一切交易的基类 - /// - public abstract class Transaction : IEquatable, IInventory - { - /// - /// 交易类型 - /// - public readonly TransactionType Type; - /// - /// 版本 - /// - public byte Version; - /// - /// 该交易所具备的额外特性 - /// - public TransactionAttribute[] Attributes; - /// - /// 输入列表 - /// - public CoinReference[] Inputs; - /// - /// 输出列表 - /// - public TransactionOutput[] Outputs; - /// - /// 用于验证该交易的脚本列表 - /// - public Witness[] Scripts { get; set; } - - private UInt256 _hash = null; - public UInt256 Hash - { - get - { - if (_hash == null) - { - _hash = new UInt256(Crypto.Default.Hash256(this.GetHashData())); - } - return _hash; - } - } - - /// - /// 清单类型 - /// - InventoryType IInventory.InventoryType => InventoryType.TX; - - private Fixed8 _network_fee = -Fixed8.Satoshi; - public virtual Fixed8 NetworkFee - { - get - { - if (_network_fee == -Fixed8.Satoshi) - { - Fixed8 input = References.Values.Where(p => p.AssetId.Equals(Blockchain.SystemCoin.Hash)).Sum(p => p.Value); - Fixed8 output = Outputs.Where(p => p.AssetId.Equals(Blockchain.SystemCoin.Hash)).Sum(p => p.Value); - _network_fee = input - output - SystemFee; - } - return _network_fee; - } - } - - private IReadOnlyDictionary _references; - /// - /// 每一个交易输入所引用的交易输出 - /// - public IReadOnlyDictionary References - { - get - { - if (_references == null) - { - Dictionary dictionary = new Dictionary(); - foreach (var group in Inputs.GroupBy(p => p.PrevHash)) - { - Transaction tx = Blockchain.Default.GetTransaction(group.Key); - if (tx == null) return null; - foreach (var reference in group.Select(p => new - { - Input = p, - Output = tx.Outputs[p.PrevIndex] - })) - { - dictionary.Add(reference.Input, reference.Output); - } - } - _references = dictionary; - } - return _references; - } - } - - public virtual int Size => sizeof(TransactionType) + sizeof(byte) + Attributes.GetVarSize() + Inputs.GetVarSize() + Outputs.GetVarSize() + Scripts.GetVarSize(); - - /// - /// 系统费用 - /// - public virtual Fixed8 SystemFee - { - get - { - if (Settings.Default.SystemFee.ContainsKey(Type)) - return Settings.Default.SystemFee[Type]; - return Fixed8.Zero; - } - } - - /// - /// 用指定的类型初始化Transaction对象 - /// - /// 交易类型 - protected Transaction(TransactionType type) - { - this.Type = type; - } - - /// - /// 反序列化 - /// - /// 数据来源 - void ISerializable.Deserialize(BinaryReader reader) - { - ((IVerifiable)this).DeserializeUnsigned(reader); - Scripts = reader.ReadSerializableArray(); - OnDeserialized(); - } - - /// - /// 反序列化交易中的额外数据 - /// - /// 数据来源 - protected virtual void DeserializeExclusiveData(BinaryReader reader) - { - } - - /// - /// 从指定的字节数组反序列化一笔交易 - /// - /// 字节数组 - /// 偏移量,反序列化从该偏移量处开始 - /// 返回反序列化后的结果 - public static Transaction DeserializeFrom(byte[] value, int offset = 0) - { - using (MemoryStream ms = new MemoryStream(value, offset, value.Length - offset, false)) - using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) - { - return DeserializeFrom(reader); - } - } - - /// - /// 反序列化 - /// - /// 数据来源 - /// 返回反序列化后的结果 - internal static Transaction DeserializeFrom(BinaryReader reader) - { - TransactionType type = (TransactionType)reader.ReadByte(); - string typeName = string.Format("{0}.{1}", typeof(Transaction).Namespace, type); - Transaction transaction = typeof(Transaction).GetTypeInfo().Assembly.CreateInstance(typeName) as Transaction; - if (transaction == null) - throw new FormatException(); - transaction.DeserializeUnsignedWithoutType(reader); - transaction.Scripts = reader.ReadSerializableArray(); - transaction.OnDeserialized(); - return transaction; - } - - void IVerifiable.DeserializeUnsigned(BinaryReader reader) - { - if ((TransactionType)reader.ReadByte() != Type) - throw new FormatException(); - DeserializeUnsignedWithoutType(reader); - } - - private void DeserializeUnsignedWithoutType(BinaryReader reader) - { - Version = reader.ReadByte(); - DeserializeExclusiveData(reader); - Attributes = reader.ReadSerializableArray(); - Inputs = reader.ReadSerializableArray(); - Outputs = reader.ReadSerializableArray(ushort.MaxValue + 1); - } - - public bool Equals(Transaction other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Hash.Equals(other.Hash); - } - - public override bool Equals(object obj) - { - return Equals(obj as Transaction); - } - - public override int GetHashCode() - { - return Hash.GetHashCode(); - } - - byte[] IScriptContainer.GetMessage() - { - return this.GetHashData(); - } - - /// - /// 获取需要校验的脚本散列值 - /// - /// 返回需要校验的脚本散列值 - public virtual UInt160[] GetScriptHashesForVerifying() - { - if (References == null) throw new InvalidOperationException(); - HashSet hashes = new HashSet(Inputs.Select(p => References[p].ScriptHash)); - hashes.UnionWith(Attributes.Where(p => p.Usage == TransactionAttributeUsage.Script).Select(p => new UInt160(p.Data))); - foreach (var group in Outputs.GroupBy(p => p.AssetId)) - { - AssetState asset = Blockchain.Default.GetAssetState(group.Key); - if (asset == null) throw new InvalidOperationException(); - if (asset.AssetType.HasFlag(AssetType.DutyFlag)) - { - hashes.UnionWith(group.Select(p => p.ScriptHash)); - } - } - return hashes.OrderBy(p => p).ToArray(); - } - - /// - /// 获取交易后各资产的变化量 - /// - /// 返回交易后各资产的变化量 - public IEnumerable GetTransactionResults() - { - if (References == null) return null; - return References.Values.Select(p => new - { - AssetId = p.AssetId, - Value = p.Value - }).Concat(Outputs.Select(p => new - { - AssetId = p.AssetId, - Value = -p.Value - })).GroupBy(p => p.AssetId, (k, g) => new TransactionResult - { - AssetId = k, - Amount = g.Sum(p => p.Value) - }).Where(p => p.Amount != Fixed8.Zero); - } - - /// - /// 通知子类反序列化完毕 - /// - protected virtual void OnDeserialized() - { - } - - /// - /// 序列化 - /// - /// 存放序列化后的结果 - void ISerializable.Serialize(BinaryWriter writer) - { - ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write(Scripts); - } - - /// - /// 序列化交易中的额外数据 - /// - /// 存放序列化后的结果 - protected virtual void SerializeExclusiveData(BinaryWriter writer) - { - } - - void IVerifiable.SerializeUnsigned(BinaryWriter writer) - { - writer.Write((byte)Type); - writer.Write(Version); - SerializeExclusiveData(writer); - writer.Write(Attributes); - writer.Write(Inputs); - writer.Write(Outputs); - } - - byte[] IInteropInterface.ToArray() - { - return this.ToArray(); - } - - /// - /// 变成json对象 - /// - /// 返回json对象 - public virtual JObject ToJson() - { - JObject json = new JObject(); - json["txid"] = Hash.ToString(); - json["size"] = Size; - json["type"] = Type; - json["version"] = Version; - json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); - json["vin"] = Inputs.Select(p => p.ToJson()).ToArray(); - json["vout"] = Outputs.Select((p, i) => p.ToJson((ushort)i)).ToArray(); - json["sys_fee"] = SystemFee.ToString(); - json["net_fee"] = NetworkFee.ToString(); - json["scripts"] = Scripts.Select(p => p.ToJson()).ToArray(); - return json; - } - - bool IInventory.Verify() - { - return Verify(Enumerable.Empty()); - } - - /// - /// 验证交易 - /// - /// 返回验证的结果 - public virtual bool Verify(IEnumerable mempool) - { - for (int i = 1; i < Inputs.Length; i++) - for (int j = 0; j < i; j++) - if (Inputs[i].PrevHash == Inputs[j].PrevHash && Inputs[i].PrevIndex == Inputs[j].PrevIndex) - return false; - if (mempool.Where(p => p != this).SelectMany(p => p.Inputs).Intersect(Inputs).Count() > 0) - return false; - if (Blockchain.Default.IsDoubleSpend(this)) - return false; - foreach (var group in Outputs.GroupBy(p => p.AssetId)) - { - AssetState asset = Blockchain.Default.GetAssetState(group.Key); - if (asset == null) return false; - foreach (TransactionOutput output in group) - if (output.Value.GetData() % (long)Math.Pow(10, 8 - asset.Precision) != 0) - return false; - } - TransactionResult[] results = GetTransactionResults()?.ToArray(); - if (results == null) return false; - TransactionResult[] results_destroy = results.Where(p => p.Amount > Fixed8.Zero).ToArray(); - if (results_destroy.Length > 1) return false; - if (results_destroy.Length == 1 && results_destroy[0].AssetId != Blockchain.SystemCoin.Hash) - return false; - if (SystemFee > Fixed8.Zero && (results_destroy.Length == 0 || results_destroy[0].Amount < SystemFee)) - return false; - TransactionResult[] results_issue = results.Where(p => p.Amount < Fixed8.Zero).ToArray(); - switch (Type) - { - case TransactionType.MinerTransaction: - case TransactionType.ClaimTransaction: - if (results_issue.Any(p => p.AssetId != Blockchain.SystemCoin.Hash)) - return false; - break; - case TransactionType.IssueTransaction: - if (results_issue.Any(p => p.AssetId == Blockchain.SystemCoin.Hash)) - return false; - break; - default: - if (results_issue.Length > 0) - return false; - break; - } - if (Attributes.Count(p => p.Usage == TransactionAttributeUsage.ECDH02 || p.Usage == TransactionAttributeUsage.ECDH03) > 1) - return false; - return this.VerifyScripts(); - } - } -} +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; +using Neo.Network; +using Neo.VM; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Neo.Core +{ + /// + /// 一切交易的基类 + /// + public abstract class Transaction : IEquatable, IInventory + { + /// + /// 交易类型 + /// + public readonly TransactionType Type; + /// + /// 版本 + /// + public byte Version; + /// + /// 该交易所具备的额外特性 + /// + public TransactionAttribute[] Attributes; + /// + /// 输入列表 + /// + public CoinReference[] Inputs; + /// + /// 输出列表 + /// + public TransactionOutput[] Outputs; + /// + /// 用于验证该交易的脚本列表 + /// + public Witness[] Scripts { get; set; } + + private UInt256 _hash = null; + public UInt256 Hash + { + get + { + if (_hash == null) + { + _hash = new UInt256(Crypto.Default.Hash256(this.GetHashData())); + } + return _hash; + } + } + + /// + /// 清单类型 + /// + InventoryType IInventory.InventoryType => InventoryType.TX; + + private Fixed8 _network_fee = -Fixed8.Satoshi; + public virtual Fixed8 NetworkFee + { + get + { + if (_network_fee == -Fixed8.Satoshi) + { + Fixed8 input = References.Values.Where(p => p.AssetId.Equals(Blockchain.SystemCoin.Hash)).Sum(p => p.Value); + Fixed8 output = Outputs.Where(p => p.AssetId.Equals(Blockchain.SystemCoin.Hash)).Sum(p => p.Value); + _network_fee = input - output - SystemFee; + } + return _network_fee; + } + } + + private IReadOnlyDictionary _references; + /// + /// 每一个交易输入所引用的交易输出 + /// + public IReadOnlyDictionary References + { + get + { + if (_references == null) + { + Dictionary dictionary = new Dictionary(); + foreach (var group in Inputs.GroupBy(p => p.PrevHash)) + { + Transaction tx = Blockchain.Default.GetTransaction(group.Key); + if (tx == null) return null; + foreach (var reference in group.Select(p => new + { + Input = p, + Output = tx.Outputs[p.PrevIndex] + })) + { + dictionary.Add(reference.Input, reference.Output); + } + } + _references = dictionary; + } + return _references; + } + } + + public virtual int Size => sizeof(TransactionType) + sizeof(byte) + Attributes.GetVarSize() + Inputs.GetVarSize() + Outputs.GetVarSize() + Scripts.GetVarSize(); + + /// + /// 系统费用 + /// + public virtual Fixed8 SystemFee + { + get + { + if (Settings.Default.SystemFee.ContainsKey(Type)) + return Settings.Default.SystemFee[Type]; + return Fixed8.Zero; + } + } + + /// + /// 用指定的类型初始化Transaction对象 + /// + /// 交易类型 + protected Transaction(TransactionType type) + { + this.Type = type; + } + + /// + /// 反序列化 + /// + /// 数据来源 + void ISerializable.Deserialize(BinaryReader reader) + { + ((IVerifiable)this).DeserializeUnsigned(reader); + Scripts = reader.ReadSerializableArray(); + OnDeserialized(); + } + + /// + /// 反序列化交易中的额外数据 + /// + /// 数据来源 + protected virtual void DeserializeExclusiveData(BinaryReader reader) + { + } + + /// + /// 从指定的字节数组反序列化一笔交易 + /// + /// 字节数组 + /// 偏移量,反序列化从该偏移量处开始 + /// 返回反序列化后的结果 + public static Transaction DeserializeFrom(byte[] value, int offset = 0) + { + using (MemoryStream ms = new MemoryStream(value, offset, value.Length - offset, false)) + using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) + { + return DeserializeFrom(reader); + } + } + + /// + /// 反序列化 + /// + /// 数据来源 + /// 返回反序列化后的结果 + internal static Transaction DeserializeFrom(BinaryReader reader) + { + TransactionType type = (TransactionType)reader.ReadByte(); + string typeName = string.Format("{0}.{1}", typeof(Transaction).Namespace, type); + Transaction transaction = typeof(Transaction).GetTypeInfo().Assembly.CreateInstance(typeName) as Transaction; + if (transaction == null) + throw new FormatException(); + transaction.DeserializeUnsignedWithoutType(reader); + transaction.Scripts = reader.ReadSerializableArray(); + transaction.OnDeserialized(); + return transaction; + } + + void IVerifiable.DeserializeUnsigned(BinaryReader reader) + { + if ((TransactionType)reader.ReadByte() != Type) + throw new FormatException(); + DeserializeUnsignedWithoutType(reader); + } + + private void DeserializeUnsignedWithoutType(BinaryReader reader) + { + Version = reader.ReadByte(); + DeserializeExclusiveData(reader); + Attributes = reader.ReadSerializableArray(); + Inputs = reader.ReadSerializableArray(); + Outputs = reader.ReadSerializableArray(ushort.MaxValue + 1); + } + + public bool Equals(Transaction other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Hash.Equals(other.Hash); + } + + public override bool Equals(object obj) + { + return Equals(obj as Transaction); + } + + public override int GetHashCode() + { + return Hash.GetHashCode(); + } + + byte[] IScriptContainer.GetMessage() + { + return this.GetHashData(); + } + + /// + /// 获取需要校验的脚本散列值 + /// + /// 返回需要校验的脚本散列值 + public virtual UInt160[] GetScriptHashesForVerifying() + { + if (References == null) throw new InvalidOperationException(); + HashSet hashes = new HashSet(Inputs.Select(p => References[p].ScriptHash)); + hashes.UnionWith(Attributes.Where(p => p.Usage == TransactionAttributeUsage.Script).Select(p => new UInt160(p.Data))); + foreach (var group in Outputs.GroupBy(p => p.AssetId)) + { + AssetState asset = Blockchain.Default.GetAssetState(group.Key); + if (asset == null) throw new InvalidOperationException(); + if (asset.AssetType.HasFlag(AssetType.DutyFlag)) + { + hashes.UnionWith(group.Select(p => p.ScriptHash)); + } + } + return hashes.OrderBy(p => p).ToArray(); + } + + /// + /// 获取交易后各资产的变化量 + /// + /// 返回交易后各资产的变化量 + public IEnumerable GetTransactionResults() + { + if (References == null) return null; + return References.Values.Select(p => new + { + AssetId = p.AssetId, + Value = p.Value + }).Concat(Outputs.Select(p => new + { + AssetId = p.AssetId, + Value = -p.Value + })).GroupBy(p => p.AssetId, (k, g) => new TransactionResult + { + AssetId = k, + Amount = g.Sum(p => p.Value) + }).Where(p => p.Amount != Fixed8.Zero); + } + + /// + /// 通知子类反序列化完毕 + /// + protected virtual void OnDeserialized() + { + } + + /// + /// 序列化 + /// + /// 存放序列化后的结果 + void ISerializable.Serialize(BinaryWriter writer) + { + ((IVerifiable)this).SerializeUnsigned(writer); + writer.Write(Scripts); + } + + /// + /// 序列化交易中的额外数据 + /// + /// 存放序列化后的结果 + protected virtual void SerializeExclusiveData(BinaryWriter writer) + { + } + + void IVerifiable.SerializeUnsigned(BinaryWriter writer) + { + writer.Write((byte)Type); + writer.Write(Version); + SerializeExclusiveData(writer); + writer.Write(Attributes); + writer.Write(Inputs); + writer.Write(Outputs); + } + + /// + /// 变成json对象 + /// + /// 返回json对象 + public virtual JObject ToJson() + { + JObject json = new JObject(); + json["txid"] = Hash.ToString(); + json["size"] = Size; + json["type"] = Type; + json["version"] = Version; + json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); + json["vin"] = Inputs.Select(p => p.ToJson()).ToArray(); + json["vout"] = Outputs.Select((p, i) => p.ToJson((ushort)i)).ToArray(); + json["sys_fee"] = SystemFee.ToString(); + json["net_fee"] = NetworkFee.ToString(); + json["scripts"] = Scripts.Select(p => p.ToJson()).ToArray(); + return json; + } + + bool IInventory.Verify() + { + return Verify(Enumerable.Empty()); + } + + /// + /// 验证交易 + /// + /// 返回验证的结果 + public virtual bool Verify(IEnumerable mempool) + { + for (int i = 1; i < Inputs.Length; i++) + for (int j = 0; j < i; j++) + if (Inputs[i].PrevHash == Inputs[j].PrevHash && Inputs[i].PrevIndex == Inputs[j].PrevIndex) + return false; + if (mempool.Where(p => p != this).SelectMany(p => p.Inputs).Intersect(Inputs).Count() > 0) + return false; + if (Blockchain.Default.IsDoubleSpend(this)) + return false; + foreach (var group in Outputs.GroupBy(p => p.AssetId)) + { + AssetState asset = Blockchain.Default.GetAssetState(group.Key); + if (asset == null) return false; + if (asset.Expiration <= Blockchain.Default.Height + 1 && asset.AssetType != AssetType.SystemShare && asset.AssetType != AssetType.SystemCoin) + return false; + foreach (TransactionOutput output in group) + if (output.Value.GetData() % (long)Math.Pow(10, 8 - asset.Precision) != 0) + return false; + } + TransactionResult[] results = GetTransactionResults()?.ToArray(); + if (results == null) return false; + TransactionResult[] results_destroy = results.Where(p => p.Amount > Fixed8.Zero).ToArray(); + if (results_destroy.Length > 1) return false; + if (results_destroy.Length == 1 && results_destroy[0].AssetId != Blockchain.SystemCoin.Hash) + return false; + if (SystemFee > Fixed8.Zero && (results_destroy.Length == 0 || results_destroy[0].Amount < SystemFee)) + return false; + TransactionResult[] results_issue = results.Where(p => p.Amount < Fixed8.Zero).ToArray(); + switch (Type) + { + case TransactionType.MinerTransaction: + case TransactionType.ClaimTransaction: + if (results_issue.Any(p => p.AssetId != Blockchain.SystemCoin.Hash)) + return false; + break; + case TransactionType.IssueTransaction: + if (results_issue.Any(p => p.AssetId == Blockchain.SystemCoin.Hash)) + return false; + break; + default: + if (results_issue.Length > 0) + return false; + break; + } + if (Attributes.Count(p => p.Usage == TransactionAttributeUsage.ECDH02 || p.Usage == TransactionAttributeUsage.ECDH03) > 1) + return false; + return this.VerifyScripts(); + } + } +} diff --git a/src/AntShares/Core/TransactionAttribute.cs b/neo/Core/TransactionAttribute.cs similarity index 94% rename from src/AntShares/Core/TransactionAttribute.cs rename to neo/Core/TransactionAttribute.cs index ef339aa8b1..2cc04194d4 100644 --- a/src/AntShares/Core/TransactionAttribute.cs +++ b/neo/Core/TransactionAttribute.cs @@ -1,11 +1,11 @@ -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.VM; +using Neo.IO; +using Neo.IO.Json; +using Neo.VM; using System; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { /// /// 交易特性 @@ -66,11 +66,6 @@ void ISerializable.Serialize(BinaryWriter writer) writer.Write(Data); } - byte[] IInteropInterface.ToArray() - { - return this.ToArray(); - } - /// /// 变成json对象 /// diff --git a/src/AntShares/Core/TransactionAttributeUsage.cs b/neo/Core/TransactionAttributeUsage.cs similarity index 98% rename from src/AntShares/Core/TransactionAttributeUsage.cs rename to neo/Core/TransactionAttributeUsage.cs index 79785a9594..74079f295d 100644 --- a/src/AntShares/Core/TransactionAttributeUsage.cs +++ b/neo/Core/TransactionAttributeUsage.cs @@ -1,4 +1,4 @@ -namespace AntShares.Core +namespace Neo.Core { /// /// 表示交易特性的用途 diff --git a/src/AntShares/Core/TransactionOutput.cs b/neo/Core/TransactionOutput.cs similarity index 88% rename from src/AntShares/Core/TransactionOutput.cs rename to neo/Core/TransactionOutput.cs index 8f05032b57..ad69315fd9 100644 --- a/src/AntShares/Core/TransactionOutput.cs +++ b/neo/Core/TransactionOutput.cs @@ -1,11 +1,11 @@ -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.VM; -using AntShares.Wallets; +using Neo.IO; +using Neo.IO.Json; +using Neo.VM; +using Neo.Wallets; using System; using System.IO; -namespace AntShares.Core +namespace Neo.Core { /// /// 交易输出 @@ -42,11 +42,6 @@ void ISerializable.Serialize(BinaryWriter writer) writer.Write(ScriptHash); } - byte[] IInteropInterface.ToArray() - { - return this.ToArray(); - } - /// /// 将交易输出转变为json对象 /// diff --git a/src/AntShares/Core/TransactionResult.cs b/neo/Core/TransactionResult.cs similarity index 92% rename from src/AntShares/Core/TransactionResult.cs rename to neo/Core/TransactionResult.cs index 060911b4a5..92448610fa 100644 --- a/src/AntShares/Core/TransactionResult.cs +++ b/neo/Core/TransactionResult.cs @@ -1,4 +1,4 @@ -namespace AntShares.Core +namespace Neo.Core { /// /// 交易结果,表示交易中资产的变化量 diff --git a/src/AntShares/Core/TransactionType.cs b/neo/Core/TransactionType.cs similarity index 89% rename from src/AntShares/Core/TransactionType.cs rename to neo/Core/TransactionType.cs index 4c6eea4941..6e6d8f08b4 100644 --- a/src/AntShares/Core/TransactionType.cs +++ b/neo/Core/TransactionType.cs @@ -1,4 +1,4 @@ -namespace AntShares.Core +namespace Neo.Core { /// /// 交易类型 @@ -13,9 +13,6 @@ public enum TransactionType : byte /// 用于分发资产的特殊交易 /// IssueTransaction = 0x01, - /// - /// 用于分配小蚁币的特殊交易 - /// ClaimTransaction = 0x02, /// /// 用于报名成为记账候选人的特殊交易 diff --git a/src/AntShares/Core/UnspentCoinState.cs b/neo/Core/UnspentCoinState.cs similarity index 92% rename from src/AntShares/Core/UnspentCoinState.cs rename to neo/Core/UnspentCoinState.cs index a94e51cfad..754567ff86 100644 --- a/src/AntShares/Core/UnspentCoinState.cs +++ b/neo/Core/UnspentCoinState.cs @@ -1,8 +1,8 @@ -using AntShares.IO; +using Neo.IO; using System.IO; using System.Linq; -namespace AntShares.Core +namespace Neo.Core { public class UnspentCoinState : StateBase { diff --git a/src/AntShares/Core/ValidatorState.cs b/neo/Core/ValidatorState.cs similarity index 93% rename from src/AntShares/Core/ValidatorState.cs rename to neo/Core/ValidatorState.cs index ee3740fb53..63d4d9082c 100644 --- a/src/AntShares/Core/ValidatorState.cs +++ b/neo/Core/ValidatorState.cs @@ -1,9 +1,9 @@ -using AntShares.Cryptography.ECC; -using AntShares.IO; -using System; +using Neo.Cryptography.ECC; +using Neo.IO; +using System; using System.IO; -namespace AntShares.Core +namespace Neo.Core { public class ValidatorState : StateBase, ICloneable, IEquatable { @@ -23,36 +23,36 @@ public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); PublicKey = ECPoint.DeserializeFrom(reader, ECCurve.Secp256r1); - } - - public bool Equals(ValidatorState other) - { - if (ReferenceEquals(this, other)) return true; - if (ReferenceEquals(null, other)) return false; - return PublicKey.Equals(other.PublicKey); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) return true; - if (ReferenceEquals(null, obj)) return false; - return Equals(obj as ValidatorState); - } + } + + public bool Equals(ValidatorState other) + { + if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) return false; + return PublicKey.Equals(other.PublicKey); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(this, obj)) return true; + if (ReferenceEquals(null, obj)) return false; + return Equals(obj as ValidatorState); + } void ICloneable.FromReplica(ValidatorState replica) { PublicKey = replica.PublicKey; } - public override int GetHashCode() - { - return PublicKey.GetHashCode(); + public override int GetHashCode() + { + return PublicKey.GetHashCode(); } public override void Serialize(BinaryWriter writer) { base.Serialize(writer); writer.Write(PublicKey); - } + } } } diff --git a/src/AntShares/Core/VerificationCode.cs b/neo/Core/VerificationCode.cs similarity index 95% rename from src/AntShares/Core/VerificationCode.cs rename to neo/Core/VerificationCode.cs index 5e3223b912..9f1673ad66 100644 --- a/src/AntShares/Core/VerificationCode.cs +++ b/neo/Core/VerificationCode.cs @@ -1,4 +1,4 @@ -namespace AntShares.Core +namespace Neo.Core { public class VerificationCode : ICode { diff --git a/src/AntShares/Core/VoteState.cs b/neo/Core/VoteState.cs similarity index 79% rename from src/AntShares/Core/VoteState.cs rename to neo/Core/VoteState.cs index 2a8eea17d1..e2de38749a 100644 --- a/src/AntShares/Core/VoteState.cs +++ b/neo/Core/VoteState.cs @@ -1,6 +1,6 @@ -using AntShares.Cryptography.ECC; +using Neo.Cryptography.ECC; -namespace AntShares.Core +namespace Neo.Core { /// /// 投票信息 diff --git a/src/AntShares/Core/Witness.cs b/neo/Core/Witness.cs similarity index 82% rename from src/AntShares/Core/Witness.cs rename to neo/Core/Witness.cs index 38cd18b370..2904ac85ec 100644 --- a/src/AntShares/Core/Witness.cs +++ b/neo/Core/Witness.cs @@ -1,8 +1,8 @@ -using AntShares.IO; -using AntShares.IO.Json; +using Neo.IO; +using Neo.IO.Json; using System.IO; -namespace AntShares.Core +namespace Neo.Core { public class Witness : ISerializable { @@ -13,8 +13,8 @@ public class Witness : ISerializable void ISerializable.Deserialize(BinaryReader reader) { - InvocationScript = reader.ReadVarBytes(); - VerificationScript = reader.ReadVarBytes(); + InvocationScript = reader.ReadVarBytes(65536); + VerificationScript = reader.ReadVarBytes(65536); } void ISerializable.Serialize(BinaryWriter writer) diff --git a/src/AntShares/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs similarity index 98% rename from src/AntShares/Cryptography/Base58.cs rename to neo/Cryptography/Base58.cs index e27e25f046..1d8107c1ff 100644 --- a/src/AntShares/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -3,7 +3,7 @@ using System.Numerics; using System.Text; -namespace AntShares.Cryptography +namespace Neo.Cryptography { public static class Base58 { diff --git a/src/AntShares/Cryptography/BloomFilter.cs b/neo/Cryptography/BloomFilter.cs similarity index 97% rename from src/AntShares/Cryptography/BloomFilter.cs rename to neo/Cryptography/BloomFilter.cs index 5b407a13a7..913a6838ea 100644 --- a/src/AntShares/Cryptography/BloomFilter.cs +++ b/neo/Cryptography/BloomFilter.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Linq; -namespace AntShares.Cryptography +namespace Neo.Cryptography { public class BloomFilter { diff --git a/src/AntShares/Cryptography/Crypto.cs b/neo/Cryptography/Crypto.cs similarity index 97% rename from src/AntShares/Cryptography/Crypto.cs rename to neo/Cryptography/Crypto.cs index 8af307f3da..a06b89b56c 100644 --- a/src/AntShares/Cryptography/Crypto.cs +++ b/neo/Cryptography/Crypto.cs @@ -1,10 +1,9 @@ -using AntShares.Cryptography; -using AntShares.VM; +using Neo.VM; using System; using System.Linq; using System.Security.Cryptography; -namespace AntShares.Cryptography +namespace Neo.Cryptography { public class Crypto : ICrypto { diff --git a/src/AntShares/Cryptography/ECC/ECCurve.cs b/neo/Cryptography/ECC/ECCurve.cs similarity index 98% rename from src/AntShares/Cryptography/ECC/ECCurve.cs rename to neo/Cryptography/ECC/ECCurve.cs index 045ae8bbae..aa4a956f76 100644 --- a/src/AntShares/Cryptography/ECC/ECCurve.cs +++ b/neo/Cryptography/ECC/ECCurve.cs @@ -1,7 +1,7 @@ using System.Globalization; using System.Numerics; -namespace AntShares.Cryptography.ECC +namespace Neo.Cryptography.ECC { /// /// ECC椭圆曲线参数 diff --git a/src/AntShares/Cryptography/ECC/ECDsa.cs b/neo/Cryptography/ECC/ECDsa.cs similarity index 99% rename from src/AntShares/Cryptography/ECC/ECDsa.cs rename to neo/Cryptography/ECC/ECDsa.cs index ff734cffba..8e0f028e99 100644 --- a/src/AntShares/Cryptography/ECC/ECDsa.cs +++ b/neo/Cryptography/ECC/ECDsa.cs @@ -3,7 +3,7 @@ using System.Numerics; using System.Security.Cryptography; -namespace AntShares.Cryptography.ECC +namespace Neo.Cryptography.ECC { /// /// 提供椭圆曲线数字签名算法(ECDSA)的功能 diff --git a/src/AntShares/Cryptography/ECC/ECFieldElement.cs b/neo/Cryptography/ECC/ECFieldElement.cs similarity index 99% rename from src/AntShares/Cryptography/ECC/ECFieldElement.cs rename to neo/Cryptography/ECC/ECFieldElement.cs index f06b75b251..4ce303dcc8 100644 --- a/src/AntShares/Cryptography/ECC/ECFieldElement.cs +++ b/neo/Cryptography/ECC/ECFieldElement.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Numerics; -namespace AntShares.Cryptography.ECC +namespace Neo.Cryptography.ECC { internal class ECFieldElement : IComparable, IEquatable { diff --git a/src/AntShares/Cryptography/ECC/ECPoint.cs b/neo/Cryptography/ECC/ECPoint.cs similarity index 98% rename from src/AntShares/Cryptography/ECC/ECPoint.cs rename to neo/Cryptography/ECC/ECPoint.cs index 4c577bf890..9e77bd7e26 100644 --- a/src/AntShares/Cryptography/ECC/ECPoint.cs +++ b/neo/Cryptography/ECC/ECPoint.cs @@ -1,17 +1,17 @@ -using AntShares.IO; +using Neo.IO; using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; -namespace AntShares.Cryptography.ECC +namespace Neo.Cryptography.ECC { public class ECPoint : IComparable, IEquatable, ISerializable { - internal readonly ECFieldElement X, Y; + internal ECFieldElement X, Y; internal readonly ECCurve Curve; - + /// /// 判断是否为无穷远点 /// @@ -22,6 +22,11 @@ public bool IsInfinity public int Size => IsInfinity ? 1 : 33; + public ECPoint() + : this(null, null, ECCurve.Secp256r1) + { + } + internal ECPoint(ECFieldElement x, ECFieldElement y, ECCurve curve) { if ((x != null && y == null) || (x == null && y != null)) @@ -117,7 +122,9 @@ private static ECPoint DecompressPoint(int yTilde, BigInteger X1, ECCurve curve) void ISerializable.Deserialize(BinaryReader reader) { - throw new NotSupportedException(); + ECPoint p = DeserializeFrom(reader, Curve); + X = p.X; + Y = p.Y; } /// diff --git a/src/AntShares/Cryptography/Helper.cs b/neo/Cryptography/Helper.cs similarity index 99% rename from src/AntShares/Cryptography/Helper.cs rename to neo/Cryptography/Helper.cs index d1762d322b..71a5b15f44 100644 --- a/src/AntShares/Cryptography/Helper.cs +++ b/neo/Cryptography/Helper.cs @@ -7,7 +7,7 @@ using System.Text; using System.Threading; -namespace AntShares.Cryptography +namespace Neo.Cryptography { /// /// 包含一系列密码学算法的扩展方法 diff --git a/src/AntShares/Cryptography/MerkleTree.cs b/neo/Cryptography/MerkleTree.cs similarity index 99% rename from src/AntShares/Cryptography/MerkleTree.cs rename to neo/Cryptography/MerkleTree.cs index 13949ded90..76b2ac198a 100644 --- a/src/AntShares/Cryptography/MerkleTree.cs +++ b/neo/Cryptography/MerkleTree.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -namespace AntShares.Cryptography +namespace Neo.Cryptography { /// /// 哈希树 diff --git a/src/AntShares/Cryptography/MerkleTreeNode.cs b/neo/Cryptography/MerkleTreeNode.cs similarity index 89% rename from src/AntShares/Cryptography/MerkleTreeNode.cs rename to neo/Cryptography/MerkleTreeNode.cs index f38af16180..6bfc75c8d1 100644 --- a/src/AntShares/Cryptography/MerkleTreeNode.cs +++ b/neo/Cryptography/MerkleTreeNode.cs @@ -1,4 +1,4 @@ -namespace AntShares.Cryptography +namespace Neo.Cryptography { internal class MerkleTreeNode { diff --git a/src/AntShares/Cryptography/Murmur3.cs b/neo/Cryptography/Murmur3.cs similarity index 98% rename from src/AntShares/Cryptography/Murmur3.cs rename to neo/Cryptography/Murmur3.cs index cbfc4b55ff..d8e5e581c3 100644 --- a/src/AntShares/Cryptography/Murmur3.cs +++ b/neo/Cryptography/Murmur3.cs @@ -2,7 +2,7 @@ using System.Runtime.CompilerServices; using System.Security.Cryptography; -namespace AntShares.Cryptography +namespace Neo.Cryptography { public class Murmur3 : HashAlgorithm { diff --git a/src/AntShares/Cryptography/ProtectedMemoryContext.cs b/neo/Cryptography/ProtectedMemoryContext.cs similarity index 96% rename from src/AntShares/Cryptography/ProtectedMemoryContext.cs rename to neo/Cryptography/ProtectedMemoryContext.cs index 615a337f88..5035d54d37 100644 --- a/src/AntShares/Cryptography/ProtectedMemoryContext.cs +++ b/neo/Cryptography/ProtectedMemoryContext.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Security.Cryptography; -namespace AntShares.Cryptography +namespace Neo.Cryptography { internal class ProtectedMemoryContext : IDisposable { diff --git a/src/AntShares/Cryptography/RIPEMD160Managed.cs b/neo/Cryptography/RIPEMD160Managed.cs similarity index 99% rename from src/AntShares/Cryptography/RIPEMD160Managed.cs rename to neo/Cryptography/RIPEMD160Managed.cs index 48aa52966b..be9feb4339 100644 --- a/src/AntShares/Cryptography/RIPEMD160Managed.cs +++ b/neo/Cryptography/RIPEMD160Managed.cs @@ -4,7 +4,7 @@ using System.Security; using System.Security.Cryptography; -namespace AntShares.Cryptography +namespace Neo.Cryptography { [ComVisible(true)] public class RIPEMD160Managed : HashAlgorithm diff --git a/src/AntShares/Fixed8.cs b/neo/Fixed8.cs similarity index 93% rename from src/AntShares/Fixed8.cs rename to neo/Fixed8.cs index 923ee0f7ca..8b27c66162 100644 --- a/src/AntShares/Fixed8.cs +++ b/neo/Fixed8.cs @@ -1,8 +1,8 @@ -using AntShares.IO; +using Neo.IO; using System; using System.IO; -namespace AntShares +namespace Neo { /// /// 精确到10^-8的64位定点数,将舍入误差降到最低。 @@ -39,6 +39,22 @@ public Fixed8 Abs() }; } + public Fixed8 Ceiling() + { + long remainder = value % D; + if (remainder == 0) return this; + if (remainder > 0) + return new Fixed8 + { + value = value - remainder + D + }; + else + return new Fixed8 + { + value = value - remainder + }; + } + public int CompareTo(Fixed8 other) { return value.CompareTo(other.value); diff --git a/src/AntShares/Helper.cs b/neo/Helper.cs similarity index 97% rename from src/AntShares/Helper.cs rename to neo/Helper.cs index 7aecb135b0..27dd2c2261 100644 --- a/src/AntShares/Helper.cs +++ b/neo/Helper.cs @@ -1,263 +1,263 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; -using System.Text; - -namespace AntShares -{ - public static class Helper - { - private static readonly DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - private static int BitLen(int w) - { - return (w < 1 << 15 ? (w < 1 << 7 - ? (w < 1 << 3 ? (w < 1 << 1 - ? (w < 1 << 0 ? (w < 0 ? 32 : 0) : 1) - : (w < 1 << 2 ? 2 : 3)) : (w < 1 << 5 - ? (w < 1 << 4 ? 4 : 5) - : (w < 1 << 6 ? 6 : 7))) - : (w < 1 << 11 - ? (w < 1 << 9 ? (w < 1 << 8 ? 8 : 9) : (w < 1 << 10 ? 10 : 11)) - : (w < 1 << 13 ? (w < 1 << 12 ? 12 : 13) : (w < 1 << 14 ? 14 : 15)))) : (w < 1 << 23 ? (w < 1 << 19 - ? (w < 1 << 17 ? (w < 1 << 16 ? 16 : 17) : (w < 1 << 18 ? 18 : 19)) - : (w < 1 << 21 ? (w < 1 << 20 ? 20 : 21) : (w < 1 << 22 ? 22 : 23))) : (w < 1 << 27 - ? (w < 1 << 25 ? (w < 1 << 24 ? 24 : 25) : (w < 1 << 26 ? 26 : 27)) - : (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31))))); - } - - internal static int GetBitLength(this BigInteger i) - { - byte[] b = i.ToByteArray(); - return (b.Length - 1) * 8 + BitLen(i.Sign > 0 ? b[b.Length - 1] : 255 - b[b.Length - 1]); - } - - internal static int GetLowestSetBit(this BigInteger i) - { - if (i.Sign == 0) - return -1; - byte[] b = i.ToByteArray(); - int w = 0; - while (b[w] == 0) - w++; - for (int x = 0; x < 8; x++) - if ((b[w] & 1 << x) > 0) - return x + w * 8; - throw new Exception(); - } - - public static byte[] HexToBytes(this string value) - { - if (value == null || value.Length == 0) - return new byte[0]; - if (value.Length % 2 == 1) - throw new FormatException(); - byte[] result = new byte[value.Length / 2]; - for (int i = 0; i < result.Length; i++) - result[i] = byte.Parse(value.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier); - return result; - } - - internal static BigInteger Mod(this BigInteger x, BigInteger y) - { - x %= y; - if (x.Sign < 0) - x += y; - return x; - } - - internal static BigInteger ModInverse(this BigInteger a, BigInteger n) - { - BigInteger i = n, v = 0, d = 1; - while (a > 0) - { - BigInteger t = i / a, x = a; - a = i % x; - i = x; - x = d; - d = v - t * x; - v = x; - } - v %= n; - if (v < 0) v = (v + n) % n; - return v; - } - - internal static BigInteger NextBigInteger(this Random rand, int sizeInBits) - { - if (sizeInBits < 0) - throw new ArgumentException("sizeInBits must be non-negative"); - if (sizeInBits == 0) - return 0; - byte[] b = new byte[sizeInBits / 8 + 1]; - rand.NextBytes(b); - if (sizeInBits % 8 == 0) - b[b.Length - 1] = 0; - else - b[b.Length - 1] &= (byte)((1 << sizeInBits % 8) - 1); - return new BigInteger(b); - } - - internal static BigInteger NextBigInteger(this RandomNumberGenerator rng, int sizeInBits) - { - if (sizeInBits < 0) - throw new ArgumentException("sizeInBits must be non-negative"); - if (sizeInBits == 0) - return 0; - byte[] b = new byte[sizeInBits / 8 + 1]; - rng.GetBytes(b); - if (sizeInBits % 8 == 0) - b[b.Length - 1] = 0; - else - b[b.Length - 1] &= (byte)((1 << sizeInBits % 8) - 1); - return new BigInteger(b); - } - - public static Fixed8 Sum(this IEnumerable source) - { - long sum = 0; - checked - { - foreach (Fixed8 item in source) - { - sum += item.value; - } - } - return new Fixed8(sum); - } - - public static Fixed8 Sum(this IEnumerable source, Func selector) - { - return source.Select(selector).Sum(); - } - - internal static bool TestBit(this BigInteger i, int index) - { - return (i & (BigInteger.One << index)) > BigInteger.Zero; - } - - public static DateTime ToDateTime(this uint timestamp) - { - return unixEpoch.AddSeconds(timestamp).ToLocalTime(); - } - - public static DateTime ToDateTime(this ulong timestamp) - { - return unixEpoch.AddSeconds(timestamp).ToLocalTime(); - } - - public static string ToHexString(this IEnumerable value) - { - StringBuilder sb = new StringBuilder(); - foreach (byte b in value) - sb.AppendFormat("{0:x2}", b); - return sb.ToString(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static int ToInt32(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((int*)pbyte); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static long ToInt64(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((long*)pbyte); - } - } - - public static uint ToTimestamp(this DateTime time) - { - return (uint)(time.ToUniversalTime() - unixEpoch).TotalSeconds; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static ushort ToUInt16(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((ushort*)pbyte); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static uint ToUInt32(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((uint*)pbyte); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static ulong ToUInt64(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((ulong*)pbyte); - } - } - - internal static long WeightedAverage(this IEnumerable source, Func valueSelector, Func weightSelector) - { - long sum_weight = 0; - long sum_value = 0; - foreach (T item in source) - { - long weight = weightSelector(item); - sum_weight += weight; - sum_value += valueSelector(item) * weight; - } - if (sum_value == 0) return 0; - return sum_value / sum_weight; - } - - internal static IEnumerable WeightedFilter(this IList source, double start, double end, Func weightSelector, Func resultSelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (start < 0 || start > 1) throw new ArgumentOutOfRangeException(nameof(start)); - if (end < start || start + end > 1) throw new ArgumentOutOfRangeException(nameof(end)); - if (weightSelector == null) throw new ArgumentNullException(nameof(weightSelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - if (source.Count == 0 || start == end) yield break; - double amount = source.Sum(weightSelector); - long sum = 0; - double current = 0; - foreach (T item in source) - { - if (current >= end) break; - long weight = weightSelector(item); - sum += weight; - double old = current; - current = sum / amount; - if (current <= start) continue; - if (old < start) - { - if (current > end) - { - weight = (long)((end - start) * amount); - } - else - { - weight = (long)((current - start) * amount); - } - } - else if (current > end) - { - weight = (long)((end - old) * amount); - } - yield return resultSelector(item, weight); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text; + +namespace Neo +{ + public static class Helper + { + private static readonly DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + private static int BitLen(int w) + { + return (w < 1 << 15 ? (w < 1 << 7 + ? (w < 1 << 3 ? (w < 1 << 1 + ? (w < 1 << 0 ? (w < 0 ? 32 : 0) : 1) + : (w < 1 << 2 ? 2 : 3)) : (w < 1 << 5 + ? (w < 1 << 4 ? 4 : 5) + : (w < 1 << 6 ? 6 : 7))) + : (w < 1 << 11 + ? (w < 1 << 9 ? (w < 1 << 8 ? 8 : 9) : (w < 1 << 10 ? 10 : 11)) + : (w < 1 << 13 ? (w < 1 << 12 ? 12 : 13) : (w < 1 << 14 ? 14 : 15)))) : (w < 1 << 23 ? (w < 1 << 19 + ? (w < 1 << 17 ? (w < 1 << 16 ? 16 : 17) : (w < 1 << 18 ? 18 : 19)) + : (w < 1 << 21 ? (w < 1 << 20 ? 20 : 21) : (w < 1 << 22 ? 22 : 23))) : (w < 1 << 27 + ? (w < 1 << 25 ? (w < 1 << 24 ? 24 : 25) : (w < 1 << 26 ? 26 : 27)) + : (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31))))); + } + + internal static int GetBitLength(this BigInteger i) + { + byte[] b = i.ToByteArray(); + return (b.Length - 1) * 8 + BitLen(i.Sign > 0 ? b[b.Length - 1] : 255 - b[b.Length - 1]); + } + + internal static int GetLowestSetBit(this BigInteger i) + { + if (i.Sign == 0) + return -1; + byte[] b = i.ToByteArray(); + int w = 0; + while (b[w] == 0) + w++; + for (int x = 0; x < 8; x++) + if ((b[w] & 1 << x) > 0) + return x + w * 8; + throw new Exception(); + } + + public static byte[] HexToBytes(this string value) + { + if (value == null || value.Length == 0) + return new byte[0]; + if (value.Length % 2 == 1) + throw new FormatException(); + byte[] result = new byte[value.Length / 2]; + for (int i = 0; i < result.Length; i++) + result[i] = byte.Parse(value.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier); + return result; + } + + internal static BigInteger Mod(this BigInteger x, BigInteger y) + { + x %= y; + if (x.Sign < 0) + x += y; + return x; + } + + internal static BigInteger ModInverse(this BigInteger a, BigInteger n) + { + BigInteger i = n, v = 0, d = 1; + while (a > 0) + { + BigInteger t = i / a, x = a; + a = i % x; + i = x; + x = d; + d = v - t * x; + v = x; + } + v %= n; + if (v < 0) v = (v + n) % n; + return v; + } + + internal static BigInteger NextBigInteger(this Random rand, int sizeInBits) + { + if (sizeInBits < 0) + throw new ArgumentException("sizeInBits must be non-negative"); + if (sizeInBits == 0) + return 0; + byte[] b = new byte[sizeInBits / 8 + 1]; + rand.NextBytes(b); + if (sizeInBits % 8 == 0) + b[b.Length - 1] = 0; + else + b[b.Length - 1] &= (byte)((1 << sizeInBits % 8) - 1); + return new BigInteger(b); + } + + internal static BigInteger NextBigInteger(this RandomNumberGenerator rng, int sizeInBits) + { + if (sizeInBits < 0) + throw new ArgumentException("sizeInBits must be non-negative"); + if (sizeInBits == 0) + return 0; + byte[] b = new byte[sizeInBits / 8 + 1]; + rng.GetBytes(b); + if (sizeInBits % 8 == 0) + b[b.Length - 1] = 0; + else + b[b.Length - 1] &= (byte)((1 << sizeInBits % 8) - 1); + return new BigInteger(b); + } + + public static Fixed8 Sum(this IEnumerable source) + { + long sum = 0; + checked + { + foreach (Fixed8 item in source) + { + sum += item.value; + } + } + return new Fixed8(sum); + } + + public static Fixed8 Sum(this IEnumerable source, Func selector) + { + return source.Select(selector).Sum(); + } + + internal static bool TestBit(this BigInteger i, int index) + { + return (i & (BigInteger.One << index)) > BigInteger.Zero; + } + + public static DateTime ToDateTime(this uint timestamp) + { + return unixEpoch.AddSeconds(timestamp).ToLocalTime(); + } + + public static DateTime ToDateTime(this ulong timestamp) + { + return unixEpoch.AddSeconds(timestamp).ToLocalTime(); + } + + public static string ToHexString(this IEnumerable value) + { + StringBuilder sb = new StringBuilder(); + foreach (byte b in value) + sb.AppendFormat("{0:x2}", b); + return sb.ToString(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + unsafe internal static int ToInt32(this byte[] value, int startIndex) + { + fixed (byte* pbyte = &value[startIndex]) + { + return *((int*)pbyte); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + unsafe internal static long ToInt64(this byte[] value, int startIndex) + { + fixed (byte* pbyte = &value[startIndex]) + { + return *((long*)pbyte); + } + } + + public static uint ToTimestamp(this DateTime time) + { + return (uint)(time.ToUniversalTime() - unixEpoch).TotalSeconds; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + unsafe internal static ushort ToUInt16(this byte[] value, int startIndex) + { + fixed (byte* pbyte = &value[startIndex]) + { + return *((ushort*)pbyte); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + unsafe internal static uint ToUInt32(this byte[] value, int startIndex) + { + fixed (byte* pbyte = &value[startIndex]) + { + return *((uint*)pbyte); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + unsafe internal static ulong ToUInt64(this byte[] value, int startIndex) + { + fixed (byte* pbyte = &value[startIndex]) + { + return *((ulong*)pbyte); + } + } + + internal static long WeightedAverage(this IEnumerable source, Func valueSelector, Func weightSelector) + { + long sum_weight = 0; + long sum_value = 0; + foreach (T item in source) + { + long weight = weightSelector(item); + sum_weight += weight; + sum_value += valueSelector(item) * weight; + } + if (sum_value == 0) return 0; + return sum_value / sum_weight; + } + + internal static IEnumerable WeightedFilter(this IList source, double start, double end, Func weightSelector, Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (start < 0 || start > 1) throw new ArgumentOutOfRangeException(nameof(start)); + if (end < start || start + end > 1) throw new ArgumentOutOfRangeException(nameof(end)); + if (weightSelector == null) throw new ArgumentNullException(nameof(weightSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source.Count == 0 || start == end) yield break; + double amount = source.Sum(weightSelector); + long sum = 0; + double current = 0; + foreach (T item in source) + { + if (current >= end) break; + long weight = weightSelector(item); + sum += weight; + double old = current; + current = sum / amount; + if (current <= start) continue; + if (old < start) + { + if (current > end) + { + weight = (long)((end - start) * amount); + } + else + { + weight = (long)((current - start) * amount); + } + } + else if (current > end) + { + weight = (long)((end - old) * amount); + } + yield return resultSelector(item, weight); + } + } + } +} diff --git a/src/AntShares/IO/Caching/Cache.cs b/neo/IO/Caching/Cache.cs similarity index 99% rename from src/AntShares/IO/Caching/Cache.cs rename to neo/IO/Caching/Cache.cs index cc87347e4a..9eb57c2ce9 100644 --- a/src/AntShares/IO/Caching/Cache.cs +++ b/neo/IO/Caching/Cache.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -namespace AntShares.IO.Caching +namespace Neo.IO.Caching { internal abstract class Cache : ICollection, IDisposable { @@ -72,33 +72,33 @@ public void Add(TValue item) } } - private void AddInternal(TKey key, TValue item) - { - if (InnerDictionary.ContainsKey(key)) - { - OnAccess(InnerDictionary[key]); - } - else - { - if (InnerDictionary.Count >= max_capacity) - { - //TODO: 对PLINQ查询进行性能测试,以便确定此处使用何种算法更优(并行或串行) - foreach (CacheItem item_del in InnerDictionary.Values.AsParallel().OrderBy(p => p.Time).Take(InnerDictionary.Count - max_capacity + 1)) - { - RemoveInternal(item_del); - } - } - InnerDictionary.Add(key, new CacheItem(key, item)); + private void AddInternal(TKey key, TValue item) + { + if (InnerDictionary.ContainsKey(key)) + { + OnAccess(InnerDictionary[key]); + } + else + { + if (InnerDictionary.Count >= max_capacity) + { + //TODO: 对PLINQ查询进行性能测试,以便确定此处使用何种算法更优(并行或串行) + foreach (CacheItem item_del in InnerDictionary.Values.AsParallel().OrderBy(p => p.Time).Take(InnerDictionary.Count - max_capacity + 1)) + { + RemoveInternal(item_del); + } + } + InnerDictionary.Add(key, new CacheItem(key, item)); } } - public void AddRange(IEnumerable items) - { + public void AddRange(IEnumerable items) + { lock (SyncRoot) { - foreach (TValue item in items) - { - TKey key = GetKeyForItem(item); + foreach (TValue item in items) + { + TKey key = GetKeyForItem(item); AddInternal(key, item); } } diff --git a/src/AntShares/IO/Caching/DataCache.cs b/neo/IO/Caching/DataCache.cs similarity index 86% rename from src/AntShares/IO/Caching/DataCache.cs rename to neo/IO/Caching/DataCache.cs index 5262157c02..65dc703744 100644 --- a/src/AntShares/IO/Caching/DataCache.cs +++ b/neo/IO/Caching/DataCache.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace AntShares.IO.Caching +namespace Neo.IO.Caching { public abstract class DataCache where TKey : IEquatable, ISerializable @@ -73,6 +73,23 @@ public void DeleteWhere(Func predicate) trackable.State = TrackState.Deleted; } + public IEnumerable> Find(byte[] key_prefix) + { + foreach (var pair in FindInternal(key_prefix)) + if (!dictionary.ContainsKey(pair.Key)) + dictionary.Add(pair.Key, new Trackable + { + Key = pair.Key, + Item = pair.Value, + State = TrackState.None + }); + foreach (var pair in dictionary) + if (pair.Value.State != TrackState.Deleted && pair.Key.ToArray().Take(key_prefix.Length).SequenceEqual(key_prefix)) + yield return new KeyValuePair(pair.Key, pair.Value.Item); + } + + protected abstract IEnumerable> FindInternal(byte[] key_prefix); + protected internal IEnumerable GetChangeSet() { return dictionary.Values.Where(p => p.State != TrackState.None); diff --git a/src/AntShares/IO/Caching/FIFOCache.cs b/neo/IO/Caching/FIFOCache.cs similarity index 88% rename from src/AntShares/IO/Caching/FIFOCache.cs rename to neo/IO/Caching/FIFOCache.cs index badd8c3e34..4aa7198b50 100644 --- a/src/AntShares/IO/Caching/FIFOCache.cs +++ b/neo/IO/Caching/FIFOCache.cs @@ -1,4 +1,4 @@ -namespace AntShares.IO.Caching +namespace Neo.IO.Caching { internal abstract class FIFOCache : Cache { diff --git a/src/AntShares/IO/Caching/ITrackable.cs b/neo/IO/Caching/ITrackable.cs similarity index 78% rename from src/AntShares/IO/Caching/ITrackable.cs rename to neo/IO/Caching/ITrackable.cs index 02d68212a8..e5a458d008 100644 --- a/src/AntShares/IO/Caching/ITrackable.cs +++ b/neo/IO/Caching/ITrackable.cs @@ -1,4 +1,4 @@ -namespace AntShares.IO.Caching +namespace Neo.IO.Caching { internal interface ITrackable { diff --git a/src/AntShares/IO/Caching/LRUCache.cs b/neo/IO/Caching/LRUCache.cs similarity index 91% rename from src/AntShares/IO/Caching/LRUCache.cs rename to neo/IO/Caching/LRUCache.cs index 313fc76a48..fa3b7a03d8 100644 --- a/src/AntShares/IO/Caching/LRUCache.cs +++ b/neo/IO/Caching/LRUCache.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.IO.Caching +namespace Neo.IO.Caching { internal abstract class LRUCache : Cache { diff --git a/src/AntShares/IO/Caching/RelayCache.cs b/neo/IO/Caching/RelayCache.cs similarity index 83% rename from src/AntShares/IO/Caching/RelayCache.cs rename to neo/IO/Caching/RelayCache.cs index 890d8670ef..0c7805074c 100644 --- a/src/AntShares/IO/Caching/RelayCache.cs +++ b/neo/IO/Caching/RelayCache.cs @@ -1,6 +1,6 @@ -using AntShares.Network; +using Neo.Network; -namespace AntShares.IO.Caching +namespace Neo.IO.Caching { internal class RelayCache : FIFOCache { diff --git a/src/AntShares/IO/Caching/TrackState.cs b/neo/IO/Caching/TrackState.cs similarity index 76% rename from src/AntShares/IO/Caching/TrackState.cs rename to neo/IO/Caching/TrackState.cs index 39c7dcca00..aa144aab34 100644 --- a/src/AntShares/IO/Caching/TrackState.cs +++ b/neo/IO/Caching/TrackState.cs @@ -1,4 +1,4 @@ -namespace AntShares.IO.Caching +namespace Neo.IO.Caching { public enum TrackState : byte { diff --git a/src/AntShares/IO/Caching/TrackableCollection.cs b/neo/IO/Caching/TrackableCollection.cs similarity index 98% rename from src/AntShares/IO/Caching/TrackableCollection.cs rename to neo/IO/Caching/TrackableCollection.cs index 81d1114d2b..29a353f3b3 100644 --- a/src/AntShares/IO/Caching/TrackableCollection.cs +++ b/neo/IO/Caching/TrackableCollection.cs @@ -2,7 +2,7 @@ using System.Collections.ObjectModel; using System.Linq; -namespace AntShares.IO.Caching +namespace Neo.IO.Caching { internal class TrackableCollection : KeyedCollection where TItem : ITrackable { diff --git a/src/AntShares/IO/Helper.cs b/neo/IO/Helper.cs similarity index 99% rename from src/AntShares/IO/Helper.cs rename to neo/IO/Helper.cs index d3ca18b547..179d757162 100644 --- a/src/AntShares/IO/Helper.cs +++ b/neo/IO/Helper.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using System.Text; -namespace AntShares.IO +namespace Neo.IO { public static class Helper { diff --git a/src/AntShares/IO/ISerializable.cs b/neo/IO/ISerializable.cs similarity index 95% rename from src/AntShares/IO/ISerializable.cs rename to neo/IO/ISerializable.cs index a1a0183372..b91b18c86d 100644 --- a/src/AntShares/IO/ISerializable.cs +++ b/neo/IO/ISerializable.cs @@ -1,6 +1,6 @@ using System.IO; -namespace AntShares.IO +namespace Neo.IO { /// /// 为序列化提供一个接口 diff --git a/src/AntShares/IO/Json/JArray.cs b/neo/IO/Json/JArray.cs similarity index 99% rename from src/AntShares/IO/Json/JArray.cs rename to neo/IO/Json/JArray.cs index 46b67913d0..9bbfcc0868 100644 --- a/src/AntShares/IO/Json/JArray.cs +++ b/neo/IO/Json/JArray.cs @@ -4,7 +4,7 @@ using System.IO; using System.Text; -namespace AntShares.IO.Json +namespace Neo.IO.Json { public class JArray : JObject, IList { diff --git a/src/AntShares/IO/Json/JBoolean.cs b/neo/IO/Json/JBoolean.cs similarity index 98% rename from src/AntShares/IO/Json/JBoolean.cs rename to neo/IO/Json/JBoolean.cs index bf0b6f03d8..b4cfc42e0f 100644 --- a/src/AntShares/IO/Json/JBoolean.cs +++ b/neo/IO/Json/JBoolean.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace AntShares.IO.Json +namespace Neo.IO.Json { public class JBoolean : JObject { diff --git a/src/AntShares/IO/Json/JNumber.cs b/neo/IO/Json/JNumber.cs similarity index 99% rename from src/AntShares/IO/Json/JNumber.cs rename to neo/IO/Json/JNumber.cs index 76741494b4..5f90c9b182 100644 --- a/src/AntShares/IO/Json/JNumber.cs +++ b/neo/IO/Json/JNumber.cs @@ -3,7 +3,7 @@ using System.Reflection; using System.Text; -namespace AntShares.IO.Json +namespace Neo.IO.Json { public class JNumber : JObject { diff --git a/src/AntShares/IO/Json/JObject.cs b/neo/IO/Json/JObject.cs similarity index 99% rename from src/AntShares/IO/Json/JObject.cs rename to neo/IO/Json/JObject.cs index e536b74f2d..01f8c8e51a 100644 --- a/src/AntShares/IO/Json/JObject.cs +++ b/neo/IO/Json/JObject.cs @@ -3,7 +3,7 @@ using System.IO; using System.Text; -namespace AntShares.IO.Json +namespace Neo.IO.Json { public class JObject { diff --git a/src/AntShares/IO/Json/JString.cs b/neo/IO/Json/JString.cs similarity index 99% rename from src/AntShares/IO/Json/JString.cs rename to neo/IO/Json/JString.cs index 4ed8b82b88..be0055f75f 100644 --- a/src/AntShares/IO/Json/JString.cs +++ b/neo/IO/Json/JString.cs @@ -5,7 +5,7 @@ using System.Text; using System.Text.Encodings.Web; -namespace AntShares.IO.Json +namespace Neo.IO.Json { public class JString : JObject { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/CachedScriptTable.cs b/neo/Implementations/Blockchains/LevelDB/CachedScriptTable.cs similarity index 80% rename from src/AntShares/Implementations/Blockchains/LevelDB/CachedScriptTable.cs rename to neo/Implementations/Blockchains/LevelDB/CachedScriptTable.cs index bf37eea575..da5c33368b 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/CachedScriptTable.cs +++ b/neo/Implementations/Blockchains/LevelDB/CachedScriptTable.cs @@ -1,7 +1,7 @@ -using AntShares.Core; -using AntShares.VM; +using Neo.Core; +using Neo.VM; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class CachedScriptTable : IScriptTable { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/DB.cs b/neo/Implementations/Blockchains/LevelDB/DB.cs similarity index 98% rename from src/AntShares/Implementations/Blockchains/LevelDB/DB.cs rename to neo/Implementations/Blockchains/LevelDB/DB.cs index 0841cdbae9..d215da5950 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/DB.cs +++ b/neo/Implementations/Blockchains/LevelDB/DB.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class DB : IDisposable { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/DataEntryPrefix.cs b/neo/Implementations/Blockchains/LevelDB/DataEntryPrefix.cs similarity index 88% rename from src/AntShares/Implementations/Blockchains/LevelDB/DataEntryPrefix.cs rename to neo/Implementations/Blockchains/LevelDB/DataEntryPrefix.cs index c12cfdfadb..74c27bd772 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/DataEntryPrefix.cs +++ b/neo/Implementations/Blockchains/LevelDB/DataEntryPrefix.cs @@ -1,4 +1,4 @@ -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal enum DataEntryPrefix : byte { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/DbCache.cs b/neo/Implementations/Blockchains/LevelDB/DbCache.cs similarity index 65% rename from src/AntShares/Implementations/Blockchains/LevelDB/DbCache.cs rename to neo/Implementations/Blockchains/LevelDB/DbCache.cs index 41abb1c9c9..b4c5ffaf50 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/DbCache.cs +++ b/neo/Implementations/Blockchains/LevelDB/DbCache.cs @@ -1,11 +1,12 @@ -using AntShares.IO; -using AntShares.IO.Caching; +using Neo.IO; +using Neo.IO.Caching; using System; +using System.Collections.Generic; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class DbCache : DataCache - where TKey : IEquatable, ISerializable + where TKey : IEquatable, ISerializable, new() where TValue : class, ISerializable, new() { private DB db; @@ -17,11 +18,6 @@ public DbCache(DB db, DataEntryPrefix prefix) this.prefix = prefix; } - protected override TValue GetInternal(TKey key) - { - return db.Get(ReadOptions.Default, prefix, key); - } - public void Commit(WriteBatch batch) { foreach (Trackable trackable in GetChangeSet()) @@ -31,6 +27,16 @@ public void Commit(WriteBatch batch) batch.Delete(prefix, trackable.Key); } + protected override IEnumerable> FindInternal(byte[] key_prefix) + { + return db.Find(ReadOptions.Default, SliceBuilder.Begin(prefix).Add(key_prefix), (k, v) => new KeyValuePair(k.ToArray().AsSerializable(), v.ToArray().AsSerializable())); + } + + protected override TValue GetInternal(TKey key) + { + return db.Get(ReadOptions.Default, prefix, key); + } + protected override TValue TryGetInternal(TKey key) { return db.TryGet(ReadOptions.Default, prefix, key); diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/Helper.cs b/neo/Implementations/Blockchains/LevelDB/Helper.cs similarity index 97% rename from src/AntShares/Implementations/Blockchains/LevelDB/Helper.cs rename to neo/Implementations/Blockchains/LevelDB/Helper.cs index f2cf8c61b4..9f9ee3a201 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/Helper.cs +++ b/neo/Implementations/Blockchains/LevelDB/Helper.cs @@ -1,9 +1,9 @@ -using AntShares.IO; +using Neo.IO; using System; using System.Collections.Generic; using System.Linq; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal static class Helper { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/Iterator.cs b/neo/Implementations/Blockchains/LevelDB/Iterator.cs similarity index 96% rename from src/AntShares/Implementations/Blockchains/LevelDB/Iterator.cs rename to neo/Implementations/Blockchains/LevelDB/Iterator.cs index 7f2dfe7234..131dbdf0b2 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/Iterator.cs +++ b/neo/Implementations/Blockchains/LevelDB/Iterator.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class Iterator : IDisposable { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs b/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs similarity index 94% rename from src/AntShares/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs rename to neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs index 6944abac2f..bce40726b3 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs +++ b/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs @@ -1,598 +1,613 @@ -using AntShares.Core; -using AntShares.Cryptography; -using AntShares.Cryptography.ECC; -using AntShares.IO; -using AntShares.IO.Caching; -using AntShares.SmartContract; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; - -namespace AntShares.Implementations.Blockchains.LevelDB -{ - public class LevelDBBlockchain : Blockchain - { - private DB db; - private Thread thread_persistence; - private List header_index = new List(); - private Dictionary header_cache = new Dictionary(); - private Dictionary block_cache = new Dictionary(); - private uint current_block_height = 0; - private uint stored_header_count = 0; - private AutoResetEvent new_block_event = new AutoResetEvent(false); - private bool disposed = false; - - public override UInt256 CurrentBlockHash => header_index[(int)current_block_height]; - public override UInt256 CurrentHeaderHash => header_index[header_index.Count - 1]; - public override uint HeaderHeight => (uint)header_index.Count - 1; - public override uint Height => current_block_height; - public bool VerifyBlocks { get; set; } = true; - - public LevelDBBlockchain(string path) - { - header_index.Add(GenesisBlock.Hash); - Version version; - Slice value; - db = DB.Open(path, new Options { CreateIfMissing = true }); - if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.SYS_Version), out value) && Version.TryParse(value.ToString(), out version) && version >= Version.Parse("1.5")) - { - ReadOptions options = new ReadOptions { FillCache = false }; - value = db.Get(options, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock)); - UInt256 current_header_hash = new UInt256(value.ToArray().Take(32).ToArray()); - this.current_block_height = value.ToArray().ToUInt32(32); - uint current_header_height = current_block_height; - if (db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentHeader), out value)) - { - current_header_hash = new UInt256(value.ToArray().Take(32).ToArray()); - current_header_height = value.ToArray().ToUInt32(32); - } - foreach (UInt256 hash in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_HeaderHashList), (k, v) => - { - using (MemoryStream ms = new MemoryStream(v.ToArray(), false)) - using (BinaryReader r = new BinaryReader(ms)) - { - return new - { - Index = k.ToArray().ToUInt32(1), - Hashes = r.ReadSerializableArray() - }; - } - }).OrderBy(p => p.Index).SelectMany(p => p.Hashes).ToArray()) - { - if (!hash.Equals(GenesisBlock.Hash)) - { - header_index.Add(hash); - } - stored_header_count++; - } - if (stored_header_count == 0) - { - Header[] headers = db.Find(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block), (k, v) => Header.FromTrimmedData(v.ToArray(), sizeof(long))).OrderBy(p => p.Index).ToArray(); - for (int i = 1; i < headers.Length; i++) - { - header_index.Add(headers[i].Hash); - } - } - else if (current_header_height >= stored_header_count) - { - for (UInt256 hash = current_header_hash; hash != header_index[(int)stored_header_count - 1];) - { - Header header = Header.FromTrimmedData(db.Get(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash)).ToArray(), sizeof(long)); - header_index.Insert((int)stored_header_count, hash); - hash = header.PrevHash; - } - } - } - else - { - WriteBatch batch = new WriteBatch(); - ReadOptions options = new ReadOptions { FillCache = false }; - using (Iterator it = db.NewIterator(options)) - { - for (it.SeekToFirst(); it.Valid(); it.Next()) - { - batch.Delete(it.Key()); - } - } - db.Write(WriteOptions.Default, batch); - Persist(GenesisBlock); - db.Put(WriteOptions.Default, SliceBuilder.Begin(DataEntryPrefix.SYS_Version), GetType().GetTypeInfo().Assembly.GetName().Version.ToString()); - } - thread_persistence = new Thread(PersistBlocks); - thread_persistence.Name = "LevelDBBlockchain.PersistBlocks"; - thread_persistence.Start(); - } - - public override bool AddBlock(Block block) - { - lock (block_cache) - { - if (!block_cache.ContainsKey(block.Hash)) - { - block_cache.Add(block.Hash, block); - } - } - lock (header_index) - { - if (block.Index - 1 >= header_index.Count) return false; - if (block.Index == header_index.Count) - { - if (VerifyBlocks && !block.Verify()) return false; - WriteBatch batch = new WriteBatch(); - OnAddHeader(block.Header, batch); - db.Write(WriteOptions.Default, batch); - } - if (block.Index < header_index.Count) - new_block_event.Set(); - } - return true; - } - - protected internal override void AddHeaders(IEnumerable
headers) - { - lock (header_index) - { - lock (header_cache) - { - WriteBatch batch = new WriteBatch(); - foreach (Header header in headers) - { - if (header.Index - 1 >= header_index.Count) break; - if (header.Index < header_index.Count) continue; - if (VerifyBlocks && !header.Verify()) break; - OnAddHeader(header, batch); - header_cache.Add(header.Hash, header); - } - db.Write(WriteOptions.Default, batch); - header_cache.Clear(); - } - } - } - - public override bool ContainsBlock(UInt256 hash) - { - return GetHeader(hash)?.Index <= current_block_height; - } - - public override bool ContainsTransaction(UInt256 hash) - { - Slice value; - return db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(hash), out value); - } - - public override bool ContainsUnspent(UInt256 hash, ushort index) - { - UnspentCoinState state = db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Coin, hash); - if (state == null) return false; - if (index >= state.Items.Length) return false; - return !state.Items[index].HasFlag(CoinState.Spent); - } - - public override void Dispose() - { - disposed = true; - new_block_event.Set(); - if (!thread_persistence.ThreadState.HasFlag(ThreadState.Unstarted)) - thread_persistence.Join(); - new_block_event.Dispose(); - if (db != null) - { - db.Dispose(); - db = null; - } - } - - public override AccountState GetAccountState(UInt160 script_hash) - { - return db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Account, script_hash); - } - - public override AssetState GetAssetState(UInt256 asset_id) - { - return db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Asset, asset_id); - } - - public override Block GetBlock(UInt256 hash) - { - return GetBlockInternal(ReadOptions.Default, hash); - } - - public override UInt256 GetBlockHash(uint height) - { - if (current_block_height < height) return null; - lock (header_index) - { - if (header_index.Count <= height) return null; - return header_index[(int)height]; - } - } - - private Block GetBlockInternal(ReadOptions options, UInt256 hash) - { - Slice value; - if (!db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash), out value)) - return null; - int height; - Block block = Block.FromTrimmedData(value.ToArray(), sizeof(long), p => GetTransaction(options, p, out height)); - if (block.Transactions.Length == 0) return null; - return block; - } - - public override ContractState GetContract(UInt160 hash) - { - return db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Contract, hash); - } - - public override IEnumerable GetEnrollments() - { - return db.Find(ReadOptions.Default, DataEntryPrefix.ST_Validator).Union(StandbyValidators.Select(p => new ValidatorState - { - PublicKey = p - })); - } - - public override Header GetHeader(uint height) - { - UInt256 hash; - lock (header_index) - { - if (header_index.Count <= height) return null; - hash = header_index[(int)height]; - } - return GetHeader(hash); - } - - public override Header GetHeader(UInt256 hash) - { - lock (header_cache) - { - if (header_cache.ContainsKey(hash)) - return header_cache[hash]; - } - Slice value; - if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash), out value)) - return null; - return Header.FromTrimmedData(value.ToArray(), sizeof(long)); - } - - public override Block GetNextBlock(UInt256 hash) - { - return GetBlockInternal(ReadOptions.Default, GetNextBlockHash(hash)); - } - - public override UInt256 GetNextBlockHash(UInt256 hash) - { - Header header = GetHeader(hash); - if (header == null) return null; - lock (header_index) - { - if (header.Index + 1 >= header_index.Count) - return null; - return header_index[(int)header.Index + 1]; - } - } - - public override StorageItem GetStorageItem(StorageKey key) - { - return db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Storage, key); - } - - public override long GetSysFeeAmount(UInt256 hash) - { - Slice value; - if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash), out value)) - return 0; - return value.ToArray().ToInt64(0); - } - - public override Transaction GetTransaction(UInt256 hash, out int height) - { - return GetTransaction(ReadOptions.Default, hash, out height); - } - - private Transaction GetTransaction(ReadOptions options, UInt256 hash, out int height) - { - Slice value; - if (db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(hash), out value)) - { - byte[] data = value.ToArray(); - height = data.ToInt32(0); - return Transaction.DeserializeFrom(data, sizeof(uint)); - } - else - { - height = -1; - return null; - } - } - - public override Dictionary GetUnclaimed(UInt256 hash) - { - int height; - Transaction tx = GetTransaction(ReadOptions.Default, hash, out height); - if (tx == null) return null; - SpentCoinState state = db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_SpentCoin, hash); - if (state != null) - { - return state.Items.ToDictionary(p => p.Key, p => new SpentCoin - { - Output = tx.Outputs[p.Key], - StartHeight = (uint)height, - EndHeight = p.Value - }); - } - else - { - return new Dictionary(); - } - } - - public override TransactionOutput GetUnspent(UInt256 hash, ushort index) - { - ReadOptions options = new ReadOptions(); - using (options.Snapshot = db.GetSnapshot()) - { - UnspentCoinState state = db.TryGet(options, DataEntryPrefix.ST_Coin, hash); - if (state == null) return null; - if (index >= state.Items.Length) return null; - if (state.Items[index].HasFlag(CoinState.Spent)) return null; - int height; - return GetTransaction(options, hash, out height).Outputs[index]; - } - } - - public override IEnumerable GetVotes(IEnumerable others) - { - ReadOptions options = new ReadOptions(); - using (options.Snapshot = db.GetSnapshot()) - { - var inputs = others.SelectMany(p => p.Inputs).GroupBy(p => p.PrevHash, (k, g) => - { - int height; - Transaction tx = GetTransaction(options, k, out height); - return g.Select(p => tx.Outputs[p.PrevIndex]); - }).SelectMany(p => p).Where(p => p.AssetId.Equals(SystemShare.Hash)).Select(p => new - { - p.ScriptHash, - Value = -p.Value - }); - var outputs = others.SelectMany(p => p.Outputs).Where(p => p.AssetId.Equals(SystemShare.Hash)).Select(p => new - { - p.ScriptHash, - p.Value - }); - var changes = inputs.Concat(outputs).GroupBy(p => p.ScriptHash).ToDictionary(p => p.Key, p => p.Sum(i => i.Value)); - var accounts = db.Find(options, DataEntryPrefix.ST_Account).Where(p => p.Votes.Length > 0).ToArray(); - if (accounts.Length > 0) - foreach (AccountState account in accounts) - { - Fixed8 balance = account.Balances.ContainsKey(SystemShare.Hash) ? account.Balances[SystemShare.Hash] : Fixed8.Zero; - if (changes.ContainsKey(account.ScriptHash)) - balance += changes[account.ScriptHash]; - if (balance <= Fixed8.Zero) continue; - yield return new VoteState - { - PublicKeys = account.Votes, - Count = balance - }; - } - else - yield return new VoteState - { - PublicKeys = StandbyValidators, - Count = SystemShare.Amount - }; - } - } - - public override bool IsDoubleSpend(Transaction tx) - { - if (tx.Inputs.Length == 0) return false; - ReadOptions options = new ReadOptions(); - using (options.Snapshot = db.GetSnapshot()) - { - foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash)) - { - UnspentCoinState state = db.TryGet(options, DataEntryPrefix.ST_Coin, group.Key); - if (state == null) return true; - if (group.Any(p => p.PrevIndex >= state.Items.Length || state.Items[p.PrevIndex].HasFlag(CoinState.Spent))) - return true; - } - } - return false; - } - - private void OnAddHeader(Header header, WriteBatch batch) - { - header_index.Add(header.Hash); - while ((int)header.Index - 2000 >= stored_header_count) - { - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter w = new BinaryWriter(ms)) - { - w.Write(header_index.Skip((int)stored_header_count).Take(2000).ToArray()); - w.Flush(); - batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_HeaderHashList).Add(stored_header_count), ms.ToArray()); - } - stored_header_count += 2000; - } - batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(header.Hash), SliceBuilder.Begin().Add(0L).Add(header.ToArray())); - batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentHeader), SliceBuilder.Begin().Add(header.Hash).Add(header.Index)); - } - - private void Persist(Block block) - { - WriteBatch batch = new WriteBatch(); - DbCache accounts = new DbCache(db, DataEntryPrefix.ST_Account); - DbCache unspentcoins = new DbCache(db, DataEntryPrefix.ST_Coin); - DbCache spentcoins = new DbCache(db, DataEntryPrefix.ST_SpentCoin); - DbCache validators = new DbCache(db, DataEntryPrefix.ST_Validator); - DbCache assets = new DbCache(db, DataEntryPrefix.ST_Asset); - DbCache contracts = new DbCache(db, DataEntryPrefix.ST_Contract); - DbCache storages = new DbCache(db, DataEntryPrefix.ST_Storage); - long amount_sysfee = GetSysFeeAmount(block.PrevHash) + (long)block.Transactions.Sum(p => p.SystemFee); - batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(block.Hash), SliceBuilder.Begin().Add(amount_sysfee).Add(block.Trim())); - foreach (Transaction tx in block.Transactions) - { - batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(tx.Hash), SliceBuilder.Begin().Add(block.Index).Add(tx.ToArray())); - unspentcoins.Add(tx.Hash, new UnspentCoinState - { - Items = Enumerable.Repeat(CoinState.Confirmed, tx.Outputs.Length).ToArray() - }); - foreach (TransactionOutput output in tx.Outputs) - { - AccountState account = accounts.GetAndChange(output.ScriptHash, () => new AccountState - { - ScriptHash = output.ScriptHash, - IsFrozen = false, - Votes = new ECPoint[0], - Balances = new Dictionary() - }); - if (account.Balances.ContainsKey(output.AssetId)) - account.Balances[output.AssetId] += output.Value; - else - account.Balances[output.AssetId] = output.Value; - } - foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash)) - { - int height; - Transaction tx_prev = GetTransaction(ReadOptions.Default, group.Key, out height); - foreach (CoinReference input in group) - { - unspentcoins.GetAndChange(input.PrevHash).Items[input.PrevIndex] |= CoinState.Spent; - if (tx_prev.Outputs[input.PrevIndex].AssetId.Equals(SystemShare.Hash)) - { - spentcoins.GetAndChange(input.PrevHash, () => new SpentCoinState - { - TransactionHash = input.PrevHash, - TransactionHeight = (uint)height, - Items = new Dictionary() - }).Items.Add(input.PrevIndex, block.Index); - } - accounts.GetAndChange(tx_prev.Outputs[input.PrevIndex].ScriptHash).Balances[tx_prev.Outputs[input.PrevIndex].AssetId] -= tx_prev.Outputs[input.PrevIndex].Value; - } - } - switch (tx.Type) - { - case TransactionType.RegisterTransaction: - { - RegisterTransaction rtx = (RegisterTransaction)tx; - assets.Add(tx.Hash, new AssetState - { - AssetId = rtx.Hash, - AssetType = rtx.AssetType, - Name = rtx.Name, - Amount = rtx.Amount, - Available = Fixed8.Zero, - Precision = rtx.Precision, - Fee = Fixed8.Zero, - FeeAddress = new UInt160(), - Owner = rtx.Owner, - Admin = rtx.Admin, - Issuer = rtx.Admin, - Expiration = block.Index + 2000000, - IsFrozen = false - }); - } - break; - case TransactionType.IssueTransaction: - foreach (TransactionResult result in tx.GetTransactionResults().Where(p => p.Amount < Fixed8.Zero)) - assets.GetAndChange(result.AssetId).Available -= result.Amount; - break; - case TransactionType.ClaimTransaction: - foreach (CoinReference input in ((ClaimTransaction)tx).Claims) - { - if (spentcoins.TryGet(input.PrevHash)?.Items.Remove(input.PrevIndex) == true) - spentcoins.GetAndChange(input.PrevHash); - } - break; - case TransactionType.EnrollmentTransaction: - { - EnrollmentTransaction enroll_tx = (EnrollmentTransaction)tx; - validators.Add(enroll_tx.PublicKey, new ValidatorState - { - PublicKey = enroll_tx.PublicKey - }); - } - break; - case TransactionType.PublishTransaction: - { - PublishTransaction publish_tx = (PublishTransaction)tx; - contracts.GetOrAdd(publish_tx.Code.ScriptHash, () => new ContractState - { - Code = publish_tx.Code, - HasStorage = publish_tx.NeedStorage, - Name = publish_tx.Name, - CodeVersion = publish_tx.CodeVersion, - Author = publish_tx.Author, - Email = publish_tx.Email, - Description = publish_tx.Description - }); - } - break; - case TransactionType.InvocationTransaction: - { - InvocationTransaction itx = (InvocationTransaction)tx; - CachedScriptTable script_table = new CachedScriptTable(contracts); - StateMachine service = new StateMachine(accounts, validators, assets, contracts, storages); - ApplicationEngine engine = new ApplicationEngine(itx, script_table, service, itx.Gas); - engine.LoadScript(itx.Script, false); - if (engine.Execute()) service.Commit(); - } - break; - } - } - accounts.DeleteWhere((k, v) => !v.IsFrozen && v.Votes.Length == 0 && v.Balances.All(p => p.Value <= Fixed8.Zero)); - accounts.Commit(batch); - unspentcoins.DeleteWhere((k, v) => v.Items.All(p => p.HasFlag(CoinState.Spent))); - unspentcoins.Commit(batch); - spentcoins.DeleteWhere((k, v) => v.Items.Count == 0); - spentcoins.Commit(batch); - validators.Commit(batch); - assets.Commit(batch); - HashSet contracts_deleted = new HashSet(contracts.GetChangeSet().Where(p => p.State == TrackState.Deleted).Select(p => p.Key)); - contracts.Commit(batch); - if (contracts_deleted.Count > 0) - storages.DeleteWhere((k, v) => contracts_deleted.Contains(k.ScriptHash)); - storages.Commit(batch); - foreach (UInt160 script_hash in contracts_deleted) - foreach (Slice key in db.Find(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.ST_Storage).Add(script_hash), (k, v) => k)) - batch.Delete(key); - batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock), SliceBuilder.Begin().Add(block.Hash).Add(block.Index)); - db.Write(WriteOptions.Default, batch); - current_block_height = block.Index; - } - - private void PersistBlocks() - { - while (!disposed) - { - new_block_event.WaitOne(); - while (!disposed) - { - UInt256 hash; - lock (header_index) - { - if (header_index.Count <= current_block_height + 1) break; - hash = header_index[(int)current_block_height + 1]; - } - Block block; - lock (block_cache) - { - if (!block_cache.ContainsKey(hash)) break; - block = block_cache[hash]; - } - Persist(block); - OnPersistCompleted(block); - lock (block_cache) - { - block_cache.Remove(hash); - } - } - } - } - } -} +using Neo.Core; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.IO.Caching; +using Neo.SmartContract; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; + +namespace Neo.Implementations.Blockchains.LevelDB +{ + public class LevelDBBlockchain : Blockchain + { + private DB db; + private Thread thread_persistence; + private List header_index = new List(); + private Dictionary header_cache = new Dictionary(); + private Dictionary block_cache = new Dictionary(); + private uint current_block_height = 0; + private uint stored_header_count = 0; + private AutoResetEvent new_block_event = new AutoResetEvent(false); + private bool disposed = false; + + public override UInt256 CurrentBlockHash => header_index[(int)current_block_height]; + public override UInt256 CurrentHeaderHash => header_index[header_index.Count - 1]; + public override uint HeaderHeight => (uint)header_index.Count - 1; + public override uint Height => current_block_height; + public bool VerifyBlocks { get; set; } = true; + + public LevelDBBlockchain(string path) + { + header_index.Add(GenesisBlock.Hash); + Version version; + Slice value; + db = DB.Open(path, new Options { CreateIfMissing = true }); + if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.SYS_Version), out value) && Version.TryParse(value.ToString(), out version) && version >= Version.Parse("1.5")) + { + ReadOptions options = new ReadOptions { FillCache = false }; + value = db.Get(options, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock)); + UInt256 current_header_hash = new UInt256(value.ToArray().Take(32).ToArray()); + this.current_block_height = value.ToArray().ToUInt32(32); + uint current_header_height = current_block_height; + if (db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentHeader), out value)) + { + current_header_hash = new UInt256(value.ToArray().Take(32).ToArray()); + current_header_height = value.ToArray().ToUInt32(32); + } + foreach (UInt256 hash in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_HeaderHashList), (k, v) => + { + using (MemoryStream ms = new MemoryStream(v.ToArray(), false)) + using (BinaryReader r = new BinaryReader(ms)) + { + return new + { + Index = k.ToArray().ToUInt32(1), + Hashes = r.ReadSerializableArray() + }; + } + }).OrderBy(p => p.Index).SelectMany(p => p.Hashes).ToArray()) + { + if (!hash.Equals(GenesisBlock.Hash)) + { + header_index.Add(hash); + } + stored_header_count++; + } + if (stored_header_count == 0) + { + Header[] headers = db.Find(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block), (k, v) => Header.FromTrimmedData(v.ToArray(), sizeof(long))).OrderBy(p => p.Index).ToArray(); + for (int i = 1; i < headers.Length; i++) + { + header_index.Add(headers[i].Hash); + } + } + else if (current_header_height >= stored_header_count) + { + for (UInt256 hash = current_header_hash; hash != header_index[(int)stored_header_count - 1];) + { + Header header = Header.FromTrimmedData(db.Get(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash)).ToArray(), sizeof(long)); + header_index.Insert((int)stored_header_count, hash); + hash = header.PrevHash; + } + } + } + else + { + WriteBatch batch = new WriteBatch(); + ReadOptions options = new ReadOptions { FillCache = false }; + using (Iterator it = db.NewIterator(options)) + { + for (it.SeekToFirst(); it.Valid(); it.Next()) + { + batch.Delete(it.Key()); + } + } + db.Write(WriteOptions.Default, batch); + Persist(GenesisBlock); + db.Put(WriteOptions.Default, SliceBuilder.Begin(DataEntryPrefix.SYS_Version), GetType().GetTypeInfo().Assembly.GetName().Version.ToString()); + } + thread_persistence = new Thread(PersistBlocks); + thread_persistence.Name = "LevelDBBlockchain.PersistBlocks"; + thread_persistence.Start(); + } + + public override bool AddBlock(Block block) + { + lock (block_cache) + { + if (!block_cache.ContainsKey(block.Hash)) + { + block_cache.Add(block.Hash, block); + } + } + lock (header_index) + { + if (block.Index - 1 >= header_index.Count) return false; + if (block.Index == header_index.Count) + { + if (VerifyBlocks && !block.Verify()) return false; + WriteBatch batch = new WriteBatch(); + OnAddHeader(block.Header, batch); + db.Write(WriteOptions.Default, batch); + } + if (block.Index < header_index.Count) + new_block_event.Set(); + } + return true; + } + + protected internal override void AddHeaders(IEnumerable
headers) + { + lock (header_index) + { + lock (header_cache) + { + WriteBatch batch = new WriteBatch(); + foreach (Header header in headers) + { + if (header.Index - 1 >= header_index.Count) break; + if (header.Index < header_index.Count) continue; + if (VerifyBlocks && !header.Verify()) break; + OnAddHeader(header, batch); + header_cache.Add(header.Hash, header); + } + db.Write(WriteOptions.Default, batch); + header_cache.Clear(); + } + } + } + + public override bool ContainsBlock(UInt256 hash) + { + return GetHeader(hash)?.Index <= current_block_height; + } + + public override bool ContainsTransaction(UInt256 hash) + { + Slice value; + return db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(hash), out value); + } + + public override bool ContainsUnspent(UInt256 hash, ushort index) + { + UnspentCoinState state = db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Coin, hash); + if (state == null) return false; + if (index >= state.Items.Length) return false; + return !state.Items[index].HasFlag(CoinState.Spent); + } + + public override void Dispose() + { + disposed = true; + new_block_event.Set(); + if (!thread_persistence.ThreadState.HasFlag(ThreadState.Unstarted)) + thread_persistence.Join(); + new_block_event.Dispose(); + if (db != null) + { + db.Dispose(); + db = null; + } + } + + public override AccountState GetAccountState(UInt160 script_hash) + { + return db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Account, script_hash); + } + + public override AssetState GetAssetState(UInt256 asset_id) + { + return db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Asset, asset_id); + } + + public override Block GetBlock(UInt256 hash) + { + return GetBlockInternal(ReadOptions.Default, hash); + } + + public override UInt256 GetBlockHash(uint height) + { + if (current_block_height < height) return null; + lock (header_index) + { + if (header_index.Count <= height) return null; + return header_index[(int)height]; + } + } + + private Block GetBlockInternal(ReadOptions options, UInt256 hash) + { + Slice value; + if (!db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash), out value)) + return null; + int height; + Block block = Block.FromTrimmedData(value.ToArray(), sizeof(long), p => GetTransaction(options, p, out height)); + if (block.Transactions.Length == 0) return null; + return block; + } + + public override ContractState GetContract(UInt160 hash) + { + return db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Contract, hash); + } + + public override IEnumerable GetEnrollments() + { + return db.Find(ReadOptions.Default, DataEntryPrefix.ST_Validator).Union(StandbyValidators.Select(p => new ValidatorState + { + PublicKey = p + })); + } + + public override Header GetHeader(uint height) + { + UInt256 hash; + lock (header_index) + { + if (header_index.Count <= height) return null; + hash = header_index[(int)height]; + } + return GetHeader(hash); + } + + public override Header GetHeader(UInt256 hash) + { + lock (header_cache) + { + if (header_cache.ContainsKey(hash)) + return header_cache[hash]; + } + Slice value; + if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash), out value)) + return null; + return Header.FromTrimmedData(value.ToArray(), sizeof(long)); + } + + public override Block GetNextBlock(UInt256 hash) + { + return GetBlockInternal(ReadOptions.Default, GetNextBlockHash(hash)); + } + + public override UInt256 GetNextBlockHash(UInt256 hash) + { + Header header = GetHeader(hash); + if (header == null) return null; + lock (header_index) + { + if (header.Index + 1 >= header_index.Count) + return null; + return header_index[(int)header.Index + 1]; + } + } + + public override StorageItem GetStorageItem(StorageKey key) + { + return db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_Storage, key); + } + + public override long GetSysFeeAmount(UInt256 hash) + { + Slice value; + if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash), out value)) + return 0; + return value.ToArray().ToInt64(0); + } + + public DataCache GetTable() + where TKey : IEquatable, ISerializable, new() + where TValue : class, ISerializable, new() + { + Type t = typeof(TValue); + if (t == typeof(AccountState)) return new DbCache(db, DataEntryPrefix.ST_Account); + if (t == typeof(UnspentCoinState)) return new DbCache(db, DataEntryPrefix.ST_Coin); + if (t == typeof(SpentCoinState)) return new DbCache(db, DataEntryPrefix.ST_SpentCoin); + if (t == typeof(ValidatorState)) return new DbCache(db, DataEntryPrefix.ST_Validator); + if (t == typeof(AssetState)) return new DbCache(db, DataEntryPrefix.ST_Asset); + if (t == typeof(ContractState)) return new DbCache(db, DataEntryPrefix.ST_Contract); + if (t == typeof(StorageItem)) return new DbCache(db, DataEntryPrefix.ST_Storage); + throw new NotSupportedException(); + } + + public override Transaction GetTransaction(UInt256 hash, out int height) + { + return GetTransaction(ReadOptions.Default, hash, out height); + } + + private Transaction GetTransaction(ReadOptions options, UInt256 hash, out int height) + { + Slice value; + if (db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(hash), out value)) + { + byte[] data = value.ToArray(); + height = data.ToInt32(0); + return Transaction.DeserializeFrom(data, sizeof(uint)); + } + else + { + height = -1; + return null; + } + } + + public override Dictionary GetUnclaimed(UInt256 hash) + { + int height; + Transaction tx = GetTransaction(ReadOptions.Default, hash, out height); + if (tx == null) return null; + SpentCoinState state = db.TryGet(ReadOptions.Default, DataEntryPrefix.ST_SpentCoin, hash); + if (state != null) + { + return state.Items.ToDictionary(p => p.Key, p => new SpentCoin + { + Output = tx.Outputs[p.Key], + StartHeight = (uint)height, + EndHeight = p.Value + }); + } + else + { + return new Dictionary(); + } + } + + public override TransactionOutput GetUnspent(UInt256 hash, ushort index) + { + ReadOptions options = new ReadOptions(); + using (options.Snapshot = db.GetSnapshot()) + { + UnspentCoinState state = db.TryGet(options, DataEntryPrefix.ST_Coin, hash); + if (state == null) return null; + if (index >= state.Items.Length) return null; + if (state.Items[index].HasFlag(CoinState.Spent)) return null; + int height; + return GetTransaction(options, hash, out height).Outputs[index]; + } + } + + public override IEnumerable GetVotes(IEnumerable others) + { + ReadOptions options = new ReadOptions(); + using (options.Snapshot = db.GetSnapshot()) + { + var inputs = others.SelectMany(p => p.Inputs).GroupBy(p => p.PrevHash, (k, g) => + { + int height; + Transaction tx = GetTransaction(options, k, out height); + return g.Select(p => tx.Outputs[p.PrevIndex]); + }).SelectMany(p => p).Where(p => p.AssetId.Equals(SystemShare.Hash)).Select(p => new + { + p.ScriptHash, + Value = -p.Value + }); + var outputs = others.SelectMany(p => p.Outputs).Where(p => p.AssetId.Equals(SystemShare.Hash)).Select(p => new + { + p.ScriptHash, + p.Value + }); + var changes = inputs.Concat(outputs).GroupBy(p => p.ScriptHash).ToDictionary(p => p.Key, p => p.Sum(i => i.Value)); + var accounts = db.Find(options, DataEntryPrefix.ST_Account).Where(p => p.Votes.Length > 0).ToArray(); + if (accounts.Length > 0) + foreach (AccountState account in accounts) + { + Fixed8 balance = account.Balances.ContainsKey(SystemShare.Hash) ? account.Balances[SystemShare.Hash] : Fixed8.Zero; + if (changes.ContainsKey(account.ScriptHash)) + balance += changes[account.ScriptHash]; + if (balance <= Fixed8.Zero) continue; + yield return new VoteState + { + PublicKeys = account.Votes, + Count = balance + }; + } + else + yield return new VoteState + { + PublicKeys = StandbyValidators, + Count = SystemShare.Amount + }; + } + } + + public override bool IsDoubleSpend(Transaction tx) + { + if (tx.Inputs.Length == 0) return false; + ReadOptions options = new ReadOptions(); + using (options.Snapshot = db.GetSnapshot()) + { + foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash)) + { + UnspentCoinState state = db.TryGet(options, DataEntryPrefix.ST_Coin, group.Key); + if (state == null) return true; + if (group.Any(p => p.PrevIndex >= state.Items.Length || state.Items[p.PrevIndex].HasFlag(CoinState.Spent))) + return true; + } + } + return false; + } + + private void OnAddHeader(Header header, WriteBatch batch) + { + header_index.Add(header.Hash); + while ((int)header.Index - 2000 >= stored_header_count) + { + using (MemoryStream ms = new MemoryStream()) + using (BinaryWriter w = new BinaryWriter(ms)) + { + w.Write(header_index.Skip((int)stored_header_count).Take(2000).ToArray()); + w.Flush(); + batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_HeaderHashList).Add(stored_header_count), ms.ToArray()); + } + stored_header_count += 2000; + } + batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(header.Hash), SliceBuilder.Begin().Add(0L).Add(header.ToArray())); + batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentHeader), SliceBuilder.Begin().Add(header.Hash).Add(header.Index)); + } + + private void Persist(Block block) + { + WriteBatch batch = new WriteBatch(); + DbCache accounts = new DbCache(db, DataEntryPrefix.ST_Account); + DbCache unspentcoins = new DbCache(db, DataEntryPrefix.ST_Coin); + DbCache spentcoins = new DbCache(db, DataEntryPrefix.ST_SpentCoin); + DbCache validators = new DbCache(db, DataEntryPrefix.ST_Validator); + DbCache assets = new DbCache(db, DataEntryPrefix.ST_Asset); + DbCache contracts = new DbCache(db, DataEntryPrefix.ST_Contract); + DbCache storages = new DbCache(db, DataEntryPrefix.ST_Storage); + long amount_sysfee = GetSysFeeAmount(block.PrevHash) + (long)block.Transactions.Sum(p => p.SystemFee); + batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(block.Hash), SliceBuilder.Begin().Add(amount_sysfee).Add(block.Trim())); + foreach (Transaction tx in block.Transactions) + { + batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(tx.Hash), SliceBuilder.Begin().Add(block.Index).Add(tx.ToArray())); + unspentcoins.Add(tx.Hash, new UnspentCoinState + { + Items = Enumerable.Repeat(CoinState.Confirmed, tx.Outputs.Length).ToArray() + }); + foreach (TransactionOutput output in tx.Outputs) + { + AccountState account = accounts.GetAndChange(output.ScriptHash, () => new AccountState + { + ScriptHash = output.ScriptHash, + IsFrozen = false, + Votes = new ECPoint[0], + Balances = new Dictionary() + }); + if (account.Balances.ContainsKey(output.AssetId)) + account.Balances[output.AssetId] += output.Value; + else + account.Balances[output.AssetId] = output.Value; + } + foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash)) + { + int height; + Transaction tx_prev = GetTransaction(ReadOptions.Default, group.Key, out height); + foreach (CoinReference input in group) + { + unspentcoins.GetAndChange(input.PrevHash).Items[input.PrevIndex] |= CoinState.Spent; + if (tx_prev.Outputs[input.PrevIndex].AssetId.Equals(SystemShare.Hash)) + { + spentcoins.GetAndChange(input.PrevHash, () => new SpentCoinState + { + TransactionHash = input.PrevHash, + TransactionHeight = (uint)height, + Items = new Dictionary() + }).Items.Add(input.PrevIndex, block.Index); + } + accounts.GetAndChange(tx_prev.Outputs[input.PrevIndex].ScriptHash).Balances[tx_prev.Outputs[input.PrevIndex].AssetId] -= tx_prev.Outputs[input.PrevIndex].Value; + } + } + switch (tx.Type) + { + case TransactionType.RegisterTransaction: + { +#pragma warning disable CS0612 + RegisterTransaction rtx = (RegisterTransaction)tx; + assets.Add(tx.Hash, new AssetState + { + AssetId = rtx.Hash, + AssetType = rtx.AssetType, + Name = rtx.Name, + Amount = rtx.Amount, + Available = Fixed8.Zero, + Precision = rtx.Precision, + Fee = Fixed8.Zero, + FeeAddress = new UInt160(), + Owner = rtx.Owner, + Admin = rtx.Admin, + Issuer = rtx.Admin, + Expiration = block.Index + 2 * 2000000, + IsFrozen = false + }); +#pragma warning restore CS0612 + } + break; + case TransactionType.IssueTransaction: + foreach (TransactionResult result in tx.GetTransactionResults().Where(p => p.Amount < Fixed8.Zero)) + assets.GetAndChange(result.AssetId).Available -= result.Amount; + break; + case TransactionType.ClaimTransaction: + foreach (CoinReference input in ((ClaimTransaction)tx).Claims) + { + if (spentcoins.TryGet(input.PrevHash)?.Items.Remove(input.PrevIndex) == true) + spentcoins.GetAndChange(input.PrevHash); + } + break; + case TransactionType.EnrollmentTransaction: + { +#pragma warning disable CS0612 + EnrollmentTransaction enroll_tx = (EnrollmentTransaction)tx; + validators.GetOrAdd(enroll_tx.PublicKey, () => new ValidatorState + { + PublicKey = enroll_tx.PublicKey + }); +#pragma warning restore CS0612 + } + break; + case TransactionType.PublishTransaction: + { +#pragma warning disable CS0612 + PublishTransaction publish_tx = (PublishTransaction)tx; + contracts.GetOrAdd(publish_tx.Code.ScriptHash, () => new ContractState + { + Code = publish_tx.Code, + HasStorage = publish_tx.NeedStorage, + Name = publish_tx.Name, + CodeVersion = publish_tx.CodeVersion, + Author = publish_tx.Author, + Email = publish_tx.Email, + Description = publish_tx.Description + }); +#pragma warning restore CS0612 + } + break; + case TransactionType.InvocationTransaction: + { + InvocationTransaction itx = (InvocationTransaction)tx; + CachedScriptTable script_table = new CachedScriptTable(contracts); + StateMachine service = new StateMachine(accounts, validators, assets, contracts, storages); + ApplicationEngine engine = new ApplicationEngine(itx, script_table, service, itx.Gas); + engine.LoadScript(itx.Script, false); + if (engine.Execute()) service.Commit(); + } + break; + } + } + accounts.DeleteWhere((k, v) => !v.IsFrozen && v.Votes.Length == 0 && v.Balances.All(p => p.Value <= Fixed8.Zero)); + accounts.Commit(batch); + unspentcoins.DeleteWhere((k, v) => v.Items.All(p => p.HasFlag(CoinState.Spent))); + unspentcoins.Commit(batch); + spentcoins.DeleteWhere((k, v) => v.Items.Count == 0); + spentcoins.Commit(batch); + validators.Commit(batch); + assets.Commit(batch); + contracts.Commit(batch); + storages.Commit(batch); + batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock), SliceBuilder.Begin().Add(block.Hash).Add(block.Index)); + db.Write(WriteOptions.Default, batch); + current_block_height = block.Index; + } + + private void PersistBlocks() + { + while (!disposed) + { + new_block_event.WaitOne(); + while (!disposed) + { + UInt256 hash; + lock (header_index) + { + if (header_index.Count <= current_block_height + 1) break; + hash = header_index[(int)current_block_height + 1]; + } + Block block; + lock (block_cache) + { + if (!block_cache.ContainsKey(hash)) break; + block = block_cache[hash]; + } + Persist(block); + OnPersistCompleted(block); + lock (block_cache) + { + block_cache.Remove(hash); + } + } + } + } + } +} diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/LevelDBException.cs b/neo/Implementations/Blockchains/LevelDB/LevelDBException.cs similarity index 77% rename from src/AntShares/Implementations/Blockchains/LevelDB/LevelDBException.cs rename to neo/Implementations/Blockchains/LevelDB/LevelDBException.cs index 859079e190..13debda71c 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/LevelDBException.cs +++ b/neo/Implementations/Blockchains/LevelDB/LevelDBException.cs @@ -1,6 +1,6 @@ using System.Data.Common; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class LevelDBException : DbException { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/Native.cs b/neo/Implementations/Blockchains/LevelDB/Native.cs similarity index 99% rename from src/AntShares/Implementations/Blockchains/LevelDB/Native.cs rename to neo/Implementations/Blockchains/LevelDB/Native.cs index 1489338d9e..8149c26db0 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/Native.cs +++ b/neo/Implementations/Blockchains/LevelDB/Native.cs @@ -2,9 +2,9 @@ using System.IO; using System.Runtime.InteropServices; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { - public enum CompressionType : byte + internal enum CompressionType : byte { kNoCompression = 0x0, kSnappyCompression = 0x1 diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/Options.cs b/neo/Implementations/Blockchains/LevelDB/Options.cs similarity index 97% rename from src/AntShares/Implementations/Blockchains/LevelDB/Options.cs rename to neo/Implementations/Blockchains/LevelDB/Options.cs index bcd2c3209f..eea4838eb0 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/Options.cs +++ b/neo/Implementations/Blockchains/LevelDB/Options.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class Options { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/ReadOptions.cs b/neo/Implementations/Blockchains/LevelDB/ReadOptions.cs similarity index 93% rename from src/AntShares/Implementations/Blockchains/LevelDB/ReadOptions.cs rename to neo/Implementations/Blockchains/LevelDB/ReadOptions.cs index cf4a3c1e35..ead136c603 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/ReadOptions.cs +++ b/neo/Implementations/Blockchains/LevelDB/ReadOptions.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class ReadOptions { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/Slice.cs b/neo/Implementations/Blockchains/LevelDB/Slice.cs similarity index 95% rename from src/AntShares/Implementations/Blockchains/LevelDB/Slice.cs rename to neo/Implementations/Blockchains/LevelDB/Slice.cs index b967676b91..1201a6a1a9 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/Slice.cs +++ b/neo/Implementations/Blockchains/LevelDB/Slice.cs @@ -1,244 +1,244 @@ -using AntShares.Cryptography; -using System; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; - -namespace AntShares.Implementations.Blockchains.LevelDB -{ - internal struct Slice : IComparable, IEquatable - { - internal byte[] buffer; - - internal Slice(IntPtr data, UIntPtr length) - { - buffer = new byte[(int)length]; - Marshal.Copy(data, buffer, 0, (int)length); - } - - public int CompareTo(Slice other) - { - for (int i = 0; i < buffer.Length && i < other.buffer.Length; i++) - { - int r = buffer[i].CompareTo(other.buffer[i]); - if (r != 0) return r; - } - return buffer.Length.CompareTo(other.buffer.Length); - } - - public bool Equals(Slice other) - { - if (buffer.Length != other.buffer.Length) return false; - return buffer.SequenceEqual(other.buffer); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (!(obj is Slice)) return false; - return Equals((Slice)obj); - } - - public override int GetHashCode() - { - return (int)buffer.Murmur32(0); - } - - public byte[] ToArray() - { - return buffer ?? new byte[0]; - } - - unsafe public bool ToBoolean() - { - if (buffer.Length != sizeof(bool)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((bool*)pbyte); - } - } - - public byte ToByte() - { - if (buffer.Length != sizeof(byte)) - throw new InvalidCastException(); - return buffer[0]; - } - - unsafe public double ToDouble() - { - if (buffer.Length != sizeof(double)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((double*)pbyte); - } - } - - unsafe public short ToInt16() - { - if (buffer.Length != sizeof(short)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((short*)pbyte); - } - } - - unsafe public int ToInt32() - { - if (buffer.Length != sizeof(int)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((int*)pbyte); - } - } - - unsafe public long ToInt64() - { - if (buffer.Length != sizeof(long)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((long*)pbyte); - } - } - - unsafe public float ToSingle() - { - if (buffer.Length != sizeof(float)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((float*)pbyte); - } - } - - public override string ToString() - { - return Encoding.UTF8.GetString(buffer); - } - - unsafe public ushort ToUInt16() - { - if (buffer.Length != sizeof(ushort)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((ushort*)pbyte); - } - } - - unsafe public uint ToUInt32() - { - if (buffer.Length != sizeof(uint)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((uint*)pbyte); - } - } - - unsafe public ulong ToUInt64() - { - if (buffer.Length != sizeof(ulong)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((ulong*)pbyte); - } - } - - public static implicit operator Slice(byte[] data) - { - return new Slice { buffer = data }; - } - - public static implicit operator Slice(bool data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(byte data) - { - return new Slice { buffer = new[] { data } }; - } - - public static implicit operator Slice(double data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(short data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(int data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(long data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(float data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(string data) - { - return new Slice { buffer = Encoding.UTF8.GetBytes(data) }; - } - - public static implicit operator Slice(ushort data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(uint data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(ulong data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static bool operator <(Slice x, Slice y) - { - return x.CompareTo(y) < 0; - } - - public static bool operator <=(Slice x, Slice y) - { - return x.CompareTo(y) <= 0; - } - - public static bool operator >(Slice x, Slice y) - { - return x.CompareTo(y) > 0; - } - - public static bool operator >=(Slice x, Slice y) - { - return x.CompareTo(y) >= 0; - } - - public static bool operator ==(Slice x, Slice y) - { - return x.Equals(y); - } - - public static bool operator !=(Slice x, Slice y) - { - return !x.Equals(y); - } - } -} +using Neo.Cryptography; +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Neo.Implementations.Blockchains.LevelDB +{ + internal struct Slice : IComparable, IEquatable + { + internal byte[] buffer; + + internal Slice(IntPtr data, UIntPtr length) + { + buffer = new byte[(int)length]; + Marshal.Copy(data, buffer, 0, (int)length); + } + + public int CompareTo(Slice other) + { + for (int i = 0; i < buffer.Length && i < other.buffer.Length; i++) + { + int r = buffer[i].CompareTo(other.buffer[i]); + if (r != 0) return r; + } + return buffer.Length.CompareTo(other.buffer.Length); + } + + public bool Equals(Slice other) + { + if (buffer.Length != other.buffer.Length) return false; + return buffer.SequenceEqual(other.buffer); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (!(obj is Slice)) return false; + return Equals((Slice)obj); + } + + public override int GetHashCode() + { + return (int)buffer.Murmur32(0); + } + + public byte[] ToArray() + { + return buffer ?? new byte[0]; + } + + unsafe public bool ToBoolean() + { + if (buffer.Length != sizeof(bool)) + throw new InvalidCastException(); + fixed (byte* pbyte = &buffer[0]) + { + return *((bool*)pbyte); + } + } + + public byte ToByte() + { + if (buffer.Length != sizeof(byte)) + throw new InvalidCastException(); + return buffer[0]; + } + + unsafe public double ToDouble() + { + if (buffer.Length != sizeof(double)) + throw new InvalidCastException(); + fixed (byte* pbyte = &buffer[0]) + { + return *((double*)pbyte); + } + } + + unsafe public short ToInt16() + { + if (buffer.Length != sizeof(short)) + throw new InvalidCastException(); + fixed (byte* pbyte = &buffer[0]) + { + return *((short*)pbyte); + } + } + + unsafe public int ToInt32() + { + if (buffer.Length != sizeof(int)) + throw new InvalidCastException(); + fixed (byte* pbyte = &buffer[0]) + { + return *((int*)pbyte); + } + } + + unsafe public long ToInt64() + { + if (buffer.Length != sizeof(long)) + throw new InvalidCastException(); + fixed (byte* pbyte = &buffer[0]) + { + return *((long*)pbyte); + } + } + + unsafe public float ToSingle() + { + if (buffer.Length != sizeof(float)) + throw new InvalidCastException(); + fixed (byte* pbyte = &buffer[0]) + { + return *((float*)pbyte); + } + } + + public override string ToString() + { + return Encoding.UTF8.GetString(buffer); + } + + unsafe public ushort ToUInt16() + { + if (buffer.Length != sizeof(ushort)) + throw new InvalidCastException(); + fixed (byte* pbyte = &buffer[0]) + { + return *((ushort*)pbyte); + } + } + + unsafe public uint ToUInt32() + { + if (buffer.Length != sizeof(uint)) + throw new InvalidCastException(); + fixed (byte* pbyte = &buffer[0]) + { + return *((uint*)pbyte); + } + } + + unsafe public ulong ToUInt64() + { + if (buffer.Length != sizeof(ulong)) + throw new InvalidCastException(); + fixed (byte* pbyte = &buffer[0]) + { + return *((ulong*)pbyte); + } + } + + public static implicit operator Slice(byte[] data) + { + return new Slice { buffer = data }; + } + + public static implicit operator Slice(bool data) + { + return new Slice { buffer = BitConverter.GetBytes(data) }; + } + + public static implicit operator Slice(byte data) + { + return new Slice { buffer = new[] { data } }; + } + + public static implicit operator Slice(double data) + { + return new Slice { buffer = BitConverter.GetBytes(data) }; + } + + public static implicit operator Slice(short data) + { + return new Slice { buffer = BitConverter.GetBytes(data) }; + } + + public static implicit operator Slice(int data) + { + return new Slice { buffer = BitConverter.GetBytes(data) }; + } + + public static implicit operator Slice(long data) + { + return new Slice { buffer = BitConverter.GetBytes(data) }; + } + + public static implicit operator Slice(float data) + { + return new Slice { buffer = BitConverter.GetBytes(data) }; + } + + public static implicit operator Slice(string data) + { + return new Slice { buffer = Encoding.UTF8.GetBytes(data) }; + } + + public static implicit operator Slice(ushort data) + { + return new Slice { buffer = BitConverter.GetBytes(data) }; + } + + public static implicit operator Slice(uint data) + { + return new Slice { buffer = BitConverter.GetBytes(data) }; + } + + public static implicit operator Slice(ulong data) + { + return new Slice { buffer = BitConverter.GetBytes(data) }; + } + + public static bool operator <(Slice x, Slice y) + { + return x.CompareTo(y) < 0; + } + + public static bool operator <=(Slice x, Slice y) + { + return x.CompareTo(y) <= 0; + } + + public static bool operator >(Slice x, Slice y) + { + return x.CompareTo(y) > 0; + } + + public static bool operator >=(Slice x, Slice y) + { + return x.CompareTo(y) >= 0; + } + + public static bool operator ==(Slice x, Slice y) + { + return x.Equals(y); + } + + public static bool operator !=(Slice x, Slice y) + { + return !x.Equals(y); + } + } +} diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/SliceBuilder.cs b/neo/Implementations/Blockchains/LevelDB/SliceBuilder.cs similarity index 95% rename from src/AntShares/Implementations/Blockchains/LevelDB/SliceBuilder.cs rename to neo/Implementations/Blockchains/LevelDB/SliceBuilder.cs index f57e9625dd..bbe1a84453 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/SliceBuilder.cs +++ b/neo/Implementations/Blockchains/LevelDB/SliceBuilder.cs @@ -1,9 +1,9 @@ -using AntShares.IO; +using Neo.IO; using System; using System.Collections.Generic; using System.Text; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class SliceBuilder { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/Snapshot.cs b/neo/Implementations/Blockchains/LevelDB/Snapshot.cs similarity index 89% rename from src/AntShares/Implementations/Blockchains/LevelDB/Snapshot.cs rename to neo/Implementations/Blockchains/LevelDB/Snapshot.cs index 7abc6b5033..d451b5c245 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/Snapshot.cs +++ b/neo/Implementations/Blockchains/LevelDB/Snapshot.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class Snapshot : IDisposable { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/WriteBatch.cs b/neo/Implementations/Blockchains/LevelDB/WriteBatch.cs similarity index 92% rename from src/AntShares/Implementations/Blockchains/LevelDB/WriteBatch.cs rename to neo/Implementations/Blockchains/LevelDB/WriteBatch.cs index 1698682bea..56005c1703 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/WriteBatch.cs +++ b/neo/Implementations/Blockchains/LevelDB/WriteBatch.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class WriteBatch { diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/WriteOptions.cs b/neo/Implementations/Blockchains/LevelDB/WriteOptions.cs similarity index 89% rename from src/AntShares/Implementations/Blockchains/LevelDB/WriteOptions.cs rename to neo/Implementations/Blockchains/LevelDB/WriteOptions.cs index 07e3f9c110..928c90201d 100644 --- a/src/AntShares/Implementations/Blockchains/LevelDB/WriteOptions.cs +++ b/neo/Implementations/Blockchains/LevelDB/WriteOptions.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Implementations.Blockchains.LevelDB +namespace Neo.Implementations.Blockchains.LevelDB { internal class WriteOptions { diff --git a/src/AntShares/Implementations/Wallets/EntityFramework/Account.cs b/neo/Implementations/Wallets/EntityFramework/Account.cs similarity index 70% rename from src/AntShares/Implementations/Wallets/EntityFramework/Account.cs rename to neo/Implementations/Wallets/EntityFramework/Account.cs index 1ef211bb86..009a6ed961 100644 --- a/src/AntShares/Implementations/Wallets/EntityFramework/Account.cs +++ b/neo/Implementations/Wallets/EntityFramework/Account.cs @@ -1,4 +1,4 @@ -namespace AntShares.Implementations.Wallets.EntityFramework +namespace Neo.Implementations.Wallets.EntityFramework { internal class Account { diff --git a/src/AntShares/Implementations/Wallets/EntityFramework/Address.cs b/neo/Implementations/Wallets/EntityFramework/Address.cs similarity index 58% rename from src/AntShares/Implementations/Wallets/EntityFramework/Address.cs rename to neo/Implementations/Wallets/EntityFramework/Address.cs index b3f84c402c..63cf54cfaa 100644 --- a/src/AntShares/Implementations/Wallets/EntityFramework/Address.cs +++ b/neo/Implementations/Wallets/EntityFramework/Address.cs @@ -1,4 +1,4 @@ -namespace AntShares.Implementations.Wallets.EntityFramework +namespace Neo.Implementations.Wallets.EntityFramework { internal class Address { diff --git a/src/AntShares/Implementations/Wallets/EntityFramework/Coin.cs b/neo/Implementations/Wallets/EntityFramework/Coin.cs similarity index 80% rename from src/AntShares/Implementations/Wallets/EntityFramework/Coin.cs rename to neo/Implementations/Wallets/EntityFramework/Coin.cs index b90774d7f6..72ead15688 100644 --- a/src/AntShares/Implementations/Wallets/EntityFramework/Coin.cs +++ b/neo/Implementations/Wallets/EntityFramework/Coin.cs @@ -1,6 +1,6 @@ -using AntShares.Core; +using Neo.Core; -namespace AntShares.Implementations.Wallets.EntityFramework +namespace Neo.Implementations.Wallets.EntityFramework { internal class Coin { diff --git a/src/AntShares/Implementations/Wallets/EntityFramework/Contract.cs b/neo/Implementations/Wallets/EntityFramework/Contract.cs similarity index 81% rename from src/AntShares/Implementations/Wallets/EntityFramework/Contract.cs rename to neo/Implementations/Wallets/EntityFramework/Contract.cs index 71dce0919e..f236887921 100644 --- a/src/AntShares/Implementations/Wallets/EntityFramework/Contract.cs +++ b/neo/Implementations/Wallets/EntityFramework/Contract.cs @@ -1,4 +1,4 @@ -namespace AntShares.Implementations.Wallets.EntityFramework +namespace Neo.Implementations.Wallets.EntityFramework { internal class Contract { diff --git a/src/AntShares/Implementations/Wallets/EntityFramework/Key.cs b/neo/Implementations/Wallets/EntityFramework/Key.cs similarity index 65% rename from src/AntShares/Implementations/Wallets/EntityFramework/Key.cs rename to neo/Implementations/Wallets/EntityFramework/Key.cs index 0a2cf4a74e..21c83e68cd 100644 --- a/src/AntShares/Implementations/Wallets/EntityFramework/Key.cs +++ b/neo/Implementations/Wallets/EntityFramework/Key.cs @@ -1,4 +1,4 @@ -namespace AntShares.Implementations.Wallets.EntityFramework +namespace Neo.Implementations.Wallets.EntityFramework { internal class Key { diff --git a/src/AntShares/Implementations/Wallets/EntityFramework/Transaction.cs b/neo/Implementations/Wallets/EntityFramework/Transaction.cs similarity index 76% rename from src/AntShares/Implementations/Wallets/EntityFramework/Transaction.cs rename to neo/Implementations/Wallets/EntityFramework/Transaction.cs index 7a6f3be8b2..923f113121 100644 --- a/src/AntShares/Implementations/Wallets/EntityFramework/Transaction.cs +++ b/neo/Implementations/Wallets/EntityFramework/Transaction.cs @@ -1,7 +1,7 @@ -using AntShares.Core; +using Neo.Core; using System; -namespace AntShares.Implementations.Wallets.EntityFramework +namespace Neo.Implementations.Wallets.EntityFramework { internal class Transaction { diff --git a/src/AntShares/Implementations/Wallets/EntityFramework/TransactionInfo.cs b/neo/Implementations/Wallets/EntityFramework/TransactionInfo.cs similarity index 60% rename from src/AntShares/Implementations/Wallets/EntityFramework/TransactionInfo.cs rename to neo/Implementations/Wallets/EntityFramework/TransactionInfo.cs index 0eada14c86..c8dcc7fb27 100644 --- a/src/AntShares/Implementations/Wallets/EntityFramework/TransactionInfo.cs +++ b/neo/Implementations/Wallets/EntityFramework/TransactionInfo.cs @@ -1,7 +1,7 @@ using System; -using CoreTransaction = AntShares.Core.Transaction; +using CoreTransaction = Neo.Core.Transaction; -namespace AntShares.Implementations.Wallets.EntityFramework +namespace Neo.Implementations.Wallets.EntityFramework { public class TransactionInfo { diff --git a/src/AntShares/Implementations/Wallets/EntityFramework/UserWallet.cs b/neo/Implementations/Wallets/EntityFramework/UserWallet.cs similarity index 98% rename from src/AntShares/Implementations/Wallets/EntityFramework/UserWallet.cs rename to neo/Implementations/Wallets/EntityFramework/UserWallet.cs index 843daceac8..8400c80896 100644 --- a/src/AntShares/Implementations/Wallets/EntityFramework/UserWallet.cs +++ b/neo/Implementations/Wallets/EntityFramework/UserWallet.cs @@ -1,18 +1,18 @@ -using AntShares.Core; -using AntShares.IO; -using AntShares.Wallets; +using Neo.Core; +using Neo.IO; +using Neo.Wallets; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Security; -using CoreTransaction = AntShares.Core.Transaction; -using WalletCoin = AntShares.Wallets.Coin; -using WalletContract = AntShares.Wallets.Contract; -using WalletKeyPair = AntShares.Wallets.KeyPair; +using CoreTransaction = Neo.Core.Transaction; +using WalletCoin = Neo.Wallets.Coin; +using WalletContract = Neo.Wallets.Contract; +using WalletKeyPair = Neo.Wallets.KeyPair; -namespace AntShares.Implementations.Wallets.EntityFramework +namespace Neo.Implementations.Wallets.EntityFramework { public class UserWallet : Wallet { diff --git a/src/AntShares/Implementations/Wallets/EntityFramework/WalletDataContext.cs b/neo/Implementations/Wallets/EntityFramework/WalletDataContext.cs similarity index 98% rename from src/AntShares/Implementations/Wallets/EntityFramework/WalletDataContext.cs rename to neo/Implementations/Wallets/EntityFramework/WalletDataContext.cs index 879d3560e7..019a56c03d 100644 --- a/src/AntShares/Implementations/Wallets/EntityFramework/WalletDataContext.cs +++ b/neo/Implementations/Wallets/EntityFramework/WalletDataContext.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; -namespace AntShares.Implementations.Wallets.EntityFramework +namespace Neo.Implementations.Wallets.EntityFramework { internal class WalletDataContext : DbContext { diff --git a/src/AntShares/Network/IInventory.cs b/neo/Network/IInventory.cs similarity index 75% rename from src/AntShares/Network/IInventory.cs rename to neo/Network/IInventory.cs index 12b747e1e4..c4784cdc8c 100644 --- a/src/AntShares/Network/IInventory.cs +++ b/neo/Network/IInventory.cs @@ -1,6 +1,6 @@ -using AntShares.Core; +using Neo.Core; -namespace AntShares.Network +namespace Neo.Network { public interface IInventory : IVerifiable { diff --git a/src/AntShares/Network/InventoryReceivingEventArgs.cs b/neo/Network/InventoryReceivingEventArgs.cs similarity index 87% rename from src/AntShares/Network/InventoryReceivingEventArgs.cs rename to neo/Network/InventoryReceivingEventArgs.cs index 1ecebba2b4..020d06678f 100644 --- a/src/AntShares/Network/InventoryReceivingEventArgs.cs +++ b/neo/Network/InventoryReceivingEventArgs.cs @@ -1,14 +1,14 @@ -using System.ComponentModel; - -namespace AntShares.Network -{ - public class InventoryReceivingEventArgs : CancelEventArgs - { - public IInventory Inventory { get; } - - public InventoryReceivingEventArgs(IInventory inventory) - { - this.Inventory = inventory; - } - } -} +using System.ComponentModel; + +namespace Neo.Network +{ + public class InventoryReceivingEventArgs : CancelEventArgs + { + public IInventory Inventory { get; } + + public InventoryReceivingEventArgs(IInventory inventory) + { + this.Inventory = inventory; + } + } +} diff --git a/src/AntShares/Network/InventoryType.cs b/neo/Network/InventoryType.cs similarity index 92% rename from src/AntShares/Network/InventoryType.cs rename to neo/Network/InventoryType.cs index 0fd83d9908..4d50374401 100644 --- a/src/AntShares/Network/InventoryType.cs +++ b/neo/Network/InventoryType.cs @@ -1,4 +1,4 @@ -namespace AntShares.Network +namespace Neo.Network { /// /// 定义清单中的对象类型 diff --git a/src/AntShares/Network/LocalNode.cs b/neo/Network/LocalNode.cs similarity index 95% rename from src/AntShares/Network/LocalNode.cs rename to neo/Network/LocalNode.cs index b84f5935c8..f721d1cbad 100644 --- a/src/AntShares/Network/LocalNode.cs +++ b/neo/Network/LocalNode.cs @@ -1,576 +1,576 @@ -using AntShares.Core; -using AntShares.IO; -using AntShares.IO.Caching; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using System.Net.WebSockets; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace AntShares.Network -{ - public class LocalNode : IDisposable - { - public static event EventHandler InventoryReceiving; - public static event EventHandler InventoryReceived; - - public const uint ProtocolVersion = 0; - private const int ConnectedMax = 10; - private const int UnconnectedMax = 1000; - public const int MemoryPoolSize = 30000; - - private static readonly Dictionary mem_pool = new Dictionary(); - private readonly HashSet temp_pool = new HashSet(); - internal static readonly HashSet KnownHashes = new HashSet(); - internal readonly RelayCache RelayCache = new RelayCache(100); - - private static readonly HashSet unconnectedPeers = new HashSet(); - private static readonly HashSet badPeers = new HashSet(); - internal readonly List connectedPeers = new List(); - - internal static readonly HashSet LocalAddresses = new HashSet(); - internal ushort Port; - internal readonly uint Nonce; - private TcpListener listener; - private IWebHost ws_host; - private Thread connectThread; - private Thread poolThread; - private readonly AutoResetEvent new_tx_event = new AutoResetEvent(false); - private int started = 0; - private int disposed = 0; - - public bool GlobalMissionsEnabled { get; set; } = true; - public int RemoteNodeCount => connectedPeers.Count; - public bool ServiceEnabled { get; set; } = true; - public bool UpnpEnabled { get; set; } = false; - public string UserAgent { get; set; } - - static LocalNode() - { - LocalAddresses.UnionWith(NetworkInterface.GetAllNetworkInterfaces().SelectMany(p => p.GetIPProperties().UnicastAddresses).Select(p => p.Address.MapToIPv6())); - } - - public LocalNode() - { - Random rand = new Random(); - this.Nonce = (uint)rand.Next(); - this.connectThread = new Thread(ConnectToPeersLoop) - { - IsBackground = true, - Name = "LocalNode.ConnectToPeersLoop" - }; - if (Blockchain.Default != null) - { - this.poolThread = new Thread(AddTransactionLoop) - { - IsBackground = true, - Name = "LocalNode.AddTransactionLoop" - }; - } - this.UserAgent = string.Format("/AntShares:{0}/", GetType().GetTypeInfo().Assembly.GetName().Version.ToString(3)); - Blockchain.PersistCompleted += Blockchain_PersistCompleted; - } - - private async void AcceptPeersAsync() - { - while (disposed == 0) - { - Socket socket; - try - { - socket = await listener.AcceptSocketAsync(); - } - catch (ObjectDisposedException) - { - break; - } - catch (SocketException) - { - break; - } - TcpRemoteNode remoteNode = new TcpRemoteNode(this, socket); - OnConnected(remoteNode); - } - } - - private static bool AddTransaction(Transaction tx) - { - if (Blockchain.Default == null) return false; - lock (mem_pool) - { - if (mem_pool.ContainsKey(tx.Hash)) return false; - if (Blockchain.Default.ContainsTransaction(tx.Hash)) return false; - if (!tx.Verify(mem_pool.Values)) return false; - mem_pool.Add(tx.Hash, tx); - CheckMemPool(); - } - return true; - } - - private void AddTransactionLoop() - { - while (disposed == 0) - { - new_tx_event.WaitOne(); - Transaction[] transactions; - lock (temp_pool) - { - if (temp_pool.Count == 0) continue; - transactions = temp_pool.ToArray(); - temp_pool.Clear(); - } - ConcurrentBag verified = new ConcurrentBag(); - lock (mem_pool) - { - transactions = transactions.Where(p => !mem_pool.ContainsKey(p.Hash) && !Blockchain.Default.ContainsTransaction(p.Hash)).ToArray(); - if (transactions.Length == 0) continue; - transactions.AsParallel().ForAll(tx => - { - if (tx.Verify(mem_pool.Values.Concat(transactions))) - verified.Add(tx); - }); - if (verified.Count == 0) continue; - foreach (Transaction tx in verified) - mem_pool.Add(tx.Hash, tx); - CheckMemPool(); - } - RelayDirectly(verified); - if (InventoryReceived != null) - foreach (Transaction tx in verified) - InventoryReceived(this, tx); - } - } - - public static void AllowHashes(IEnumerable hashes) - { - lock (KnownHashes) - { - KnownHashes.ExceptWith(hashes); - } - } - - private void Blockchain_PersistCompleted(object sender, Block block) - { - lock (mem_pool) - { - foreach (Transaction tx in block.Transactions) - { - mem_pool.Remove(tx.Hash); - } - if (mem_pool.Count == 0) return; - Transaction[] remain = mem_pool.Values.ToArray(); - mem_pool.Clear(); - lock (temp_pool) - { - temp_pool.UnionWith(remain); - } - new_tx_event.Set(); - } - } - - private static void CheckMemPool() - { - if (mem_pool.Count <= MemoryPoolSize) return; - UInt256[] hashes = mem_pool.Values.AsParallel().OrderBy(p => p.NetworkFee / p.Size).Take(mem_pool.Count - MemoryPoolSize).Select(p => p.Hash).ToArray(); - foreach (UInt256 hash in hashes) - mem_pool.Remove(hash); - } - - public async Task ConnectToPeerAsync(string hostNameOrAddress, int port) - { - IPAddress ipAddress; - if (IPAddress.TryParse(hostNameOrAddress, out ipAddress)) - { - ipAddress = ipAddress.MapToIPv6(); - } - else - { - IPHostEntry entry; - try - { - entry = await Dns.GetHostEntryAsync(hostNameOrAddress); - } - catch (SocketException) - { - return; - } - ipAddress = entry.AddressList.FirstOrDefault(p => p.AddressFamily == AddressFamily.InterNetwork || p.IsIPv6Teredo)?.MapToIPv6(); - if (ipAddress == null) return; - } - await ConnectToPeerAsync(new IPEndPoint(ipAddress, port)); - } - - public async Task ConnectToPeerAsync(IPEndPoint remoteEndpoint) - { - if (remoteEndpoint.Port == Port && LocalAddresses.Contains(remoteEndpoint.Address)) return; - lock (unconnectedPeers) - { - unconnectedPeers.Remove(remoteEndpoint); - } - lock (connectedPeers) - { - if (connectedPeers.Any(p => remoteEndpoint.Equals(p.ListenerEndpoint))) - return; - } - TcpRemoteNode remoteNode = new TcpRemoteNode(this, remoteEndpoint); - if (await remoteNode.ConnectAsync()) - { - OnConnected(remoteNode); - } - } - - private void ConnectToPeersLoop() - { - while (disposed == 0) - { - int connectedCount = connectedPeers.Count; - int unconnectedCount = unconnectedPeers.Count; - if (connectedCount < ConnectedMax) - { - Task[] tasks = { }; - if (unconnectedCount > 0) - { - IPEndPoint[] endpoints; - lock (unconnectedPeers) - { - endpoints = unconnectedPeers.Take(ConnectedMax - connectedCount).ToArray(); - } - tasks = endpoints.Select(p => ConnectToPeerAsync(p)).ToArray(); - } - else if (connectedCount > 0) - { - lock (connectedPeers) - { - foreach (RemoteNode node in connectedPeers) - node.RequestPeers(); - } - } - else - { - tasks = Settings.Default.SeedList.OfType().Select(p => p.Split(':')).Select(p => ConnectToPeerAsync(p[0], int.Parse(p[1]))).ToArray(); - } - Task.WaitAll(tasks); - } - for (int i = 0; i < 50 && disposed == 0; i++) - { - Thread.Sleep(100); - } - } - } - - public static bool ContainsTransaction(UInt256 hash) - { - lock (mem_pool) - { - return mem_pool.ContainsKey(hash); - } - } - - public void Dispose() - { - if (Interlocked.Exchange(ref disposed, 1) == 0) - { - if (started > 0) - { - Blockchain.PersistCompleted -= Blockchain_PersistCompleted; - if (listener != null) listener.Stop(); - if (!connectThread.ThreadState.HasFlag(ThreadState.Unstarted)) connectThread.Join(); - lock (unconnectedPeers) - { - if (unconnectedPeers.Count < UnconnectedMax) - { - lock (connectedPeers) - { - unconnectedPeers.UnionWith(connectedPeers.Select(p => p.ListenerEndpoint).Where(p => p != null).Take(UnconnectedMax - unconnectedPeers.Count)); - } - } - } - RemoteNode[] nodes; - lock (connectedPeers) - { - nodes = connectedPeers.ToArray(); - } - Task.WaitAll(nodes.Select(p => Task.Run(() => p.Disconnect(false))).ToArray()); - new_tx_event.Set(); - if (poolThread?.ThreadState.HasFlag(ThreadState.Unstarted) == false) - poolThread.Join(); - new_tx_event.Dispose(); - } - } - } - - public static IEnumerable GetMemoryPool() - { - lock (mem_pool) - { - foreach (Transaction tx in mem_pool.Values) - yield return tx; - } - } - - public RemoteNode[] GetRemoteNodes() - { - lock (connectedPeers) - { - return connectedPeers.ToArray(); - } - } - - public static Transaction GetTransaction(UInt256 hash) - { - lock (mem_pool) - { - Transaction tx; - if (!mem_pool.TryGetValue(hash, out tx)) - return null; - return tx; - } - } - - private static bool IsIntranetAddress(IPAddress address) - { - byte[] data = address.MapToIPv4().GetAddressBytes(); - Array.Reverse(data); - uint value = data.ToUInt32(0); - return (value & 0xff000000) == 0x0a000000 || (value & 0xfff00000) == 0xac100000 || (value & 0xffff0000) == 0xc0a80000; - } - - public static void LoadState(Stream stream) - { - unconnectedPeers.Clear(); - using (BinaryReader reader = new BinaryReader(stream, Encoding.ASCII, true)) - { - int count = reader.ReadInt32(); - for (int i = 0; i < count; i++) - { - IPAddress address = new IPAddress(reader.ReadBytes(4)); - int port = reader.ReadUInt16(); - unconnectedPeers.Add(new IPEndPoint(address.MapToIPv6(), port)); - } - } - } - - private void OnConnected(RemoteNode remoteNode) - { - lock (connectedPeers) - { - connectedPeers.Add(remoteNode); - } - remoteNode.Disconnected += RemoteNode_Disconnected; - remoteNode.InventoryReceived += RemoteNode_InventoryReceived; - remoteNode.PeersReceived += RemoteNode_PeersReceived; - remoteNode.StartProtocol(); - } - - private async Task ProcessWebSocketAsync(HttpContext context) - { - if (!context.WebSockets.IsWebSocketRequest) return; - WebSocket ws = await context.WebSockets.AcceptWebSocketAsync(); - WebSocketRemoteNode remoteNode = new WebSocketRemoteNode(this, ws, new IPEndPoint(context.Connection.RemoteIpAddress, context.Connection.RemotePort)); - OnConnected(remoteNode); - } - - public bool Relay(IInventory inventory) - { - if (inventory is MinerTransaction) return false; - lock (KnownHashes) - { - if (!KnownHashes.Add(inventory.Hash)) return false; - } - InventoryReceivingEventArgs args = new InventoryReceivingEventArgs(inventory); - InventoryReceiving?.Invoke(this, args); - if (args.Cancel) return false; - if (inventory is Block) - { - if (Blockchain.Default == null) return false; - Block block = (Block)inventory; - if (Blockchain.Default.ContainsBlock(block.Hash)) return false; - if (!Blockchain.Default.AddBlock(block)) return false; - } - else if (inventory is Transaction) - { - if (!AddTransaction((Transaction)inventory)) return false; - } - else //if (inventory is Consensus) - { - if (!inventory.Verify()) return false; - } - bool relayed = RelayDirectly(inventory); - InventoryReceived?.Invoke(this, inventory); - return relayed; - } - - public bool RelayDirectly(IInventory inventory) - { - bool relayed = false; - lock (connectedPeers) - { - RelayCache.Add(inventory); - foreach (RemoteNode node in connectedPeers) - relayed |= node.Relay(inventory); - } - return relayed; - } - - private void RelayDirectly(IReadOnlyCollection transactions) - { - lock (connectedPeers) - { - foreach (RemoteNode node in connectedPeers) - node.Relay(transactions); - } - } - - private void RemoteNode_Disconnected(object sender, bool error) - { - RemoteNode remoteNode = (RemoteNode)sender; - remoteNode.Disconnected -= RemoteNode_Disconnected; - remoteNode.InventoryReceived -= RemoteNode_InventoryReceived; - remoteNode.PeersReceived -= RemoteNode_PeersReceived; - if (error && remoteNode.ListenerEndpoint != null) - { - lock (badPeers) - { - badPeers.Add(remoteNode.ListenerEndpoint); - } - } - lock (unconnectedPeers) - { - lock (connectedPeers) - { - if (remoteNode.ListenerEndpoint != null) - { - unconnectedPeers.Remove(remoteNode.ListenerEndpoint); - } - connectedPeers.Remove(remoteNode); - } - } - } - - private void RemoteNode_InventoryReceived(object sender, IInventory inventory) - { - if (inventory is Transaction) - { - if (Blockchain.Default == null) return; - lock (KnownHashes) - { - if (!KnownHashes.Add(inventory.Hash)) return; - } - InventoryReceivingEventArgs args = new InventoryReceivingEventArgs(inventory); - InventoryReceiving?.Invoke(this, args); - if (args.Cancel) return; - lock (temp_pool) - { - temp_pool.Add((Transaction)inventory); - } - new_tx_event.Set(); - } - else - { - Relay(inventory); - } - } - - private void RemoteNode_PeersReceived(object sender, IPEndPoint[] peers) - { - lock (unconnectedPeers) - { - if (unconnectedPeers.Count < UnconnectedMax) - { - lock (badPeers) - { - lock (connectedPeers) - { - unconnectedPeers.UnionWith(peers); - unconnectedPeers.ExceptWith(badPeers); - unconnectedPeers.ExceptWith(connectedPeers.Select(p => p.ListenerEndpoint)); - } - } - } - } - } - - public static void SaveState(Stream stream) - { - IPEndPoint[] peers; - lock (unconnectedPeers) - { - peers = unconnectedPeers.Take(UnconnectedMax).ToArray(); - } - using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) - { - writer.Write(peers.Length); - foreach (IPEndPoint endpoint in peers) - { - writer.Write(endpoint.Address.MapToIPv4().GetAddressBytes()); - writer.Write((ushort)endpoint.Port); - } - } - } - - public void Start(int port = 0, int ws_port = 0) - { - if (Interlocked.Exchange(ref started, 1) == 0) - { - Task.Run(async () => - { - if (port > 0 || ws_port > 0) - { - IPAddress address = LocalAddresses.FirstOrDefault(p => p.AddressFamily == AddressFamily.InterNetwork && !IsIntranetAddress(p)); - if (address == null && UpnpEnabled && await UPnP.DiscoverAsync()) - { - try - { - address = await UPnP.GetExternalIPAsync(); - if (port > 0) - await UPnP.ForwardPortAsync(port, ProtocolType.Tcp, "AntShares"); - if (ws_port > 0) - await UPnP.ForwardPortAsync(ws_port, ProtocolType.Tcp, "AntShares WebSocket"); - LocalAddresses.Add(address); - } - catch { } - } - } - connectThread.Start(); - poolThread?.Start(); - if (port > 0) - { - listener = new TcpListener(IPAddress.Any, port); - try - { - listener.Start(); - Port = (ushort)port; - AcceptPeersAsync(); - } - catch (SocketException) { } - } - if (ws_port > 0) - { - ws_host = new WebHostBuilder().UseKestrel().UseUrls($"http://*:{ws_port}").Configure(app => app.UseWebSockets().Run(ProcessWebSocketAsync)).Build(); - ws_host.Start(); - } - }); - } - } - - public void SynchronizeMemoryPool() - { - lock (connectedPeers) - { - foreach (RemoteNode node in connectedPeers) - node.RequestMemoryPool(); - } - } - } -} +using Neo.Core; +using Neo.IO; +using Neo.IO.Caching; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Net.WebSockets; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Neo.Network +{ + public class LocalNode : IDisposable + { + public static event EventHandler InventoryReceiving; + public static event EventHandler InventoryReceived; + + public const uint ProtocolVersion = 0; + private const int ConnectedMax = 10; + private const int UnconnectedMax = 1000; + public const int MemoryPoolSize = 30000; + + private static readonly Dictionary mem_pool = new Dictionary(); + private readonly HashSet temp_pool = new HashSet(); + internal static readonly HashSet KnownHashes = new HashSet(); + internal readonly RelayCache RelayCache = new RelayCache(100); + + private static readonly HashSet unconnectedPeers = new HashSet(); + private static readonly HashSet badPeers = new HashSet(); + internal readonly List connectedPeers = new List(); + + internal static readonly HashSet LocalAddresses = new HashSet(); + internal ushort Port; + internal readonly uint Nonce; + private TcpListener listener; + private IWebHost ws_host; + private Thread connectThread; + private Thread poolThread; + private readonly AutoResetEvent new_tx_event = new AutoResetEvent(false); + private int started = 0; + private int disposed = 0; + + public bool GlobalMissionsEnabled { get; set; } = true; + public int RemoteNodeCount => connectedPeers.Count; + public bool ServiceEnabled { get; set; } = true; + public bool UpnpEnabled { get; set; } = false; + public string UserAgent { get; set; } + + static LocalNode() + { + LocalAddresses.UnionWith(NetworkInterface.GetAllNetworkInterfaces().SelectMany(p => p.GetIPProperties().UnicastAddresses).Select(p => p.Address.MapToIPv6())); + } + + public LocalNode() + { + Random rand = new Random(); + this.Nonce = (uint)rand.Next(); + this.connectThread = new Thread(ConnectToPeersLoop) + { + IsBackground = true, + Name = "LocalNode.ConnectToPeersLoop" + }; + if (Blockchain.Default != null) + { + this.poolThread = new Thread(AddTransactionLoop) + { + IsBackground = true, + Name = "LocalNode.AddTransactionLoop" + }; + } + this.UserAgent = string.Format("/NEO:{0}/", GetType().GetTypeInfo().Assembly.GetName().Version.ToString(3)); + Blockchain.PersistCompleted += Blockchain_PersistCompleted; + } + + private async void AcceptPeersAsync() + { + while (disposed == 0) + { + Socket socket; + try + { + socket = await listener.AcceptSocketAsync(); + } + catch (ObjectDisposedException) + { + break; + } + catch (SocketException) + { + break; + } + TcpRemoteNode remoteNode = new TcpRemoteNode(this, socket); + OnConnected(remoteNode); + } + } + + private static bool AddTransaction(Transaction tx) + { + if (Blockchain.Default == null) return false; + lock (mem_pool) + { + if (mem_pool.ContainsKey(tx.Hash)) return false; + if (Blockchain.Default.ContainsTransaction(tx.Hash)) return false; + if (!tx.Verify(mem_pool.Values)) return false; + mem_pool.Add(tx.Hash, tx); + CheckMemPool(); + } + return true; + } + + private void AddTransactionLoop() + { + while (disposed == 0) + { + new_tx_event.WaitOne(); + Transaction[] transactions; + lock (temp_pool) + { + if (temp_pool.Count == 0) continue; + transactions = temp_pool.ToArray(); + temp_pool.Clear(); + } + ConcurrentBag verified = new ConcurrentBag(); + lock (mem_pool) + { + transactions = transactions.Where(p => !mem_pool.ContainsKey(p.Hash) && !Blockchain.Default.ContainsTransaction(p.Hash)).ToArray(); + if (transactions.Length == 0) continue; + transactions.AsParallel().ForAll(tx => + { + if (tx.Verify(mem_pool.Values.Concat(transactions))) + verified.Add(tx); + }); + if (verified.Count == 0) continue; + foreach (Transaction tx in verified) + mem_pool.Add(tx.Hash, tx); + CheckMemPool(); + } + RelayDirectly(verified); + if (InventoryReceived != null) + foreach (Transaction tx in verified) + InventoryReceived(this, tx); + } + } + + public static void AllowHashes(IEnumerable hashes) + { + lock (KnownHashes) + { + KnownHashes.ExceptWith(hashes); + } + } + + private void Blockchain_PersistCompleted(object sender, Block block) + { + lock (mem_pool) + { + foreach (Transaction tx in block.Transactions) + { + mem_pool.Remove(tx.Hash); + } + if (mem_pool.Count == 0) return; + Transaction[] remain = mem_pool.Values.ToArray(); + mem_pool.Clear(); + lock (temp_pool) + { + temp_pool.UnionWith(remain); + } + new_tx_event.Set(); + } + } + + private static void CheckMemPool() + { + if (mem_pool.Count <= MemoryPoolSize) return; + UInt256[] hashes = mem_pool.Values.AsParallel().OrderBy(p => p.NetworkFee / p.Size).Take(mem_pool.Count - MemoryPoolSize).Select(p => p.Hash).ToArray(); + foreach (UInt256 hash in hashes) + mem_pool.Remove(hash); + } + + public async Task ConnectToPeerAsync(string hostNameOrAddress, int port) + { + IPAddress ipAddress; + if (IPAddress.TryParse(hostNameOrAddress, out ipAddress)) + { + ipAddress = ipAddress.MapToIPv6(); + } + else + { + IPHostEntry entry; + try + { + entry = await Dns.GetHostEntryAsync(hostNameOrAddress); + } + catch (SocketException) + { + return; + } + ipAddress = entry.AddressList.FirstOrDefault(p => p.AddressFamily == AddressFamily.InterNetwork || p.IsIPv6Teredo)?.MapToIPv6(); + if (ipAddress == null) return; + } + await ConnectToPeerAsync(new IPEndPoint(ipAddress, port)); + } + + public async Task ConnectToPeerAsync(IPEndPoint remoteEndpoint) + { + if (remoteEndpoint.Port == Port && LocalAddresses.Contains(remoteEndpoint.Address)) return; + lock (unconnectedPeers) + { + unconnectedPeers.Remove(remoteEndpoint); + } + lock (connectedPeers) + { + if (connectedPeers.Any(p => remoteEndpoint.Equals(p.ListenerEndpoint))) + return; + } + TcpRemoteNode remoteNode = new TcpRemoteNode(this, remoteEndpoint); + if (await remoteNode.ConnectAsync()) + { + OnConnected(remoteNode); + } + } + + private void ConnectToPeersLoop() + { + while (disposed == 0) + { + int connectedCount = connectedPeers.Count; + int unconnectedCount = unconnectedPeers.Count; + if (connectedCount < ConnectedMax) + { + Task[] tasks = { }; + if (unconnectedCount > 0) + { + IPEndPoint[] endpoints; + lock (unconnectedPeers) + { + endpoints = unconnectedPeers.Take(ConnectedMax - connectedCount).ToArray(); + } + tasks = endpoints.Select(p => ConnectToPeerAsync(p)).ToArray(); + } + else if (connectedCount > 0) + { + lock (connectedPeers) + { + foreach (RemoteNode node in connectedPeers) + node.RequestPeers(); + } + } + else + { + tasks = Settings.Default.SeedList.OfType().Select(p => p.Split(':')).Select(p => ConnectToPeerAsync(p[0], int.Parse(p[1]))).ToArray(); + } + Task.WaitAll(tasks); + } + for (int i = 0; i < 50 && disposed == 0; i++) + { + Thread.Sleep(100); + } + } + } + + public static bool ContainsTransaction(UInt256 hash) + { + lock (mem_pool) + { + return mem_pool.ContainsKey(hash); + } + } + + public void Dispose() + { + if (Interlocked.Exchange(ref disposed, 1) == 0) + { + if (started > 0) + { + Blockchain.PersistCompleted -= Blockchain_PersistCompleted; + if (listener != null) listener.Stop(); + if (!connectThread.ThreadState.HasFlag(ThreadState.Unstarted)) connectThread.Join(); + lock (unconnectedPeers) + { + if (unconnectedPeers.Count < UnconnectedMax) + { + lock (connectedPeers) + { + unconnectedPeers.UnionWith(connectedPeers.Select(p => p.ListenerEndpoint).Where(p => p != null).Take(UnconnectedMax - unconnectedPeers.Count)); + } + } + } + RemoteNode[] nodes; + lock (connectedPeers) + { + nodes = connectedPeers.ToArray(); + } + Task.WaitAll(nodes.Select(p => Task.Run(() => p.Disconnect(false))).ToArray()); + new_tx_event.Set(); + if (poolThread?.ThreadState.HasFlag(ThreadState.Unstarted) == false) + poolThread.Join(); + new_tx_event.Dispose(); + } + } + } + + public static IEnumerable GetMemoryPool() + { + lock (mem_pool) + { + foreach (Transaction tx in mem_pool.Values) + yield return tx; + } + } + + public RemoteNode[] GetRemoteNodes() + { + lock (connectedPeers) + { + return connectedPeers.ToArray(); + } + } + + public static Transaction GetTransaction(UInt256 hash) + { + lock (mem_pool) + { + Transaction tx; + if (!mem_pool.TryGetValue(hash, out tx)) + return null; + return tx; + } + } + + private static bool IsIntranetAddress(IPAddress address) + { + byte[] data = address.MapToIPv4().GetAddressBytes(); + Array.Reverse(data); + uint value = data.ToUInt32(0); + return (value & 0xff000000) == 0x0a000000 || (value & 0xfff00000) == 0xac100000 || (value & 0xffff0000) == 0xc0a80000; + } + + public static void LoadState(Stream stream) + { + unconnectedPeers.Clear(); + using (BinaryReader reader = new BinaryReader(stream, Encoding.ASCII, true)) + { + int count = reader.ReadInt32(); + for (int i = 0; i < count; i++) + { + IPAddress address = new IPAddress(reader.ReadBytes(4)); + int port = reader.ReadUInt16(); + unconnectedPeers.Add(new IPEndPoint(address.MapToIPv6(), port)); + } + } + } + + private void OnConnected(RemoteNode remoteNode) + { + lock (connectedPeers) + { + connectedPeers.Add(remoteNode); + } + remoteNode.Disconnected += RemoteNode_Disconnected; + remoteNode.InventoryReceived += RemoteNode_InventoryReceived; + remoteNode.PeersReceived += RemoteNode_PeersReceived; + remoteNode.StartProtocol(); + } + + private async Task ProcessWebSocketAsync(HttpContext context) + { + if (!context.WebSockets.IsWebSocketRequest) return; + WebSocket ws = await context.WebSockets.AcceptWebSocketAsync(); + WebSocketRemoteNode remoteNode = new WebSocketRemoteNode(this, ws, new IPEndPoint(context.Connection.RemoteIpAddress, context.Connection.RemotePort)); + OnConnected(remoteNode); + } + + public bool Relay(IInventory inventory) + { + if (inventory is MinerTransaction) return false; + lock (KnownHashes) + { + if (!KnownHashes.Add(inventory.Hash)) return false; + } + InventoryReceivingEventArgs args = new InventoryReceivingEventArgs(inventory); + InventoryReceiving?.Invoke(this, args); + if (args.Cancel) return false; + if (inventory is Block) + { + if (Blockchain.Default == null) return false; + Block block = (Block)inventory; + if (Blockchain.Default.ContainsBlock(block.Hash)) return false; + if (!Blockchain.Default.AddBlock(block)) return false; + } + else if (inventory is Transaction) + { + if (!AddTransaction((Transaction)inventory)) return false; + } + else //if (inventory is Consensus) + { + if (!inventory.Verify()) return false; + } + bool relayed = RelayDirectly(inventory); + InventoryReceived?.Invoke(this, inventory); + return relayed; + } + + public bool RelayDirectly(IInventory inventory) + { + bool relayed = false; + lock (connectedPeers) + { + RelayCache.Add(inventory); + foreach (RemoteNode node in connectedPeers) + relayed |= node.Relay(inventory); + } + return relayed; + } + + private void RelayDirectly(IReadOnlyCollection transactions) + { + lock (connectedPeers) + { + foreach (RemoteNode node in connectedPeers) + node.Relay(transactions); + } + } + + private void RemoteNode_Disconnected(object sender, bool error) + { + RemoteNode remoteNode = (RemoteNode)sender; + remoteNode.Disconnected -= RemoteNode_Disconnected; + remoteNode.InventoryReceived -= RemoteNode_InventoryReceived; + remoteNode.PeersReceived -= RemoteNode_PeersReceived; + if (error && remoteNode.ListenerEndpoint != null) + { + lock (badPeers) + { + badPeers.Add(remoteNode.ListenerEndpoint); + } + } + lock (unconnectedPeers) + { + lock (connectedPeers) + { + if (remoteNode.ListenerEndpoint != null) + { + unconnectedPeers.Remove(remoteNode.ListenerEndpoint); + } + connectedPeers.Remove(remoteNode); + } + } + } + + private void RemoteNode_InventoryReceived(object sender, IInventory inventory) + { + if (inventory is Transaction) + { + if (Blockchain.Default == null) return; + lock (KnownHashes) + { + if (!KnownHashes.Add(inventory.Hash)) return; + } + InventoryReceivingEventArgs args = new InventoryReceivingEventArgs(inventory); + InventoryReceiving?.Invoke(this, args); + if (args.Cancel) return; + lock (temp_pool) + { + temp_pool.Add((Transaction)inventory); + } + new_tx_event.Set(); + } + else + { + Relay(inventory); + } + } + + private void RemoteNode_PeersReceived(object sender, IPEndPoint[] peers) + { + lock (unconnectedPeers) + { + if (unconnectedPeers.Count < UnconnectedMax) + { + lock (badPeers) + { + lock (connectedPeers) + { + unconnectedPeers.UnionWith(peers); + unconnectedPeers.ExceptWith(badPeers); + unconnectedPeers.ExceptWith(connectedPeers.Select(p => p.ListenerEndpoint)); + } + } + } + } + } + + public static void SaveState(Stream stream) + { + IPEndPoint[] peers; + lock (unconnectedPeers) + { + peers = unconnectedPeers.Take(UnconnectedMax).ToArray(); + } + using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) + { + writer.Write(peers.Length); + foreach (IPEndPoint endpoint in peers) + { + writer.Write(endpoint.Address.MapToIPv4().GetAddressBytes()); + writer.Write((ushort)endpoint.Port); + } + } + } + + public void Start(int port = 0, int ws_port = 0) + { + if (Interlocked.Exchange(ref started, 1) == 0) + { + Task.Run(async () => + { + if (port > 0 || ws_port > 0) + { + IPAddress address = LocalAddresses.FirstOrDefault(p => p.AddressFamily == AddressFamily.InterNetwork && !IsIntranetAddress(p)); + if (address == null && UpnpEnabled && await UPnP.DiscoverAsync()) + { + try + { + address = await UPnP.GetExternalIPAsync(); + if (port > 0) + await UPnP.ForwardPortAsync(port, ProtocolType.Tcp, "NEO"); + if (ws_port > 0) + await UPnP.ForwardPortAsync(ws_port, ProtocolType.Tcp, "NEO WebSocket"); + LocalAddresses.Add(address); + } + catch { } + } + } + connectThread.Start(); + poolThread?.Start(); + if (port > 0) + { + listener = new TcpListener(IPAddress.Any, port); + try + { + listener.Start(); + Port = (ushort)port; + AcceptPeersAsync(); + } + catch (SocketException) { } + } + if (ws_port > 0) + { + ws_host = new WebHostBuilder().UseKestrel().UseUrls($"http://*:{ws_port}").Configure(app => app.UseWebSockets().Run(ProcessWebSocketAsync)).Build(); + ws_host.Start(); + } + }); + } + } + + public void SynchronizeMemoryPool() + { + lock (connectedPeers) + { + foreach (RemoteNode node in connectedPeers) + node.RequestMemoryPool(); + } + } + } +} diff --git a/src/AntShares/Network/Message.cs b/neo/Network/Message.cs similarity index 98% rename from src/AntShares/Network/Message.cs rename to neo/Network/Message.cs index 9ad27f0da8..cc32013735 100644 --- a/src/AntShares/Network/Message.cs +++ b/neo/Network/Message.cs @@ -1,5 +1,5 @@ -using AntShares.Cryptography; -using AntShares.IO; +using Neo.Cryptography; +using Neo.IO; using System; using System.IO; using System.Net.WebSockets; @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; -namespace AntShares.Network +namespace Neo.Network { public class Message : ISerializable { diff --git a/src/AntShares/Network/Payloads/AddrPayload.cs b/neo/Network/Payloads/AddrPayload.cs similarity index 88% rename from src/AntShares/Network/Payloads/AddrPayload.cs rename to neo/Network/Payloads/AddrPayload.cs index e22567ed95..f4c25027f9 100644 --- a/src/AntShares/Network/Payloads/AddrPayload.cs +++ b/neo/Network/Payloads/AddrPayload.cs @@ -1,7 +1,7 @@ -using AntShares.IO; +using Neo.IO; using System.IO; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { internal class AddrPayload : ISerializable { @@ -19,7 +19,7 @@ public static AddrPayload Create(params NetworkAddressWithTime[] addresses) void ISerializable.Deserialize(BinaryReader reader) { - this.AddressList = reader.ReadSerializableArray(); + this.AddressList = reader.ReadSerializableArray(200); } void ISerializable.Serialize(BinaryWriter writer) diff --git a/src/AntShares/Network/Payloads/ConsensusPayload.cs b/neo/Network/Payloads/ConsensusPayload.cs similarity index 91% rename from src/AntShares/Network/Payloads/ConsensusPayload.cs rename to neo/Network/Payloads/ConsensusPayload.cs index fdf1928228..40a6948984 100644 --- a/src/AntShares/Network/Payloads/ConsensusPayload.cs +++ b/neo/Network/Payloads/ConsensusPayload.cs @@ -1,13 +1,13 @@ -using AntShares.Core; -using AntShares.Cryptography; -using AntShares.Cryptography.ECC; -using AntShares.IO; -using AntShares.VM; -using AntShares.Wallets; +using Neo.Core; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.VM; +using Neo.Wallets; using System; using System.IO; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { public class ConsensusPayload : IInventory { @@ -99,11 +99,6 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.WriteVarBytes(Data); } - byte[] IInteropInterface.ToArray() - { - return this.ToArray(); - } - public bool Verify() { if (Blockchain.Default == null) return false; diff --git a/src/AntShares/Network/Payloads/FilterAddPayload.cs b/neo/Network/Payloads/FilterAddPayload.cs similarity index 87% rename from src/AntShares/Network/Payloads/FilterAddPayload.cs rename to neo/Network/Payloads/FilterAddPayload.cs index 920bcfc8bc..24a48acdae 100644 --- a/src/AntShares/Network/Payloads/FilterAddPayload.cs +++ b/neo/Network/Payloads/FilterAddPayload.cs @@ -1,7 +1,7 @@ -using AntShares.IO; +using Neo.IO; using System.IO; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { internal class FilterAddPayload : ISerializable { diff --git a/src/AntShares/Network/Payloads/FilterLoadPayload.cs b/neo/Network/Payloads/FilterLoadPayload.cs similarity index 92% rename from src/AntShares/Network/Payloads/FilterLoadPayload.cs rename to neo/Network/Payloads/FilterLoadPayload.cs index 9f340f1353..337e275eb6 100644 --- a/src/AntShares/Network/Payloads/FilterLoadPayload.cs +++ b/neo/Network/Payloads/FilterLoadPayload.cs @@ -1,9 +1,9 @@ -using AntShares.Cryptography; -using AntShares.IO; +using Neo.Cryptography; +using Neo.IO; using System; using System.IO; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { internal class FilterLoadPayload : ISerializable { diff --git a/src/AntShares/Network/Payloads/GetBlocksPayload.cs b/neo/Network/Payloads/GetBlocksPayload.cs similarity index 86% rename from src/AntShares/Network/Payloads/GetBlocksPayload.cs rename to neo/Network/Payloads/GetBlocksPayload.cs index c28db10fbb..d91ac172b1 100644 --- a/src/AntShares/Network/Payloads/GetBlocksPayload.cs +++ b/neo/Network/Payloads/GetBlocksPayload.cs @@ -1,7 +1,7 @@ -using AntShares.IO; +using Neo.IO; using System.IO; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { internal class GetBlocksPayload : ISerializable { @@ -21,7 +21,7 @@ public static GetBlocksPayload Create(UInt256 hash_start, UInt256 hash_stop = nu void ISerializable.Deserialize(BinaryReader reader) { - HashStart = reader.ReadSerializableArray(); + HashStart = reader.ReadSerializableArray(16); HashStop = reader.ReadSerializable(); } diff --git a/src/AntShares/Network/Payloads/HeadersPayload.cs b/neo/Network/Payloads/HeadersPayload.cs similarity index 81% rename from src/AntShares/Network/Payloads/HeadersPayload.cs rename to neo/Network/Payloads/HeadersPayload.cs index bc281a6b7d..69ddf2f3ab 100644 --- a/src/AntShares/Network/Payloads/HeadersPayload.cs +++ b/neo/Network/Payloads/HeadersPayload.cs @@ -1,10 +1,10 @@ -using AntShares.Core; -using AntShares.IO; +using Neo.Core; +using Neo.IO; using System.Collections.Generic; using System.IO; using System.Linq; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { internal class HeadersPayload : ISerializable { @@ -22,7 +22,7 @@ public static HeadersPayload Create(IEnumerable
headers) void ISerializable.Deserialize(BinaryReader reader) { - Headers = reader.ReadSerializableArray
(); + Headers = reader.ReadSerializableArray
(2000); } void ISerializable.Serialize(BinaryWriter writer) diff --git a/src/AntShares/Network/Payloads/InvPayload.cs b/neo/Network/Payloads/InvPayload.cs similarity index 93% rename from src/AntShares/Network/Payloads/InvPayload.cs rename to neo/Network/Payloads/InvPayload.cs index 0f882f8901..03d15efb35 100644 --- a/src/AntShares/Network/Payloads/InvPayload.cs +++ b/neo/Network/Payloads/InvPayload.cs @@ -1,8 +1,8 @@ -using AntShares.IO; +using Neo.IO; using System; using System.IO; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { internal class InvPayload : ISerializable { diff --git a/src/AntShares/Network/Payloads/MerkleBlockPayload.cs b/neo/Network/Payloads/MerkleBlockPayload.cs similarity index 94% rename from src/AntShares/Network/Payloads/MerkleBlockPayload.cs rename to neo/Network/Payloads/MerkleBlockPayload.cs index 59cccaf98e..1571968ebb 100644 --- a/src/AntShares/Network/Payloads/MerkleBlockPayload.cs +++ b/neo/Network/Payloads/MerkleBlockPayload.cs @@ -1,11 +1,11 @@ -using AntShares.Core; -using AntShares.Cryptography; -using AntShares.IO; +using Neo.Core; +using Neo.Cryptography; +using Neo.IO; using System.Collections; using System.IO; using System.Linq; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { internal class MerkleBlockPayload : BlockBase { diff --git a/src/AntShares/Network/Payloads/NetworkAddressWithTime.cs b/neo/Network/Payloads/NetworkAddressWithTime.cs similarity index 95% rename from src/AntShares/Network/Payloads/NetworkAddressWithTime.cs rename to neo/Network/Payloads/NetworkAddressWithTime.cs index a11c105987..6964bd76aa 100644 --- a/src/AntShares/Network/Payloads/NetworkAddressWithTime.cs +++ b/neo/Network/Payloads/NetworkAddressWithTime.cs @@ -1,10 +1,10 @@ -using AntShares.IO; +using Neo.IO; using System; using System.IO; using System.Linq; using System.Net; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { internal class NetworkAddressWithTime : ISerializable { diff --git a/src/AntShares/Network/Payloads/VersionPayload.cs b/neo/Network/Payloads/VersionPayload.cs similarity index 95% rename from src/AntShares/Network/Payloads/VersionPayload.cs rename to neo/Network/Payloads/VersionPayload.cs index 0fa75b100c..7c33b64168 100644 --- a/src/AntShares/Network/Payloads/VersionPayload.cs +++ b/neo/Network/Payloads/VersionPayload.cs @@ -1,9 +1,9 @@ -using AntShares.Core; -using AntShares.IO; +using Neo.Core; +using Neo.IO; using System; using System.IO; -namespace AntShares.Network.Payloads +namespace Neo.Network.Payloads { public class VersionPayload : ISerializable { diff --git a/src/AntShares/Network/RPC/RpcException.cs b/neo/Network/RPC/RpcException.cs similarity index 85% rename from src/AntShares/Network/RPC/RpcException.cs rename to neo/Network/RPC/RpcException.cs index 6027c8b560..5a2120f083 100644 --- a/src/AntShares/Network/RPC/RpcException.cs +++ b/neo/Network/RPC/RpcException.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Network.RPC +namespace Neo.Network.RPC { public class RpcException : Exception { diff --git a/src/AntShares/Network/RPC/RpcServer.cs b/neo/Network/RPC/RpcServer.cs similarity index 96% rename from src/AntShares/Network/RPC/RpcServer.cs rename to neo/Network/RPC/RpcServer.cs index b2578524de..5a60337a30 100644 --- a/src/AntShares/Network/RPC/RpcServer.cs +++ b/neo/Network/RPC/RpcServer.cs @@ -1,275 +1,275 @@ -using AntShares.Core; -using AntShares.IO; -using AntShares.IO.Json; -using AntShares.Wallets; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AntShares.Network.RPC -{ - public class RpcServer : IDisposable - { - protected readonly LocalNode LocalNode; - private IWebHost host; - - public RpcServer(LocalNode localNode) - { - this.LocalNode = localNode; - } - - private static JObject CreateErrorResponse(JObject id, int code, string message, JObject data = null) - { - JObject response = CreateResponse(id); - response["error"] = new JObject(); - response["error"]["code"] = code; - response["error"]["message"] = message; - if (data != null) - response["error"]["data"] = data; - return response; - } - - private static JObject CreateResponse(JObject id) - { - JObject response = new JObject(); - response["jsonrpc"] = "2.0"; - response["id"] = id; - return response; - } - - public void Dispose() - { - if (host != null) - { - host.Dispose(); - host = null; - } - } - - protected virtual JObject Process(string method, JArray _params) - { - switch (method) - { - case "getbestblockhash": - return Blockchain.Default.CurrentBlockHash.ToString(); - case "getblock": - { - Block block; - if (_params[0] is JNumber) - { - uint index = (uint)_params[0].AsNumber(); - block = Blockchain.Default.GetBlock(index); - } - else - { - UInt256 hash = UInt256.Parse(_params[0].AsString()); - block = Blockchain.Default.GetBlock(hash); - } - if (block == null) - throw new RpcException(-100, "Unknown block"); - bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false); - if (verbose) - { - JObject json = block.ToJson(); - json["confirmations"] = Blockchain.Default.Height - block.Index + 1; - UInt256 hash = Blockchain.Default.GetNextBlockHash(block.Hash); - if (hash != null) - json["nextblockhash"] = hash.ToString(); - return json; - } - else - { - return block.ToArray().ToHexString(); - } - } - case "getblockcount": - return Blockchain.Default.Height + 1; - case "getblockhash": - { - uint height = (uint)_params[0].AsNumber(); - return Blockchain.Default.GetBlockHash(height).ToString(); - } - case "getconnectioncount": - return LocalNode.RemoteNodeCount; - case "getrawmempool": - return new JArray(LocalNode.GetMemoryPool().Select(p => (JObject)p.Hash.ToString())); - case "getrawtransaction": - { - UInt256 hash = UInt256.Parse(_params[0].AsString()); - bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false); - int height = -1; - Transaction tx = LocalNode.GetTransaction(hash); - if (tx == null) - tx = Blockchain.Default.GetTransaction(hash, out height); - if (tx == null) - throw new RpcException(-101, "Unknown transaction"); - if (verbose) - { - JObject json = tx.ToJson(); - if (height >= 0) - { - Header header = Blockchain.Default.GetHeader((uint)height); - json["blockhash"] = header.Hash.ToString(); - json["confirmations"] = Blockchain.Default.Height - header.Index + 1; - json["blocktime"] = header.Timestamp; - } - return json; - } - else - { - return tx.ToArray().ToHexString(); - } - } - case "gettxout": - { - UInt256 hash = UInt256.Parse(_params[0].AsString()); - ushort index = (ushort)_params[1].AsNumber(); - return Blockchain.Default.GetUnspent(hash, index)?.ToJson(index); - } - case "sendrawtransaction": - { - Transaction tx = Transaction.DeserializeFrom(_params[0].AsString().HexToBytes()); - return LocalNode.Relay(tx); - } - case "submitblock": - { - Block block = _params[0].AsString().HexToBytes().AsSerializable(); - return LocalNode.Relay(block); - } - case "validateaddress": - { - JObject json = new JObject(); - UInt160 scriptHash; - try - { - scriptHash = Wallet.ToScriptHash(_params[0].AsString()); - } - catch - { - scriptHash = null; - } - json["address"] = _params[0]; - json["isvalid"] = scriptHash != null; - return json; - } - default: - throw new RpcException(-32601, "Method not found"); - } - } - - private async Task ProcessAsync(HttpContext context) - { - context.Response.Headers["Access-Control-Allow-Origin"] = "*"; - context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST"; - context.Response.Headers["Access-Control-Allow-Headers"] = "Content-Type"; - context.Response.Headers["Access-Control-Max-Age"] = "31536000"; - if (context.Request.Method != "GET" && context.Request.Method != "POST") return; - JObject request = null; - if (context.Request.Method == "GET") - { - string jsonrpc = context.Request.Query["jsonrpc"]; - string id = context.Request.Query["id"]; - string method = context.Request.Query["method"]; - string _params = context.Request.Query["params"]; - if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(method) && !string.IsNullOrEmpty(_params)) - { - try - { - _params = Encoding.UTF8.GetString(Convert.FromBase64String(_params)); - } - catch (FormatException) { } - request = new JObject(); - if (!string.IsNullOrEmpty(jsonrpc)) - request["jsonrpc"] = jsonrpc; - request["id"] = double.Parse(id); - request["method"] = method; - request["params"] = JObject.Parse(_params); - } - } - else if (context.Request.Method == "POST") - { - using (StreamReader reader = new StreamReader(context.Request.Body)) - { - try - { - request = JObject.Parse(reader); - } - catch (FormatException) { } - } - } - JObject response; - if (request == null) - { - response = CreateErrorResponse(null, -32700, "Parse error"); - } - else if (request is JArray) - { - JArray array = (JArray)request; - if (array.Count == 0) - { - response = CreateErrorResponse(request["id"], -32600, "Invalid Request"); - } - else - { - response = array.Select(p => ProcessRequest(p)).Where(p => p != null).ToArray(); - } - } - else - { - response = ProcessRequest(request); - } - if (response == null || (response as JArray)?.Count == 0) return; - context.Response.ContentType = "application/json-rpc"; - await context.Response.WriteAsync(response.ToString()); - } - - private JObject ProcessRequest(JObject request) - { - if (!request.ContainsProperty("id")) return null; - if (!request.ContainsProperty("method") || !request.ContainsProperty("params") || !(request["params"] is JArray)) - { - return CreateErrorResponse(request["id"], -32600, "Invalid Request"); - } - JObject result = null; - try - { - result = Process(request["method"].AsString(), (JArray)request["params"]); - } - catch (Exception ex) - { -#if DEBUG - return CreateErrorResponse(request["id"], ex.HResult, ex.Message, ex.StackTrace); -#else - return CreateErrorResponse(request["id"], ex.HResult, ex.Message); -#endif - } - JObject response = CreateResponse(request["id"]); - response["result"] = result; - return response; - } - - public void Start(params string[] uriPrefix) - { - Start(uriPrefix, null, null); - } - - public void Start(string[] uriPrefix, string sslCert, string password) - { - if (uriPrefix.Length == 0) - throw new ArgumentException(); - IWebHostBuilder builder = new WebHostBuilder(); - if (uriPrefix.Any(p => p.StartsWith("https"))) - builder = builder.UseKestrel(options => options.UseHttps(sslCert, password)); - else - builder = builder.UseKestrel(); - builder = builder.UseUrls(uriPrefix).Configure(app => app.Run(ProcessAsync)); - host = builder.Build(); - host.Start(); - } - } -} +using Neo.Core; +using Neo.IO; +using Neo.IO.Json; +using Neo.Wallets; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.Network.RPC +{ + public class RpcServer : IDisposable + { + protected readonly LocalNode LocalNode; + private IWebHost host; + + public RpcServer(LocalNode localNode) + { + this.LocalNode = localNode; + } + + private static JObject CreateErrorResponse(JObject id, int code, string message, JObject data = null) + { + JObject response = CreateResponse(id); + response["error"] = new JObject(); + response["error"]["code"] = code; + response["error"]["message"] = message; + if (data != null) + response["error"]["data"] = data; + return response; + } + + private static JObject CreateResponse(JObject id) + { + JObject response = new JObject(); + response["jsonrpc"] = "2.0"; + response["id"] = id; + return response; + } + + public void Dispose() + { + if (host != null) + { + host.Dispose(); + host = null; + } + } + + protected virtual JObject Process(string method, JArray _params) + { + switch (method) + { + case "getbestblockhash": + return Blockchain.Default.CurrentBlockHash.ToString(); + case "getblock": + { + Block block; + if (_params[0] is JNumber) + { + uint index = (uint)_params[0].AsNumber(); + block = Blockchain.Default.GetBlock(index); + } + else + { + UInt256 hash = UInt256.Parse(_params[0].AsString()); + block = Blockchain.Default.GetBlock(hash); + } + if (block == null) + throw new RpcException(-100, "Unknown block"); + bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false); + if (verbose) + { + JObject json = block.ToJson(); + json["confirmations"] = Blockchain.Default.Height - block.Index + 1; + UInt256 hash = Blockchain.Default.GetNextBlockHash(block.Hash); + if (hash != null) + json["nextblockhash"] = hash.ToString(); + return json; + } + else + { + return block.ToArray().ToHexString(); + } + } + case "getblockcount": + return Blockchain.Default.Height + 1; + case "getblockhash": + { + uint height = (uint)_params[0].AsNumber(); + return Blockchain.Default.GetBlockHash(height).ToString(); + } + case "getconnectioncount": + return LocalNode.RemoteNodeCount; + case "getrawmempool": + return new JArray(LocalNode.GetMemoryPool().Select(p => (JObject)p.Hash.ToString())); + case "getrawtransaction": + { + UInt256 hash = UInt256.Parse(_params[0].AsString()); + bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false); + int height = -1; + Transaction tx = LocalNode.GetTransaction(hash); + if (tx == null) + tx = Blockchain.Default.GetTransaction(hash, out height); + if (tx == null) + throw new RpcException(-101, "Unknown transaction"); + if (verbose) + { + JObject json = tx.ToJson(); + if (height >= 0) + { + Header header = Blockchain.Default.GetHeader((uint)height); + json["blockhash"] = header.Hash.ToString(); + json["confirmations"] = Blockchain.Default.Height - header.Index + 1; + json["blocktime"] = header.Timestamp; + } + return json; + } + else + { + return tx.ToArray().ToHexString(); + } + } + case "gettxout": + { + UInt256 hash = UInt256.Parse(_params[0].AsString()); + ushort index = (ushort)_params[1].AsNumber(); + return Blockchain.Default.GetUnspent(hash, index)?.ToJson(index); + } + case "sendrawtransaction": + { + Transaction tx = Transaction.DeserializeFrom(_params[0].AsString().HexToBytes()); + return LocalNode.Relay(tx); + } + case "submitblock": + { + Block block = _params[0].AsString().HexToBytes().AsSerializable(); + return LocalNode.Relay(block); + } + case "validateaddress": + { + JObject json = new JObject(); + UInt160 scriptHash; + try + { + scriptHash = Wallet.ToScriptHash(_params[0].AsString()); + } + catch + { + scriptHash = null; + } + json["address"] = _params[0]; + json["isvalid"] = scriptHash != null; + return json; + } + default: + throw new RpcException(-32601, "Method not found"); + } + } + + private async Task ProcessAsync(HttpContext context) + { + context.Response.Headers["Access-Control-Allow-Origin"] = "*"; + context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST"; + context.Response.Headers["Access-Control-Allow-Headers"] = "Content-Type"; + context.Response.Headers["Access-Control-Max-Age"] = "31536000"; + if (context.Request.Method != "GET" && context.Request.Method != "POST") return; + JObject request = null; + if (context.Request.Method == "GET") + { + string jsonrpc = context.Request.Query["jsonrpc"]; + string id = context.Request.Query["id"]; + string method = context.Request.Query["method"]; + string _params = context.Request.Query["params"]; + if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(method) && !string.IsNullOrEmpty(_params)) + { + try + { + _params = Encoding.UTF8.GetString(Convert.FromBase64String(_params)); + } + catch (FormatException) { } + request = new JObject(); + if (!string.IsNullOrEmpty(jsonrpc)) + request["jsonrpc"] = jsonrpc; + request["id"] = double.Parse(id); + request["method"] = method; + request["params"] = JObject.Parse(_params); + } + } + else if (context.Request.Method == "POST") + { + using (StreamReader reader = new StreamReader(context.Request.Body)) + { + try + { + request = JObject.Parse(reader); + } + catch (FormatException) { } + } + } + JObject response; + if (request == null) + { + response = CreateErrorResponse(null, -32700, "Parse error"); + } + else if (request is JArray) + { + JArray array = (JArray)request; + if (array.Count == 0) + { + response = CreateErrorResponse(request["id"], -32600, "Invalid Request"); + } + else + { + response = array.Select(p => ProcessRequest(p)).Where(p => p != null).ToArray(); + } + } + else + { + response = ProcessRequest(request); + } + if (response == null || (response as JArray)?.Count == 0) return; + context.Response.ContentType = "application/json-rpc"; + await context.Response.WriteAsync(response.ToString()); + } + + private JObject ProcessRequest(JObject request) + { + if (!request.ContainsProperty("id")) return null; + if (!request.ContainsProperty("method") || !request.ContainsProperty("params") || !(request["params"] is JArray)) + { + return CreateErrorResponse(request["id"], -32600, "Invalid Request"); + } + JObject result = null; + try + { + result = Process(request["method"].AsString(), (JArray)request["params"]); + } + catch (Exception ex) + { +#if DEBUG + return CreateErrorResponse(request["id"], ex.HResult, ex.Message, ex.StackTrace); +#else + return CreateErrorResponse(request["id"], ex.HResult, ex.Message); +#endif + } + JObject response = CreateResponse(request["id"]); + response["result"] = result; + return response; + } + + public void Start(params string[] uriPrefix) + { + Start(uriPrefix, null, null); + } + + public void Start(string[] uriPrefix, string sslCert, string password) + { + if (uriPrefix.Length == 0) + throw new ArgumentException(); + IWebHostBuilder builder = new WebHostBuilder(); + if (uriPrefix.Any(p => p.StartsWith("https"))) + builder = builder.UseKestrel(options => options.UseHttps(sslCert, password)); + else + builder = builder.UseKestrel(); + builder = builder.UseUrls(uriPrefix).Configure(app => app.Run(ProcessAsync)); + host = builder.Build(); + host.Start(); + } + } +} diff --git a/src/AntShares/Network/RemoteNode.cs b/neo/Network/RemoteNode.cs similarity index 96% rename from src/AntShares/Network/RemoteNode.cs rename to neo/Network/RemoteNode.cs index dfde9df7c2..45203234a2 100644 --- a/src/AntShares/Network/RemoteNode.cs +++ b/neo/Network/RemoteNode.cs @@ -1,472 +1,474 @@ -using AntShares.Core; -using AntShares.Cryptography; -using AntShares.IO; -using AntShares.Network.Payloads; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace AntShares.Network -{ - public abstract class RemoteNode : IDisposable - { - public event EventHandler Disconnected; - internal event EventHandler InventoryReceived; - internal event EventHandler PeersReceived; - - private static readonly TimeSpan HalfMinute = TimeSpan.FromSeconds(30); - private static readonly TimeSpan OneMinute = TimeSpan.FromMinutes(1); - private static readonly TimeSpan HalfHour = TimeSpan.FromMinutes(30); - - private Queue message_queue = new Queue(); - private static HashSet missions_global = new HashSet(); - private HashSet missions = new HashSet(); - - private LocalNode localNode; - private int disposed = 0; - private BloomFilter bloom_filter; - - public VersionPayload Version { get; private set; } - public IPEndPoint RemoteEndpoint { get; protected set; } - public IPEndPoint ListenerEndpoint { get; protected set; } - - protected RemoteNode(LocalNode localNode) - { - this.localNode = localNode; - } - - public virtual void Disconnect(bool error) - { - if (Interlocked.Exchange(ref disposed, 1) == 0) - { - Disconnected?.Invoke(this, error); - lock (missions_global) - lock (missions) - { - missions_global.ExceptWith(missions); - } - } - } - - public void Dispose() - { - Disconnect(false); - } - - public void EnqueueMessage(string command, ISerializable payload = null) - { - EnqueueMessage(command, payload, false); - } - - private void EnqueueMessage(string command, ISerializable payload, bool is_single) - { - lock (message_queue) - { - if (!is_single || message_queue.All(p => p.Command != command)) - { - message_queue.Enqueue(Message.Create(command, payload)); - } - } - } - - private void OnAddrMessageReceived(AddrPayload payload) - { - IPEndPoint[] peers = payload.AddressList.Select(p => p.EndPoint).Where(p => p.Port != localNode.Port || !LocalNode.LocalAddresses.Contains(p.Address)).ToArray(); - if (peers.Length > 0) PeersReceived?.Invoke(this, peers); - } - - private void OnFilterAddMessageReceived(FilterAddPayload payload) - { - if (bloom_filter != null) - bloom_filter.Add(payload.Data); - } - - private void OnFilterClearMessageReceived() - { - bloom_filter = null; - } - - private void OnFilterLoadMessageReceived(FilterLoadPayload payload) - { - bloom_filter = new BloomFilter(payload.Filter.Length * 8, payload.K, payload.Tweak, payload.Filter); - } - - private void OnGetAddrMessageReceived() - { - if (!localNode.ServiceEnabled) return; - AddrPayload payload; - lock (localNode.connectedPeers) - { - payload = AddrPayload.Create(localNode.connectedPeers.Where(p => p.ListenerEndpoint != null && p.Version != null).Take(100).Select(p => NetworkAddressWithTime.Create(p.ListenerEndpoint, p.Version.Services, p.Version.Timestamp)).ToArray()); - } - EnqueueMessage("addr", payload, true); - } - - private void OnGetBlocksMessageReceived(GetBlocksPayload payload) - { - if (!localNode.ServiceEnabled) return; - if (Blockchain.Default == null) return; - UInt256 hash = payload.HashStart.Select(p => Blockchain.Default.GetHeader(p)).Where(p => p != null).OrderBy(p => p.Index).Select(p => p.Hash).FirstOrDefault(); - if (hash == null || hash == payload.HashStop) return; - List hashes = new List(); - do - { - hash = Blockchain.Default.GetNextBlockHash(hash); - if (hash == null) break; - hashes.Add(hash); - } while (hash != payload.HashStop && hashes.Count < 500); - EnqueueMessage("inv", InvPayload.Create(InventoryType.Block, hashes.ToArray())); - } - - private void OnGetDataMessageReceived(InvPayload payload) - { - foreach (UInt256 hash in payload.Hashes.Distinct()) - { - IInventory inventory; - if (!localNode.RelayCache.TryGet(hash, out inventory) && !localNode.ServiceEnabled) - continue; - switch (payload.Type) - { - case InventoryType.TX: - if (inventory == null) - inventory = LocalNode.GetTransaction(hash); - if (inventory == null && Blockchain.Default != null) - inventory = Blockchain.Default.GetTransaction(hash); - if (inventory != null) - EnqueueMessage("tx", inventory); - break; - case InventoryType.Block: - if (inventory == null && Blockchain.Default != null) - inventory = Blockchain.Default.GetBlock(hash); - if (inventory != null) - { - BloomFilter filter = bloom_filter; - if (filter == null) - { - EnqueueMessage("block", inventory); - } - else - { - Block block = (Block)inventory; - BitArray flags = new BitArray(block.Transactions.Select(p => TestFilter(filter, p)).ToArray()); - EnqueueMessage("merkleblock", MerkleBlockPayload.Create(block, flags)); - } - } - break; - case InventoryType.Consensus: - if (inventory != null) - EnqueueMessage("consensus", inventory); - break; - } - } - } - - private void OnGetHeadersMessageReceived(GetBlocksPayload payload) - { - if (!localNode.ServiceEnabled) return; - if (Blockchain.Default == null) return; - UInt256 hash = payload.HashStart.Select(p => Blockchain.Default.GetHeader(p)).Where(p => p != null).OrderBy(p => p.Index).Select(p => p.Hash).FirstOrDefault(); - if (hash == null || hash == payload.HashStop) return; - List
headers = new List
(); - do - { - hash = Blockchain.Default.GetNextBlockHash(hash); - if (hash == null) break; - headers.Add(Blockchain.Default.GetHeader(hash)); - } while (hash != payload.HashStop && headers.Count < 2000); - EnqueueMessage("headers", HeadersPayload.Create(headers)); - } - - private void OnHeadersMessageReceived(HeadersPayload payload) - { - if (Blockchain.Default == null) return; - Blockchain.Default.AddHeaders(payload.Headers); - if (Blockchain.Default.HeaderHeight < Version.StartHeight) - { - EnqueueMessage("getheaders", GetBlocksPayload.Create(Blockchain.Default.CurrentHeaderHash), true); - } - } - - private void OnInventoryReceived(IInventory inventory) - { - lock (missions_global) - { - missions_global.Remove(inventory.Hash); - } - lock (missions) - { - missions.Remove(inventory.Hash); - } - if (inventory is MinerTransaction) return; - InventoryReceived?.Invoke(this, inventory); - } - - private void OnInvMessageReceived(InvPayload payload) - { - if (payload.Type != InventoryType.TX && payload.Type != InventoryType.Block && payload.Type != InventoryType.Consensus) - return; - UInt256[] hashes = payload.Hashes.Distinct().ToArray(); - lock (LocalNode.KnownHashes) - { - hashes = hashes.Where(p => !LocalNode.KnownHashes.Contains(p)).ToArray(); - } - if (hashes.Length == 0) return; - lock (missions_global) - { - if (localNode.GlobalMissionsEnabled) - hashes = hashes.Where(p => !missions_global.Contains(p)).ToArray(); - missions_global.UnionWith(hashes); - } - lock (missions) - { - missions.UnionWith(hashes); - } - if (hashes.Length == 0) return; - EnqueueMessage("getdata", InvPayload.Create(payload.Type, hashes)); - } - - private void OnMemPoolMessageReceived() - { - EnqueueMessage("inv", InvPayload.Create(InventoryType.TX, LocalNode.GetMemoryPool().Select(p => p.Hash).ToArray())); - } - - private void OnMessageReceived(Message message) - { - switch (message.Command) - { - case "addr": - OnAddrMessageReceived(message.Payload.AsSerializable()); - break; - case "block": - OnInventoryReceived(message.Payload.AsSerializable()); - break; - case "consensus": - OnInventoryReceived(message.Payload.AsSerializable()); - break; - case "filteradd": - OnFilterAddMessageReceived(message.Payload.AsSerializable()); - break; - case "filterclear": - OnFilterClearMessageReceived(); - break; - case "filterload": - OnFilterLoadMessageReceived(message.Payload.AsSerializable()); - break; - case "getaddr": - OnGetAddrMessageReceived(); - break; - case "getblocks": - OnGetBlocksMessageReceived(message.Payload.AsSerializable()); - break; - case "getdata": - OnGetDataMessageReceived(message.Payload.AsSerializable()); - break; - case "getheaders": - OnGetHeadersMessageReceived(message.Payload.AsSerializable()); - break; - case "headers": - OnHeadersMessageReceived(message.Payload.AsSerializable()); - break; - case "inv": - OnInvMessageReceived(message.Payload.AsSerializable()); - break; - case "mempool": - OnMemPoolMessageReceived(); - break; - case "tx": - if (message.Payload.Length <= 1024 * 1024) - OnInventoryReceived(Transaction.DeserializeFrom(message.Payload)); - break; - case "verack": - case "version": - Disconnect(true); - break; - case "alert": - case "merkleblock": - case "notfound": - case "ping": - case "pong": - case "reject": - default: - //暂时忽略 - break; - } - } - - protected abstract Task ReceiveMessageAsync(TimeSpan timeout); - - internal bool Relay(IInventory data) - { - if (Version?.Relay != true) return false; - if (data.InventoryType == InventoryType.TX) - { - BloomFilter filter = bloom_filter; - if (filter != null && !TestFilter(filter, (Transaction)data)) - return false; - } - EnqueueMessage("inv", InvPayload.Create(data.InventoryType, data.Hash)); - return true; - } - - internal void Relay(IEnumerable transactions) - { - if (Version?.Relay != true) return; - BloomFilter filter = bloom_filter; - if (filter != null) - transactions = transactions.Where(p => TestFilter(filter, p)); - UInt256[] hashes = transactions.Select(p => p.Hash).ToArray(); - if (hashes.Length == 0) return; - EnqueueMessage("inv", InvPayload.Create(InventoryType.TX, hashes)); - } - - internal void RequestMemoryPool() - { - EnqueueMessage("mempool", null, true); - } - - internal void RequestPeers() - { - EnqueueMessage("getaddr", null, true); - } - - protected abstract Task SendMessageAsync(Message message); - - internal async void StartProtocol() - { - if (!await SendMessageAsync(Message.Create("version", VersionPayload.Create(localNode.Port, localNode.Nonce, localNode.UserAgent)))) - return; - Message message = await ReceiveMessageAsync(HalfMinute); - if (message == null) return; - if (message.Command != "version") - { - Disconnect(true); - return; - } - try - { - Version = message.Payload.AsSerializable(); - } - catch (EndOfStreamException) - { - Disconnect(false); - return; - } - catch (FormatException) - { - Disconnect(true); - return; - } - if (Version.Nonce == localNode.Nonce) - { - Disconnect(true); - return; - } - lock (localNode.connectedPeers) - { - if (localNode.connectedPeers.Where(p => p != this).Any(p => p.RemoteEndpoint.Address.Equals(RemoteEndpoint.Address) && p.Version?.Nonce == Version.Nonce)) - { - Disconnect(false); - return; - } - } - if (ListenerEndpoint != null) - { - if (ListenerEndpoint.Port != Version.Port) - { - Disconnect(true); - return; - } - } - else if (Version.Port > 0) - { - ListenerEndpoint = new IPEndPoint(RemoteEndpoint.Address, Version.Port); - } - if (!await SendMessageAsync(Message.Create("verack"))) return; - message = await ReceiveMessageAsync(HalfMinute); - if (message == null) return; - if (message.Command != "verack") - { - Disconnect(true); - return; - } - if (Blockchain.Default?.HeaderHeight < Version.StartHeight) - { - EnqueueMessage("getheaders", GetBlocksPayload.Create(Blockchain.Default.CurrentHeaderHash), true); - } - StartSendLoop(); - while (disposed == 0) - { - if (Blockchain.Default != null) - { - if (missions.Count == 0 && Blockchain.Default.Height < Version.StartHeight) - { - EnqueueMessage("getblocks", GetBlocksPayload.Create(Blockchain.Default.CurrentBlockHash), true); - } - } - TimeSpan timeout = missions.Count == 0 ? HalfHour : OneMinute; - message = await ReceiveMessageAsync(timeout); - if (message == null) break; - try - { - OnMessageReceived(message); - } - catch (EndOfStreamException) - { - Disconnect(false); - break; - } - catch (FormatException) - { - Disconnect(true); - break; - } - } - } - - private async void StartSendLoop() - { - while (disposed == 0) - { - Message message = null; - lock (message_queue) - { - if (message_queue.Count > 0) - { - message = message_queue.Dequeue(); - } - } - if (message == null) - { - for (int i = 0; i < 10 && disposed == 0; i++) - { - Thread.Sleep(100); - } - } - else - { - await SendMessageAsync(message); - } - } - } - - private bool TestFilter(BloomFilter filter, Transaction tx) - { - if (filter.Check(tx.Hash.ToArray())) return true; - if (tx.Outputs.Any(p => filter.Check(p.ScriptHash.ToArray()))) return true; - if (tx.Inputs.Any(p => filter.Check(p.ToArray()))) return true; - if (tx.Scripts.Any(p => filter.Check(p.VerificationScript.ToScriptHash().ToArray()))) - return true; - if (tx.Type == TransactionType.RegisterTransaction) - { - RegisterTransaction asset = (RegisterTransaction)tx; - if (filter.Check(asset.Admin.ToArray())) return true; - } - return false; - } - } -} +using Neo.Core; +using Neo.Cryptography; +using Neo.IO; +using Neo.Network.Payloads; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Neo.Network +{ + public abstract class RemoteNode : IDisposable + { + public event EventHandler Disconnected; + internal event EventHandler InventoryReceived; + internal event EventHandler PeersReceived; + + private static readonly TimeSpan HalfMinute = TimeSpan.FromSeconds(30); + private static readonly TimeSpan OneMinute = TimeSpan.FromMinutes(1); + private static readonly TimeSpan HalfHour = TimeSpan.FromMinutes(30); + + private Queue message_queue = new Queue(); + private static HashSet missions_global = new HashSet(); + private HashSet missions = new HashSet(); + + private LocalNode localNode; + private int disposed = 0; + private BloomFilter bloom_filter; + + public VersionPayload Version { get; private set; } + public IPEndPoint RemoteEndpoint { get; protected set; } + public IPEndPoint ListenerEndpoint { get; protected set; } + + protected RemoteNode(LocalNode localNode) + { + this.localNode = localNode; + } + + public virtual void Disconnect(bool error) + { + if (Interlocked.Exchange(ref disposed, 1) == 0) + { + Disconnected?.Invoke(this, error); + lock (missions_global) + lock (missions) + { + missions_global.ExceptWith(missions); + } + } + } + + public void Dispose() + { + Disconnect(false); + } + + public void EnqueueMessage(string command, ISerializable payload = null) + { + EnqueueMessage(command, payload, false); + } + + private void EnqueueMessage(string command, ISerializable payload, bool is_single) + { + lock (message_queue) + { + if (!is_single || message_queue.All(p => p.Command != command)) + { + message_queue.Enqueue(Message.Create(command, payload)); + } + } + } + + private void OnAddrMessageReceived(AddrPayload payload) + { + IPEndPoint[] peers = payload.AddressList.Select(p => p.EndPoint).Where(p => p.Port != localNode.Port || !LocalNode.LocalAddresses.Contains(p.Address)).ToArray(); + if (peers.Length > 0) PeersReceived?.Invoke(this, peers); + } + + private void OnFilterAddMessageReceived(FilterAddPayload payload) + { + if (bloom_filter != null) + bloom_filter.Add(payload.Data); + } + + private void OnFilterClearMessageReceived() + { + bloom_filter = null; + } + + private void OnFilterLoadMessageReceived(FilterLoadPayload payload) + { + bloom_filter = new BloomFilter(payload.Filter.Length * 8, payload.K, payload.Tweak, payload.Filter); + } + + private void OnGetAddrMessageReceived() + { + if (!localNode.ServiceEnabled) return; + AddrPayload payload; + lock (localNode.connectedPeers) + { + payload = AddrPayload.Create(localNode.connectedPeers.Where(p => p.ListenerEndpoint != null && p.Version != null).Take(100).Select(p => NetworkAddressWithTime.Create(p.ListenerEndpoint, p.Version.Services, p.Version.Timestamp)).ToArray()); + } + EnqueueMessage("addr", payload, true); + } + + private void OnGetBlocksMessageReceived(GetBlocksPayload payload) + { + if (!localNode.ServiceEnabled) return; + if (Blockchain.Default == null) return; + UInt256 hash = payload.HashStart.Select(p => Blockchain.Default.GetHeader(p)).Where(p => p != null).OrderBy(p => p.Index).Select(p => p.Hash).FirstOrDefault(); + if (hash == null || hash == payload.HashStop) return; + List hashes = new List(); + do + { + hash = Blockchain.Default.GetNextBlockHash(hash); + if (hash == null) break; + hashes.Add(hash); + } while (hash != payload.HashStop && hashes.Count < 500); + EnqueueMessage("inv", InvPayload.Create(InventoryType.Block, hashes.ToArray())); + } + + private void OnGetDataMessageReceived(InvPayload payload) + { + foreach (UInt256 hash in payload.Hashes.Distinct()) + { + IInventory inventory; + if (!localNode.RelayCache.TryGet(hash, out inventory) && !localNode.ServiceEnabled) + continue; + switch (payload.Type) + { + case InventoryType.TX: + if (inventory == null) + inventory = LocalNode.GetTransaction(hash); + if (inventory == null && Blockchain.Default != null) + inventory = Blockchain.Default.GetTransaction(hash); + if (inventory != null) + EnqueueMessage("tx", inventory); + break; + case InventoryType.Block: + if (inventory == null && Blockchain.Default != null) + inventory = Blockchain.Default.GetBlock(hash); + if (inventory != null) + { + BloomFilter filter = bloom_filter; + if (filter == null) + { + EnqueueMessage("block", inventory); + } + else + { + Block block = (Block)inventory; + BitArray flags = new BitArray(block.Transactions.Select(p => TestFilter(filter, p)).ToArray()); + EnqueueMessage("merkleblock", MerkleBlockPayload.Create(block, flags)); + } + } + break; + case InventoryType.Consensus: + if (inventory != null) + EnqueueMessage("consensus", inventory); + break; + } + } + } + + private void OnGetHeadersMessageReceived(GetBlocksPayload payload) + { + if (!localNode.ServiceEnabled) return; + if (Blockchain.Default == null) return; + UInt256 hash = payload.HashStart.Select(p => Blockchain.Default.GetHeader(p)).Where(p => p != null).OrderBy(p => p.Index).Select(p => p.Hash).FirstOrDefault(); + if (hash == null || hash == payload.HashStop) return; + List
headers = new List
(); + do + { + hash = Blockchain.Default.GetNextBlockHash(hash); + if (hash == null) break; + headers.Add(Blockchain.Default.GetHeader(hash)); + } while (hash != payload.HashStop && headers.Count < 2000); + EnqueueMessage("headers", HeadersPayload.Create(headers)); + } + + private void OnHeadersMessageReceived(HeadersPayload payload) + { + if (Blockchain.Default == null) return; + Blockchain.Default.AddHeaders(payload.Headers); + if (Blockchain.Default.HeaderHeight < Version.StartHeight) + { + EnqueueMessage("getheaders", GetBlocksPayload.Create(Blockchain.Default.CurrentHeaderHash), true); + } + } + + private void OnInventoryReceived(IInventory inventory) + { + lock (missions_global) + { + missions_global.Remove(inventory.Hash); + } + lock (missions) + { + missions.Remove(inventory.Hash); + } + if (inventory is MinerTransaction) return; + InventoryReceived?.Invoke(this, inventory); + } + + private void OnInvMessageReceived(InvPayload payload) + { + if (payload.Type != InventoryType.TX && payload.Type != InventoryType.Block && payload.Type != InventoryType.Consensus) + return; + UInt256[] hashes = payload.Hashes.Distinct().ToArray(); + lock (LocalNode.KnownHashes) + { + hashes = hashes.Where(p => !LocalNode.KnownHashes.Contains(p)).ToArray(); + } + if (hashes.Length == 0) return; + lock (missions_global) + { + if (localNode.GlobalMissionsEnabled) + hashes = hashes.Where(p => !missions_global.Contains(p)).ToArray(); + missions_global.UnionWith(hashes); + } + lock (missions) + { + missions.UnionWith(hashes); + } + if (hashes.Length == 0) return; + EnqueueMessage("getdata", InvPayload.Create(payload.Type, hashes)); + } + + private void OnMemPoolMessageReceived() + { + EnqueueMessage("inv", InvPayload.Create(InventoryType.TX, LocalNode.GetMemoryPool().Select(p => p.Hash).ToArray())); + } + + private void OnMessageReceived(Message message) + { + switch (message.Command) + { + case "addr": + OnAddrMessageReceived(message.Payload.AsSerializable()); + break; + case "block": + OnInventoryReceived(message.Payload.AsSerializable()); + break; + case "consensus": + OnInventoryReceived(message.Payload.AsSerializable()); + break; + case "filteradd": + OnFilterAddMessageReceived(message.Payload.AsSerializable()); + break; + case "filterclear": + OnFilterClearMessageReceived(); + break; + case "filterload": + OnFilterLoadMessageReceived(message.Payload.AsSerializable()); + break; + case "getaddr": + OnGetAddrMessageReceived(); + break; + case "getblocks": + OnGetBlocksMessageReceived(message.Payload.AsSerializable()); + break; + case "getdata": + OnGetDataMessageReceived(message.Payload.AsSerializable()); + break; + case "getheaders": + OnGetHeadersMessageReceived(message.Payload.AsSerializable()); + break; + case "headers": + OnHeadersMessageReceived(message.Payload.AsSerializable()); + break; + case "inv": + OnInvMessageReceived(message.Payload.AsSerializable()); + break; + case "mempool": + OnMemPoolMessageReceived(); + break; + case "tx": + if (message.Payload.Length <= 1024 * 1024) + OnInventoryReceived(Transaction.DeserializeFrom(message.Payload)); + break; + case "verack": + case "version": + Disconnect(true); + break; + case "alert": + case "merkleblock": + case "notfound": + case "ping": + case "pong": + case "reject": + default: + //暂时忽略 + break; + } + } + + protected abstract Task ReceiveMessageAsync(TimeSpan timeout); + + internal bool Relay(IInventory data) + { + if (Version?.Relay != true) return false; + if (data.InventoryType == InventoryType.TX) + { + BloomFilter filter = bloom_filter; + if (filter != null && !TestFilter(filter, (Transaction)data)) + return false; + } + EnqueueMessage("inv", InvPayload.Create(data.InventoryType, data.Hash)); + return true; + } + + internal void Relay(IEnumerable transactions) + { + if (Version?.Relay != true) return; + BloomFilter filter = bloom_filter; + if (filter != null) + transactions = transactions.Where(p => TestFilter(filter, p)); + UInt256[] hashes = transactions.Select(p => p.Hash).ToArray(); + if (hashes.Length == 0) return; + EnqueueMessage("inv", InvPayload.Create(InventoryType.TX, hashes)); + } + + internal void RequestMemoryPool() + { + EnqueueMessage("mempool", null, true); + } + + internal void RequestPeers() + { + EnqueueMessage("getaddr", null, true); + } + + protected abstract Task SendMessageAsync(Message message); + + internal async void StartProtocol() + { + if (!await SendMessageAsync(Message.Create("version", VersionPayload.Create(localNode.Port, localNode.Nonce, localNode.UserAgent)))) + return; + Message message = await ReceiveMessageAsync(HalfMinute); + if (message == null) return; + if (message.Command != "version") + { + Disconnect(true); + return; + } + try + { + Version = message.Payload.AsSerializable(); + } + catch (EndOfStreamException) + { + Disconnect(false); + return; + } + catch (FormatException) + { + Disconnect(true); + return; + } + if (Version.Nonce == localNode.Nonce) + { + Disconnect(true); + return; + } + lock (localNode.connectedPeers) + { + if (localNode.connectedPeers.Where(p => p != this).Any(p => p.RemoteEndpoint.Address.Equals(RemoteEndpoint.Address) && p.Version?.Nonce == Version.Nonce)) + { + Disconnect(false); + return; + } + } + if (ListenerEndpoint != null) + { + if (ListenerEndpoint.Port != Version.Port) + { + Disconnect(true); + return; + } + } + else if (Version.Port > 0) + { + ListenerEndpoint = new IPEndPoint(RemoteEndpoint.Address, Version.Port); + } + if (!await SendMessageAsync(Message.Create("verack"))) return; + message = await ReceiveMessageAsync(HalfMinute); + if (message == null) return; + if (message.Command != "verack") + { + Disconnect(true); + return; + } + if (Blockchain.Default?.HeaderHeight < Version.StartHeight) + { + EnqueueMessage("getheaders", GetBlocksPayload.Create(Blockchain.Default.CurrentHeaderHash), true); + } + StartSendLoop(); + while (disposed == 0) + { + if (Blockchain.Default != null) + { + if (missions.Count == 0 && Blockchain.Default.Height < Version.StartHeight) + { + EnqueueMessage("getblocks", GetBlocksPayload.Create(Blockchain.Default.CurrentBlockHash), true); + } + } + TimeSpan timeout = missions.Count == 0 ? HalfHour : OneMinute; + message = await ReceiveMessageAsync(timeout); + if (message == null) break; + try + { + OnMessageReceived(message); + } + catch (EndOfStreamException) + { + Disconnect(false); + break; + } + catch (FormatException) + { + Disconnect(true); + break; + } + } + } + + private async void StartSendLoop() + { + while (disposed == 0) + { + Message message = null; + lock (message_queue) + { + if (message_queue.Count > 0) + { + message = message_queue.Dequeue(); + } + } + if (message == null) + { + for (int i = 0; i < 10 && disposed == 0; i++) + { + Thread.Sleep(100); + } + } + else + { + await SendMessageAsync(message); + } + } + } + + private bool TestFilter(BloomFilter filter, Transaction tx) + { + if (filter.Check(tx.Hash.ToArray())) return true; + if (tx.Outputs.Any(p => filter.Check(p.ScriptHash.ToArray()))) return true; + if (tx.Inputs.Any(p => filter.Check(p.ToArray()))) return true; + if (tx.Scripts.Any(p => filter.Check(p.VerificationScript.ToScriptHash().ToArray()))) + return true; + if (tx.Type == TransactionType.RegisterTransaction) + { +#pragma warning disable CS0612 + RegisterTransaction asset = (RegisterTransaction)tx; + if (filter.Check(asset.Admin.ToArray())) return true; +#pragma warning restore CS0612 + } + return false; + } + } +} diff --git a/src/AntShares/Network/TcpRemoteNode.cs b/neo/Network/TcpRemoteNode.cs similarity index 95% rename from src/AntShares/Network/TcpRemoteNode.cs rename to neo/Network/TcpRemoteNode.cs index c4927aaa13..caa026032d 100644 --- a/src/AntShares/Network/TcpRemoteNode.cs +++ b/neo/Network/TcpRemoteNode.cs @@ -1,111 +1,111 @@ -using AntShares.IO; -using System; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -namespace AntShares.Network -{ - internal class TcpRemoteNode : RemoteNode - { - private Socket socket; - private NetworkStream stream; - private bool connected = false; - private int disposed = 0; - - public TcpRemoteNode(LocalNode localNode, IPEndPoint remoteEndpoint) - : base(localNode) - { - this.socket = new Socket(remoteEndpoint.Address.IsIPv4MappedToIPv6 ? AddressFamily.InterNetwork : remoteEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - this.ListenerEndpoint = remoteEndpoint; - } - - public TcpRemoteNode(LocalNode localNode, Socket socket) - : base(localNode) - { - this.socket = socket; - OnConnected(); - } - - public async Task ConnectAsync() - { - IPAddress address = ListenerEndpoint.Address; - if (address.IsIPv4MappedToIPv6) - address = address.MapToIPv4(); - try - { - await socket.ConnectAsync(address, ListenerEndpoint.Port); - OnConnected(); - } - catch (SocketException) - { - Disconnect(false); - return false; - } - return true; - } - - public override void Disconnect(bool error) - { - if (Interlocked.Exchange(ref disposed, 1) == 0) - { - if (stream != null) stream.Dispose(); - socket.Dispose(); - base.Disconnect(error); - } - } - - private void OnConnected() - { - IPEndPoint remoteEndpoint = (IPEndPoint)socket.RemoteEndPoint; - RemoteEndpoint = new IPEndPoint(remoteEndpoint.Address.MapToIPv6(), remoteEndpoint.Port); - stream = new NetworkStream(socket); - connected = true; - } - - protected override async Task ReceiveMessageAsync(TimeSpan timeout) - { - CancellationTokenSource source = new CancellationTokenSource(timeout); - try - { - return await Message.DeserializeFromAsync(stream, source.Token); - } - catch (ArgumentException) { } - catch (ObjectDisposedException) { } - catch (Exception ex) when (ex is FormatException || ex is IOException || ex is OperationCanceledException) - { - Disconnect(true); - } - finally - { - source.Dispose(); - } - return null; - } - - protected override async Task SendMessageAsync(Message message) - { - if (!connected) throw new InvalidOperationException(); - if (disposed > 0) return false; - byte[] buffer = message.ToArray(); - CancellationTokenSource source = new CancellationTokenSource(10000); - try - { - await stream.WriteAsync(buffer, 0, buffer.Length, source.Token); - return true; - } - catch (ObjectDisposedException) { } - catch (Exception ex) when (ex is IOException || ex is OperationCanceledException) - { - Disconnect(false); - } - finally - { - source.Dispose(); - } - return false; - } - } -} +using Neo.IO; +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Neo.Network +{ + internal class TcpRemoteNode : RemoteNode + { + private Socket socket; + private NetworkStream stream; + private bool connected = false; + private int disposed = 0; + + public TcpRemoteNode(LocalNode localNode, IPEndPoint remoteEndpoint) + : base(localNode) + { + this.socket = new Socket(remoteEndpoint.Address.IsIPv4MappedToIPv6 ? AddressFamily.InterNetwork : remoteEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + this.ListenerEndpoint = remoteEndpoint; + } + + public TcpRemoteNode(LocalNode localNode, Socket socket) + : base(localNode) + { + this.socket = socket; + OnConnected(); + } + + public async Task ConnectAsync() + { + IPAddress address = ListenerEndpoint.Address; + if (address.IsIPv4MappedToIPv6) + address = address.MapToIPv4(); + try + { + await socket.ConnectAsync(address, ListenerEndpoint.Port); + OnConnected(); + } + catch (SocketException) + { + Disconnect(false); + return false; + } + return true; + } + + public override void Disconnect(bool error) + { + if (Interlocked.Exchange(ref disposed, 1) == 0) + { + if (stream != null) stream.Dispose(); + socket.Dispose(); + base.Disconnect(error); + } + } + + private void OnConnected() + { + IPEndPoint remoteEndpoint = (IPEndPoint)socket.RemoteEndPoint; + RemoteEndpoint = new IPEndPoint(remoteEndpoint.Address.MapToIPv6(), remoteEndpoint.Port); + stream = new NetworkStream(socket); + connected = true; + } + + protected override async Task ReceiveMessageAsync(TimeSpan timeout) + { + CancellationTokenSource source = new CancellationTokenSource(timeout); + try + { + return await Message.DeserializeFromAsync(stream, source.Token); + } + catch (ArgumentException) { } + catch (ObjectDisposedException) { } + catch (Exception ex) when (ex is FormatException || ex is IOException || ex is OperationCanceledException) + { + Disconnect(true); + } + finally + { + source.Dispose(); + } + return null; + } + + protected override async Task SendMessageAsync(Message message) + { + if (!connected) throw new InvalidOperationException(); + if (disposed > 0) return false; + byte[] buffer = message.ToArray(); + CancellationTokenSource source = new CancellationTokenSource(10000); + try + { + await stream.WriteAsync(buffer, 0, buffer.Length, source.Token); + return true; + } + catch (ObjectDisposedException) { } + catch (Exception ex) when (ex is IOException || ex is OperationCanceledException) + { + Disconnect(false); + } + finally + { + source.Dispose(); + } + return false; + } + } +} diff --git a/src/AntShares/Network/UPnP.cs b/neo/Network/UPnP.cs similarity index 99% rename from src/AntShares/Network/UPnP.cs rename to neo/Network/UPnP.cs index 0b7281d624..b4041e7386 100644 --- a/src/AntShares/Network/UPnP.cs +++ b/neo/Network/UPnP.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using System.Xml; -namespace AntShares.Network +namespace Neo.Network { public class UPnP { diff --git a/src/AntShares/Network/WebSocketRemoteNode.cs b/neo/Network/WebSocketRemoteNode.cs similarity index 97% rename from src/AntShares/Network/WebSocketRemoteNode.cs rename to neo/Network/WebSocketRemoteNode.cs index 0b43262af0..8c67005e3d 100644 --- a/src/AntShares/Network/WebSocketRemoteNode.cs +++ b/neo/Network/WebSocketRemoteNode.cs @@ -1,4 +1,4 @@ -using AntShares.IO; +using Neo.IO; using System; using System.IO; using System.Net; @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; -namespace AntShares.Network +namespace Neo.Network { internal class WebSocketRemoteNode : RemoteNode { diff --git a/src/AntShares/Settings.cs b/neo/Settings.cs similarity index 96% rename from src/AntShares/Settings.cs rename to neo/Settings.cs index 6d8cb92f14..73a0a2c6ed 100644 --- a/src/AntShares/Settings.cs +++ b/neo/Settings.cs @@ -1,10 +1,10 @@ -using AntShares.Core; +using Neo.Core; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Linq; -namespace AntShares +namespace Neo { internal class Settings { diff --git a/neo/SmartContract/ApplicationEngine.cs b/neo/SmartContract/ApplicationEngine.cs new file mode 100644 index 0000000000..498cd8f6c3 --- /dev/null +++ b/neo/SmartContract/ApplicationEngine.cs @@ -0,0 +1,251 @@ +using Neo.VM; +using System; +using System.Text; + +namespace Neo.SmartContract +{ + public class ApplicationEngine : ExecutionEngine + { + private const long ratio = 100000; + private const long gas_free = 10 * 100000000; + private readonly long gas_amount; + private long gas_consumed = 0; + private readonly bool testMode; + + public Fixed8 GasConsumed => new Fixed8(gas_consumed); + + public ApplicationEngine(IScriptContainer container, IScriptTable table, InteropService service, Fixed8 gas, bool testMode = false) + : base(container, Cryptography.Crypto.Default, table, service) + { + this.gas_amount = gas_free + gas.GetData(); + this.testMode = testMode; + } + + private bool CheckArraySize() + { + const uint MaxArraySize = 1024; + if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length) + return true; + OpCode opcode = CurrentContext.NextInstruction; + switch (opcode) + { + case OpCode.PACK: + case OpCode.NEWARRAY: + { + int size = (int)EvaluationStack.Peek().GetBigInteger(); + if (size > MaxArraySize) return false; + return true; + } + default: + return true; + } + } + + private bool CheckInvocationStack() + { + const uint MaxStackSize = 1024; + if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length) + return true; + OpCode opcode = CurrentContext.NextInstruction; + switch (opcode) + { + case OpCode.CALL: + case OpCode.APPCALL: + if (InvocationStack.Count >= MaxStackSize) return false; + return true; + default: + return true; + } + } + + private bool CheckItemSize() + { + const uint MaxItemSize = 1024 * 1024; + if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length) + return true; + OpCode opcode = CurrentContext.NextInstruction; + switch (opcode) + { + case OpCode.PUSHDATA4: + { + if (CurrentContext.InstructionPointer + 4 >= CurrentContext.Script.Length) + return false; + uint length = CurrentContext.Script.ToUInt32(CurrentContext.InstructionPointer + 1); + if (length > MaxItemSize) return false; + return true; + } + case OpCode.CAT: + { + if (EvaluationStack.Count < 2) return false; + int length; + try + { + length = EvaluationStack.Peek(0).GetByteArray().Length + EvaluationStack.Peek(1).GetByteArray().Length; + } + catch (NotSupportedException) + { + return false; + } + if (length > MaxItemSize) return false; + return true; + } + default: + return true; + } + } + + private bool CheckStackSize() + { + const uint MaxStackSize = 2 * 1024; + if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length) + return true; + int size = 0; + OpCode opcode = CurrentContext.NextInstruction; + if (opcode <= OpCode.PUSH16) + size = 1; + else + switch (opcode) + { + case OpCode.DEPTH: + case OpCode.DUP: + case OpCode.OVER: + case OpCode.TUCK: + size = 1; + break; + case OpCode.UNPACK: + StackItem item = EvaluationStack.Peek(); + if (!item.IsArray) return false; + size = item.GetArray().Length; + break; + } + if (size == 0) return true; + size += EvaluationStack.Count + AltStack.Count; + if (size > MaxStackSize) return false; + return true; + } + + public new bool Execute() + { + while (!State.HasFlag(VMState.HALT) && !State.HasFlag(VMState.FAULT)) + { + try + { + gas_consumed = checked(gas_consumed + GetPrice() * ratio); + } + catch (OverflowException) + { + return false; + } + if (!testMode && gas_consumed > gas_amount) return false; + if (!CheckItemSize()) return false; + if (!CheckStackSize()) return false; + if (!CheckArraySize()) return false; + if (!CheckInvocationStack()) return false; + StepInto(); + } + return !State.HasFlag(VMState.FAULT); + } + + protected virtual long GetPrice() + { + if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length) + return 0; + OpCode opcode = CurrentContext.NextInstruction; + if (opcode <= OpCode.PUSH16) return 0; + switch (opcode) + { + case OpCode.NOP: + return 0; + case OpCode.APPCALL: + case OpCode.TAILCALL: + return 10; + case OpCode.SYSCALL: + return GetPriceForSysCall(); + case OpCode.SHA1: + case OpCode.SHA256: + return 10; + case OpCode.HASH160: + case OpCode.HASH256: + return 20; + case OpCode.CHECKSIG: + return 100; + case OpCode.CHECKMULTISIG: + { + if (EvaluationStack.Count == 0) return 1; + int n = (int)EvaluationStack.Peek().GetBigInteger(); + if (n < 1) return 1; + return 100 * n; + } + default: return 1; + } + } + + protected virtual long GetPriceForSysCall() + { + if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length - 3) + return 1; + byte length = CurrentContext.Script[CurrentContext.InstructionPointer + 1]; + if (CurrentContext.InstructionPointer > CurrentContext.Script.Length - length - 2) + return 1; + string api_name = Encoding.ASCII.GetString(CurrentContext.Script, CurrentContext.InstructionPointer + 2, length); + switch (api_name) + { + case "Neo.Runtime.CheckWitness": + case "AntShares.Runtime.CheckWitness": + return 200; + case "Neo.Blockchain.GetHeader": + case "AntShares.Blockchain.GetHeader": + return 100; + case "Neo.Blockchain.GetBlock": + case "AntShares.Blockchain.GetBlock": + return 200; + case "Neo.Blockchain.GetTransaction": + case "AntShares.Blockchain.GetTransaction": + return 100; + case "Neo.Blockchain.GetAccount": + case "AntShares.Blockchain.GetAccount": + return 100; + case "Neo.Blockchain.GetValidators": + case "AntShares.Blockchain.GetValidators": + return 200; + case "Neo.Blockchain.GetAsset": + case "AntShares.Blockchain.GetAsset": + return 100; + case "Neo.Blockchain.GetContract": + case "AntShares.Blockchain.GetContract": + return 100; + case "Neo.Transaction.GetReferences": + case "AntShares.Transaction.GetReferences": + return 200; + case "Neo.Account.SetVotes": + case "AntShares.Account.SetVotes": + return 1000; + case "Neo.Validator.Register": + case "AntShares.Validator.Register": + return 1000L * 100000000L / ratio; + case "Neo.Asset.Create": + case "AntShares.Asset.Create": + return 5000L * 100000000L / ratio; + case "Neo.Asset.Renew": + case "AntShares.Asset.Renew": + return (byte)EvaluationStack.Peek(1).GetBigInteger() * 5000L * 100000000L / ratio; + case "Neo.Contract.Create": + case "Neo.Contract.Migrate": + case "AntShares.Contract.Create": + case "AntShares.Contract.Migrate": + return 500L * 100000000L / ratio; + case "Neo.Storage.Get": + case "AntShares.Storage.Get": + return 100; + case "Neo.Storage.Put": + case "AntShares.Storage.Put": + return ((EvaluationStack.Peek(1).GetByteArray().Length + EvaluationStack.Peek(2).GetByteArray().Length - 1) / 1024 + 1) * 1000; + case "Neo.Storage.Delete": + case "AntShares.Storage.Delete": + return 100; + default: + return 1; + } + } + } +} diff --git a/src/AntShares/SmartContract/CloneCache.cs b/neo/SmartContract/CloneCache.cs similarity index 75% rename from src/AntShares/SmartContract/CloneCache.cs rename to neo/SmartContract/CloneCache.cs index 917b8eef70..3a45e9da6e 100644 --- a/src/AntShares/SmartContract/CloneCache.cs +++ b/neo/SmartContract/CloneCache.cs @@ -1,9 +1,10 @@ -using AntShares.Core; -using AntShares.IO; -using AntShares.IO.Caching; +using Neo.Core; +using Neo.IO; +using Neo.IO.Caching; using System; +using System.Collections.Generic; -namespace AntShares.SmartContract +namespace Neo.SmartContract { internal class CloneCache : DataCache where TKey : IEquatable, ISerializable @@ -16,11 +17,6 @@ public CloneCache(DataCache innerCache) this.innerCache = innerCache; } - protected override TValue GetInternal(TKey key) - { - return innerCache[key].Clone(); - } - public void Commit() { foreach (Trackable trackable in GetChangeSet()) @@ -38,6 +34,17 @@ public void Commit() } } + protected override IEnumerable> FindInternal(byte[] key_prefix) + { + foreach (KeyValuePair pair in innerCache.Find(key_prefix)) + yield return new KeyValuePair(pair.Key, pair.Value.Clone()); + } + + protected override TValue GetInternal(TKey key) + { + return innerCache[key].Clone(); + } + protected override TValue TryGetInternal(TKey key) { return innerCache.TryGet(key)?.Clone(); diff --git a/neo/SmartContract/LogEventArgs.cs b/neo/SmartContract/LogEventArgs.cs new file mode 100644 index 0000000000..aba62be290 --- /dev/null +++ b/neo/SmartContract/LogEventArgs.cs @@ -0,0 +1,19 @@ +using Neo.VM; +using System; + +namespace Neo.SmartContract +{ + public class LogEventArgs : EventArgs + { + public IScriptContainer ScriptContainer { get; } + public UInt160 ScriptHash { get; } + public string Message { get; } + + public LogEventArgs(IScriptContainer container, UInt160 script_hash, string message) + { + this.ScriptContainer = container; + this.ScriptHash = script_hash; + this.Message = message; + } + } +} diff --git a/neo/SmartContract/NotifyEventArgs.cs b/neo/SmartContract/NotifyEventArgs.cs new file mode 100644 index 0000000000..e40238d2af --- /dev/null +++ b/neo/SmartContract/NotifyEventArgs.cs @@ -0,0 +1,19 @@ +using Neo.VM; +using System; + +namespace Neo.SmartContract +{ + public class NotifyEventArgs : EventArgs + { + public IScriptContainer ScriptContainer { get; } + public UInt160 ScriptHash { get; } + public StackItem State { get; } + + public NotifyEventArgs(IScriptContainer container, UInt160 script_hash, StackItem state) + { + this.ScriptContainer = container; + this.ScriptHash = script_hash; + this.State = state; + } + } +} diff --git a/neo/SmartContract/StateMachine.cs b/neo/SmartContract/StateMachine.cs new file mode 100644 index 0000000000..96d1e9d1d4 --- /dev/null +++ b/neo/SmartContract/StateMachine.cs @@ -0,0 +1,356 @@ +using Neo.Core; +using Neo.Cryptography.ECC; +using Neo.IO.Caching; +using Neo.VM; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Neo.SmartContract +{ + public class StateMachine : StateReader + { + private CloneCache accounts; + private CloneCache validators; + private CloneCache assets; + private CloneCache contracts; + private CloneCache storages; + + private Dictionary contracts_created = new Dictionary(); + + public StateMachine(DataCache accounts, DataCache validators, DataCache assets, DataCache contracts, DataCache storages) + { + this.accounts = new CloneCache(accounts); + this.validators = new CloneCache(validators); + this.assets = new CloneCache(assets); + this.contracts = new CloneCache(contracts); + this.storages = new CloneCache(storages); + Register("Neo.Account.SetVotes", Account_SetVotes); + Register("Neo.Validator.Register", Validator_Register); + Register("Neo.Asset.Create", Asset_Create); + Register("Neo.Asset.Renew", Asset_Renew); + Register("Neo.Contract.Create", Contract_Create); + Register("Neo.Contract.Migrate", Contract_Migrate); + Register("Neo.Contract.GetStorageContext", Contract_GetStorageContext); + Register("Neo.Contract.Destroy", Contract_Destroy); + Register("Neo.Storage.Put", Storage_Put); + Register("Neo.Storage.Delete", Storage_Delete); + #region Old AntShares APIs + Register("AntShares.Account.SetVotes", Account_SetVotes); + Register("AntShares.Validator.Register", Validator_Register); + Register("AntShares.Asset.Create", Asset_Create); + Register("AntShares.Asset.Renew", Asset_Renew); + Register("AntShares.Contract.Create", Contract_Create); + Register("AntShares.Contract.Migrate", Contract_Migrate); + Register("AntShares.Contract.GetStorageContext", Contract_GetStorageContext); + Register("AntShares.Contract.Destroy", Contract_Destroy); + Register("AntShares.Storage.Put", Storage_Put); + Register("AntShares.Storage.Delete", Storage_Delete); + #endregion + } + + private bool CheckStorageContext(StorageContext context) + { + ContractState contract = contracts.TryGet(context.ScriptHash); + if (contract == null) return false; + if (!contract.HasStorage) return false; + return true; + } + + public void Commit() + { + accounts.Commit(); + validators.Commit(); + assets.Commit(); + contracts.Commit(); + storages.Commit(); + } + + protected override bool Blockchain_GetAccount(ExecutionEngine engine) + { + UInt160 hash = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); + engine.EvaluationStack.Push(StackItem.FromInterface(accounts[hash])); + return true; + } + + protected override bool Blockchain_GetAsset(ExecutionEngine engine) + { + UInt256 hash = new UInt256(engine.EvaluationStack.Pop().GetByteArray()); + AssetState asset = assets.TryGet(hash); + if (asset == null) return false; + engine.EvaluationStack.Push(StackItem.FromInterface(asset)); + return true; + } + + protected override bool Blockchain_GetContract(ExecutionEngine engine) + { + UInt160 hash = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); + ContractState contract = contracts.TryGet(hash); + if (contract == null) return false; + engine.EvaluationStack.Push(StackItem.FromInterface(contract)); + return true; + } + + private bool Account_SetVotes(ExecutionEngine engine) + { + AccountState account = engine.EvaluationStack.Pop().GetInterface(); + ECPoint[] votes = engine.EvaluationStack.Pop().GetArray().Select(p => ECPoint.DecodePoint(p.GetByteArray(), ECCurve.Secp256r1)).ToArray(); + if (account == null) return false; + if (votes.Length > 1024) return false; + account = accounts[account.ScriptHash]; + if (account.IsFrozen) return false; + if ((!account.Balances.ContainsKey(Blockchain.SystemShare.Hash) || account.Balances[Blockchain.SystemShare.Hash].Equals(Fixed8.Zero)) && votes.Length > 0) + return false; + if (!CheckWitness(engine, account.ScriptHash)) return false; + account = accounts.GetAndChange(account.ScriptHash); + account.Votes = votes.Distinct().ToArray(); + return true; + } + + private bool Validator_Register(ExecutionEngine engine) + { + ECPoint pubkey = ECPoint.DecodePoint(engine.EvaluationStack.Pop().GetByteArray(), ECCurve.Secp256r1); + if (pubkey.IsInfinity) return false; + if (!CheckWitness(engine, pubkey)) return false; + ValidatorState validator = validators.GetOrAdd(pubkey, () => new ValidatorState + { + PublicKey = pubkey + }); + engine.EvaluationStack.Push(StackItem.FromInterface(validator)); + return true; + } + + private bool Asset_Create(ExecutionEngine engine) + { + InvocationTransaction tx = (InvocationTransaction)engine.ScriptContainer; + AssetType asset_type = (AssetType)(byte)engine.EvaluationStack.Pop().GetBigInteger(); + if (!Enum.IsDefined(typeof(AssetType), asset_type) || asset_type == AssetType.CreditFlag || asset_type == AssetType.DutyFlag || asset_type == AssetType.SystemShare || asset_type == AssetType.SystemCoin) + return false; + if (engine.EvaluationStack.Peek().GetByteArray().Length > 1024) + return false; + string name = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + Fixed8 amount = new Fixed8((long)engine.EvaluationStack.Pop().GetBigInteger()); + if (amount == Fixed8.Zero || amount < -Fixed8.Satoshi) return false; + if (asset_type == AssetType.Invoice && amount != -Fixed8.Satoshi) + return false; + byte precision = (byte)engine.EvaluationStack.Pop().GetBigInteger(); + if (precision > 8) return false; + if (asset_type == AssetType.Share && precision != 0) return false; + if (amount != -Fixed8.Satoshi && amount.GetData() % (long)Math.Pow(10, 8 - precision) != 0) + return false; + ECPoint owner = ECPoint.DecodePoint(engine.EvaluationStack.Pop().GetByteArray(), ECCurve.Secp256r1); + if (owner.IsInfinity) return false; + if (!CheckWitness(engine, owner)) + return false; + UInt160 admin = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); + UInt160 issuer = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); + AssetState asset = assets.GetOrAdd(tx.Hash, () => new AssetState + { + AssetId = tx.Hash, + AssetType = asset_type, + Name = name, + Amount = amount, + Available = Fixed8.Zero, + Precision = precision, + Fee = Fixed8.Zero, + FeeAddress = new UInt160(), + Owner = owner, + Admin = admin, + Issuer = issuer, + Expiration = Blockchain.Default.Height + 1 + 2000000, + IsFrozen = false + }); + engine.EvaluationStack.Push(StackItem.FromInterface(asset)); + return true; + } + + private bool Asset_Renew(ExecutionEngine engine) + { + AssetState asset = engine.EvaluationStack.Pop().GetInterface(); + if (asset == null) return false; + byte years = (byte)engine.EvaluationStack.Pop().GetBigInteger(); + asset = assets.GetAndChange(asset.AssetId); + if (asset.Expiration < Blockchain.Default.Height + 1) + asset.Expiration = Blockchain.Default.Height + 1; + try + { + asset.Expiration = checked(asset.Expiration + years * 2000000u); + } + catch (OverflowException) + { + asset.Expiration = uint.MaxValue; + } + engine.EvaluationStack.Push(asset.Expiration); + return true; + } + + private bool Contract_Create(ExecutionEngine engine) + { + byte[] script = engine.EvaluationStack.Pop().GetByteArray(); + if (script.Length > 1024 * 1024) return false; + ContractParameterType[] parameter_list = engine.EvaluationStack.Pop().GetByteArray().Select(p => (ContractParameterType)p).ToArray(); + if (parameter_list.Length > 252) return false; + ContractParameterType return_type = (ContractParameterType)(byte)engine.EvaluationStack.Pop().GetBigInteger(); + bool need_storage = engine.EvaluationStack.Pop().GetBoolean(); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return false; + string name = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return false; + string version = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return false; + string author = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return false; + string email = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 65536) return false; + string description = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + UInt160 hash = script.ToScriptHash(); + ContractState contract = contracts.TryGet(hash); + if (contract == null) + { + contract = new ContractState + { + Code = new FunctionCode + { + Script = script, + ParameterList = parameter_list, + ReturnType = return_type + }, + HasStorage = need_storage, + Name = name, + CodeVersion = version, + Author = author, + Email = email, + Description = description + }; + contracts.Add(hash, contract); + contracts_created.Add(hash, new UInt160(engine.CurrentContext.ScriptHash)); + } + engine.EvaluationStack.Push(StackItem.FromInterface(contract)); + return true; + } + + private bool Contract_Migrate(ExecutionEngine engine) + { + byte[] script = engine.EvaluationStack.Pop().GetByteArray(); + if (script.Length > 1024 * 1024) return false; + ContractParameterType[] parameter_list = engine.EvaluationStack.Pop().GetByteArray().Select(p => (ContractParameterType)p).ToArray(); + if (parameter_list.Length > 252) return false; + ContractParameterType return_type = (ContractParameterType)(byte)engine.EvaluationStack.Pop().GetBigInteger(); + bool need_storage = engine.EvaluationStack.Pop().GetBoolean(); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return false; + string name = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return false; + string version = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return false; + string author = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 252) return false; + string email = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + if (engine.EvaluationStack.Peek().GetByteArray().Length > 65536) return false; + string description = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + UInt160 hash = script.ToScriptHash(); + ContractState contract = contracts.TryGet(hash); + if (contract == null) + { + contract = new ContractState + { + Code = new FunctionCode + { + Script = script, + ParameterList = parameter_list, + ReturnType = return_type + }, + HasStorage = need_storage, + Name = name, + CodeVersion = version, + Author = author, + Email = email, + Description = description + }; + contracts.Add(hash, contract); + contracts_created.Add(hash, new UInt160(engine.CurrentContext.ScriptHash)); + if (need_storage) + { + foreach (var pair in storages.Find(engine.CurrentContext.ScriptHash).ToArray()) + { + storages.Add(new StorageKey + { + ScriptHash = hash, + Key = pair.Key.Key + }, new StorageItem + { + Value = pair.Value.Value + }); + } + } + } + engine.EvaluationStack.Push(StackItem.FromInterface(contract)); + return true; + } + + private bool Contract_GetStorageContext(ExecutionEngine engine) + { + ContractState contract = engine.EvaluationStack.Pop().GetInterface(); + if (!contracts_created.ContainsKey(contract.ScriptHash)) return false; + if (!contracts_created[contract.ScriptHash].Equals(new UInt160(engine.CurrentContext.ScriptHash))) return false; + engine.EvaluationStack.Push(StackItem.FromInterface(new StorageContext + { + ScriptHash = contract.ScriptHash + })); + return true; + } + + private bool Contract_Destroy(ExecutionEngine engine) + { + UInt160 hash = new UInt160(engine.CurrentContext.ScriptHash); + ContractState contract = contracts.TryGet(hash); + if (contract == null) return true; + contracts.Delete(hash); + if (contract.HasStorage) + foreach (var pair in storages.Find(hash.ToArray())) + storages.Delete(pair.Key); + return true; + } + + protected override bool Storage_Get(ExecutionEngine engine) + { + StorageContext context = engine.EvaluationStack.Pop().GetInterface(); + if (!CheckStorageContext(context)) return false; + byte[] key = engine.EvaluationStack.Pop().GetByteArray(); + StorageItem item = storages.TryGet(new StorageKey + { + ScriptHash = context.ScriptHash, + Key = key + }); + engine.EvaluationStack.Push(item?.Value ?? new byte[0]); + return true; + } + + private bool Storage_Put(ExecutionEngine engine) + { + StorageContext context = engine.EvaluationStack.Pop().GetInterface(); + if (!CheckStorageContext(context)) return false; + byte[] key = engine.EvaluationStack.Pop().GetByteArray(); + if (key.Length > 1024) return false; + byte[] value = engine.EvaluationStack.Pop().GetByteArray(); + storages.GetAndChange(new StorageKey + { + ScriptHash = context.ScriptHash, + Key = key + }, () => new StorageItem()).Value = value; + return true; + } + + private bool Storage_Delete(ExecutionEngine engine) + { + StorageContext context = engine.EvaluationStack.Pop().GetInterface(); + if (!CheckStorageContext(context)) return false; + byte[] key = engine.EvaluationStack.Pop().GetByteArray(); + storages.Delete(new StorageKey + { + ScriptHash = context.ScriptHash, + Key = key + }); + return true; + } + } +} diff --git a/src/AntShares/SmartContract/StateReader.cs b/neo/SmartContract/StateReader.cs similarity index 69% rename from src/AntShares/SmartContract/StateReader.cs rename to neo/SmartContract/StateReader.cs index 8215bb31ce..abcc49e72d 100644 --- a/src/AntShares/SmartContract/StateReader.cs +++ b/neo/SmartContract/StateReader.cs @@ -1,23 +1,84 @@ -using AntShares.Core; -using AntShares.VM; +using Neo.Core; +using Neo.Cryptography.ECC; +using Neo.VM; +using Neo.Wallets; +using System; +using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Text; -namespace AntShares.SmartContract +namespace Neo.SmartContract { public class StateReader : InteropService { + public static event EventHandler Notify; + public static event EventHandler Log; + public static readonly StateReader Default = new StateReader(); public StateReader() { + Register("Neo.Runtime.CheckWitness", Runtime_CheckWitness); + Register("Neo.Runtime.Notify", Runtime_Notify); + Register("Neo.Runtime.Log", Runtime_Log); + Register("Neo.Blockchain.GetHeight", Blockchain_GetHeight); + Register("Neo.Blockchain.GetHeader", Blockchain_GetHeader); + Register("Neo.Blockchain.GetBlock", Blockchain_GetBlock); + Register("Neo.Blockchain.GetTransaction", Blockchain_GetTransaction); + Register("Neo.Blockchain.GetAccount", Blockchain_GetAccount); + Register("Neo.Blockchain.GetValidators", Blockchain_GetValidators); + Register("Neo.Blockchain.GetAsset", Blockchain_GetAsset); + Register("Neo.Blockchain.GetContract", Blockchain_GetContract); + Register("Neo.Header.GetHash", Header_GetHash); + Register("Neo.Header.GetVersion", Header_GetVersion); + Register("Neo.Header.GetPrevHash", Header_GetPrevHash); + Register("Neo.Header.GetMerkleRoot", Header_GetMerkleRoot); + Register("Neo.Header.GetTimestamp", Header_GetTimestamp); + Register("Neo.Header.GetConsensusData", Header_GetConsensusData); + Register("Neo.Header.GetNextConsensus", Header_GetNextConsensus); + Register("Neo.Block.GetTransactionCount", Block_GetTransactionCount); + Register("Neo.Block.GetTransactions", Block_GetTransactions); + Register("Neo.Block.GetTransaction", Block_GetTransaction); + Register("Neo.Transaction.GetHash", Transaction_GetHash); + Register("Neo.Transaction.GetType", Transaction_GetType); + Register("Neo.Transaction.GetAttributes", Transaction_GetAttributes); + Register("Neo.Transaction.GetInputs", Transaction_GetInputs); + Register("Neo.Transaction.GetOutputs", Transaction_GetOutputs); + Register("Neo.Transaction.GetReferences", Transaction_GetReferences); + Register("Neo.Attribute.GetUsage", Attribute_GetUsage); + Register("Neo.Attribute.GetData", Attribute_GetData); + Register("Neo.Input.GetHash", Input_GetHash); + Register("Neo.Input.GetIndex", Input_GetIndex); + Register("Neo.Output.GetAssetId", Output_GetAssetId); + Register("Neo.Output.GetValue", Output_GetValue); + Register("Neo.Output.GetScriptHash", Output_GetScriptHash); + Register("Neo.Account.GetScriptHash", Account_GetScriptHash); + Register("Neo.Account.GetVotes", Account_GetVotes); + Register("Neo.Account.GetBalance", Account_GetBalance); + Register("Neo.Asset.GetAssetId", Asset_GetAssetId); + Register("Neo.Asset.GetAssetType", Asset_GetAssetType); + Register("Neo.Asset.GetAmount", Asset_GetAmount); + Register("Neo.Asset.GetAvailable", Asset_GetAvailable); + Register("Neo.Asset.GetPrecision", Asset_GetPrecision); + Register("Neo.Asset.GetOwner", Asset_GetOwner); + Register("Neo.Asset.GetAdmin", Asset_GetAdmin); + Register("Neo.Asset.GetIssuer", Asset_GetIssuer); + Register("Neo.Contract.GetScript", Contract_GetScript); + Register("Neo.Storage.GetContext", Storage_GetContext); + Register("Neo.Storage.Get", Storage_Get); + #region Old AntShares APIs + Register("AntShares.Runtime.CheckWitness", Runtime_CheckWitness); + Register("AntShares.Runtime.Notify", Runtime_Notify); + Register("AntShares.Runtime.Log", Runtime_Log); Register("AntShares.Blockchain.GetHeight", Blockchain_GetHeight); Register("AntShares.Blockchain.GetHeader", Blockchain_GetHeader); Register("AntShares.Blockchain.GetBlock", Blockchain_GetBlock); Register("AntShares.Blockchain.GetTransaction", Blockchain_GetTransaction); Register("AntShares.Blockchain.GetAccount", Blockchain_GetAccount); + Register("AntShares.Blockchain.GetValidators", Blockchain_GetValidators); Register("AntShares.Blockchain.GetAsset", Blockchain_GetAsset); - + Register("AntShares.Blockchain.GetContract", Blockchain_GetContract); Register("AntShares.Header.GetHash", Header_GetHash); Register("AntShares.Header.GetVersion", Header_GetVersion); Register("AntShares.Header.GetPrevHash", Header_GetPrevHash); @@ -28,10 +89,8 @@ public StateReader() Register("AntShares.Block.GetTransactionCount", Block_GetTransactionCount); Register("AntShares.Block.GetTransactions", Block_GetTransactions); Register("AntShares.Block.GetTransaction", Block_GetTransaction); - Register("AntShares.Transaction.GetHash", Transaction_GetHash); Register("AntShares.Transaction.GetType", Transaction_GetType); - Register("AntShares.Enrollment.GetPublicKey", Enrollment_GetPublicKey); Register("AntShares.Transaction.GetAttributes", Transaction_GetAttributes); Register("AntShares.Transaction.GetInputs", Transaction_GetInputs); Register("AntShares.Transaction.GetOutputs", Transaction_GetOutputs); @@ -43,11 +102,9 @@ public StateReader() Register("AntShares.Output.GetAssetId", Output_GetAssetId); Register("AntShares.Output.GetValue", Output_GetValue); Register("AntShares.Output.GetScriptHash", Output_GetScriptHash); - Register("AntShares.Account.GetScriptHash", Account_GetScriptHash); Register("AntShares.Account.GetVotes", Account_GetVotes); Register("AntShares.Account.GetBalance", Account_GetBalance); - Register("AntShares.Asset.GetAssetId", Asset_GetAssetId); Register("AntShares.Asset.GetAssetType", Asset_GetAssetType); Register("AntShares.Asset.GetAmount", Asset_GetAmount); @@ -56,6 +113,54 @@ public StateReader() Register("AntShares.Asset.GetOwner", Asset_GetOwner); Register("AntShares.Asset.GetAdmin", Asset_GetAdmin); Register("AntShares.Asset.GetIssuer", Asset_GetIssuer); + Register("AntShares.Contract.GetScript", Contract_GetScript); + Register("AntShares.Storage.GetContext", Storage_GetContext); + Register("AntShares.Storage.Get", Storage_Get); + #endregion + } + + private HashSet _hashes_for_verifying = null; + protected bool CheckWitness(ExecutionEngine engine, UInt160 hash) + { + if (_hashes_for_verifying == null) + { + IVerifiable container = (IVerifiable)engine.ScriptContainer; + _hashes_for_verifying = new HashSet(container.GetScriptHashesForVerifying()); + } + return _hashes_for_verifying.Contains(hash); + } + + protected bool CheckWitness(ExecutionEngine engine, ECPoint pubkey) + { + return CheckWitness(engine, Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()); + } + + protected virtual bool Runtime_CheckWitness(ExecutionEngine engine) + { + byte[] hashOrPubkey = engine.EvaluationStack.Pop().GetByteArray(); + bool result; + if (hashOrPubkey.Length == 20) + result = CheckWitness(engine, new UInt160(hashOrPubkey)); + else if (hashOrPubkey.Length == 33) + result = CheckWitness(engine, ECPoint.DecodePoint(hashOrPubkey, ECCurve.Secp256r1)); + else + return false; + engine.EvaluationStack.Push(result); + return true; + } + + protected virtual bool Runtime_Notify(ExecutionEngine engine) + { + StackItem state = engine.EvaluationStack.Pop(); + Notify?.Invoke(this, new NotifyEventArgs(engine.ScriptContainer, new UInt160(engine.CurrentContext.ScriptHash), state)); + return true; + } + + protected virtual bool Runtime_Log(ExecutionEngine engine) + { + string message = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); + Log?.Invoke(this, new LogEventArgs(engine.ScriptContainer, new UInt160(engine.CurrentContext.ScriptHash), message)); + return true; } protected virtual bool Blockchain_GetHeight(ExecutionEngine engine) @@ -147,6 +252,13 @@ protected virtual bool Blockchain_GetAccount(ExecutionEngine engine) return true; } + protected virtual bool Blockchain_GetValidators(ExecutionEngine engine) + { + ECPoint[] validators = Blockchain.Default.GetValidators(); + engine.EvaluationStack.Push(validators.Select(p => (StackItem)p.EncodePoint(true)).ToArray()); + return true; + } + protected virtual bool Blockchain_GetAsset(ExecutionEngine engine) { byte[] hash = engine.EvaluationStack.Pop().GetByteArray(); @@ -155,6 +267,15 @@ protected virtual bool Blockchain_GetAsset(ExecutionEngine engine) return true; } + protected virtual bool Blockchain_GetContract(ExecutionEngine engine) + { + UInt160 hash = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); + ContractState contract = Blockchain.Default.GetContract(hash); + if (contract == null) return false; + engine.EvaluationStack.Push(StackItem.FromInterface(contract)); + return true; + } + protected virtual bool Header_GetHash(ExecutionEngine engine) { BlockBase header = engine.EvaluationStack.Pop().GetInterface(); @@ -254,14 +375,6 @@ protected virtual bool Transaction_GetType(ExecutionEngine engine) return true; } - protected virtual bool Enrollment_GetPublicKey(ExecutionEngine engine) - { - EnrollmentTransaction tx = engine.EvaluationStack.Pop().GetInterface(); - if (tx == null) return false; - engine.EvaluationStack.Push(tx.PublicKey.EncodePoint(true)); - return true; - } - protected virtual bool Transaction_GetAttributes(ExecutionEngine engine) { Transaction tx = engine.EvaluationStack.Pop().GetInterface(); @@ -439,5 +552,38 @@ protected virtual bool Asset_GetIssuer(ExecutionEngine engine) engine.EvaluationStack.Push(asset.Issuer.ToArray()); return true; } + + protected virtual bool Contract_GetScript(ExecutionEngine engine) + { + ContractState contract = engine.EvaluationStack.Pop().GetInterface(); + if (contract == null) return false; + engine.EvaluationStack.Push(contract.Code.Script); + return true; + } + + protected virtual bool Storage_GetContext(ExecutionEngine engine) + { + engine.EvaluationStack.Push(StackItem.FromInterface(new StorageContext + { + ScriptHash = new UInt160(engine.CurrentContext.ScriptHash) + })); + return true; + } + + protected virtual bool Storage_Get(ExecutionEngine engine) + { + StorageContext context = engine.EvaluationStack.Pop().GetInterface(); + ContractState contract = Blockchain.Default.GetContract(context.ScriptHash); + if (contract == null) return false; + if (!contract.HasStorage) return false; + byte[] key = engine.EvaluationStack.Pop().GetByteArray(); + StorageItem item = Blockchain.Default.GetStorageItem(new StorageKey + { + ScriptHash = context.ScriptHash, + Key = key + }); + engine.EvaluationStack.Push(item?.Value ?? new byte[0]); + return true; + } } } diff --git a/neo/SmartContract/StorageContext.cs b/neo/SmartContract/StorageContext.cs new file mode 100644 index 0000000000..637811bdfe --- /dev/null +++ b/neo/SmartContract/StorageContext.cs @@ -0,0 +1,14 @@ +using Neo.VM; + +namespace Neo.SmartContract +{ + internal class StorageContext : IInteropInterface + { + public UInt160 ScriptHash; + + public byte[] ToArray() + { + return ScriptHash.ToArray(); + } + } +} diff --git a/src/AntShares/UInt160.cs b/neo/UInt160.cs similarity index 99% rename from src/AntShares/UInt160.cs rename to neo/UInt160.cs index 5e710b4b98..ba17841534 100644 --- a/src/AntShares/UInt160.cs +++ b/neo/UInt160.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Linq; -namespace AntShares +namespace Neo { public class UInt160 : UIntBase, IComparable, IEquatable { diff --git a/src/AntShares/UInt256.cs b/neo/UInt256.cs similarity index 99% rename from src/AntShares/UInt256.cs rename to neo/UInt256.cs index 61371663cc..ba00e4921c 100644 --- a/src/AntShares/UInt256.cs +++ b/neo/UInt256.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Linq; -namespace AntShares +namespace Neo { public class UInt256 : UIntBase, IComparable, IEquatable { diff --git a/src/AntShares/UIntBase.cs b/neo/UIntBase.cs similarity index 98% rename from src/AntShares/UIntBase.cs rename to neo/UIntBase.cs index bb440389ad..d05b89a951 100644 --- a/src/AntShares/UIntBase.cs +++ b/neo/UIntBase.cs @@ -1,9 +1,9 @@ -using AntShares.IO; +using Neo.IO; using System; using System.IO; using System.Linq; -namespace AntShares +namespace Neo { public abstract class UIntBase : IEquatable, ISerializable { @@ -46,8 +46,8 @@ public override bool Equals(object obj) if (!(obj is UIntBase)) return false; return this.Equals((UIntBase)obj); - } - + } + public override int GetHashCode() { return data_bytes.ToInt32(0); diff --git a/src/AntShares/Wallets/AddressState.cs b/neo/Wallets/AddressState.cs similarity index 84% rename from src/AntShares/Wallets/AddressState.cs rename to neo/Wallets/AddressState.cs index 7d9ca6bc35..feefce9ccb 100644 --- a/src/AntShares/Wallets/AddressState.cs +++ b/neo/Wallets/AddressState.cs @@ -1,6 +1,6 @@ using System; -namespace AntShares.Wallets +namespace Neo.Wallets { [Flags] internal enum AddressState : byte diff --git a/src/AntShares/Wallets/Coin.cs b/neo/Wallets/Coin.cs similarity index 95% rename from src/AntShares/Wallets/Coin.cs rename to neo/Wallets/Coin.cs index 561a63d072..9931775a2a 100644 --- a/src/AntShares/Wallets/Coin.cs +++ b/neo/Wallets/Coin.cs @@ -1,8 +1,8 @@ -using AntShares.Core; -using AntShares.IO.Caching; +using Neo.Core; +using Neo.IO.Caching; using System; -namespace AntShares.Wallets +namespace Neo.Wallets { public class Coin : IEquatable, ITrackable { diff --git a/src/AntShares/Wallets/Contract.cs b/neo/Wallets/Contract.cs similarity index 98% rename from src/AntShares/Wallets/Contract.cs rename to neo/Wallets/Contract.cs index d383a08db7..758330ee5f 100644 --- a/src/AntShares/Wallets/Contract.cs +++ b/neo/Wallets/Contract.cs @@ -1,12 +1,12 @@ -using AntShares.Core; -using AntShares.Cryptography.ECC; -using AntShares.IO; -using AntShares.VM; +using Neo.Core; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.VM; using System; using System.IO; using System.Linq; -namespace AntShares.Wallets +namespace Neo.Wallets { public class Contract : VerificationCode, IEquatable, ISerializable { diff --git a/src/AntShares/Wallets/ContractType.cs b/neo/Wallets/ContractType.cs similarity index 80% rename from src/AntShares/Wallets/ContractType.cs rename to neo/Wallets/ContractType.cs index 8aa6f5fd60..3d7b13258a 100644 --- a/src/AntShares/Wallets/ContractType.cs +++ b/neo/Wallets/ContractType.cs @@ -1,4 +1,4 @@ -namespace AntShares.Wallets +namespace Neo.Wallets { public enum ContractType : byte { diff --git a/src/AntShares/Wallets/KeyPair.cs b/neo/Wallets/KeyPair.cs similarity index 96% rename from src/AntShares/Wallets/KeyPair.cs rename to neo/Wallets/KeyPair.cs index 69519f8b68..c1a1089808 100644 --- a/src/AntShares/Wallets/KeyPair.cs +++ b/neo/Wallets/KeyPair.cs @@ -1,10 +1,10 @@ -using AntShares.Core; -using AntShares.Cryptography; +using Neo.Core; +using Neo.Cryptography; using System; using System.Linq; using System.Security.Cryptography; -namespace AntShares.Wallets +namespace Neo.Wallets { public class KeyPair : IEquatable { diff --git a/src/AntShares/Wallets/SignatureContext.cs b/neo/Wallets/SignatureContext.cs similarity index 98% rename from src/AntShares/Wallets/SignatureContext.cs rename to neo/Wallets/SignatureContext.cs index 9c5c536adc..85b8c9fefd 100644 --- a/src/AntShares/Wallets/SignatureContext.cs +++ b/neo/Wallets/SignatureContext.cs @@ -1,7 +1,7 @@ -using AntShares.Core; -using AntShares.Cryptography.ECC; -using AntShares.IO.Json; -using AntShares.VM; +using Neo.Core; +using Neo.Cryptography.ECC; +using Neo.IO.Json; +using Neo.VM; using System; using System.Collections.Generic; using System.IO; @@ -10,7 +10,7 @@ using System.Reflection; using System.Text; -namespace AntShares.Wallets +namespace Neo.Wallets { /// /// 签名上下文 diff --git a/src/AntShares/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs similarity index 99% rename from src/AntShares/Wallets/Wallet.cs rename to neo/Wallets/Wallet.cs index 516c3e9f92..028a92f295 100644 --- a/src/AntShares/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -1,6 +1,6 @@ -using AntShares.Core; -using AntShares.Cryptography; -using AntShares.IO.Caching; +using Neo.Core; +using Neo.Cryptography; +using Neo.IO.Caching; using System; using System.Collections.Generic; using System.Linq; @@ -9,7 +9,7 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; -namespace AntShares.Wallets +namespace Neo.Wallets { public abstract class Wallet : IDisposable { diff --git a/src/AntShares/AntShares.csproj b/neo/neo.csproj similarity index 76% rename from src/AntShares/AntShares.csproj rename to neo/neo.csproj index 1aa5c29e8b..d3c85ac3d3 100644 --- a/src/AntShares/AntShares.csproj +++ b/neo/neo.csproj @@ -1,19 +1,22 @@  - 2015-2017 The Antshares team - AntShares - 1.6.2 - The Antshares team + 2015-2017 The Neo Project + Neo + 2.0.0 + The Neo Project netstandard1.6;net461 true - AntShares - AntShares - AntShares;Blockchain;Smart Contract - https://github.com/AntShares/AntShares + Neo + Neo + NEO;AntShares;Blockchain;Smart Contract + https://github.com/neo-project/neo git - https://github.com/AntShares/AntShares.git + https://github.com/neo-project/neo.git 1.6.1 + Neo + The Neo Project + Neo @@ -31,14 +34,15 @@ - + + diff --git a/src/AntShares/policy.json b/neo/policy.json similarity index 100% rename from src/AntShares/policy.json rename to neo/policy.json diff --git a/src/AntShares/protocol.json b/neo/protocol.json similarity index 82% rename from src/AntShares/protocol.json rename to neo/protocol.json index 4d08e95234..633757a078 100644 --- a/src/AntShares/protocol.json +++ b/neo/protocol.json @@ -12,11 +12,11 @@ "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70" ], "SeedList": [ - "seed1.antshares.org:10333", - "seed2.antshares.org:10333", - "seed3.antshares.org:10333", - "seed4.antshares.org:10333", - "seed5.antshares.org:10333" + "seed1.neo.org:10333", + "seed2.neo.org:10333", + "seed3.neo.org:10333", + "seed4.neo.org:10333", + "seed5.neo.org:10333" ], "SystemFee": { "EnrollmentTransaction": 1000, diff --git a/src/AntShares/SmartContract/ApplicationEngine.cs b/src/AntShares/SmartContract/ApplicationEngine.cs deleted file mode 100644 index 6da836e7bd..0000000000 --- a/src/AntShares/SmartContract/ApplicationEngine.cs +++ /dev/null @@ -1,87 +0,0 @@ -using AntShares.VM; -using System.Text; - -namespace AntShares.SmartContract -{ - public class ApplicationEngine : ExecutionEngine - { - private const long ratio = 100000; - private const long gas_free = 10 * 100000000; - private long gas; - - public ApplicationEngine(IScriptContainer container, IScriptTable table, StateMachine state, Fixed8 gas) - : base(container, Cryptography.Crypto.Default, table, state) - { - this.gas = gas_free + gas.GetData(); - } - - public new bool Execute() - { - while (!State.HasFlag(VMState.HALT) && !State.HasFlag(VMState.FAULT)) - { - gas = checked(gas - GetPrice() * ratio); - if (gas < 0) return false; - StepInto(); - } - return !State.HasFlag(VMState.FAULT); - } - - protected virtual long GetPrice() - { - if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length) - return 0; - OpCode opcode = CurrentContext.NextInstruction; - if (opcode <= OpCode.PUSH16) return 0; - switch (opcode) - { - case OpCode.NOP: - return 0; - case OpCode.APPCALL: - case OpCode.TAILCALL: - return 10; - case OpCode.SYSCALL: - return GetPriceForSysCall(); - case OpCode.SHA1: - case OpCode.SHA256: - return 10; - case OpCode.HASH160: - case OpCode.HASH256: - return 20; - case OpCode.CHECKSIG: - return 100; - case OpCode.CHECKMULTISIG: - { - if (EvaluationStack.Count == 0) return 1; - int n = (int)EvaluationStack.Peek().GetBigInteger(); - if (n < 1) return 1; - return 100 * n; - } - default: return 1; - } - } - - protected virtual long GetPriceForSysCall() - { - if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length - 3) - return 1; - byte length = CurrentContext.Script[CurrentContext.InstructionPointer + 1]; - if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length - length - 2) - return 1; - string api_name = Encoding.ASCII.GetString(CurrentContext.Script, CurrentContext.InstructionPointer + 2, length); - switch (api_name) - { - case "AntShares.Blockchain.GetHeader": return 100; - case "AntShares.Blockchain.GetBlock": return 200; - case "AntShares.Blockchain.GetTransaction": return 100; - case "AntShares.Blockchain.GetAccount": return 100; - case "AntShares.Blockchain.GetAsset": return 100; - case "AntShares.Transaction.GetReferences": return 200; - case "AntShares.Account.SetVotes": return 1000; - case "AntShares.Storage.Get": return 100; - case "AntShares.Storage.Put": return 1000; - case "AntShares.Storage.Delete": return 100; - default: return 1; - } - } - } -} diff --git a/src/AntShares/SmartContract/StateMachine.cs b/src/AntShares/SmartContract/StateMachine.cs deleted file mode 100644 index bec2ef7a29..0000000000 --- a/src/AntShares/SmartContract/StateMachine.cs +++ /dev/null @@ -1,148 +0,0 @@ -using AntShares.Core; -using AntShares.Cryptography.ECC; -using AntShares.IO.Caching; -using AntShares.VM; -using System.Collections.Generic; -using System.Linq; - -namespace AntShares.SmartContract -{ - public class StateMachine : StateReader - { - private CloneCache accounts; - private CloneCache validators; - private CloneCache assets; - private CloneCache contracts; - private CloneCache storages; - - public StateMachine(DataCache accounts, DataCache validators, DataCache assets, DataCache contracts, DataCache storages) - { - this.accounts = new CloneCache(accounts); - this.validators = new CloneCache(validators); - this.assets = new CloneCache(assets); - this.contracts = new CloneCache(contracts); - this.storages = new CloneCache(storages); - Register("AntShares.Account.SetVotes", Account_SetVotes); - Register("AntShares.Storage.Get", Storage_Get); - Register("AntShares.Storage.Put", Storage_Put); - Register("AntShares.Storage.Delete", Storage_Delete); - } - - private UInt160 CheckStorageContext(ExecutionEngine engine, StorageContext context) - { - byte[] hash = null; - switch (context) - { - case StorageContext.Current: - hash = engine.CurrentContext.ScriptHash; - break; - case StorageContext.CallingContract: - hash = engine.CallingContext?.ScriptHash; - break; - case StorageContext.EntryContract: - hash = engine.EntryContext.ScriptHash; - break; - } - if (hash == null) return null; - UInt160 script_hash = new UInt160(hash); - ContractState contract = contracts.TryGet(script_hash); - if (contract == null) return null; - if (!contract.HasStorage) return null; - return script_hash; - } - - public void Commit() - { - accounts.Commit(); - validators.Commit(); - assets.Commit(); - contracts.Commit(); - storages.Commit(); - } - - private HashSet _hashes_for_verifying = null; - private HashSet GetScriptHashesForVerifying(ExecutionEngine engine) - { - if (_hashes_for_verifying == null) - { - IVerifiable container = (IVerifiable)engine.ScriptContainer; - _hashes_for_verifying = new HashSet(container.GetScriptHashesForVerifying()); - } - return _hashes_for_verifying; - } - - protected override bool Blockchain_GetAccount(ExecutionEngine engine) - { - UInt160 hash = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); - engine.EvaluationStack.Push(StackItem.FromInterface(accounts[hash])); - return true; - } - - protected override bool Blockchain_GetAsset(ExecutionEngine engine) - { - UInt256 hash = new UInt256(engine.EvaluationStack.Pop().GetByteArray()); - AssetState asset = assets.TryGet(hash); - if (asset == null) return false; - engine.EvaluationStack.Push(StackItem.FromInterface(asset)); - return true; - } - - private bool Account_SetVotes(ExecutionEngine engine) - { - AccountState account = engine.EvaluationStack.Pop().GetInterface(); - if (account == null) return false; - account = accounts[account.ScriptHash]; - if (account.IsFrozen) return false; - if (!account.Balances.ContainsKey(Blockchain.SystemShare.Hash) || account.Balances[Blockchain.SystemShare.Hash].Equals(Fixed8.Zero)) - return false; - if (!GetScriptHashesForVerifying(engine).Contains(account.ScriptHash)) return false; - account = accounts.GetAndChange(account.ScriptHash); - account.Votes = engine.EvaluationStack.Pop().GetArray().Select(p => ECPoint.DecodePoint(p.GetByteArray(), ECCurve.Secp256r1)).ToArray(); - return true; - } - - private bool Storage_Get(ExecutionEngine engine) - { - StorageContext context = (StorageContext)(byte)engine.EvaluationStack.Pop().GetBigInteger(); - UInt160 script_hash = CheckStorageContext(engine, context); - if (script_hash == null) return false; - byte[] key = engine.EvaluationStack.Pop().GetByteArray(); - StorageItem item = storages.TryGet(new StorageKey - { - ScriptHash = script_hash, - Key = key - }); - engine.EvaluationStack.Push(item?.Value ?? new byte[0]); - return true; - } - - private bool Storage_Put(ExecutionEngine engine) - { - StorageContext context = (StorageContext)(byte)engine.EvaluationStack.Pop().GetBigInteger(); - UInt160 script_hash = CheckStorageContext(engine, context); - if (script_hash == null) return false; - byte[] key = engine.EvaluationStack.Pop().GetByteArray(); - byte[] value = engine.EvaluationStack.Pop().GetByteArray(); - storages.GetAndChange(new StorageKey - { - ScriptHash = script_hash, - Key = key - }, () => new StorageItem()).Value = value; - return true; - } - - private bool Storage_Delete(ExecutionEngine engine) - { - StorageContext context = (StorageContext)(byte)engine.EvaluationStack.Pop().GetBigInteger(); - UInt160 script_hash = CheckStorageContext(engine, context); - if (script_hash == null) return false; - byte[] key = engine.EvaluationStack.Pop().GetByteArray(); - storages.Delete(new StorageKey - { - ScriptHash = script_hash, - Key = key - }); - return true; - } - } -} diff --git a/src/AntShares/SmartContract/StorageContext.cs b/src/AntShares/SmartContract/StorageContext.cs deleted file mode 100644 index 96f13e5a5d..0000000000 --- a/src/AntShares/SmartContract/StorageContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace AntShares.SmartContract -{ - [Flags] - internal enum StorageContext : byte - { - Current = 1, - CallingContract = 2, - EntryContract = 4 - } -}