private void CheckAuthorizedAllOk(AuthState state)
        {
            if (state.GetIsAuthoringSuccess(_board))
            {
                if (state.Saving)
                {
                    return;
                }

                state.Saving = true;
                _            = Task.Run(() =>
                {
                    // do commit
                    var block            = state.InputMsg.Block;
                    block.Authorizations = state.OutputMsgs.Select(a => a.AuthSign).ToList();

                    if (block.BlockType != BlockTypes.Consolidation)
                    {
                        // pickup UIndex
                        try
                        {
                            block.UIndex = state.ConsensusUIndex;
                        }
                        catch (Exception ex)
                        {
                            _log.LogError("Can't get UIndex. System fail: " + ex.Message);
                            return;
                        }
                    }

                    if (block.UIndex != _UIndexSeed - 1)
                    {
                        // local node out of sync
                        _UIndexSeed = block.UIndex + 1;
                        Mode        = ConsensusWorkingMode.OutofSyncWaiting;
                        LyraSystem.Singleton.TheBlockchain.Tell(new BlockChain.NeedSync {
                            ToUIndex = block.UIndex
                        });
                    }

                    block.UHash = SignableObject.CalculateHash($"{block.UIndex}|{block.Index}|{block.Hash}");

                    BlockChain.Singleton.AddBlock(block);

                    var msg = new AuthorizerCommitMsg
                    {
                        From       = NodeService.Instance.PosWallet.AccountId,
                        MsgType    = ChatMessageType.AuthorizerCommit,
                        BlockHash  = state.InputMsg.Block.Hash,
                        BlockIndex = block.UIndex,
                        Commited   = true
                    };

                    state.AddCommitedResult(msg);
                    Send2P2pNetwork(msg);

                    _log.LogInformation($"Consensus: OnPrepare Commited: BlockUIndex: {msg.BlockHash}");
                });
            }
        }
        public ConsensusService(IActorRef localNode)
        {
            _localNode = localNode;
            _log       = new SimpleLogger("ConsensusService").Logger;

            _outOfOrderedMessages = new Dictionary <string, List <SourceSignedMessage> >();
            _activeConsensus      = new Dictionary <string, AuthState>();

            _authorizers = new AuthorizersFactory();
            while (BlockChain.Singleton == null)
            {
                Task.Delay(100).Wait();
            }

            _UIndexSeed = BlockChain.Singleton.GetBlockCount() + 1;
            Mode        = ConsensusWorkingMode.OutofSyncWaiting;

            Receive <Consolidate>((_) =>
            {
                _log.LogInformation("Doing Consolidate");
                OnNodeActive(NodeService.Instance.PosWallet.AccountId);     // update billboard

                if (Mode == ConsensusWorkingMode.Normal)
                {
                    BroadCastBillBoard();
                    GenerateConsolidateBlock();
                }
            });

            Receive <BillBoard>((bb) => {
                _board = bb;
            });

            Receive <AskForBillboard>((_) => Sender.Tell(_board));

            Receive <AuthorizingMsg>(async msg => {
                if (msg.Version != LyraGlobal.ProtocolVersion)
                {
                    Sender.Tell(null);
                }

                OnNodeActive(NodeService.Instance.PosWallet.AccountId);     // update billboard

                // first try auth locally
                var state           = CreateAuthringState(msg);
                var localAuthResult = LocalAuthorizingAsync(msg);
                state.AddAuthResult(localAuthResult);

                if (!localAuthResult.IsSuccess)
                {
                    state.Done.Set();
                    Sender.Tell(state);
                }
                else
                {
                    Send2P2pNetwork(msg);
                    Send2P2pNetwork(localAuthResult);

                    var sender = Context.Sender;

                    await Task.Run(() =>
                    {
                        _ = state.Done.WaitOne();
                    }).ConfigureAwait(false);

                    sender.Tell(state);
                }
            });

            Receive <SignedMessageRelay>(relayMsg =>
            {
                if (relayMsg.signedMessage.Version == LyraGlobal.ProtocolVersion)
                {
                    OnNextConsensusMessage(relayMsg.signedMessage);
                }
                else
                {
                    _log.LogWarning("Protocol Version Mismatch. Do nothing.");
                }
            });

            Receive <BlockChainSynced>(_ =>
            {
                Mode        = ConsensusWorkingMode.Normal;
                _UIndexSeed = BlockChain.Singleton.GetBlockCount() + 1;

                // declare to the network
                var msg = new ChatMsg
                {
                    From    = NodeService.Instance.PosWallet.AccountId,
                    MsgType = ChatMessageType.NodeUp,
                    Text    = "Staking with () Lyra"
                };

                Send2P2pNetwork(msg);
            });

            Task.Run(async() => {
                while (true)
                {
                    StateClean();
                    await Task.Delay(1000).ConfigureAwait(false);
                }
            });
        }
        private void GenerateConsolidateBlock()
        {
            var authGenesis = BlockChain.Singleton.GetLastServiceBlock();
            var lastCons    = BlockChain.Singleton.GetSyncBlock();
            var consBlock   = new ConsolidationBlock
            {
                UIndex       = _UIndexSeed++,
                NetworkId    = authGenesis.NetworkId,
                ShardId      = authGenesis.ShardId,
                ServiceHash  = authGenesis.Hash,
                SvcAccountID = NodeService.Instance.PosWallet.AccountId
            };

            // use merkle tree to consolidate all previous blocks, from lastCons.UIndex to consBlock.UIndex -1
            var mt = new MerkleTree();

            for (var ndx = lastCons.UIndex; ndx < consBlock.UIndex; ndx++)      // TODO: handling "losing" block here
            {
                var block = BlockChain.Singleton.GetBlockByUIndex(ndx);
                if (block == null)
                {
                    // block lost
                    _log.LogError($"Block lost for No. {ndx}. Try to resync with other seeds...");

                    // triggering a resync
                    Mode = ConsensusWorkingMode.OutofSyncWaiting;
                    LyraSystem.Singleton.TheBlockchain.Tell(new BlockChain.NeedSync {
                        ToUIndex = ndx
                    });

                    return;
                }
                var mhash = MerkleHash.Create(block.UHash);
                mt.AppendLeaf(mhash);
            }
            consBlock.MerkelTreeHash = mt.BuildTree().ToString();

            consBlock.InitializeBlock(lastCons, NodeService.Instance.PosWallet.PrivateKey,
                                      authGenesis.NetworkId, authGenesis.ShardId,
                                      NodeService.Instance.PosWallet.AccountId);
            //consBlock.UHash = SignableObject.CalculateHash($"{consBlock.UIndex}|{consBlock.Index}|{consBlock.Hash}");
            //consBlock.Authorizations = new List<AuthorizationSignature>();
            //consBlock.Authorizations.Add(new AuthorizationSignature
            //{
            //    Key = NodeService.Instance.PosWallet.AccountId,
            //    Signature = Signatures.GetSignature(NodeService.Instance.PosWallet.PrivateKey, consBlock.Hash + consBlock.ServiceHash, NodeService.Instance.PosWallet.AccountId)
            //});

            //BlockChain.Singleton.AddBlock(consBlock);

            //// broadcast to whole network
            //var msg = new ChatMsg(NodeService.Instance.PosWallet.AccountId, JsonConvert.SerializeObject(consBlock));
            //msg.MsgType = ChatMessageType.BlockConsolidation;
            //Send2P2pNetwork(msg);

            // no, we do consensus
            AuthorizingMsg msg = new AuthorizingMsg
            {
                From    = NodeService.Instance.PosWallet.AccountId,
                Block   = consBlock,
                MsgType = ChatMessageType.AuthorizerPrePrepare
            };

            var state           = CreateAuthringState(msg);
            var localAuthResult = LocalAuthorizingAsync(msg);

            state.AddAuthResult(localAuthResult);

            if (!localAuthResult.IsSuccess)
            {
                _log.LogError("Fatal Error: Consolidate block local authorization failed.");
            }
            else
            {
                Send2P2pNetwork(msg);
                Send2P2pNetwork(localAuthResult);
            }
        }