Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Source Code Analysis of NEO DBFT consensus algorithm

2025-03-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly introduces the "NEO DBFT consensus algorithm source code analysis" related knowledge, the editor through the actual case to show you the operation process, the method of operation is simple and fast, practical, I hope that this "NEO DBFT consensus algorithm source code analysis" article can help you solve the problem.

NEO DBFT consensus algorithm

Dbft improves the self-algorithm pbft algorithm, pbft algorithm through multiple network requests to confirm, and finally get the majority consensus. The disadvantage is that with the increase of nodes, the network overhead increases exponentially, and the network communication expands, so it is difficult to reach an agreement quickly. The solution of neo is to select a certain number of nodes as bookkeepers by voting, thus reducing network consumption and taking into account the transaction speed, which is also the origin of d in dbft.

Code structure description ├── Consensus │ ├── ChangeView.cs / / viewchange message │ ├── ConsensusContext.cs / / consensus context │ ├── ConsensusMessage.cs / / consensus message │ ├── ConsensusMessageType.cs / / consensus message type ChangeView/PrepareRequest/PrepareResponse │ ├── ConsensusService.cs / / consensus core code │ ├── ConsensusState.cs / / Node consensus status │ ├── PrepareRequest.cs / / request message │ └── PrepareResponse.cs / / signature return message consensus status change process

1: the nodes that open the consensus can be divided into two categories: non-bookkeeper and bookkeeper node, non-bookkeeper does not participate in consensus, and bookkeeper participates in the consensus process.

2: select Speaker, Neo Speaker selection mechanism is based on the current block height and the number of bookkeepers to do MOD calculation, Speaker is actually elected sequentially

3: the node is initialized, the speaker is the primary node and the congressman is the backup node.

4: the Speaker sends PrepareRequest when the block condition is met.

5: after receiving the request, the congressman verifies that the PrepareResponse is sent by signature.

6: after the accounting node receives the PrepareResponse, the node saves the signature information of the other party, and checks that if it exceeds 2/3, it will send block

7: the node is reinitialized after receiving the block,PersistCompleted event trigger

Consensus context core member public const uint Version = 0; public ConsensusState State; / / Node current consensus state public UInt256 PrevHash; public uint BlockIndex; / / Block height public byte ViewNumber; / / attempt status public ECPoint [] Validators; / / bookkeeper public int MyIndex / / current bookkeeping sequence public uint PrimaryIndex; / / current bookkeeper public uint Timest public ulong Nonce; public UInt160 NextConsensus; / / consensus logo public UInt256 [] TransactionHashes; public Dictionary Transactions; public byte [] Signatures; / / bookkeeper signature public byte [] ExpectedView / / bookkeeper attempted public KeyPair KeyPair; public int M = > Validators.Length-(Validators.Length-1) / 3; / / 2/3 quantity

ExpectedView maintains the view state, which is used to re-initiate a new round of consensus when the speaker does not work properly.

Signatures is used to maintain the confirmation status during the consensus process.

Node consensus status [Flags] internal enum ConsensusState: byte {Initial = 0x00, / / 0 Primary = 0x01, / / 1 Backup = 0x02, / / 10 RequestSent = 0x04, / / 100 RequestReceived = 0x08, / / 1000 SignatureSent = 0x10, / / 10000 BlockSent = 0x20 / / 100000 ViewChanging = 0x40, / / 1000000} proxy node selection

The delegate part of neo's dbft algorithm is mainly reflected in a function, which reorders assets after each block is generated, and selects the nodes of the number of former bookkeepers to participate in the next round of consensus.

