Exemplo n.º 1
0
 private static ConsensusPayload MakeSignedPayload(ConsensusContext context, ConsensusMessage message, ushort validatorIndex, byte[] witnessInvocationScript)
 {
     return(new ConsensusPayload
     {
         Version = context.Block.Version,
         PrevHash = context.Block.PrevHash,
         BlockIndex = context.Block.Index,
         ValidatorIndex = validatorIndex,
         ConsensusMessage = message,
         Witness = new Witness
         {
             InvocationScript = witnessInvocationScript,
             VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[validatorIndex])
         }
     });
 }
Exemplo n.º 2
0
        private Block SignBlock(ConsensusContext context)
        {
            context.Block.MerkleRoot = null;

            // Fake commits

            for (int x = 0; x < _validatorKeys.Length; x++)
            {
                _context.MyIndex = x;

                var com = _context.MakeCommit();
                _context.CommitPayloads[_context.MyIndex] = com;
            }

            return(context.CreateBlock());
        }
Exemplo n.º 3
0
        private void EnsureContext(ConsensusContext context, params Transaction[] expected)
        {
            // Check all tx

            Assert.AreEqual(expected.Length, context.Transactions.Count);
            Assert.IsTrue(expected.All(tx => context.Transactions.ContainsKey(tx.Hash)));

            Assert.AreEqual(expected.Length, context.TransactionHashes.Length);
            Assert.IsTrue(expected.All(tx => context.TransactionHashes.Count(t => t == tx.Hash) == 1));

            // Ensure length

            var block = SignBlock(context);

            Assert.AreEqual(context.GetExpectedBlockSize(), block.Size);
            Assert.IsTrue(block.Size < NativeContract.Policy.GetMaxBlockSize(context.Snapshot));
        }
Exemplo n.º 4
0
        Block RunConsensus()
        {
            if (chain.ConsensusNodes.Count == 1)
            {
                var ctx = new ConsensusContext(nodeWallet, Blockchain.Singleton.Store);
                ctx.Reset(0);
                ctx.MakePrepareRequest();
                ctx.MakeCommit();
                return(ctx.CreateBlock());
            }

            // create ConsensusContext for each ConsensusNode
            var contexts = new ConsensusContext[chain.ConsensusNodes.Count];

            for (int x = 0; x < contexts.Length; x++)
            {
                contexts[x] = new ConsensusContext(DevWallet.FromExpressWallet(chain.ConsensusNodes[x].Wallet), Blockchain.Singleton.Store);
                contexts[x].Reset(0);
            }

            // find the primary node for this consensus round
            var primary = contexts.Single(c => c.IsPrimary);
            var prepareRequestPayload = primary.MakePrepareRequest();

            for (int x = 0; x < contexts.Length; x++)
            {
                var context = contexts[x];
                if (context.MyIndex == primary.MyIndex)
                {
                    continue;
                }
                var prepareRequestMessage = context.GetMessage <PrepareRequest>(prepareRequestPayload);
                OnPrepareRequestReceived(context, prepareRequestPayload, prepareRequestMessage);
                var commitPayload = context.MakeCommit();
                var commitMessage = primary.GetMessage <Commit>(commitPayload);
                OnCommitReceived(primary, commitPayload, commitMessage);
            }

            return(primary.CreateBlock());
        }
