Пример #1
0
        public void Test_Tx_Pool_Replacement()
        {
            _blockManager.TryBuildGenesisBlock();
            TestTxPoolAdding();

            var privateKey = Crypto.GeneratePrivateKey().ToPrivateKey();
            var keyPair    = new EcdsaKeyPair(privateKey);

            AddRandomTxesToPool(keyPair, out var randomTxes);
            var rnd = new Random();

            randomTxes = randomTxes.OrderBy(item => rnd.Next()).ToList();
            bool useNewChainId = HardforkHeights.IsHardfork_9Active(_blockManager.GetHeight() + 1);
            var  signer        = new TransactionSigner();

            foreach (var tx in randomTxes)
            {
                Console.WriteLine($"nonce: {tx.Transaction.Nonce}");
                var newTx = new Transaction(tx.Transaction);
                // lower price
                newTx.GasPrice = tx.Transaction.GasPrice - 1;
                var randomTx = TestUtils.GetRandomTransaction(useNewChainId).Transaction;
                newTx.To       = randomTx.To;
                newTx.Value    = randomTx.Value;
                newTx.GasLimit = randomTx.GasLimit;
                var newTxReceipt = signer.Sign(newTx, keyPair, useNewChainId);
                var result       = _transactionPool.Add(newTxReceipt);
                Console.WriteLine($"old gas price: {tx.Transaction.GasPrice}, new gas price: {newTxReceipt.Transaction.GasPrice}");
                Assert.AreEqual(OperatingError.Underpriced, result);
                // equal price
                newTx.GasPrice = tx.Transaction.GasPrice;
                newTxReceipt   = signer.Sign(newTx, keyPair, useNewChainId);
                result         = _transactionPool.Add(newTxReceipt);
                Console.WriteLine($"old gas price: {tx.Transaction.GasPrice}, new gas price: {newTxReceipt.Transaction.GasPrice}");
                Assert.AreEqual(OperatingError.Underpriced, result);
                // higher price
                newTx.GasPrice = tx.Transaction.GasPrice + 1;
                newTxReceipt   = signer.Sign(newTx, keyPair, useNewChainId);
                result         = _transactionPool.Add(newTxReceipt);
                Console.WriteLine($"old gas price: {tx.Transaction.GasPrice}, new gas price: {newTxReceipt.Transaction.GasPrice}");
                Assert.AreEqual(OperatingError.DuplicatedTransaction, result);
                // higher price and all fields same
                newTx          = new Transaction(tx.Transaction);
                newTx.GasPrice = tx.Transaction.GasPrice + 1;
                newTxReceipt   = signer.Sign(newTx, keyPair, useNewChainId);
                result         = _transactionPool.Add(newTxReceipt);
                Console.WriteLine($"old gas price: {tx.Transaction.GasPrice}, new gas price: {newTxReceipt.Transaction.GasPrice}");
                Assert.AreEqual(OperatingError.Ok, result);
            }
        }
        /// <inheritdoc />
        public override async Task Handle(TransactionMessage message, IPeer sender)
        {
            var transaction = message.Payload;

            if (transaction is MinerTransaction)
            {
                return;
            }

            if (transaction.Hash == null)
            {
                _transactionSigner.Sign(transaction);
            }

            if (await _transactionRepository.ContainsTransaction(transaction.Hash))
            {
                _logger.LogInformation($"The transaction \"{transaction.Hash?.ToString(true)}\" exists already on the blockchain.");
                return;
            }

            // Transaction is not added right away but queued to be verified and added.
            // It is the reason why we do not broadcast immediately.

            // TODO #374: It is a bit more complicated

            _transactionPool.Add(transaction);
            _logger.LogInformation($"Transaction with Hash {transaction.Hash?.ToString(true)} added to the TransactionPool.");
        }
Пример #3
0
        public void ExecuteTxesInSeveralBlocks(List <TransactionReceipt> txes)
        {
            txes = txes.OrderBy(x => x, new ReceiptComparer()).ToList();
            foreach (var tx in txes)
            {
                _transactionPool.Add(tx);
            }
            int total = 10;

            for (int it = 0; it < total; it++)
            {
                var remBlocks  = total - it;
                var txesToTake = txes.Count / remBlocks;
                var takenTxes  = _transactionPool.Peek(txesToTake, txesToTake, _blockManager.GetHeight() + 1);
                var block      = BuildNextBlock(takenTxes.ToArray());
                var result     = ExecuteBlock(block, takenTxes.ToArray());
                Assert.AreEqual(result, OperatingError.Ok);
                var executedBlock = _stateManager.LastApprovedSnapshot.Blocks.GetBlockByHeight(block.Header.Index);
                Assert.AreEqual(executedBlock !.TransactionHashes.Count, takenTxes.Count);

                // check if the txes are executed properly
                foreach (var tx in takenTxes)
                {
                    var executedTx = _stateManager.LastApprovedSnapshot.Transactions.GetTransactionByHash(tx.Hash);
                    Assert.AreNotEqual(null, executedTx, $"Transaction {tx.Hash.ToHex()} not found");
                    Assert.AreEqual(TransactionStatus.Executed, executedTx !.Status,
                                    "Transaction {tx.Hash.ToHex()} was not executed properly");
                }
            }
        }