/ get the bookkeeper list of the next block / return a set of public keys Represents the bookkeeper list of the next block public ECPoint [] GetValidators () {lock (_ validators) {if (_ validators.Count = = 0) {_ validators.AddRange (GetValidators (Enumerable.Empty () } return _ validators.ToArray ();}} public virtual IEnumerable GetValidators (IEnumerable others) {DataCache accounts = GetStates (); DataCache validators = GetStates (); MetaDataCache validators_count = GetMetaData () / / update account assets foreach (Transaction tx in others) {foreach (TransactionOutput output in tx.Outputs) {AccountState account = accounts.GetAndChange (output.ScriptHash, () = > new AccountState (output.ScriptHash)) If (account.Balances.ContainsKey (output.AssetId)) account.Balances [output.AssetId] + = output.Value; else account.Balances [output.AssetId] = output.Value If (output.AssetId.Equals (GoverningToken.Hash) & & account.Votes.Length > 0) {foreach (ECPoint pubkey in account.Votes) validators.GetAndChange (pubkey,) = > new ValidatorState (pubkey). Votes + = output.Value Validators_count.GetAndChange () .Votes [account.Votes.Length-1] + = output.Value;}} foreach (var group in tx.Inputs.GroupBy (p = > p.PrevHash)) {Transaction tx_prev = GetTransaction (group.Key, out int height) Foreach (CoinReference input in group) {TransactionOutput out_prev = tx_ output [input.PrevIndex]; AccountState account = accounts.GetAndChange (out_prev.ScriptHash) If (out_prev.AssetId.Equals (GoverningToken.Hash)) {if (account.Votes.Length > 0) {foreach (ECPoint pubkey in account.Votes) {ValidatorState validator = validators.GetAndChange (pubkey) Validator.Votes-= out_prev.Value; if (! validator.Registered & & validator.Votes.Equals (Fixed8.Zero)) validators.Delete (pubkey) } validators_count.GetAndChange () .Votes [account.Votes.Length-1]-= out_prev.Value;}} account.balances [out _ prev.AssetId]-= out_prev.Value }} switch (tx) {# pragma warning disable CS0612 case EnrollmentTransaction tx_enrollment: validators.GetAndChange (tx_enrollment.PublicKey, () = > new ValidatorState (tx_enrollment.PublicKey)) .Registered = true; break # pragma warning restore CS0612 case StateTransaction tx_state: foreach (StateDescriptor descriptor in tx_state.Descriptors) switch (descriptor.Type) {case StateType.Account: ProcessAccountStateDescriptor (descriptor, accounts, validators, validators_count) Break; case StateType.Validator: ProcessValidatorStateDescriptor (descriptor, validators); break;} break Sort int count = (int) validators_count.Get (). Votes.Select ((p, I) = > new {Count = I, Votes = p}) .Where (p = > p.Votes > Fixed8.Zero) .ToArray (). WeightedFilter (0.25,0.75) P = > p.Votes.GetData (), (p, w) = > new {p.Count, Weight = w}) .Weightedaverage (p = > p.Count, p = > p.Weight) Count = Math.Max (count, StandbyValidators.Length); HashSet sv = new HashSet (StandbyValidators); ECPoint [] pubkeys = validators.Find (). Select (p = > p.Value) .Where (p = > (p.Registered & & p.Votes > Fixed8.Zero) | | sv.Contains (p.PublicKey)) .OrderByDescending (p = > p.Votes) .ThenBy (p = > p.PublicKey). Select (p = > p.PublicKey). Take (count). ToArray () IEnumerable result; if (pubkeys.Length = = count) {result = pubkeys;} else {HashSet hashSet = new HashSet (pubkeys); for (int I = 0; I

< StandbyValidators.Length && hashSet.Count < count; i++) hashSet.Add(StandbyValidators[i]); result = hashSet; } return result.OrderBy(p =>

P);} Speaker's Choice

PrimaryIndex is set when the consensus state is initialized, and the current speaker is informed. The principle is a simple MOD operation. There are two cases here. If the node is normal, the block height and the number of bookkeepers can be calculated directly by mod. If there is a situation, it needs to be adjusted according to view_number.

/ / file / Consensus/ConsensusService.cs InitializeConsensus method if (view_number = = 0) context.Reset (wallet); else context.ChangeView (view_number); / / file / Consensus/ConsensusContext.cs public void ChangeView (byte view_number) {int p = ((int) BlockIndex-view_number)% Validators.Length; State & = ConsensusState.SignatureSent; ViewNumber = view_number; PrimaryIndex = p > = 0? (uint) p: (uint) (p + Validators.Length); / / current bookkeeper if (State = = ConsensusState.Initial) {TransactionHashes = null; Signatures = new byte [Validators.Length] [];} ExpectedView [MyIndex] = view_number; _ header = null;} / / file / Consensus/ConsensusContext.cs public void Reset (Wallet wallet) {State = ConsensusState.Initial PrevHash = Blockchain.Default.CurrentBlockHash; BlockIndex = Blockchain.Default.Height + 1; ViewNumber = 0; Validators = Blockchain.Default.GetValidators (); MyIndex =-1; PrimaryIndex = BlockIndex% (uint) Validators.Length; / / current bookkeeper TransactionHashes = null; Signatures = new byte [Validators.Length] []; ExpectedView = new byte [Validators.Length]; KeyPair = null For (int I = 0; I

< Validators.Length; i++) { WalletAccount account = wallet.GetAccount(Validators[i]); if (account?.HasKey == true) { MyIndex = i; KeyPair = account.GetKey(); break; } } _header = null; }状态初始化 如果是议长则状态标记为ConsensusState.Primary,同时改变定时器触发事件,再上次出块15s后触发。议员则设置状态为 ConsensusState.Backup,时间调整为30s后触发,如果议长不能正常工作,则这个触发器会开始起作用(具体后边再详细分析)。 //file /Consensus/ConsensusContext.cs private void InitializeConsensus(byte view_number) { lock (context) { if (view_number == 0) context.Reset(wallet); else context.ChangeView(view_number); if (context.MyIndex < 0) return; Log($"initialize: height={context.BlockIndex} view={view_number} index={context.MyIndex} role={(context.MyIndex == context.PrimaryIndex ? ConsensusState.Primary : ConsensusState.Backup)}"); if (context.MyIndex == context.PrimaryIndex) { context.State |= ConsensusState.Primary; if (!context.State.HasFlag(ConsensusState.SignatureSent)) { FillContext(); //生成mine区块 } if (context.TransactionHashes.Length >

1) {/ / broadcast its own transaction InvPayload invPayload = InvPayload.Create (InventoryType.TX, context.TransactionHashes.Skip (1). ToArray ()); foreach (RemoteNode node in localNode.GetRemoteNodes ()) node.EnqueueMessage ("inv", invPayload);} timer_height = context.BlockIndex Timer_view = view_number; TimeSpan span = DateTime.Now-block_received_time; if (span > = Blockchain.TimePerBlock) timer.Change (0, Timeout.Infinite); else timer.Change (Blockchain.TimePerBlock-span, Timeout.InfiniteTimeSpan) } else {context.State = ConsensusState.Backup; timer_height = context.BlockIndex; timer_view = view_number; timer.Change (TimeSpan.FromSeconds (Blockchain.SecondsPerBlock! context.Transactions.ContainsKey (I)) .ToArray (); LocalNode.AllowHashes (hashes) InvPayload msg = InvPayload.Create (InventoryType.TX, hashes); foreach (RemoteNode node in localNode.GetRemoteNodes ()) node.EnqueueMessage ("getdata", msg) }} / / file / Consensus/ConsensusContext.cs private bool AddTransaction (Transaction tx, bool verify) {if (Blockchain.Default.ContainsTransaction (tx.Hash) | | (verify & &! tx.Verify (context.Transactions.Values)) | |! CheckPolicy (tx)) {Log ($"reject tx: {tx.Hash} {tx.ToArray (). ToHexString ()}") RequestChangeView (); return false;} context.Transactions [tx.Hash] = tx; if (context.TransactionHashes.Length = = context.Transactions.Count) {if (Blockchain.GetConsensusAddress (Blockchain.Default.GetValidators (context.Transactions.Values). ToArray ()) .equals (context.NextConsensus)) {Log ($"send perpare response") Context.State | = ConsensusState.SignatureSent; context.Signatures [context.MyIndex] = context.MakeHeader () .Sign (context.KeyPair); SignAndRelay (context.MakePrepareResponse (context.Signatures [context.MyIndex])); CheckSignatures ();} else {RequestChangeView (); return false }} return true;} broadcast block after consensus is reached

