From 07dd1f5c9ca25ad3d2e91e97c55608d1cf938cbf Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 12 Jul 2017 04:07:23 -0500 Subject: [PATCH] v2.0 (#25) * new features 1. Annual fee for assets 2. Add asset APIs 3. Add contract APIs 4. Allow to read storage in verification code 5. New storage context model * use ApplicationEngine for verification code * add validator APIs * limit the size of stack and items * neo contract and security improvement 1. Limit max transactions per block to 500 2. Limit max length of scripts to 65536 bytes 3. Limit array size and stack size 4. Modify the gas price of Storage.Put 5. Add more apis to smart contract 6. Allow users to migrate contract and storage 7. Upgrade to AntShares.VM 1.8.0 * add test mode to ApplicationEngine * fix bug of calculating fee of InvocationTransaction * fix issue of verifying gas * add events and logs for smart contracts * rebrand to NEO * change version to v2.0.0 --- AntShares.sln => neo.sln | 49 +- .../AntShares => neo}/Consensus/ChangeView.cs | 2 +- .../Consensus/ConsensusContext.cs | 14 +- .../Consensus/ConsensusMessage.cs | 4 +- .../Consensus/ConsensusMessageType.cs | 2 +- .../Consensus/ConsensusService.cs | 697 +++++----- .../Consensus/ConsensusState.cs | 2 +- .../Consensus/PerpareRequest.cs | 6 +- .../Consensus/PerpareResponse.cs | 2 +- {src/AntShares => neo}/Core/AccountState.cs | 8 +- {src/AntShares => neo}/Core/AssetState.cs | 10 +- {src/AntShares => neo}/Core/AssetType.cs | 2 +- {src/AntShares => neo}/Core/Block.cs | 10 +- {src/AntShares => neo}/Core/BlockBase.cs | 17 +- {src/AntShares => neo}/Core/Blockchain.cs | 28 +- .../Core/ClaimTransaction.cs | 13 +- {src/AntShares => neo}/Core/CoinReference.cs | 13 +- {src/AntShares => neo}/Core/CoinState.cs | 2 +- .../Core/ContractParameterType.cs | 4 +- {src/AntShares => neo}/Core/ContractState.cs | 4 +- .../Core/ContractTransaction.cs | 10 +- .../Core/EnrollmentTransaction.cs | 25 +- {src/AntShares => neo}/Core/FunctionCode.cs | 4 +- {src/AntShares => neo}/Core/Header.cs | 6 +- {src/AntShares => neo}/Core/Helper.cs | 182 ++- {src/AntShares => neo}/Core/ICloneable.cs | 2 +- {src/AntShares => neo}/Core/ICode.cs | 2 +- {src/AntShares => neo}/Core/IVerifiable.cs | 6 +- .../Core/InvocationTransaction.cs | 12 +- .../Core/IssueTransaction.cs | 8 +- .../Core/MinerTransaction.cs | 5 +- .../Core/PublishTransaction.cs | 21 +- .../Core/RegisterTransaction.cs | 28 +- {src/AntShares => neo}/Core/SpentCoin.cs | 2 +- {src/AntShares => neo}/Core/SpentCoinState.cs | 4 +- {src/AntShares => neo}/Core/StateBase.cs | 12 +- {src/AntShares => neo}/Core/StorageItem.cs | 4 +- {src/AntShares => neo}/Core/StorageKey.cs | 6 +- {src/AntShares => neo}/Core/Transaction.cs | 761 ++++++----- .../Core/TransactionAttribute.cs | 13 +- .../Core/TransactionAttributeUsage.cs | 2 +- .../Core/TransactionOutput.cs | 15 +- .../Core/TransactionResult.cs | 2 +- .../AntShares => neo}/Core/TransactionType.cs | 5 +- .../Core/UnspentCoinState.cs | 4 +- {src/AntShares => neo}/Core/ValidatorState.cs | 46 +- .../Core/VerificationCode.cs | 2 +- {src/AntShares => neo}/Core/VoteState.cs | 4 +- {src/AntShares => neo}/Core/Witness.cs | 10 +- {src/AntShares => neo}/Cryptography/Base58.cs | 2 +- .../Cryptography/BloomFilter.cs | 2 +- {src/AntShares => neo}/Cryptography/Crypto.cs | 5 +- .../Cryptography/ECC/ECCurve.cs | 2 +- .../Cryptography/ECC/ECDsa.cs | 2 +- .../Cryptography/ECC/ECFieldElement.cs | 2 +- .../Cryptography/ECC/ECPoint.cs | 17 +- {src/AntShares => neo}/Cryptography/Helper.cs | 2 +- .../Cryptography/MerkleTree.cs | 2 +- .../Cryptography/MerkleTreeNode.cs | 2 +- .../AntShares => neo}/Cryptography/Murmur3.cs | 2 +- .../Cryptography/ProtectedMemoryContext.cs | 2 +- .../Cryptography/RIPEMD160Managed.cs | 2 +- {src/AntShares => neo}/Fixed8.cs | 20 +- {src/AntShares => neo}/Helper.cs | 526 +++---- {src/AntShares => neo}/IO/Caching/Cache.cs | 46 +- .../AntShares => neo}/IO/Caching/DataCache.cs | 19 +- .../AntShares => neo}/IO/Caching/FIFOCache.cs | 2 +- .../IO/Caching/ITrackable.cs | 2 +- {src/AntShares => neo}/IO/Caching/LRUCache.cs | 2 +- .../IO/Caching/RelayCache.cs | 4 +- .../IO/Caching/TrackState.cs | 2 +- .../IO/Caching/TrackableCollection.cs | 2 +- {src/AntShares => neo}/IO/Helper.cs | 2 +- {src/AntShares => neo}/IO/ISerializable.cs | 2 +- {src/AntShares => neo}/IO/Json/JArray.cs | 2 +- {src/AntShares => neo}/IO/Json/JBoolean.cs | 2 +- {src/AntShares => neo}/IO/Json/JNumber.cs | 2 +- {src/AntShares => neo}/IO/Json/JObject.cs | 2 +- {src/AntShares => neo}/IO/Json/JString.cs | 2 +- .../Blockchains/LevelDB/CachedScriptTable.cs | 6 +- .../Implementations/Blockchains/LevelDB/DB.cs | 2 +- .../Blockchains/LevelDB/DataEntryPrefix.cs | 2 +- .../Blockchains/LevelDB/DbCache.cs | 24 +- .../Blockchains/LevelDB/Helper.cs | 4 +- .../Blockchains/LevelDB/Iterator.cs | 2 +- .../Blockchains/LevelDB/LevelDBBlockchain.cs | 1211 +++++++++-------- .../Blockchains/LevelDB/LevelDBException.cs | 2 +- .../Blockchains/LevelDB/Native.cs | 4 +- .../Blockchains/LevelDB/Options.cs | 2 +- .../Blockchains/LevelDB/ReadOptions.cs | 2 +- .../Blockchains/LevelDB/Slice.cs | 488 +++---- .../Blockchains/LevelDB/SliceBuilder.cs | 4 +- .../Blockchains/LevelDB/Snapshot.cs | 2 +- .../Blockchains/LevelDB/WriteBatch.cs | 2 +- .../Blockchains/LevelDB/WriteOptions.cs | 2 +- .../Wallets/EntityFramework/Account.cs | 2 +- .../Wallets/EntityFramework/Address.cs | 2 +- .../Wallets/EntityFramework/Coin.cs | 4 +- .../Wallets/EntityFramework/Contract.cs | 2 +- .../Wallets/EntityFramework/Key.cs | 2 +- .../Wallets/EntityFramework/Transaction.cs | 4 +- .../EntityFramework/TransactionInfo.cs | 4 +- .../Wallets/EntityFramework/UserWallet.cs | 16 +- .../EntityFramework/WalletDataContext.cs | 2 +- {src/AntShares => neo}/Network/IInventory.cs | 4 +- .../Network/InventoryReceivingEventArgs.cs | 28 +- .../Network/InventoryType.cs | 2 +- {src/AntShares => neo}/Network/LocalNode.cs | 1152 ++++++++-------- {src/AntShares => neo}/Network/Message.cs | 6 +- .../Network/Payloads/AddrPayload.cs | 6 +- .../Network/Payloads/ConsensusPayload.cs | 19 +- .../Network/Payloads/FilterAddPayload.cs | 4 +- .../Network/Payloads/FilterLoadPayload.cs | 6 +- .../Network/Payloads/GetBlocksPayload.cs | 6 +- .../Network/Payloads/HeadersPayload.cs | 8 +- .../Network/Payloads/InvPayload.cs | 4 +- .../Network/Payloads/MerkleBlockPayload.cs | 8 +- .../Payloads/NetworkAddressWithTime.cs | 4 +- .../Network/Payloads/VersionPayload.cs | 6 +- .../Network/RPC/RpcException.cs | 2 +- .../Network/RPC/RpcServer.cs | 550 ++++---- {src/AntShares => neo}/Network/RemoteNode.cs | 946 ++++++------- .../Network/TcpRemoteNode.cs | 222 +-- {src/AntShares => neo}/Network/UPnP.cs | 2 +- .../Network/WebSocketRemoteNode.cs | 4 +- {src/AntShares => neo}/Settings.cs | 4 +- neo/SmartContract/ApplicationEngine.cs | 251 ++++ .../SmartContract/CloneCache.cs | 25 +- neo/SmartContract/LogEventArgs.cs | 19 + neo/SmartContract/NotifyEventArgs.cs | 19 + neo/SmartContract/StateMachine.cs | 356 +++++ .../SmartContract/StateReader.cs | 178 ++- neo/SmartContract/StorageContext.cs | 14 + {src/AntShares => neo}/UInt160.cs | 2 +- {src/AntShares => neo}/UInt256.cs | 2 +- {src/AntShares => neo}/UIntBase.cs | 8 +- .../AntShares => neo}/Wallets/AddressState.cs | 2 +- {src/AntShares => neo}/Wallets/Coin.cs | 6 +- {src/AntShares => neo}/Wallets/Contract.cs | 10 +- .../AntShares => neo}/Wallets/ContractType.cs | 2 +- {src/AntShares => neo}/Wallets/KeyPair.cs | 6 +- .../Wallets/SignatureContext.cs | 10 +- {src/AntShares => neo}/Wallets/Wallet.cs | 8 +- .../AntShares.csproj => neo/neo.csproj | 24 +- {src/AntShares => neo}/policy.json | 0 {src/AntShares => neo}/protocol.json | 10 +- .../SmartContract/ApplicationEngine.cs | 87 -- src/AntShares/SmartContract/StateMachine.cs | 148 -- src/AntShares/SmartContract/StorageContext.cs | 12 - 149 files changed, 4670 insertions(+), 4094 deletions(-) rename AntShares.sln => neo.sln (63%) rename {src/AntShares => neo}/Consensus/ChangeView.cs (95%) rename {src/AntShares => neo}/Consensus/ConsensusContext.cs (95%) rename {src/AntShares => neo}/Consensus/ConsensusMessage.cs (96%) rename {src/AntShares => neo}/Consensus/ConsensusMessageType.cs (82%) rename {src/AntShares => neo}/Consensus/ConsensusService.cs (96%) rename {src/AntShares => neo}/Consensus/ConsensusState.cs (90%) rename {src/AntShares => neo}/Consensus/PerpareRequest.cs (94%) rename {src/AntShares => neo}/Consensus/PerpareResponse.cs (94%) rename {src/AntShares => neo}/Core/AccountState.cs (95%) rename {src/AntShares => neo}/Core/AssetState.cs (97%) rename {src/AntShares => neo}/Core/AssetType.cs (92%) rename {src/AntShares => neo}/Core/Block.cs (98%) rename {src/AntShares => neo}/Core/BlockBase.cs (94%) rename {src/AntShares => neo}/Core/Blockchain.cs (96%) rename {src/AntShares => neo}/Core/ClaimTransaction.cs (93%) rename {src/AntShares => neo}/Core/CoinReference.cs (92%) rename {src/AntShares => neo}/Core/CoinState.cs (91%) rename {src/AntShares => neo}/Core/ContractParameterType.cs (93%) rename {src/AntShares => neo}/Core/ContractState.cs (97%) rename {src/AntShares => neo}/Core/ContractTransaction.cs (56%) rename {src/AntShares => neo}/Core/EnrollmentTransaction.cs (85%) rename {src/AntShares => neo}/Core/FunctionCode.cs (96%) rename {src/AntShares => neo}/Core/Header.cs (95%) rename {src/AntShares => neo}/Core/Helper.cs (65%) rename {src/AntShares => neo}/Core/ICloneable.cs (79%) rename {src/AntShares => neo}/Core/ICode.cs (88%) rename {src/AntShares => neo}/Core/IVerifiable.cs (93%) rename {src/AntShares => neo}/Core/InvocationTransaction.cs (83%) rename {src/AntShares => neo}/Core/IssueTransaction.cs (92%) rename {src/AntShares => neo}/Core/MinerTransaction.cs (94%) rename {src/AntShares => neo}/Core/PublishTransaction.cs (87%) rename {src/AntShares => neo}/Core/RegisterTransaction.cs (83%) rename {src/AntShares => neo}/Core/SpentCoin.cs (87%) rename {src/AntShares => neo}/Core/SpentCoinState.cs (96%) rename {src/AntShares => neo}/Core/StateBase.cs (72%) rename {src/AntShares => neo}/Core/StorageItem.cs (94%) rename {src/AntShares => neo}/Core/StorageKey.cs (94%) rename {src/AntShares => neo}/Core/Transaction.cs (95%) rename {src/AntShares => neo}/Core/TransactionAttribute.cs (94%) rename {src/AntShares => neo}/Core/TransactionAttributeUsage.cs (98%) rename {src/AntShares => neo}/Core/TransactionOutput.cs (88%) rename {src/AntShares => neo}/Core/TransactionResult.cs (92%) rename {src/AntShares => neo}/Core/TransactionType.cs (89%) rename {src/AntShares => neo}/Core/UnspentCoinState.cs (92%) rename {src/AntShares => neo}/Core/ValidatorState.cs (93%) rename {src/AntShares => neo}/Core/VerificationCode.cs (95%) rename {src/AntShares => neo}/Core/VoteState.cs (79%) rename {src/AntShares => neo}/Core/Witness.cs (82%) rename {src/AntShares => neo}/Cryptography/Base58.cs (98%) rename {src/AntShares => neo}/Cryptography/BloomFilter.cs (97%) rename {src/AntShares => neo}/Cryptography/Crypto.cs (97%) rename {src/AntShares => neo}/Cryptography/ECC/ECCurve.cs (98%) rename {src/AntShares => neo}/Cryptography/ECC/ECDsa.cs (99%) rename {src/AntShares => neo}/Cryptography/ECC/ECFieldElement.cs (99%) rename {src/AntShares => neo}/Cryptography/ECC/ECPoint.cs (98%) rename {src/AntShares => neo}/Cryptography/Helper.cs (99%) rename {src/AntShares => neo}/Cryptography/MerkleTree.cs (99%) rename {src/AntShares => neo}/Cryptography/MerkleTreeNode.cs (89%) rename {src/AntShares => neo}/Cryptography/Murmur3.cs (98%) rename {src/AntShares => neo}/Cryptography/ProtectedMemoryContext.cs (96%) rename {src/AntShares => neo}/Cryptography/RIPEMD160Managed.cs (99%) rename {src/AntShares => neo}/Fixed8.cs (93%) rename {src/AntShares => neo}/Helper.cs (97%) rename {src/AntShares => neo}/IO/Caching/Cache.cs (99%) rename {src/AntShares => neo}/IO/Caching/DataCache.cs (86%) rename {src/AntShares => neo}/IO/Caching/FIFOCache.cs (88%) rename {src/AntShares => neo}/IO/Caching/ITrackable.cs (78%) rename {src/AntShares => neo}/IO/Caching/LRUCache.cs (91%) rename {src/AntShares => neo}/IO/Caching/RelayCache.cs (83%) rename {src/AntShares => neo}/IO/Caching/TrackState.cs (76%) rename {src/AntShares => neo}/IO/Caching/TrackableCollection.cs (98%) rename {src/AntShares => neo}/IO/Helper.cs (99%) rename {src/AntShares => neo}/IO/ISerializable.cs (95%) rename {src/AntShares => neo}/IO/Json/JArray.cs (99%) rename {src/AntShares => neo}/IO/Json/JBoolean.cs (98%) rename {src/AntShares => neo}/IO/Json/JNumber.cs (99%) rename {src/AntShares => neo}/IO/Json/JObject.cs (99%) rename {src/AntShares => neo}/IO/Json/JString.cs (99%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/CachedScriptTable.cs (80%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/DB.cs (98%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/DataEntryPrefix.cs (88%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/DbCache.cs (65%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/Helper.cs (97%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/Iterator.cs (96%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs (94%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/LevelDBException.cs (77%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/Native.cs (99%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/Options.cs (97%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/ReadOptions.cs (93%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/Slice.cs (95%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/SliceBuilder.cs (95%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/Snapshot.cs (89%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/WriteBatch.cs (92%) rename {src/AntShares => neo}/Implementations/Blockchains/LevelDB/WriteOptions.cs (89%) rename {src/AntShares => neo}/Implementations/Wallets/EntityFramework/Account.cs (70%) rename {src/AntShares => neo}/Implementations/Wallets/EntityFramework/Address.cs (58%) rename {src/AntShares => neo}/Implementations/Wallets/EntityFramework/Coin.cs (80%) rename {src/AntShares => neo}/Implementations/Wallets/EntityFramework/Contract.cs (81%) rename {src/AntShares => neo}/Implementations/Wallets/EntityFramework/Key.cs (65%) rename {src/AntShares => neo}/Implementations/Wallets/EntityFramework/Transaction.cs (76%) rename {src/AntShares => neo}/Implementations/Wallets/EntityFramework/TransactionInfo.cs (60%) rename {src/AntShares => neo}/Implementations/Wallets/EntityFramework/UserWallet.cs (98%) rename {src/AntShares => neo}/Implementations/Wallets/EntityFramework/WalletDataContext.cs (98%) rename {src/AntShares => neo}/Network/IInventory.cs (75%) rename {src/AntShares => neo}/Network/InventoryReceivingEventArgs.cs (87%) rename {src/AntShares => neo}/Network/InventoryType.cs (92%) rename {src/AntShares => neo}/Network/LocalNode.cs (95%) rename {src/AntShares => neo}/Network/Message.cs (98%) rename {src/AntShares => neo}/Network/Payloads/AddrPayload.cs (88%) rename {src/AntShares => neo}/Network/Payloads/ConsensusPayload.cs (91%) rename {src/AntShares => neo}/Network/Payloads/FilterAddPayload.cs (87%) rename {src/AntShares => neo}/Network/Payloads/FilterLoadPayload.cs (92%) rename {src/AntShares => neo}/Network/Payloads/GetBlocksPayload.cs (86%) rename {src/AntShares => neo}/Network/Payloads/HeadersPayload.cs (81%) rename {src/AntShares => neo}/Network/Payloads/InvPayload.cs (93%) rename {src/AntShares => neo}/Network/Payloads/MerkleBlockPayload.cs (94%) rename {src/AntShares => neo}/Network/Payloads/NetworkAddressWithTime.cs (95%) rename {src/AntShares => neo}/Network/Payloads/VersionPayload.cs (95%) rename {src/AntShares => neo}/Network/RPC/RpcException.cs (85%) rename {src/AntShares => neo}/Network/RPC/RpcServer.cs (96%) rename {src/AntShares => neo}/Network/RemoteNode.cs (96%) rename {src/AntShares => neo}/Network/TcpRemoteNode.cs (95%) rename {src/AntShares => neo}/Network/UPnP.cs (99%) rename {src/AntShares => neo}/Network/WebSocketRemoteNode.cs (97%) rename {src/AntShares => neo}/Settings.cs (96%) create mode 100644 neo/SmartContract/ApplicationEngine.cs rename {src/AntShares => neo}/SmartContract/CloneCache.cs (75%) create mode 100644 neo/SmartContract/LogEventArgs.cs create mode 100644 neo/SmartContract/NotifyEventArgs.cs create mode 100644 neo/SmartContract/StateMachine.cs rename {src/AntShares => neo}/SmartContract/StateReader.cs (69%) create mode 100644 neo/SmartContract/StorageContext.cs rename {src/AntShares => neo}/UInt160.cs (99%) rename {src/AntShares => neo}/UInt256.cs (99%) rename {src/AntShares => neo}/UIntBase.cs (98%) rename {src/AntShares => neo}/Wallets/AddressState.cs (84%) rename {src/AntShares => neo}/Wallets/Coin.cs (95%) rename {src/AntShares => neo}/Wallets/Contract.cs (98%) rename {src/AntShares => neo}/Wallets/ContractType.cs (80%) rename {src/AntShares => neo}/Wallets/KeyPair.cs (96%) rename {src/AntShares => neo}/Wallets/SignatureContext.cs (98%) rename {src/AntShares => neo}/Wallets/Wallet.cs (99%) rename src/AntShares/AntShares.csproj => neo/neo.csproj (76%) rename {src/AntShares => neo}/policy.json (100%) rename {src/AntShares => neo}/protocol.json (82%) delete mode 100644 src/AntShares/SmartContract/ApplicationEngine.cs delete mode 100644 src/AntShares/SmartContract/StateMachine.cs delete mode 100644 src/AntShares/SmartContract/StorageContext.cs 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 - } -}