Exemplo n.º 5
0
        public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart()
        {
            TestProbe subscriber = CreateTestProbe();
            var       mockWallet = new Mock <Wallet>();

            mockWallet.Setup(p => p.GetAccount(It.IsAny <UInt160>())).Returns <UInt160>(p => new TestWalletAccount(p));
            ConsensusContext context = new ConsensusContext(mockWallet.Object, TestBlockchain.GetStore());

            int timeIndex  = 0;
            var timeValues = new[] {
                //new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc), // For tests here
                new DateTime(1968, 06, 01, 0, 0, 1, DateTimeKind.Utc),                               // For receiving block
                new DateTime(1968, 06, 01, 0, 0, (int)Blockchain.SecondsPerBlock, DateTimeKind.Utc), // For Initialize
                new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc),                              // unused
                new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc)                               // unused
            };

            //TimeProvider.Current.UtcNow.ToTimestamp().Should().Be(4244941711); //1968-06-01 00:00:15

            Console.WriteLine($"time 0: {timeValues[0].ToString()} 1: {timeValues[1].ToString()} 2: {timeValues[2].ToString()} 3: {timeValues[3].ToString()}");

            var timeMock = new Mock <TimeProvider>();

            timeMock.SetupGet(tp => tp.UtcNow).Returns(() => timeValues[timeIndex])
            .Callback(() => timeIndex++);
            //new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc));
            TimeProvider.Current = timeMock.Object;

            //public void Log(string message, LogLevel level)
            // TODO: create ILogPlugin for Tests

            /*
             * mockConsensusContext.Setup(mr => mr.Log(It.IsAny<string>(), It.IsAny<LogLevel>()))
             *           .Callback((string message, LogLevel level) => {
             *                           Console.WriteLine($"CONSENSUS LOG: {message}");
             *                                                     }
             *                    );
             */

            // Creating proposed block
            Header header = new Header();

            TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal);
            header.Size.Should().Be(101);

            Console.WriteLine($"header {header} hash {header.Hash} timstamp {timestampVal}");

            timestampVal.Should().Be(4244941696); //1968-06-01 00:00:00
                                                  // check basic ConsensusContext
                                                  //mockConsensusContext.Object.block_received_time.ToTimestamp().Should().Be(4244941697); //1968-06-01 00:00:01

            // ============================================================================
            //                      creating ConsensusService actor
            // ============================================================================

            TestActorRef <ConsensusService> actorConsensus = ActorOfAsTestActorRef <ConsensusService>(
                Akka.Actor.Props.Create(() => (ConsensusService)Activator.CreateInstance(typeof(ConsensusService), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { subscriber, subscriber, context }, null))
                );

            Console.WriteLine("will trigger OnPersistCompleted!");
            actorConsensus.Tell(new Blockchain.PersistCompleted
            {
                Block = new Block
                {
                    Version       = header.Version,
                    PrevHash      = header.PrevHash,
                    MerkleRoot    = header.MerkleRoot,
                    Timestamp     = header.Timestamp,
                    Index         = header.Index,
                    NextConsensus = header.NextConsensus
                }
            });

            // OnPersist will not launch timer, we need OnStart

            Console.WriteLine("will start consensus!");
            actorConsensus.Tell(new ConsensusService.Start
            {
                IgnoreRecoveryLogs = true
            });

            Console.WriteLine("OnTimer should expire!");
            Console.WriteLine("Waiting for subscriber message!");
            // Timer should expire in one second (block_received_time at :01, initialized at :02)

            var answer = subscriber.ExpectMsg <LocalNode.SendDirectly>();

            Console.WriteLine($"MESSAGE 1: {answer}");
            //var answer2 = subscriber.ExpectMsg<LocalNode.SendDirectly>(); // expects to fail!

            // ============================================================================
            //                      finalize ConsensusService actor
            // ============================================================================

            //Thread.Sleep(4000);
            Sys.Stop(actorConsensus);
            TimeProvider.ResetToDefault();

            Assert.AreEqual(1, 1);
        }
