예제 #1
0
        private void OnPrepareResponseReceived(ExtensiblePayload payload, PrepareResponse message)
        {
            if (message.ViewNumber != context.ViewNumber)
            {
                return;
            }
            if (context.PreparationPayloads[message.ValidatorIndex] != null || context.NotAcceptingPayloadsDueToViewChanging)
            {
                return;
            }
            if (context.PreparationPayloads[context.Block.PrimaryIndex] != null && !message.PreparationHash.Equals(context.PreparationPayloads[context.Block.PrimaryIndex].Hash))
            {
                return;
            }

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

            Log($"{nameof(OnPrepareResponseReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex}");
            context.PreparationPayloads[message.ValidatorIndex] = payload;
            if (context.WatchOnly || context.CommitSent)
            {
                return;
            }
            if (context.RequestSentOrReceived)
            {
                CheckPreparations();
            }
        }
예제 #2
0
 private PreparationPayloadCompact GetPreparationPayloadCompact(ExtensiblePayload payload)
 {
     return(new PreparationPayloadCompact
     {
         ValidatorIndex = GetMessage(payload).ValidatorIndex,
         InvocationScript = payload.Witness.InvocationScript
     });
 }
예제 #3
0
        private ExtensiblePayload MakeSignedPayload(ConsensusMessage message)
        {
            message.BlockIndex     = Block.Index;
            message.ValidatorIndex = (byte)MyIndex;
            message.ViewNumber     = ViewNumber;
            ExtensiblePayload payload = CreatePayload(message, null);

            SignPayload(payload);
            return(payload);
        }
예제 #4
0
        private ChangeViewPayloadCompact GetChangeViewPayloadCompact(ExtensiblePayload payload)
        {
            ChangeView message = GetMessage <ChangeView>(payload);

            return(new ChangeViewPayloadCompact
            {
                ValidatorIndex = message.ValidatorIndex,
                OriginalViewNumber = message.ViewNumber,
                Timestamp = message.Timestamp,
                InvocationScript = payload.Witness.InvocationScript
            });
        }
예제 #5
0
        private VerifyResult OnNewExtensiblePayload(ExtensiblePayload payload)
        {
            DataCache snapshot = system.StoreView;

            extensibleWitnessWhiteList ??= UpdateExtensibleWitnessWhiteList(snapshot);
            if (!payload.Verify(snapshot, extensibleWitnessWhiteList))
            {
                return(VerifyResult.Invalid);
            }
            system.RelayCache.Add(payload);
            return(VerifyResult.Succeed);
        }
예제 #6
0
        private CommitPayloadCompact GetCommitPayloadCompact(ExtensiblePayload payload)
        {
            Commit message = GetMessage <Commit>(payload);

            return(new CommitPayloadCompact
            {
                ViewNumber = message.ViewNumber,
                ValidatorIndex = message.ValidatorIndex,
                Signature = message.Signature,
                InvocationScript = payload.Witness.InvocationScript
            });
        }
예제 #7
0
 public ConsensusMessage GetMessage(ExtensiblePayload payload)
 {
     if (payload is null)
     {
         return(null);
     }
     if (!cachedMessages.TryGetValue(payload.Hash, out ConsensusMessage message))
     {
         cachedMessages.Add(payload.Hash, message = ConsensusMessage.DeserializeFrom(payload.Data));
     }
     return(message);
 }
예제 #8
0
        private bool ReverifyAndProcessPayload(ExtensiblePayload payload)
        {
            RelayResult relayResult = blockchain.Ask <RelayResult>(new Blockchain.Reverify {
                Inventories = new IInventory[] { payload }
            }).Result;

            if (relayResult.Result != VerifyResult.Succeed)
            {
                return(false);
            }
            OnConsensusPayload(payload);
            return(true);
        }
예제 #9
0
 private void CheckPreparations()
 {
     if (context.PreparationPayloads.Count(p => p != null) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p)))
     {
         ExtensiblePayload payload = context.MakeCommit();
         Log($"Sending {nameof(Commit)}");
         context.Save();
         localNode.Tell(new LocalNode.SendDirectly {
             Inventory = payload
         });
         // Set timer, so we will resend the commit in case of a networking issue
         ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock));
         CheckCommits();
     }
 }
        private void SignPayload(ExtensiblePayload payload)
        {
            ContractParametersContext sc;

            try
            {
                sc = new ContractParametersContext(neoSystem.StoreView, payload, dbftSettings.Network);
                wallet.Sign(sc);
            }
            catch (InvalidOperationException)
            {
                return;
            }
            payload.Witness = sc.GetWitnesses()[0];
        }
예제 #11
0
        public void Size_Get()
        {
            var test = new ExtensiblePayload()
            {
                Sender   = Array.Empty <byte>().ToScriptHash(),
                Category = "123",
                Data     = new byte[] { 1, 2, 3 },
                Witness  = new Witness()
                {
                    InvocationScript = new byte[] { 3, 5, 6 }, VerificationScript = Array.Empty <byte>()
                }
            };

            test.Size.Should().Be(42);
        }
