// System using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; // GUPS - AntiCheat - Core using GUPS.AntiCheat.Core.Watch; // GUPS - AntiCheat using GUPS.AntiCheat.Detector; namespace GUPS.AntiCheat.Protected.Collection.Chain { /// /// Represents an implementation of the interface, storing linked blocks to form a blockchain. The /// blockchain can be synchronized with a remote source to retrieve and upload data while maintaining its integrity. Valid use cases /// could be to store game data, such as scores, achievements, or player progress and synchronize it with a remote server. This /// implementation is not designed to store large amounts of data, but rather to store small amounts of data that require integrity /// and synchronization. /// /// The value type of the content stored in the block transactions. /// /// /// The class implements the interface and supports observation through the /// interface. The blockchain consists of blocks, each containing a specified number of transactions storing /// content of type . /// /// /// The blocks are linked together based on their hash, ensuring the integrity of the chain. New transactions can be appended to the chain /// only if it maintains its integrity. /// /// /// Everytime the blockchain is modified (through synchronization or appending new transactions), the integrity of the chain is verified. /// If the integrity is compromised, the blockchain notifies the primitive cheating detector of the possible cheating attempt. /// /// /// Note: Only primitive types or structs are supported. /// /// public class BlockChain : IDataChain>, IWatchedSubject where T : struct { /// /// Represents the blockchain as a readonly linked list of blocks. /// private readonly LinkedList> chain; /// /// Gets the blockchain as ordered linked list of blocks. /// public LinkedList> Chain => this.chain; /// /// The size of each block in the blockchain. /// private readonly int blockSize; /// /// Get the last block of the chain, may be null if the chain is empty. /// public Block LastBlock => this.chain.Last?.Value ?? null; /// /// The synchronizer is used to synchronize the blockchain with a remote source. /// private ISynchronizer synchronizer; /// /// Get if the protected value has integrity, i.e., whether it has maintained its original state. /// public bool HasIntegrity { get; private set; } = true; /// /// Initializes a new instance of the class with a default block size of 10 and without a synchronizer. /// Without a synchronizer, the blockchain cannot be synchronized with a remote source and is only used for local purposes. /// public BlockChain() :this(null) { } /// /// Initializes a new instance of the class with the specified block size and without a synchronizer. /// Without a synchronizer, the blockchain cannot be synchronized with a remote source and is only used for local purposes. /// /// The size of each block in the blockchain. public BlockChain(int _BlockSize) : this(_BlockSize, null) { } /// /// Initializes a new instance of the class with the default block size of 10 and specified synchronizer. /// Without a synchronizer, the blockchain cannot be synchronized with a remote source and is only used for local purposes. /// /// The synchronizer used to synchronize the blockchain with a remote source. public BlockChain(ISynchronizer _Synchronizer) :this(10, _Synchronizer) { } /// /// Initializes a new instance of the class with the specified block size and synchronizer. /// Without a synchronizer, the blockchain cannot be synchronized with a remote source and is only used for local purposes. /// /// The size of each block in the blockchain. /// The synchronizer used to synchronize the blockchain with a remote source. public BlockChain(int _BlockSize, ISynchronizer _Synchronizer) { // Assign the synchronizer. this.synchronizer = _Synchronizer; // Initialize the blockchain as an empty linked list. this.chain = new LinkedList>(); this.blockSize = _BlockSize; } /// /// Synchronizes the blockchain with the remote source, appending all remote transactions to the local chain beginning from the last sync timestamp. /// If no synchronizer is available, no synchronization is performed and the method just returns true. /// /// True if the synchronization is successful; otherwise, false. public async Task SynchronizeAsync() { // If the synchronizer is null, just return true, because the blockchain is local and does not need to be synchronized. if (this.synchronizer == null) { return false; } // Check if the last block of the blockchain has its integrity before synchronizing. if (!this.CheckIntegrityOfLastBlock()) { // The integrity of the blockchain is compromised, return false. return false; } // Get all the transactions from the remote source beginning from the last sync timestamp. ITransaction[] var_RemoteTransactions = await this.synchronizer.ReadAsync(this.LastBlock?.Last?.Timestamp ?? 0); // Check if the remote transactions are null, return false. if (var_RemoteTransactions == null) { return false; } // Append all the transactions to the local blockchain. this.Append(var_RemoteTransactions); // Return true if the synchronization is successful. return true; } /// /// Appends a transaction to the blockchain without synchronizing it with the remote source and without checking the integrity of the chain. /// /// The transaction to append to the blockchain. private void Append(ITransaction _Transaction) { // Find the last block in the chain. Block var_LastBlock = null; if (this.chain.Count == 0) { var_LastBlock = new Block(this.blockSize); this.chain.AddFirst(var_LastBlock); } else { var_LastBlock = this.chain.Last.Value; } // If the last block is full, create a new block and append it to the chain. if (var_LastBlock.Count == var_LastBlock.Size) { int var_Nonce = var_LastBlock.Hash; var_LastBlock = new Block(this.blockSize, var_Nonce); this.chain.AddLast(var_LastBlock); } // Append the item to the last block. var_LastBlock.Append(_Transaction); } /// /// Appends an array of transactions to the blockchain without synchronizing it with the remote source and without checking the integrity of the chain. /// /// The transaction to append to the blockchain. private void Append(ITransaction[] _Transactions) { // Iterate through the transactions and append each to the blockchain. foreach (ITransaction var_Transaction in _Transactions) { // Append the transaction to the blockchain. this.Append(var_Transaction); } } /// /// Appends a new item to the blockchain. This item will be packed into a transaction and synchronized with the remote source if available. /// /// The item to be appended to the blockchain. /// True if the item is appended successfully and the chain has still its integrity; otherwise, false. public bool Append(T _Item) { // Verify the integrity of the current block, each time a new item is appended. if(!this.CheckIntegrityOfLastBlock()) { // The integrity of the blockchain is compromised, return false. return false; } // If the synchronizer is null, directly append the item to the blockchain... if(this.synchronizer == null) { this.Append(new Transaction(_Item)); return true; } // ... else write the transaction to the remote source. var writeTask = Task.Run(async () => { await this.synchronizer.WriteAsync(new Transaction(_Item)); }); writeTask.Wait(); // Synchronize the blockchain with the remote source. var syncTask = Task.Run(async () => { return await this.SynchronizeAsync(); }); syncTask.Wait(); // Return the result of the synchronization. return syncTask.Result; } /// /// Appends a new item to the blockchain. This item will be packed into a transaction and synchronized with the remote source if available. /// /// The item to be appended to the blockchain. /// True if the item is appended successfully and the chain has still its integrity; otherwise, false. public async Task AppendAsync(T _Item) { // Verify the integrity of the current block, each time a new item is appended. if (!this.CheckIntegrityOfLastBlock()) { // The integrity of the blockchain is compromised, return false. return false; } // If the synchronizer is null, directly append the item to the blockchain... if (this.synchronizer == null) { this.Append(new Transaction(_Item)); return true; } // ... else write the transaction to the remote source. await this.synchronizer.WriteAsync(new Transaction(_Item)); // Synchronize the blockchain with the remote source. return await this.SynchronizeAsync(); } /// /// Appends the blocks content to the blockchain. The content will be synchronized with the remote source if available. /// /// The block to be appended to the blockchain. /// True if the block is appended successfully and the chain has still its integrity; otherwise, false. bool IDataChain>.Append(Block _Item) { // Iterate through the transactions and append each content to the blockchain. foreach (ITransaction var_Transaction in _Item.Items) { // Append the transactions content to the blockchain. this.Append(var_Transaction.Content); } return true; } /// /// Appends the blocks content to the blockchain. The content will be synchronized with the remote source if available. /// /// The block to be appended to the blockchain. /// True if the block is appended successfully and the chain has still its integrity; otherwise, false. async Task IDataChain>.AppendAsync(Block _Item) { // Iterate through the transactions and append each content to the blockchain. foreach (ITransaction var_Transaction in _Item.Items) { // Append the transactions content to the blockchain. await this.AppendAsync(var_Transaction.Content); } return true; } /// /// Check the integrity of the passed block, also verify the chain integrity with the previous block. /// /// The block to verify. /// True if the block integrity is verified successfully; otherwise, false. private bool CheckIntegrityOfBlock(LinkedListNode> _Node) { // Verify the integrity of the passed block. if (!_Node.Value.Verify()) { // The integrity of the block is compromised, return false. return false; } // Verify the nonce of the passed block with the hash of the previous block. if (_Node.Previous != null && _Node.Value.nonce != _Node.Previous.Value.hash) { // The integrity of the block or previous is compromised, return false. return false; } // The integrity of the block is not compromised, return true. return true; } /// /// Verifies the integrity of the entire blockchain, notifying observers if a change is detected. /// /// True if the blockchain is verified successfully; otherwise, false. public bool CheckIntegrity() { // If the integrity of the chain is already compromised, return false. if (!this.HasIntegrity) { return false; } // Iterate backward through the chain and verify each block. var var_Node = this.chain.Last; while (var_Node != null) { // Verify the current block. if (!this.CheckIntegrityOfBlock(var_Node)) { this.HasIntegrity = false; break; } // Move to the previous block. var_Node = var_Node.Previous; } // Notify the primitive cheating detector of the result if the blockchain has no longer integrity. if (!this.HasIntegrity) { AntiCheatMonitor.Instance.GetDetector()?.OnNext(this); } // Return the result. return this.HasIntegrity; } /// /// Verifies the integrity of the last block in the blockchain, notifying observers if a change is detected. /// /// True if the last block is verified successfully; otherwise, false. public bool CheckIntegrityOfLastBlock() { // If the integrity of the chain is already compromised, return false. if (!this.HasIntegrity) { return false; } // If the chain has at least one block, verify the last block. if (this.chain.Count > 0) { // Verify the last block. if(!this.CheckIntegrityOfBlock(this.chain.Last)) { this.HasIntegrity = false; } } // Notify the primitive cheating detector of the result if the last block has no longer integrity. if (!this.HasIntegrity) { AntiCheatMonitor.Instance.GetDetector()?.OnNext(this); } // Return the result. return this.HasIntegrity; } /// /// Provides an enumerator for iterating over the blocks in the blockchain. /// /// An enumerator for the blockchain. public IEnumerator> GetEnumerator() { return this.chain.GetEnumerator(); } /// /// Provides a non-generic enumerator for iterating over the blocks in the blockchain. /// /// A non-generic enumerator for the blockchain. IEnumerator IEnumerable.GetEnumerator() { return this.chain.GetEnumerator(); } /// /// Does nothing. /// public void Dispose() { // Does nothing. } } }