After receiving the PrepareResponse, other nodes record each other's signature information in their own signature list, and then check whether their signature list has more than 2/3 signatures, and some decide that the consensus is reached to start broadcasting the generated blocks. The status changes to ConsensusState.BlockSent.

Private void CheckSignatures () {if (context.Signatures.Count (p = > p! = null) > = context.M & & context.TransactionHashes.All (p = > context.Transactions.ContainsKey (p) {Contract contract = Contract.CreateMultiSigContract (context.M, context.Validators); Block block = context.MakeHeader (); ContractParametersContext sc = new ContractParametersContext (block); for (int I = 0, j = 0; I

< context.Validators.Length && j < context.M; i++) if (context.Signatures[i] != null) { sc.AddSignature(contract, context.Validators[i], context.Signatures[i]); j++; } sc.Verifiable.Scripts = sc.GetScripts(); block.Transactions = context.TransactionHashes.Select(p =>

Context.Transactions [p]) .ToArray (); Log ($"relay block: {block.Hash}"); if (! localNode.Relay (block)) Log ($"reject block: {block.Hash}"); context.State | = ConsensusState.BlockSent;}} Blockchain_PersistCompleted

After the block broadcast, the node receives this information, saves the block, triggers the PersistCompleted event after the save is completed, and finally returns to the initial state to start a new round of consensus.

Private void Blockchain_PersistCompleted (object sender, Block block) {Log ($"persist block: {block.Hash}"); block_received_time = DateTime.Now; InitializeConsensus (0);} error analysis if the speaker is unable to complete the accounting task

In this case, the Speaker is still unable to reach a consensus after the expiration of time, and at this time, members will increase their expected view value and broadcast it. If it is checked that 2/3 of the members' viewnumber have been added 1, then start a new round of consensus among these nodes with the same viewnumber, re-select the Speaker, and initiate a consensus. If the original speaker returns to normal at this time, time will grow its own viewnumber after the time expires, slowly catching up with the current view state.

On the "NEO DBFT consensus algorithm source code analysis" content is introduced here, thank you for reading. If you want to know more about the industry, you can follow the industry information channel. The editor will update different knowledge points for you every day.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report