예제 #12
0
        private void OnStatePayload(ExtensiblePayload payload)
        {
            if (payload.Data.Length == 0)
            {
                return;
            }
            if ((MessageType)payload.Data.Span[0] != MessageType.StateRoot)
            {
                return;
            }
            StateRoot message;

            try
            {
                message = payload.Data[1..].AsSerializable <StateRoot>();
예제 #13
0
        private void OnVoteMessage(ExtensiblePayload payload)
        {
            if (payload.Data.Length == 0)
            {
                return;
            }
            if ((MessageType)payload.Data.Span[0] != MessageType.Vote)
            {
                return;
            }
            Vote message;

            try
            {
                message = payload.Data[1..].AsSerializable <Vote>();
예제 #14
0
        private void SignPayload(ExtensiblePayload payload)
        {
            ContractParametersContext sc;

            try
            {
                sc = new ContractParametersContext(payload);
                wallet.Sign(sc);
            }
            catch (InvalidOperationException)
            {
                return;
            }
            payload.Witness = sc.GetWitnesses()[0];
        }
        private void SignPayload(ExtensiblePayload payload)
        {
            ContractParametersContext sc;

            try
            {
                sc = new ContractParametersContext(neoSystem.StoreView, payload, dbftSettings.Network);
                wallet.Sign(sc);
            }
            catch (InvalidOperationException exception)
            {
                Utility.Log(nameof(ConsensusContext), LogLevel.Debug, exception.ToString());
                return;
            }
            payload.Witness = sc.GetWitnesses()[0];
        }
예제 #16
0
        private IVerifiable deserializeContainer(dynamic args)
        {
            byte[]      serializedContainer = (byte[])args.buffer;
            string      typeIn = (string)args.type;
            IVerifiable container;

            ContainerType type;

            if (Enum.TryParse <ContainerType>(typeIn, out type))
            {
                switch (type)
                {
                case ContainerType.Block:
                    container = new Block();
                    break;

                case ContainerType.Header:
                    container = new Header();
                    break;

                case ContainerType.Transaction:
                    container = new Transaction();
                    break;

                case ContainerType.Signers:
                    container = new Signers();
                    break;

                case ContainerType.ExtensiblePayload:
                    container = new ExtensiblePayload();
                    break;

                default:
                    throw new ArgumentException($"{typeIn} is not a valid container type");
                }
                using (MemoryStream ms = new MemoryStream(serializedContainer))
                    using (BinaryReader reader = new BinaryReader(ms))
                    {
                        container.Deserialize(reader);
                    }

                return(container);
            }

            throw new ArgumentException($"{typeIn} is not a valid container type");
        }
예제 #17
0
        private void OnStatePayload(ExtensiblePayload payload)
        {
            StateRoot state_root = null;

            try
            {
                state_root = payload.Data?.AsSerializable <StateRoot>();
            }
            catch (Exception ex)
            {
                Utility.Log(nameof(StateStore), LogLevel.Warning, " invalid state root " + ex.Message);
                return;
            }
            if (state_root != null)
            {
                OnNewStateRoot(state_root);
            }
        }
예제 #18
0
        private void OnInventory(IInventory inventory, bool relay = true)
        {
            VerifyResult result = inventory switch
            {
                Block block => OnNewBlock(block),
                Transaction transaction => OnNewTransaction(transaction),
                ExtensiblePayload payload => OnNewExtensiblePayload(payload),
                _ => throw new NotSupportedException()
            };

            if (result == VerifyResult.Succeed && relay)
            {
                system.LocalNode.Tell(new LocalNode.RelayDirectly {
                    Inventory = inventory
                });
            }
            SendRelayResult(inventory, result);
        }
예제 #19
0
        private void OnStatePayload(ExtensiblePayload payload)
        {
            if (payload.Data.Length == 0)
            {
                return;
            }
            if ((MessageType)payload.Data[0] != MessageType.StateRoot)
            {
                return;
            }
            StateRoot message;

            try
            {
                message = payload.Data.AsSerializable <StateRoot>(1);
            }
            catch (FormatException)
            {
                return;
            }
            OnNewStateRoot(message);
        }
예제 #20
0
        public void DeserializeAndSerialize()
        {
            var test = new ExtensiblePayload()
            {
                Category        = "123",
                ValidBlockStart = 456,
                ValidBlockEnd   = 789,
                Sender          = Array.Empty <byte>().ToScriptHash(),
                Data            = new byte[] { 1, 2, 3 },
                Witness         = new Witness()
                {
                    InvocationScript = new byte[] { (byte)OpCode.PUSH1, (byte)OpCode.PUSH2, (byte)OpCode.PUSH3 }, VerificationScript = Array.Empty <byte>()
                }
            };
            var clone = test.ToArray().AsSerializable <ExtensiblePayload>();

            Assert.AreEqual(test.Sender, clone.Witness.ScriptHash);
            Assert.AreEqual(test.Hash, clone.Hash);
            Assert.AreEqual(test.ValidBlockStart, clone.ValidBlockStart);
            Assert.AreEqual(test.ValidBlockEnd, clone.ValidBlockEnd);
            Assert.AreEqual(test.Category, clone.Category);
        }
예제 #21
0
        private void OnVoteMessage(ExtensiblePayload payload)
        {
            if (payload.Data.Length == 0)
            {
                return;
            }
            if ((MessageType)payload.Data[0] != MessageType.Vote)
            {
                return;
            }
            Vote message;

            try
            {
                message = payload.Data.AsSerializable <Vote>(1);
            }
            catch (FormatException)
            {
                return;
            }
            OnStateRootVote(message);
        }
예제 #22
0
        private void OnChangeViewReceived(ExtensiblePayload payload, ChangeView message)
        {
            if (message.NewViewNumber <= context.ViewNumber)
            {
                OnRecoveryRequestReceived(payload, message);
            }

            if (context.CommitSent)
            {
                return;
            }

            var expectedView = context.GetMessage <ChangeView>(context.ChangeViewPayloads[message.ValidatorIndex])?.NewViewNumber ?? 0;

            if (message.NewViewNumber <= expectedView)
            {
                return;
            }

            Log($"{nameof(OnChangeViewReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex} nv={message.NewViewNumber} reason={message.Reason}");
            context.ChangeViewPayloads[message.ValidatorIndex] = payload;
            CheckExpectedView(message.NewViewNumber);
        }
예제 #23
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);
            }
        }
예제 #24
0
 public T GetMessage <T>(ExtensiblePayload payload) where T : ConsensusMessage
 {
     return((T)GetMessage(payload));
 }
예제 #25
0
        private void OnPrepareRequestReceived(ExtensiblePayload payload, PrepareRequest message)
        {
            if (context.RequestSentOrReceived || context.NotAcceptingPayloadsDueToViewChanging)
            {
                return;
            }
            if (message.ValidatorIndex != context.Block.PrimaryIndex || message.ViewNumber != context.ViewNumber)
            {
                return;
            }
            if (message.Version != context.Block.Version || message.PrevHash != context.Block.PrevHash)
            {
                return;
            }
            if (message.TransactionHashes.Length > neoSystem.Settings.MaxTransactionsPerBlock)
            {
                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 * neoSystem.Settings.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.Header.Timestamp = message.Timestamp;
            context.Block.Header.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().GetSignData(neoSystem.Settings.Network);
            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.Span, 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 = neoSystem.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash);
            List <Transaction> unverified = new List <Transaction>();

            foreach (UInt256 hash in context.TransactionHashes)
            {
                if (mempoolVerified.TryGetValue(hash, out Transaction tx))
                {
                    if (!AddTransaction(tx, false))
                    {
                        return;
                    }
                }
                else
                {
                    if (neoSystem.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)
                });
            }
        }