Пример #4
0
        // Changed GetBalance to public
        public void TestGetBalancePending()
        {
            var tx = TestUtils.GetRandomTransaction(false);

            _stateManager.LastApprovedSnapshot.Balances.AddBalance(tx.Transaction.From, Money.Parse("1000"));
            var result = _transactionPool.Add(tx);

            Assert.AreEqual(OperatingError.Ok, result);

            var beforeBalance     = _stateManager.LastApprovedSnapshot.Balances.GetBalance(tx.Transaction.From);
            var sentValue         = new Money(tx.Transaction.Value);
            var afterBalance      = beforeBalance - sentValue - new Money(tx.Transaction.GasLimit * tx.Transaction.GasPrice);
            var knownAfterBalance = Money.Parse("1000") - sentValue - new Money(tx.Transaction.GasLimit * tx.Transaction.GasPrice);

            Assert.AreEqual(knownAfterBalance, afterBalance);
            var balance = _apiService.GetBalance(tx.Transaction.From.ToHex(), "pending");

            Assert.IsTrue(Web3DataFormatUtils.Web3Number(afterBalance.ToWei().ToUInt256()) == balance);
        }
Пример #5
0
        public uint HandleTransactionsFromPeer(IEnumerable <TransactionReceipt> transactions, ECDSAPublicKey publicKey)
        {
            lock (_txLock)
            {
                var txs = transactions.ToArray();
                Logger.LogTrace($"Received {txs.Length} transactions from peer {publicKey.ToHex()}");
                var persisted = 0u;
                foreach (var tx in txs)
                {
                    if (tx.Signature.IsZero())
                    {
                        Logger.LogTrace($"Received zero-signature transaction: {tx.Hash.ToHex()}");
                        if (_transactionPool.Add(tx, false) == OperatingError.Ok)
                        {
                            persisted++;
                        }
                        continue;
                    }

                    var error = _transactionManager.Verify(tx, HardforkHeights.IsHardfork_9Active(_blockManager.GetHeight() + 1));
                    if (error != OperatingError.Ok)
                    {
                        Logger.LogTrace($"Unable to verify transaction: {tx.Hash.ToHex()} ({error})");
                        continue;
                    }

                    error = _transactionPool.Add(tx, false);
                    if (error == OperatingError.Ok)
                    {
                        persisted++;
                    }
                    else
                    {
                        Logger.LogTrace($"Transaction {tx.Hash.ToHex()} not persisted: {error}");
                    }
                }

                lock (_peerHasTransactions)
                    Monitor.PulseAll(_peerHasTransactions);
                Logger.LogTrace($"Persisted {persisted} transactions from peer {publicKey.ToHex()}");
                return(persisted);
            }
        }
Пример #6
0
        public void Test_ContractDeployAndInvoke()
        {
            int blockNum = 1;

            TestContractDeployAndInvoke(blockNum);
            Assert.IsFalse(HardforkHeights.IsHardfork_9Active(_blockManager.GetHeight()));
            blockNum = (int)_blockManager.GetHeight() + 1;
            while (!HardforkHeights.IsHardfork_9Active((ulong)blockNum - 1))
            {
                GenerateBlock(blockNum);
                blockNum++;
            }

            var topUpTx = TopUpBalanceTx(_wallet.EcdsaKeyPair.PublicKey.GetAddress(), Money.Parse("10.0").ToUInt256(), 0, true);
            var error   = _transactionPool.Add(topUpTx);

            Assert.AreEqual(OperatingError.Ok, error, $"failed to add top up tx to pool: {error}");
            GenerateBlock(blockNum);
            blockNum++;
            TestContractDeployAndInvoke(blockNum);
        }
