public void TestDBChangeSetStorageMapClear() { var storage = new KeyStoreStorage(CreateKeyStoreAdapterTest("test2")); var changeSet = new StorageChangeSetContext(storage); var testMapKey = Encoding.UTF8.GetBytes($".test._valueMap"); var testMapKey2 = Encoding.UTF8.GetBytes($".test2._valueMap"); var testMap = new StorageMap(testMapKey, changeSet); var testMap2 = new StorageMap(testMapKey2, changeSet); testMap.Set("test1", "Value1"); testMap.Set("test2", "Value2"); testMap.Set("test3", "Value3"); testMap.Set("test4", "Value4"); Assert.IsTrue(testMap.Count() == 4); testMap2.Set <BigInteger, string>(new BigInteger(1), "Value21"); testMap2.Set <BigInteger, string>(new BigInteger(2), "Value22"); testMap2.Set <BigInteger, string>(new BigInteger(3), "Value23"); testMap2.Set <BigInteger, string>(new BigInteger(4), "Value24"); Assert.IsTrue(testMap2.Count() == 4); changeSet.Execute(); var count = 0; testMap.Visit <string, string>((key, value) => { count++; }); testMap2.Visit <BigInteger, string>((key, value) => { count++; }); Console.WriteLine($"visit: {count} count: {(int)testMap.Count() + testMap2.Count()}"); Assert.AreEqual(count, (int)testMap.Count() + testMap2.Count()); testMap.Clear(); testMap2.Clear(); Assert.IsTrue(testMap.Count() == 0); Assert.IsTrue(testMap2.Count() == 0); Assert.IsNull(testMap.Get <string, string>("test1")); Assert.IsNull(testMap.Get <string, string>("test2")); Assert.IsNull(testMap.Get <string, string>("test3")); Assert.IsNull(testMap.Get <string, string>("test4")); Assert.IsNull(testMap2.Get <BigInteger, string>(new BigInteger(1))); Assert.IsNull(testMap2.Get <BigInteger, string>(new BigInteger(2))); Assert.IsNull(testMap2.Get <BigInteger, string>(new BigInteger(3))); Assert.IsNull(testMap2.Get <BigInteger, string>(new BigInteger(4))); }
// TODO This test fails sometimes on first "Assert.IsTrue(testMap.Count() == 0);" check. // Looks like it's random and related to tests execution order. // Calling TestDBChangeSetStorageMapClear() test before this test makes it complete successfully. // Changing "test2" to other unique key also helps. public void TestDBChangeSetStorageMapClearEmpty() { var storage = (StorageContext) new KeyStoreStorage(CreateKeyStoreAdapterTest("test2")); var changeSet = new StorageChangeSetContext(storage); var testMapKey = Encoding.UTF8.GetBytes($".test._valueMap"); var testMap = new StorageMap(testMapKey, changeSet); changeSet.Execute(); Assert.IsTrue(testMap.Count() == 0); testMap.Clear(); Assert.IsTrue(testMap.Count() == 0); }
public void AddBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee) { /*if (CurrentEpoch != null && CurrentEpoch.IsSlashed(Timestamp.Now)) * { * return false; * }*/ if (LastBlock != null) { if (LastBlock.Height != block.Height - 1) { throw new BlockGenerationException($"height of block should be {LastBlock.Height + 1}"); } if (block.PreviousHash != LastBlock.Hash) { throw new BlockGenerationException($"previous hash should be {LastBlock.PreviousHash}"); } } var inputHashes = new HashSet <Hash>(transactions.Select(x => x.Hash)); foreach (var hash in block.TransactionHashes) { if (!inputHashes.Contains(hash)) { throw new BlockGenerationException($"missing in inputs transaction with hash {hash}"); } } var outputHashes = new HashSet <Hash>(block.TransactionHashes); foreach (var tx in transactions) { if (!outputHashes.Contains(tx.Hash)) { throw new BlockGenerationException($"missing in outputs transaction with hash {tx.Hash}"); } } foreach (var tx in transactions) { if (!tx.IsValid(this)) { throw new InvalidTransactionException(tx.Hash, $"invalid transaction with hash {tx.Hash}"); } } var changeSet = new StorageChangeSetContext(this.Storage); var oracle = Nexus.CreateOracleReader(); foreach (var tx in transactions) { byte[] result; try { if (tx.Execute(this, block.Timestamp, changeSet, block.Notify, oracle, minimumFee, out result)) { if (result != null) { block.SetResultForHash(tx.Hash, result); } } else { throw new InvalidTransactionException(tx.Hash, $"execution failed"); } } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } throw new InvalidTransactionException(tx.Hash, e.Message); } } block.MergeOracle(oracle); // from here on, the block is accepted _blockHeightMap[block.Height] = block.Hash; _blocks[block.Hash] = block; _blockChangeSets[block.Hash] = changeSet; changeSet.Execute(); Dictionary <string, BigInteger> synchMap = null; foreach (Transaction tx in transactions) { _transactions[tx.Hash] = tx; _transactionBlockMap[tx.Hash] = block.Hash; var evts = block.GetEventsForTransaction(tx.Hash); foreach (var evt in evts) { if (evt.Kind == EventKind.TokenMint || evt.Kind == EventKind.TokenBurn || evt.Kind == EventKind.TokenReceive || evt.Kind == EventKind.TokenSend) { var eventData = evt.GetContent <TokenEventData>(); var token = Nexus.GetTokenInfo(eventData.symbol); if (!token.IsFungible) { // TODO support this continue; } if (token.IsCapped) { BigInteger balance; if (synchMap == null) { synchMap = new Dictionary <string, BigInteger>(); balance = 0; } else { balance = synchMap.ContainsKey(eventData.symbol) ? synchMap[eventData.symbol] : 0; } if (evt.Kind == EventKind.TokenBurn || evt.Kind == EventKind.TokenSend) { balance -= eventData.value; } else { balance += eventData.value; } synchMap[eventData.symbol] = balance; } } } } if (synchMap != null) { SynchronizeSupplies(synchMap); } var blockValidator = GetValidatorForBlock(block); if (blockValidator.IsNull) { throw new BlockGenerationException("no validator for this block"); } Nexus.PluginTriggerBlock(this, block); }
public void AddBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee, StorageChangeSetContext changeSet) { if (!block.IsSigned) { throw new BlockGenerationException($"block must be signed"); } var unsignedBytes = block.ToByteArray(false); if (!block.Signature.Verify(unsignedBytes, block.Validator)) { throw new BlockGenerationException($"block signature does not match validator {block.Validator.Text}"); } var hashList = new StorageList(BlockHeightListTag, this.Storage); var expectedBlockHeight = hashList.Count() + 1; if (expectedBlockHeight != block.Height) { throw new ChainException("unexpected block height"); } // from here on, the block is accepted using (var m = new ProfileMarker("changeSet.Execute")) changeSet.Execute(); hashList.Add <Hash>(block.Hash); using (var m = new ProfileMarker("Compress")) { var blockMap = new StorageMap(BlockHashMapTag, this.Storage); var blockBytes = block.ToByteArray(true); blockBytes = CompressionUtils.Compress(blockBytes); blockMap.Set <Hash, byte[]>(block.Hash, blockBytes); var txMap = new StorageMap(TransactionHashMapTag, this.Storage); var txBlockMap = new StorageMap(TxBlockHashMapTag, this.Storage); foreach (Transaction tx in transactions) { var txBytes = tx.ToByteArray(true); txBytes = CompressionUtils.Compress(txBytes); txMap.Set <Hash, byte[]>(tx.Hash, txBytes); txBlockMap.Set <Hash, Hash>(tx.Hash, block.Hash); } } using (var m = new ProfileMarker("AddressBlockHashMapTag")) foreach (var transaction in transactions) { var addresses = new HashSet <Address>(); var events = block.GetEventsForTransaction(transaction.Hash); foreach (var evt in events) { if (evt.Address.IsSystem) { continue; } addresses.Add(evt.Address); } var addressTxMap = new StorageMap(AddressBlockHashMapTag, this.Storage); foreach (var address in addresses) { var addressList = addressTxMap.Get <Address, StorageList>(address); addressList.Add <Hash>(transaction.Hash); } } using (var m = new ProfileMarker("Nexus.PluginTriggerBlock")) Nexus.PluginTriggerBlock(this, block); }
public void AddBlock(Block block, IEnumerable <Transaction> transactions, OracleReaderDelegate oracleReader) { /*if (CurrentEpoch != null && CurrentEpoch.IsSlashed(Timestamp.Now)) * { * return false; * }*/ if (LastBlock != null) { if (LastBlock.Height != block.Height - 1) { throw new BlockGenerationException($"height of block should be {LastBlock.Height + 1}"); } if (block.PreviousHash != LastBlock.Hash) { throw new BlockGenerationException($"previous hash should be {LastBlock.PreviousHash}"); } } var inputHashes = new HashSet <Hash>(transactions.Select(x => x.Hash)); foreach (var hash in block.TransactionHashes) { if (!inputHashes.Contains(hash)) { throw new BlockGenerationException($"missing in inputs transaction with hash {hash}"); } } var outputHashes = new HashSet <Hash>(block.TransactionHashes); foreach (var tx in transactions) { if (!outputHashes.Contains(tx.Hash)) { throw new BlockGenerationException($"missing in outputs transaction with hash {tx.Hash}"); } } foreach (var tx in transactions) { if (!tx.IsValid(this)) { throw new InvalidTransactionException(tx.Hash, $"invalid transaction with hash {tx.Hash}"); } } var changeSet = new StorageChangeSetContext(this.Storage); foreach (var tx in transactions) { byte[] result; if (tx.Execute(this, block, changeSet, block.Notify, oracleReader, out result)) { if (result != null) { block.SetResultForHash(tx.Hash, result); } } else { throw new InvalidTransactionException(tx.Hash, $"transaction execution failed with hash {tx.Hash}"); } } // from here on, the block is accepted _blockHeightMap[block.Height] = block; _blocks[block.Hash] = block; _blockChangeSets[block.Hash] = changeSet; changeSet.Execute(); if (CurrentEpoch == null) { GenerateEpoch(); } CurrentEpoch.AddBlockHash(block.Hash); CurrentEpoch.UpdateHash(); LastBlock = block; foreach (Transaction tx in transactions) { _transactions[tx.Hash] = tx; _transactionBlockMap[tx.Hash] = block.Hash; } Nexus.PluginTriggerBlock(this, block); }
public void AddBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee) { /*if (CurrentEpoch != null && CurrentEpoch.IsSlashed(Timestamp.Now)) * { * return false; * }*/ var lastBlockHash = GetLastBlockHash(); var lastBlock = GetBlockByHash(lastBlockHash); if (lastBlock != null) { if (lastBlock.Height != block.Height - 1) { throw new BlockGenerationException($"height of block should be {lastBlock.Height + 1}"); } if (block.PreviousHash != lastBlock.Hash) { throw new BlockGenerationException($"previous hash should be {lastBlock.PreviousHash}"); } } var inputHashes = new HashSet <Hash>(transactions.Select(x => x.Hash)); foreach (var hash in block.TransactionHashes) { if (!inputHashes.Contains(hash)) { throw new BlockGenerationException($"missing in inputs transaction with hash {hash}"); } } var outputHashes = new HashSet <Hash>(block.TransactionHashes); foreach (var tx in transactions) { if (!outputHashes.Contains(tx.Hash)) { throw new BlockGenerationException($"missing in outputs transaction with hash {tx.Hash}"); } } // TODO avoid fetching this every time var expectedProtocol = Nexus.GetGovernanceValue(Nexus.RootStorage, Nexus.NexusProtocolVersionTag); if (block.Protocol != expectedProtocol) { throw new BlockGenerationException($"invalid protocol number {block.Protocol}, expected protocol {expectedProtocol}"); } foreach (var tx in transactions) { if (!tx.IsValid(this)) { throw new InvalidTransactionException(tx.Hash, $"invalid transaction with hash {tx.Hash}"); } } var changeSet = new StorageChangeSetContext(this.Storage); var oracle = Nexus.CreateOracleReader(); foreach (var tx in transactions) { byte[] result; try { if (ExecuteTransaction(tx, block.Timestamp, changeSet, block.Notify, oracle, minimumFee, out result)) { if (result != null) { block.SetResultForHash(tx.Hash, result); } } else { throw new InvalidTransactionException(tx.Hash, $"execution failed"); } } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } throw new InvalidTransactionException(tx.Hash, e.Message); } } block.MergeOracle(oracle); var hashList = new StorageList(BlockHeightListTag, this.Storage); var expectedBlockHeight = hashList.Count() + 1; if (expectedBlockHeight != block.Height) { throw new ChainException("unexpected block height"); } // from here on, the block is accepted changeSet.Execute(); hashList.Add <Hash>(block.Hash); var blockMap = new StorageMap(BlockHashMapTag, this.Storage); blockMap.Set <Hash, Block>(block.Hash, block); var txMap = new StorageMap(TransactionHashMapTag, this.Storage); var txBlockMap = new StorageMap(TxBlockHashMapTag, this.Storage); foreach (Transaction tx in transactions) { txMap.Set <Hash, Transaction>(tx.Hash, tx); txBlockMap.Set <Hash, Hash>(tx.Hash, block.Hash); } var blockValidator = GetValidatorForBlock(block); if (blockValidator.IsNull) { throw new BlockGenerationException("no validator for this block"); } Nexus.PluginTriggerBlock(this, block); }