예제 #26
0
        private void OnConsensusPayload(ExtensiblePayload payload)
        {
            if (context.BlockSent)
            {
                return;
            }
            ConsensusMessage message;

            try
            {
                message = context.GetMessage(payload);
            }
            catch (Exception ex)
            {
                Utility.Log(nameof(ConsensusService), LogLevel.Debug, ex.ToString());
                return;
            }

            if (!message.Verify(neoSystem.Settings))
            {
                return;
            }
            if (message.BlockIndex != context.Block.Index)
            {
                if (context.Block.Index < message.BlockIndex)
                {
                    Log($"Chain is behind: expected={message.BlockIndex} current={context.Block.Index - 1}", LogLevel.Warning);
                }
                return;
            }
            if (message.ValidatorIndex >= context.Validators.Length)
            {
                return;
            }
            if (payload.Sender != Contract.CreateSignatureRedeemScript(context.Validators[message.ValidatorIndex]).ToScriptHash())
            {
                return;
            }
            context.LastSeenMessage[context.Validators[message.ValidatorIndex]] = message.BlockIndex;
            switch (message)
            {
            case PrepareRequest request:
                OnPrepareRequestReceived(payload, request);
                break;

            case PrepareResponse response:
                OnPrepareResponseReceived(payload, response);
                break;

            case ChangeView view:
                OnChangeViewReceived(payload, view);
                break;

            case Commit commit:
                OnCommitReceived(payload, commit);
                break;

            case RecoveryRequest request:
                OnRecoveryRequestReceived(payload, request);
                break;

            case RecoveryMessage recovery:
                OnRecoveryMessageReceived(recovery);
                break;
            }
        }
예제 #27
0
 private void OnCommitReceived(ExtensiblePayload payload, Commit commit)
 {
     ref ExtensiblePayload existingCommitPayload = ref context.CommitPayloads[commit.ValidatorIndex];