Пример #7
0
        /*
         * DeployContract
         * contract, string, contract bytecode as a hexstring
         * constructor params according to contract constructor
         */
        public string DeployContract(string[] arguments)
        {
            var from  = _keyPair.PublicKey.GetAddress();
            var nonce = _stateManager.LastApprovedSnapshot.Transactions.GetTotalTransactionCount(from);
            /* calculate contract hash */
            var hash = from.ToBytes().Concat(nonce.ToBytes()).Ripemd();

            Console.WriteLine("Contract Hash: " + hash.ToHex());
            var byteCode = arguments[1].HexToBytes();

            if (!VirtualMachine.VerifyContract(byteCode, true))
            {
                return("Unable to validate smart-contract code");
            }
            // TODO: use deploy abi if required
            var tx       = _transactionBuilder.DeployTransaction(from, byteCode);
            var signedTx = _transactionSigner.Sign(tx, _keyPair, HardforkHeights.IsHardfork_9Active(_blockManager.GetHeight() + 1));

            _transactionPool.Add(signedTx);
            return(signedTx.Hash.ToHex());
        }
Пример #8
0
        private void AddTxToPool(Transaction tx)
        {
            var useNewChainId =
                HardforkHeights.IsHardfork_9Active(_stateManager.LastApprovedSnapshot.Blocks.GetTotalBlockHeight() + 1);
            var receipt = _transactionSigner.Sign(tx, _privateWallet.EcdsaKeyPair, useNewChainId);

            _sendingTxHash = tx.FullHash(receipt.Signature, useNewChainId);
            var result = _transactionPool.Add(receipt);

            Logger.LogDebug(result == OperatingError.Ok
                ? $"Transaction successfully submitted: {receipt.Hash.ToHex()}"
                : $"Cannot add tx to pool: {result}");
        }
Пример #9
0
        //changed GetTransactionReceipt from private to public
        public void Test_GetTransactionReceipt()
        {
            _blockManager.TryBuildGenesisBlock();
            var tx = TestUtils.GetRandomTransaction(HardforkHeights.IsHardfork_9Active(1));

            _stateManager.LastApprovedSnapshot.Balances.AddBalance(tx.Transaction.From, Money.Parse("1000"));
            var result = _transactionPool.Add(tx);

            Assert.AreEqual(OperatingError.Ok, result);
            GenerateBlocks(1, 1);
            var txHashSent     = tx.Hash.ToHex();
            var txReceipt      = _apiService.GetTransactionReceipt(txHashSent);
            var txHashReceived = txReceipt !["transactionHash"] !.ToString();
Пример #10
0
        private void _Bench_Tx_Pool(
            ITransactionBuilder transactionBuilder,
            ITransactionSigner transactionSigner,
            EcdsaKeyPair keyPair)
        {
            const int txGenerate = 1000;
            const int txPerBlock = 1000;

            Logger.LogInformation($"Setting initial balance for the 'From' address");
            _stateManager.LastApprovedSnapshot.Balances.AddBalance(keyPair.PublicKey.GetAddress(),
                                                                   Money.Parse("200000"));

            var txReceipts = new List <TransactionReceipt>();

            for (int i = 0; i < txGenerate; i++)
            {
                var randomValue = new Random().Next(1, 100);
                var amount      = Money.Parse($"{randomValue}.0").ToUInt256();

                byte[] random = new byte[32];
                RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
                rng.GetBytes(random);

                var tx = new Transaction
                {
                    To       = random.Slice(0, 20).ToUInt160(),
                    From     = keyPair.PublicKey.GetAddress(),
                    GasPrice = (ulong)Money.Parse("0.0000001").ToWei(),
                    GasLimit = 100000000,
                    Nonce    = (ulong)i,
                    Value    = amount
                };
                txReceipts.Add(transactionSigner.Sign(tx, keyPair, true));
            }

            ITransactionPool transactionPool = _container.Resolve <ITransactionPool>();
            var watch = System.Diagnostics.Stopwatch.StartNew();

            foreach (var txr in txReceipts)
            {
                transactionPool.Add(txr, false);
            }
            watch.Stop();
            Console.WriteLine($"Time to Add {transactionPool.Transactions.Count} Tx to pool: {watch.ElapsedMilliseconds} ms");

            watch.Restart();
            var txs = transactionPool.Peek(txGenerate, txGenerate);

            watch.Stop();
            Console.WriteLine($"Time to Peek {txs.Count} Tx from pool: {watch.ElapsedMilliseconds} ms");
        }
Пример #11
0
        public void Run()
        {
            var cancellationToken = _cancellationTokenSource.Token;

            Task.Factory.StartNew(async() =>
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    if (!_unverifiedTransactionPool.Any())
                    {
                        await _asyncDelayer.Delay(DefaultTransactionPollingInterval, cancellationToken);
                        continue;
                    }

                    var unverifiedTransactionHashes = _unverifiedTransactionPool.Keys;
                    var transactionPool             = _verifiedTransactionPool.Concat(
                        _unverifiedTransactionPool.Values.Where(t => unverifiedTransactionHashes.Contains(t.Hash)))
                                                      .ToArray();

                    foreach (var transactionHash in unverifiedTransactionHashes)
                    {
                        if (!_unverifiedTransactionPool.TryGetValue(transactionHash, out var transaction))
                        {
                            continue;
                        }

                        var valid = this._transactionVerifier.Verify(transaction);

                        if (transactionPool
                            .Where(t => t.Hash != transactionHash)
                            .Where(p => p != transaction)
                            .SelectMany(p => p.Inputs)
                            .Intersect(transaction.Inputs)
                            .Any())
                        {
                            valid = false;
                        }

                        if (valid)
                        {
                            _verifiedTransactionPool.Add(transaction);
                        }

                        _unverifiedTransactionPool.TryRemove(transactionHash, out _);

                        OnTransactionProcessed?.Invoke(this, transaction);
                    }
                }
            }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
        }