Exemplo n.º 6
0
        public void TestSerializeAndDeserializeConsensusContext()
        {
            var consensusContext = new ConsensusContext(null, null)
            {
                Block = new Block
                {
                    PrevHash      = Blockchain.GenesisBlock.Hash,
                    Index         = 1,
                    Timestamp     = 4244941711,
                    NextConsensus = UInt160.Parse("5555AAAA5555AAAA5555AAAA5555AAAA5555AAAA"),
                    ConsensusData = new ConsensusData
                    {
                        PrimaryIndex = 6
                    }
                },
                ViewNumber = 2,
                Validators = new ECPoint[7]
                {
                    ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", Cryptography.ECC.ECCurve.Secp256r1),
                    ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", Cryptography.ECC.ECCurve.Secp256r1),
                    ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", Cryptography.ECC.ECCurve.Secp256r1),
                    ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", Cryptography.ECC.ECCurve.Secp256r1),
                    ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", Cryptography.ECC.ECCurve.Secp256r1),
                    ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", Cryptography.ECC.ECCurve.Secp256r1),
                    ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", Cryptography.ECC.ECCurve.Secp256r1)
                },
                MyIndex = -1
            };
            var testTx1 = TestUtils.CreateRandomHashTransaction();
            var testTx2 = TestUtils.CreateRandomHashTransaction();

            int txCountToInlcude = 256;

            consensusContext.TransactionHashes = new UInt256[txCountToInlcude];

            Transaction[] txs = new Transaction[txCountToInlcude];
            for (int i = 0; i < txCountToInlcude; i++)
            {
                txs[i] = TestUtils.CreateRandomHashTransaction();
                consensusContext.TransactionHashes[i] = txs[i].Hash;
            }
            // consensusContext.TransactionHashes = new UInt256[2] {testTx1.Hash, testTx2.Hash};
            consensusContext.Transactions = txs.ToDictionary(p => p.Hash);

            consensusContext.PreparationPayloads = new ConsensusPayload[consensusContext.Validators.Length];
            var prepareRequestMessage = new PrepareRequest
            {
                TransactionHashes = consensusContext.TransactionHashes,
                Timestamp         = 23
            };

            consensusContext.PreparationPayloads[6] = MakeSignedPayload(consensusContext, prepareRequestMessage, 6, new[] { (byte)'3', (byte)'!' });
            consensusContext.PreparationPayloads[0] = MakeSignedPayload(consensusContext, new PrepareResponse {
                PreparationHash = consensusContext.PreparationPayloads[6].Hash
            }, 0, new[] { (byte)'t', (byte)'e' });
            consensusContext.PreparationPayloads[1] = MakeSignedPayload(consensusContext, new PrepareResponse {
                PreparationHash = consensusContext.PreparationPayloads[6].Hash
            }, 1, new[] { (byte)'s', (byte)'t' });
            consensusContext.PreparationPayloads[2] = null;
            consensusContext.PreparationPayloads[3] = MakeSignedPayload(consensusContext, new PrepareResponse {
                PreparationHash = consensusContext.PreparationPayloads[6].Hash
            }, 3, new[] { (byte)'1', (byte)'2' });
            consensusContext.PreparationPayloads[4] = null;
            consensusContext.PreparationPayloads[5] = null;

            consensusContext.CommitPayloads = new ConsensusPayload[consensusContext.Validators.Length];
            using (SHA256 sha256 = SHA256.Create())
            {
                consensusContext.CommitPayloads[3] = MakeSignedPayload(consensusContext, new Commit {
                    Signature = sha256.ComputeHash(testTx1.Hash.ToArray())
                }, 3, new[] { (byte)'3', (byte)'4' });
                consensusContext.CommitPayloads[6] = MakeSignedPayload(consensusContext, new Commit {
                    Signature = sha256.ComputeHash(testTx2.Hash.ToArray())
                }, 3, new[] { (byte)'6', (byte)'7' });
            }

            consensusContext.Block.Timestamp = TimeProvider.Current.UtcNow.ToTimestamp();

            consensusContext.ChangeViewPayloads    = new ConsensusPayload[consensusContext.Validators.Length];
            consensusContext.ChangeViewPayloads[0] = MakeSignedPayload(consensusContext, new ChangeView {
                ViewNumber = 1, Timestamp = 6
            }, 0, new[] { (byte)'A' });
            consensusContext.ChangeViewPayloads[1] = MakeSignedPayload(consensusContext, new ChangeView {
                ViewNumber = 1, Timestamp = 5
            }, 1, new[] { (byte)'B' });
            consensusContext.ChangeViewPayloads[2] = null;
            consensusContext.ChangeViewPayloads[3] = MakeSignedPayload(consensusContext, new ChangeView {
                ViewNumber = 1, Timestamp = uint.MaxValue
            }, 3, new[] { (byte)'C' });
            consensusContext.ChangeViewPayloads[4] = null;
            consensusContext.ChangeViewPayloads[5] = null;
            consensusContext.ChangeViewPayloads[6] = MakeSignedPayload(consensusContext, new ChangeView {
                ViewNumber = 1, Timestamp = 1
            }, 6, new[] { (byte)'D' });

            consensusContext.LastChangeViewPayloads = new ConsensusPayload[consensusContext.Validators.Length];

            var copiedContext = TestUtils.CopyMsgBySerialization(consensusContext, new ConsensusContext(null, null));

            copiedContext.Block.PrevHash.Should().Be(consensusContext.Block.PrevHash);
            copiedContext.Block.Index.Should().Be(consensusContext.Block.Index);
            copiedContext.ViewNumber.Should().Be(consensusContext.ViewNumber);
            copiedContext.Validators.ShouldAllBeEquivalentTo(consensusContext.Validators);
            copiedContext.MyIndex.Should().Be(consensusContext.MyIndex);
            copiedContext.Block.ConsensusData.PrimaryIndex.Should().Be(consensusContext.Block.ConsensusData.PrimaryIndex);
            copiedContext.Block.Timestamp.Should().Be(consensusContext.Block.Timestamp);
            copiedContext.Block.NextConsensus.Should().Be(consensusContext.Block.NextConsensus);
            copiedContext.TransactionHashes.ShouldAllBeEquivalentTo(consensusContext.TransactionHashes);
            copiedContext.Transactions.ShouldAllBeEquivalentTo(consensusContext.Transactions);
            copiedContext.Transactions.Values.ShouldAllBeEquivalentTo(consensusContext.Transactions.Values);
            copiedContext.PreparationPayloads.ShouldAllBeEquivalentTo(consensusContext.PreparationPayloads);
            copiedContext.CommitPayloads.ShouldAllBeEquivalentTo(consensusContext.CommitPayloads);
            copiedContext.ChangeViewPayloads.ShouldAllBeEquivalentTo(consensusContext.ChangeViewPayloads);
        }