Пример #12
0
        private JObject SendRawTransaction(string rawTransaction, string signature)
        {
            bool useNewChainId =
                HardforkHeights.IsHardfork_9Active(_stateManager.LastApprovedSnapshot.Blocks.GetTotalBlockHeight() + 1);
            var transaction = Transaction.Parser.ParseFrom(rawTransaction.HexToBytes());
            var s           = signature.HexToBytes().ToSignature(useNewChainId);
            var json        = new JObject {
                ["hash"] = transaction.FullHash(s, useNewChainId).ToHex()
            };
            var result = _transactionPool.Add(
                transaction, signature.HexToBytes().ToSignature(useNewChainId));

            if (result != OperatingError.Ok)
            {
                json["failed"] = true;
            }
            else
            {
                json["failed"] = false;
            }
            json["result"] = result.ToString();
            return(json);
        }
Пример #13
0
        public void Test_StakeSize()
        {
            _blockManager.TryBuildGenesisBlock();
            var systemContractReader = _container?.Resolve <ISystemContractReader>() ?? throw new Exception("Container is not loaded");
            var tx = TopUpBalanceTx(systemContractReader.NodeAddress(), Money.Parse("3000.0").ToUInt256(), 0,
                                    HardforkHeights.IsHardfork_9Active(1));

            Assert.AreEqual(OperatingError.Ok, _transactionPool.Add(tx));
            GenerateBlocks(1, 1);
            var balance      = _stateManager.CurrentSnapshot.Balances.GetBalance(systemContractReader.NodeAddress());
            var placeToStake = Money.Parse("2000.0");

            Assert.That(balance > placeToStake, "Not enough balance to make stake");
            _validatorStatusManager.StartWithStake(placeToStake.ToUInt256());
            Assert.That(_validatorStatusManager.IsStarted(), "Manager is not started");
            Assert.That(!_validatorStatusManager.IsWithdrawTriggered(), "Withdraw was triggered from the beggining");

            int blockNum = (int)_blockManager.GetHeight();

            Assert.IsFalse(HardforkHeights.IsHardfork_9Active((ulong)blockNum));
            while (!HardforkHeights.IsHardfork_9Active((ulong)blockNum))
            {
                blockNum++;
                GenerateBlocks(blockNum, blockNum);
            }

            var stake = new Money(systemContractReader.GetStake(systemContractReader.NodeAddress()));

            // TODO: fix it
//            Assert.That(stake == placeToStake, $"Stake is not as intended: {stake} != {placeToStake}");
            _validatorStatusManager.WithdrawStakeAndStop();
            Assert.That(_validatorStatusManager.IsStarted(), "Manager is stopped right after withdraw request");
            Assert.That(_validatorStatusManager.IsWithdrawTriggered(), "Withdraw is not triggered");
            // Test node is the only vaidator, so it is a next validator always
            // and it can't withdraw its stake.
            //    GenerateBlocks(50);
            //    Assert.That(!_validatorStatusManager.IsStarted(), "Manager is not stopped");
            //_validatorStatusManager.Stop();
        }
Пример #14
0
        // For every cycle, a new set of keys are required for the validators. This key generation process
        // is done on-chain. That means, every communication between participating nodes happen via transactions
        // in the block. For example, if node A wants to send a msg to node B, then node A encrypts the
        // msg with node B's public key and broadcast this as a transaction to the governance contract.
        // After this transaction is added to the chain, node B can decrypt the msg and read it.

        // During block execution, after every system transaction is executed, the following method
        // is invoked. It evaluates the transaction and if it's keygen related, it produces
        // appropriate response in form of a transaction and adds it to the pool for the addition
        // in the block.

        private void BlockManagerOnSystemContractInvoked(object _, InvocationContext context)
        {
            if (context.Receipt is null)
            {
                return;
            }
            var highestBlock    = _blockSynchronizer.GetHighestBlock();
            var willParticipate =
                !highestBlock.HasValue ||
                GovernanceContract.IsKeygenBlock(context.Receipt.Block) &&
                GovernanceContract.SameCycle(highestBlock.Value, context.Receipt.Block);

            if (!willParticipate)
            {
                Logger.LogInformation(
                    highestBlock != null
                        ? $"Will not participate in keygen: highest block is {highestBlock.Value}, call block is {context.Receipt.Block}"
                        : $"Will not participate in keygen: highest block is null, call block is {context.Receipt.Block}"
                    );
            }

            var tx = context.Receipt.Transaction;

            if (
                !tx.To.Equals(ContractRegisterer.GovernanceContract) &&
                !tx.To.Equals(ContractRegisterer.StakingContract)
                )
            {
                return;
            }
            if (context.Receipt.Block < _blockManager.GetHeight() &&
                !GovernanceContract.SameCycle(context.Receipt.Block, _blockManager.GetHeight()))
            {
                Logger.LogWarning(
                    $"System contract invoked from outdated tx: {context.Receipt.Hash}, tx block {context.Receipt.Block}, our height is {_blockManager.GetHeight()}");
                return;
            }

            if (tx.Invocation.Length < 4)
            {
                return;
            }

            var signature       = ContractEncoder.MethodSignatureAsInt(tx.Invocation);
            var decoder         = new ContractDecoder(tx.Invocation.ToArray());
            var contractAddress = tx.To;

            if (contractAddress.Equals(ContractRegisterer.GovernanceContract) && signature ==
                ContractEncoder.MethodSignatureAsInt(GovernanceInterface.MethodFinishCycle))
            {
                Logger.LogDebug("Aborting ongoing keygen because cycle was finished");
                _keyGenRepository.SaveKeyGenState(Array.Empty <byte>());
            }
            else if (signature == ContractEncoder.MethodSignatureAsInt(StakingInterface.MethodFinishVrfLottery))
            {
                Logger.LogDebug($"Detected call of StakingInterface.{StakingInterface.MethodFinishVrfLottery}");
                var cycle      = GovernanceContract.GetCycleByBlockNumber(context.Receipt.Block);
                var data       = new GovernanceContract(context).GetNextValidators();
                var publicKeys =
                    (data ?? throw new ArgumentException("Cannot parse method args"))
                    .Select(x => x.ToPublicKey())
                    .ToArray();
                Logger.LogDebug(
                    $"Keygen is started in cycle={cycle}, block={context.Receipt.Block} for validator set: {string.Join(",", publicKeys.Select(x => x.ToHex()))}"
                    );
                if (!publicKeys.Contains(_privateWallet.EcdsaKeyPair.PublicKey))
                {
                    Logger.LogWarning("Skipping validator change event since we are not new validator");
                    return;
                }

                var keygen = GetCurrentKeyGen();
                if (keygen != null && keygen.Cycle == cycle)
                {
                    throw new ArgumentException("Cannot start keygen, since one is already running");
                }

                if (keygen != null)
                {
                    Logger.LogWarning($"Aborted keygen for cycle {keygen.Cycle} to start keygen for cycle {cycle}");
                }

                _keyGenRepository.SaveKeyGenState(Array.Empty <byte>());

                var faulty = (publicKeys.Length - 1) / 3;
                keygen = new TrustlessKeygen(_privateWallet.EcdsaKeyPair, publicKeys, faulty, cycle);
                var commitTx = MakeCommitTransaction(keygen.StartKeygen(), cycle);
                Logger.LogTrace($"Produced commit tx with hash: {commitTx.Hash.ToHex()}");
                if (willParticipate)
                {
                    Logger.LogInformation($"Try to send KeyGen Commit transaction");
                    if (_transactionPool.Add(commitTx) is var error && error != OperatingError.Ok)
                    {
                        Logger.LogError($"Error creating commit transaction ({commitTx.Hash.ToHex()}): {error}");
                    }
                    else
                    {
                        Logger.LogInformation($"KeyGen Commit transaction sent");
                    }
                }

                Logger.LogDebug($"Saving keygen {keygen.ToBytes().ToHex()}");
                _keyGenRepository.SaveKeyGenState(keygen.ToBytes());
            }