Exemplo n.º 7
0
        // TODO: remove if https://github.com/neo-project/neo/issues/2061 is fixed
        // this logic is lifted from ConsensusService.OnPrepareRequestReceived
        // Log, Timer, Task and CheckPrepare logic has been commented out for offline consensus
        private static void OnPrepareRequestReceived(ConsensusContext context, ExtensiblePayload payload, PrepareRequest message)
        {
            if (context.RequestSentOrReceived || context.NotAcceptingPayloadsDueToViewChanging)
            {
                return;
            }
            if (message.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex || message.ViewNumber != context.ViewNumber)
            {
                return;
            }
            if (message.Version != context.Block.Version || message.PrevHash != context.Block.PrevHash)
            {
                return;
            }
            // Log($"{nameof(OnPrepareRequestReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex} tx={message.TransactionHashes.Length}");
            if (message.Timestamp <= context.PrevHeader.Timestamp || message.Timestamp > TimeProvider.Current.UtcNow.AddMilliseconds(8 * Blockchain.MillisecondsPerBlock).ToTimestampMS())
            {
                // Log($"Timestamp incorrect: {message.Timestamp}", LogLevel.Warning);
                return;
            }
            if (message.TransactionHashes.Any(p => NativeContract.Ledger.ContainsTransaction(context.Snapshot, p)))
            {
                // Log($"Invalid request: transaction already exists", LogLevel.Warning);
                return;
            }

            // Timeout extension: prepare request has been received with success
            // around 2*15/M=30.0/5 ~ 40% block time (for M=5)
            // ExtendTimerByFactor(2);

            context.Block.Timestamp           = message.Timestamp;
            context.Block.ConsensusData.Nonce = message.Nonce;
            context.TransactionHashes         = message.TransactionHashes;
            context.Transactions        = new Dictionary <UInt256, Transaction>();
            context.VerificationContext = new TransactionVerificationContext();
            for (int i = 0; i < context.PreparationPayloads.Length; i++)
            {
                if (context.PreparationPayloads[i] != null)
                {
                    if (!context.GetMessage <PrepareResponse>(context.PreparationPayloads[i]).PreparationHash.Equals(payload.Hash))
                    {
                        context.PreparationPayloads[i] = null;
                    }
                }
            }
            context.PreparationPayloads[message.ValidatorIndex] = payload;
            byte[] hashData = context.EnsureHeader().GetHashData();
            for (int i = 0; i < context.CommitPayloads.Length; i++)
            {
                if (context.GetMessage(context.CommitPayloads[i])?.ViewNumber == context.ViewNumber)
                {
                    if (!Crypto.VerifySignature(hashData, context.GetMessage <Commit>(context.CommitPayloads[i]).Signature, context.Validators[i]))
                    {
                        context.CommitPayloads[i] = null;
                    }
                }
            }

            if (context.TransactionHashes.Length == 0)
            {
                // There are no tx so we should act like if all the transactions were filled
                // CheckPrepareResponse();
                return;
            }

            Dictionary <UInt256, Transaction> mempoolVerified = Blockchain.Singleton.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash);
            List <Transaction> unverified = new List <Transaction>();

            foreach (UInt256 hash in context.TransactionHashes)
            {
                if (mempoolVerified.TryGetValue(hash, out var tx))
                {
                    if (!AddTransaction(tx, false))
                    {
                        return;
                    }
                }
                else
                {
                    if (Blockchain.Singleton.MemPool.TryGetValue(hash, out tx))
                    {
                        unverified.Add(tx);
                    }
                }
            }
            foreach (Transaction tx in unverified)
            {
                if (!AddTransaction(tx, true))
                {
                    return;
                }
            }
            if (context.Transactions.Count < context.TransactionHashes.Length)
            {
                UInt256[] hashes = context.TransactionHashes.Where(i => !context.Transactions.ContainsKey(i)).ToArray();
                // taskManager.Tell(new TaskManager.RestartTasks
                // {
                //     Payload = InvPayload.Create(InventoryType.TX, hashes)
                // });
            }

            bool AddTransaction(Transaction tx, bool verify)
            {
                if (verify)
                {
                    VerifyResult result = tx.Verify(context.Snapshot, context.VerificationContext);
                    if (result != VerifyResult.Succeed)
                    {
                        // Log($"Rejected tx: {tx.Hash}, {result}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
                        // RequestChangeView(result == VerifyResult.PolicyFail ? ChangeViewReason.TxRejectedByPolicy : ChangeViewReason.TxInvalid);
                        return(false);
                    }
                }
                context.Transactions[tx.Hash] = tx;
                context.VerificationContext.AddTransaction(tx);
                return(CheckPrepareResponse());
            }

            bool CheckPrepareResponse()
            {
                if (context.TransactionHashes.Length == context.Transactions.Count)
                {
                    // if we are the primary for this view, but acting as a backup because we recovered our own
                    // previously sent prepare request, then we don't want to send a prepare response.
                    if (context.IsPrimary || context.WatchOnly)
                    {
                        return(true);
                    }

                    // TODO: Replace reflection after https://github.com/neo-project/neo-modules/pull/503 is merged
                    // Check maximum block size via Native Contract policy
                    var expectedBlockSize = (int)(getExpectedBlockSizeMethodInfo.Value.Invoke(context, Array.Empty <object>())) !;
                    if (expectedBlockSize > NativeContract.Policy.GetMaxBlockSize(context.Snapshot))
                    {
                        // Log($"Rejected block: {context.Block.Index} The size exceed the policy", LogLevel.Warning);
                        // RequestChangeView(ChangeViewReason.BlockRejectedByPolicy);
                        return(false);
                    }

                    // TODO: Replace duplicate code for calculating blockSystemFee after https://github.com/neo-project/neo-modules/pull/503 is merged
                    // Check maximum block system fee via Native Contract policy
                    var blockSystemFee = context.Transactions.Values.Sum(u => u.SystemFee);
                    if (blockSystemFee > NativeContract.Policy.GetMaxBlockSystemFee(context.Snapshot))
                    {
                        // Log($"Rejected block: {context.Block.Index} The system fee exceed the policy", LogLevel.Warning);
                        // RequestChangeView(ChangeViewReason.BlockRejectedByPolicy);
                        return(false);
                    }

                    // Timeout extension due to prepare response sent
                    // around 2*15/M=30.0/5 ~ 40% block time (for M=5)
                    // ExtendTimerByFactor(2);

                    // Log($"Sending {nameof(PrepareResponse)}");
                    // localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareResponse() });
                    // CheckPreparations();
                }
                return(true);
            }
        }