private void SignAndRelay(ConsensusPayload payload) { SignatureContext sc; try { sc = new SignatureContext(payload); } catch (InvalidOperationException) { return; } wallet.Sign(sc); sc.Signable.Scripts = sc.GetScripts(); localNode.RelayDirectly(payload); }
private void SignAndRelay(ConsensusPayload payload) { ContractParametersContext sc; try { sc = new ContractParametersContext(payload); wallet.Sign(sc); } catch (InvalidOperationException) { return; } sc.Verifiable.Scripts = sc.GetScripts(); localNode.RelayDirectly(payload); }
private ConsensusPayload MakeSignedPayload(ConsensusMessage message) { message.ViewNumber = ViewNumber; ConsensusPayload payload = new ConsensusPayload { Version = Version, PrevHash = PrevHash, BlockIndex = BlockIndex, ValidatorIndex = (ushort)MyIndex, Timestamp = Timestamp, Data = message.ToArray() }; SignPayload(payload); return(payload); }
private void SignAndRelay(ConsensusPayload payload) { ContractParametersContext sc; try { sc = new ContractParametersContext(payload); wallet.Sign(sc); } catch (InvalidOperationException) { return; } sc.Verifiable.Witnesses = sc.GetWitnesses(); system.LocalNode.Tell(new LocalNode.SendDirectly { Inventory = payload }); }
private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse message) { if (context.Signatures[payload.ValidatorIndex] != null) { return; } Log($"{nameof(OnPrepareResponseReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); byte[] hashData = context.MakeHeader()?.GetHashData(); if (hashData == null) { context.Signatures[payload.ValidatorIndex] = message.Signature; } else if (Crypto.Default.VerifySignature(hashData, message.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) { context.Signatures[payload.ValidatorIndex] = message.Signature; CheckSignatures(); } }
private void OnCommitReceived(ConsensusPayload payload, Commit commit) { if (context.CommitPayloads[payload.ValidatorIndex] != null) { return; } Log($"{nameof(OnCommitReceived)}: height={payload.BlockIndex} view={commit.ViewNumber} index={payload.ValidatorIndex}"); byte[] hashData = context.MakeHeader()?.GetHashData(); if (hashData == null) { context.CommitPayloads[payload.ValidatorIndex] = payload; } else if (Crypto.Default.VerifySignature(hashData, commit.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) { context.CommitPayloads[payload.ValidatorIndex] = payload; CheckCommits(); } }
private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) { Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber}"); if (message.NewViewNumber == 0xFF) { // New Start and Restart ChangeViewing. InitializeConsensus(0); } else { if (message.NewViewNumber <= context.ExpectedView[payload.ValidatorIndex]) { return; } context.ExpectedView[payload.ValidatorIndex] = message.NewViewNumber; CheckExpectedView(message.NewViewNumber); } }
private void OnPerpareRequestReceived(ConsensusPayload payload, PerpareRequest message) { Log($"{nameof(OnPerpareRequestReceived)} h:{payload.Height} v:{message.ViewNumber} i:{payload.MinerIndex} tx:{message.TransactionHashes.Length}"); if (!context.State.HasFlag(ConsensusState.Backup) || context.State.HasFlag(ConsensusState.RequestReceived)) { return; } if (payload.MinerIndex != context.PrimaryIndex) { return; } if (payload.Timestamp <= Blockchain.Default.GetHeader(context.PrevHash).Timestamp || payload.Timestamp > DateTime.Now.AddMinutes(10).ToTimestamp()) { Log($"Timestamp incorrect:{payload.Timestamp}"); return; } context.State |= ConsensusState.RequestReceived; context.Timestamp = payload.Timestamp; context.Nonce = message.Nonce; context.TransactionHashes = message.TransactionHashes; context.Transactions = new Dictionary <UInt256, Transaction>(); if (!context.MakeHeader().VerifySignature(context.Miners[payload.MinerIndex], message.Signature)) { return; } context.Signatures[payload.MinerIndex] = message.Signature; if (!AddTransaction(message.MinerTransaction)) { return; } Dictionary <UInt256, Transaction> mempool = LocalNode.GetMemoryPool().ToDictionary(p => p.Hash); foreach (UInt256 hash in context.TransactionHashes.Skip(1)) { if (mempool.ContainsKey(hash)) { if (!AddTransaction(mempool[hash])) { return; } } } }
private void LocalNode_InventoryReceived(object sender, IInventory inventory) { ConsensusPayload payload = inventory as ConsensusPayload; if (payload != null) { lock (context) { if (payload.ValidatorIndex == context.MyIndex) { return; } if (payload.Version != ConsensusContext.Version || payload.PrevHash != context.PrevHash || payload.BlockIndex != context.BlockIndex) { return; } if (payload.ValidatorIndex >= context.Validators.Length) { return; } ConsensusMessage message = ConsensusMessage.DeserializeFrom(payload.Data); if (message.ViewNumber != context.ViewNumber && message.Type != ConsensusMessageType.ChangeView) { return; } switch (message.Type) { case ConsensusMessageType.ChangeView: OnChangeViewReceived(payload, (ChangeView)message); break; case ConsensusMessageType.PerpareRequest: OnPerpareRequestReceived(payload, (PerpareRequest)message); break; case ConsensusMessageType.PerpareResponse: OnPerpareResponseReceived(payload, (PerpareResponse)message); break; } } } }
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.Transaction: container = new Transaction(); break; case ContainerType.Signers: container = new Signers(); break; case ContainerType.ConsensusPayload: container = new ConsensusPayload(); 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"); }
public static PreparationPayloadCompact FromPayload(ConsensusPayload payload) { byte[] StateRootSignature = Array.Empty <byte>(); ConsensusMessage message = payload.ConsensusMessage; if (message is PrepareRequest req) { StateRootSignature = req.StateRootSignature; } else if (message is PrepareResponse resp) { StateRootSignature = resp.StateRootSignature; } return(new PreparationPayloadCompact { ValidatorIndex = payload.ValidatorIndex, InvocationScript = payload.Witness.InvocationScript, StateRootSignature = StateRootSignature }); }
private void SignAndRelay(ConsensusPayload payload) { ReportNeoBlockchain reportObj = new ReportNeoBlockchain("[NeoConsensusService-SignAndRelay]"); ContractParametersContext sc; try { sc = new ContractParametersContext(payload); wallet.Sign(sc); } catch (InvalidOperationException) { return; } sc.Verifiable.Scripts = sc.GetScripts(); localNode.RelayDirectly(payload); reportObj.appendElapsedTime(); }
private void OnPerpareResponseReceived(ConsensusPayload payload, PerpareResponse message) { Log($"{nameof(OnPerpareResponseReceived)} h:{payload.Height} v:{message.ViewNumber} i:{payload.MinerIndex}"); if (context.State.HasFlag(ConsensusState.BlockSent)) { return; } if (context.Signatures[payload.MinerIndex] != null) { return; } Block header = context.MakeHeader(); if (header == null || !header.VerifySignature(context.Miners[payload.MinerIndex], message.Signature)) { return; } context.Signatures[payload.MinerIndex] = message.Signature; CheckSignatures(); }
internal ConsensusPayload[] GetChangeViewPayloads(ConsensusContext context, ConsensusPayload payload) { return(ChangeViewMessages.Values.Select(p => new ConsensusPayload { Version = payload.Version, PrevHash = payload.PrevHash, BlockIndex = payload.BlockIndex, ValidatorIndex = p.ValidatorIndex, ConsensusMessage = new ChangeView { ViewNumber = p.OriginalViewNumber, Timestamp = p.Timestamp }, Witness = new Witness { InvocationScript = p.InvocationScript, VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[p.ValidatorIndex]) } }).ToArray()); }
private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse message) { Log($"{nameof(OnPrepareResponseReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); if (context.State.HasFlag(ConsensusState.BlockSent)) { return; } if (context.Signatures[payload.ValidatorIndex] != null) { return; } Block header = context.MakeHeader(); if (header == null || !Crypto.Default.VerifySignature(header.GetHashData(), message.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) { return; } context.Signatures[payload.ValidatorIndex] = message.Signature; CheckSignatures(); }
private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse message) { if (context.PreparationPayloads[payload.ValidatorIndex] != null) { return; } if (context.PreparationPayloads[context.PrimaryIndex] != null && !message.PreparationHash.Equals(context.PreparationPayloads[context.PrimaryIndex].Hash)) { return; } Log($"{nameof(OnPrepareResponseReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); context.PreparationPayloads[payload.ValidatorIndex] = payload; if (context.CommitSent()) { return; } if (context.RequestSentOrReceived()) { CheckPreparations(); } }
private void OnInventory(IInventory inventory, bool relay = true) { RelayResult rr = new RelayResult { Inventory = inventory, Result = inventory switch { Block block => OnNewBlock(block), Transaction transaction => OnNewTransaction(transaction), ConsensusPayload payload => OnNewConsensus(payload), _ => VerifyResult.Unknown } }; if (relay && rr.Result == VerifyResult.Succeed) { system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = inventory }); } Context.System.EventStream.Publish(rr); }
private void CheckPreparations() { if (context.PreparationPayloads.Count(p => p != null) >= context.M() && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) { ConsensusPayload payload = context.MakeCommit(); Log($"send 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.FromSeconds(Blockchain.SecondsPerBlock)); StateRoot stateRoot = context.CreateStateRoot(); Log($"relay stateRoot: height={stateRoot.Index} hash={stateRoot.Root}"); localNode.Tell(new LocalNode.Relay { Inventory = stateRoot }); CheckCommits(); } }
private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) { if (message.NewViewNumber <= context.ViewNumber) { OnRecoveryRequestReceived(payload); } if (context.CommitSent) { return; } var expectedView = context.ChangeViewPayloads[payload.ValidatorIndex]?.GetDeserializedMessage <ChangeView>().NewViewNumber ?? (byte)0; if (message.NewViewNumber <= expectedView) { return; } Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber} reason={message.Reason}"); context.ChangeViewPayloads[payload.ValidatorIndex] = payload; CheckExpectedView(message.NewViewNumber); }
internal ConsensusPayload GetPrepareRequestPayload(ConsensusContext context, ConsensusPayload payload) { if (PrepareRequestMessage == null) { return(null); } if (!PreparationMessages.TryGetValue((int)context.Block.ConsensusData.PrimaryIndex, out RecoveryMessage.PreparationPayloadCompact compact)) { return(null); } return(new ConsensusPayload { Version = payload.Version, PrevHash = payload.PrevHash, BlockIndex = payload.BlockIndex, ValidatorIndex = (ushort)context.Block.ConsensusData.PrimaryIndex, ConsensusMessage = PrepareRequestMessage, Witness = new Witness { InvocationScript = compact.InvocationScript, VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[context.Block.ConsensusData.PrimaryIndex]) } }); }
private void LocalNode_InventoryReceived(object sender, IInventory inventory) { ConsensusPayload payload = inventory as ConsensusPayload; if (payload != null) { lock (context) { if (payload.ValidatorIndex == context.MyIndex) { return; } if (payload.Version != ConsensusContext.Version) { return; } if (payload.PrevHash != context.PrevHash || payload.BlockIndex != context.BlockIndex) { // Request blocks if (Blockchain.Default?.Height + 1 < payload.BlockIndex) { Log($"chain sync: expected={payload.BlockIndex} current: {Blockchain.Default?.Height}"); localNode.RequestGetBlocks(); } return; } if (payload.ValidatorIndex >= context.Validators.Length) { return; } ConsensusMessage message; try { message = ConsensusMessage.DeserializeFrom(payload.Data); } catch { return; } if (message.ViewNumber != context.ViewNumber && message.Type != ConsensusMessageType.ChangeView) { return; } switch (message.Type) { case ConsensusMessageType.ChangeView: OnChangeViewReceived(payload, (ChangeView)message); break; case ConsensusMessageType.PrepareRequest: OnPrepareRequestReceived(payload, (PrepareRequest)message); break; case ConsensusMessageType.PrepareResponse: OnPrepareResponseReceived(payload, (PrepareResponse)message); break; } } } }
private void LocalNode_NewInventory(object sender, Inventory inventory) { ConsensusPayload payload = inventory as ConsensusPayload; if (payload != null) { lock (context) { if (payload.MinerIndex == context.MinerIndex) { return; } if (payload.Version != ConsensusContext.Version || payload.PrevHash != context.PrevHash || payload.Height != context.Height) { return; } if (payload.MinerIndex >= context.Miners.Length) { return; } ConsensusMessage message = ConsensusMessage.DeserializeFrom(payload.Data); if (message.ViewNumber != context.ViewNumber && message.Type != ConsensusMessageType.ChangeView) { return; } switch (message.Type) { case ConsensusMessageType.ChangeView: OnChangeViewReceived(payload, (ChangeView)message); break; case ConsensusMessageType.PerpareRequest: OnPerpareRequestReceived(payload, (PerpareRequest)message); break; case ConsensusMessageType.PerpareResponse: OnPerpareResponseReceived(payload, (PerpareResponse)message); break; } } } Transaction tx = inventory as Transaction; if (tx != null) { lock (context) { if (!context.State.HasFlag(ConsensusState.Backup) || !context.State.HasFlag(ConsensusState.RequestReceived) || context.State.HasFlag(ConsensusState.SignatureSent)) { return; } if (context.Transactions.ContainsKey(tx.Hash)) { return; } if (!context.TransactionHashes.Contains(tx.Hash)) { return; } AddTransaction(tx); } } }
private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest message) { if (context.State.HasFlag(ConsensusState.RequestReceived)) { return; } if (payload.ValidatorIndex != context.PrimaryIndex) { return; } Log($"{nameof(OnPrepareRequestReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} tx={message.TransactionHashes.Length}"); if (!context.State.HasFlag(ConsensusState.Backup)) { return; } if (payload.Timestamp <= context.PrevHeader.Timestamp || payload.Timestamp > TimeProvider.Current.UtcNow.AddMinutes(10).ToTimestamp()) { Log($"Timestamp incorrect: {payload.Timestamp}", LogLevel.Warning); return; } if (message.TransactionHashes.Any(p => context.TransactionExists(p))) { Log($"Invalid request: transaction already exists", LogLevel.Warning); return; } context.State |= ConsensusState.RequestReceived; context.Timestamp = payload.Timestamp; context.Nonce = message.Nonce; context.NextConsensus = message.NextConsensus; context.TransactionHashes = message.TransactionHashes; context.Transactions = new Dictionary <UInt256, Transaction>(); byte[] hashData = context.MakeHeader().GetHashData(); if (!Crypto.Default.VerifySignature(hashData, message.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) { return; } for (int i = 0; i < context.Signatures.Length; i++) { if (context.Signatures[i] != null) { if (!Crypto.Default.VerifySignature(hashData, context.Signatures[i], context.Validators[i].EncodePoint(false))) { context.Signatures[i] = null; } } } context.Signatures[payload.ValidatorIndex] = message.Signature; Dictionary <UInt256, Transaction> mempoolVerified = Blockchain.Singleton.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash); List <Transaction> unverified = new List <Transaction>(); foreach (UInt256 hash in context.TransactionHashes.Skip(1)) { if (mempoolVerified.TryGetValue(hash, out Transaction 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 (!AddTransaction(message.MinerTransaction, 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) }); } }
internal ConsensusPayload[] GetCommitPayloadsFromRecoveryMessage(ConsensusContext context, ConsensusPayload payload) { return(CommitMessages.Values.Select(p => new ConsensusPayload { Version = payload.Version, PrevHash = payload.PrevHash, BlockIndex = payload.BlockIndex, ValidatorIndex = p.ValidatorIndex, ConsensusMessage = new Commit { ViewNumber = p.ViewNumber, Signature = p.Signature }, Witness = new Witness { InvocationScript = p.InvocationScript, VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[p.ValidatorIndex]) } }).ToArray()); }
internal ConsensusPayload[] GetPrepareResponsePayloads(ConsensusContext context, ConsensusPayload payload) { UInt256 preparationHash = PreparationHash ?? context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex]?.Hash; if (preparationHash is null) { return(new ConsensusPayload[0]); } return(PreparationMessages.Values.Where(p => p.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex).Select(p => new ConsensusPayload { Version = payload.Version, PrevHash = payload.PrevHash, BlockIndex = payload.BlockIndex, ValidatorIndex = p.ValidatorIndex, ConsensusMessage = new PrepareResponse { ViewNumber = ViewNumber, PreparationHash = preparationHash }, Witness = new Witness { InvocationScript = p.InvocationScript, VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[p.ValidatorIndex]) } }).ToArray()); }
private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) { // We keep track of the payload hashes received in this block, and don't respond with recovery // in response to the same payload that we already responded to previously. // ChangeView messages include a Timestamp when the change view is sent, thus if a node restarts // and issues a change view for the same view, it will have a different hash and will correctly respond // again; however replay attacks of the ChangeView message from arbitrary nodes will not trigger an // additional recovery message response. if (!knownHashes.Add(payload.Hash)) { return; } if (message.NewViewNumber <= context.ViewNumber) { if (context.WatchOnly()) { return; } if (!context.CommitSent()) { bool shouldSendRecovery = false; // Limit recovery to be sent from, at least, `f` nodes when the request is from a lower view number. int allowedRecoveryNodeCount = context.F(); for (int i = 0; i < allowedRecoveryNodeCount; i++) { var eligibleResponders = context.Validators.Length - 1; var chosenIndex = (payload.ValidatorIndex + i + message.NewViewNumber) % eligibleResponders; if (chosenIndex >= payload.ValidatorIndex) { chosenIndex++; } if (chosenIndex != context.MyIndex) { continue; } shouldSendRecovery = true; break; } if (!shouldSendRecovery) { return; } } Log($"send recovery from view: {message.ViewNumber} to view: {context.ViewNumber}"); localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryMessage() }); } if (context.CommitSent()) { return; } var expectedView = context.ChangeViewPayloads[payload.ValidatorIndex]?.GetDeserializedMessage <ChangeView>().NewViewNumber ?? (byte)0; if (message.NewViewNumber <= expectedView) { return; } Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber}"); context.ChangeViewPayloads[payload.ValidatorIndex] = payload; CheckExpectedView(message.NewViewNumber); }
public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() { TestProbe subscriber = CreateTestProbe(); var mockConsensusContext = new Mock <IConsensusContext>(); var mockStore = new Mock <Store>(); // context.Reset(): do nothing //mockConsensusContext.Setup(mr => mr.Reset()).Verifiable(); // void mockConsensusContext.SetupGet(mr => mr.MyIndex).Returns(2); // MyIndex == 2 mockConsensusContext.SetupGet(mr => mr.BlockIndex).Returns(2); mockConsensusContext.SetupGet(mr => mr.PrimaryIndex).Returns(2); mockConsensusContext.SetupGet(mr => mr.ViewNumber).Returns(0); mockConsensusContext.SetupProperty(mr => mr.Nonce); mockConsensusContext.SetupProperty(mr => mr.NextConsensus); mockConsensusContext.Object.NextConsensus = UInt160.Zero; mockConsensusContext.SetupGet(mr => mr.PreparationPayloads).Returns(new ConsensusPayload[7]); mockConsensusContext.SetupGet(mr => mr.CommitPayloads).Returns(new ConsensusPayload[7]); 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()}"); //mockConsensusContext.Object.block_received_time = new DateTime(1968, 06, 01, 0, 0, 1, DateTimeKind.Utc); //mockConsensusContext.Setup(mr => mr.GetUtcNow()).Returns(new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc)); 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 ulong consensusDataVal, out Witness scriptVal); header.Size.Should().Be(109); Console.WriteLine($"header {header} hash {header.Hash} timstamp {timestampVal}"); timestampVal.Should().Be(4244941696); //1968-06-01 00:00:00 // check basic ConsensusContext mockConsensusContext.Object.MyIndex.Should().Be(2); //mockConsensusContext.Object.block_received_time.ToTimestamp().Should().Be(4244941697); //1968-06-01 00:00:01 MinerTransaction minerTx = new MinerTransaction { Attributes = new TransactionAttribute[0], Inputs = new CoinReference[0], Outputs = new TransactionOutput[0], Witnesses = new Witness[0], Nonce = 42 }; PrepareRequest prep = new PrepareRequest { Nonce = mockConsensusContext.Object.Nonce, NextConsensus = mockConsensusContext.Object.NextConsensus, TransactionHashes = new UInt256[0], MinerTransaction = minerTx //(MinerTransaction)Transactions[TransactionHashes[0]], }; ConsensusPayload prepPayload = new ConsensusPayload { Version = 0, PrevHash = mockConsensusContext.Object.PrevHash, BlockIndex = mockConsensusContext.Object.BlockIndex, ValidatorIndex = (ushort)mockConsensusContext.Object.MyIndex, ConsensusMessage = prep }; mockConsensusContext.Setup(mr => mr.MakePrepareRequest()).Returns(prepPayload); // ============================================================================ // creating ConsensusService actor // ============================================================================ TestActorRef <ConsensusService> actorConsensus = ActorOfAsTestActorRef <ConsensusService>( Akka.Actor.Props.Create(() => new ConsensusService(subscriber, subscriber, mockConsensusContext.Object)) ); 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, ConsensusData = header.ConsensusData, NextConsensus = header.NextConsensus } }); // OnPersist will not launch timer, we need OnStart Console.WriteLine("will start consensus!"); actorConsensus.Tell(new ConsensusService.Start()); 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); }
// Since ConensusContext's constructor is internal, it can't be used from neo-express. // CreatePreloadBlock replicates the following logic for creating an empty block with ConensusContext // var ctx = new Neo.Consensus.ConsensusContext(wallet, store); // ctx.Reset(0); // ctx.MakePrepareRequest(); // ctx.MakeCommit(); // ctx.Save(); // Block block = ctx.CreateBlock(); static Block CreatePreloadBlock(Neo.Wallets.Wallet wallet, Random random, Transaction?transaction = null) { using var snapshot = Blockchain.Singleton.GetSnapshot(); var validators = snapshot.GetValidators(); if (validators.Length != 1) { throw new InvalidOperationException("Preload only supported for single-node blockchains"); } var amountNetFee = Block.CalculateNetFee(Enumerable.Empty <Transaction>()); if (amountNetFee != Fixed8.Zero) { throw new InvalidOperationException("amountNetFee must be zero"); } var keyPair = wallet.GetAccount(validators[0]).GetKey(); var prevHash = snapshot.CurrentBlockHash; var prevBlock = snapshot.GetBlock(prevHash); var nonce = NextNonce(random); var minerTx = new MinerTransaction { Nonce = (uint)(nonce % (uint.MaxValue + 1ul)), Attributes = Array.Empty <TransactionAttribute>(), Inputs = Array.Empty <CoinReference>(), Outputs = Array.Empty <TransactionOutput>(), Witnesses = Array.Empty <Witness>() }; var blockTransactions = transaction == null ? new Transaction[] { minerTx } : new Transaction[] { minerTx, transaction }; var txHashes = blockTransactions.Select(tx => tx.Hash).ToArray(); var merkleRoot = MerkleTree.ComputeRoot(txHashes); var nextConsensus = Blockchain.GetConsensusAddress(snapshot.GetValidators(blockTransactions).ToArray()); var consensusData = nonce; var block = new Block() { Version = 0, PrevHash = prevHash, MerkleRoot = merkleRoot, Timestamp = prevBlock.Timestamp + 1, Index = prevBlock.Index + 1, ConsensusData = nonce, NextConsensus = nextConsensus, Transactions = Array.Empty <Transaction>(), }; var commit = new Neo.Consensus.Commit() { ViewNumber = 0, Signature = block.Sign(keyPair) }; var payload = new ConsensusPayload { Version = 0, PrevHash = prevHash, BlockIndex = block.Index, ValidatorIndex = (ushort)0, Data = Neo.IO.Helper.ToArray(commit) }; { var sc = new ContractParametersContext(payload); wallet.Sign(sc); payload.Witness = sc.GetWitnesses()[0]; } { var m = validators.Length - ((validators.Length - 1) / 3); Contract contract = Contract.CreateMultiSigContract(m, validators); ContractParametersContext sc = new ContractParametersContext(block); for (int i = 0, j = 0; i < validators.Length && j < m; i++) { sc.AddSignature(contract, validators[0], payload.GetDeserializedMessage <Neo.Consensus.Commit>().Signature); j++; } block.Witness = sc.GetWitnesses()[0]; block.Transactions = blockTransactions; } return(block); }
private void OnCommitReceived(ConsensusPayload payload, Commit commit) { ref ConsensusPayload existingCommitPayload = ref context.CommitPayloads[payload.ValidatorIndex];
/// <summary> /// 收到议长共识请求 /// </summary> /// <param name="payload">议长的共识参数</param> /// <param name="message"></param> private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest message) { Log($"{nameof(OnPrepareRequestReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} tx={message.TransactionHashes.Length}"); // 当前不处于退回状态或者已经收到了重置请求 if (!context.State.HasFlag(ConsensusState.Backup) || context.State.HasFlag(ConsensusState.RequestReceived)) { return; } // 只接受议长发起的共识请求 if (payload.ValidatorIndex != context.PrimaryIndex) { return; } if (payload.Timestamp <= Blockchain.Default.GetHeader(context.PrevHash).Timestamp || payload.Timestamp > DateTime.Now.AddMinutes(10).ToTimestamp()) { Log($"Timestamp incorrect: {payload.Timestamp}"); return; } context.State |= ConsensusState.RequestReceived; // 设置状态为收到议长共识请求 context.Timestamp = payload.Timestamp; // 时间戳同步 context.Nonce = message.Nonce; // 区块随机数同步 context.NextConsensus = message.NextConsensus; context.TransactionHashes = message.TransactionHashes; // 交易哈希 context.Transactions = new Dictionary <UInt256, Transaction>(); // 议长公钥验证 if (!Crypto.Default.VerifySignature(context.MakeHeader().GetHashData(), message.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) { return; } // 添加议长签名到议员签名列表 context.Signatures = new byte[context.Validators.Length][]; context.Signatures[payload.ValidatorIndex] = message.Signature; // 将内存缓存的交易添加到共识的 context 中 Dictionary <UInt256, Transaction> mempool = LocalNode.GetMemoryPool().ToDictionary(p => p.Hash); foreach (UInt256 hash in context.TransactionHashes.Skip(1)) { if (mempool.TryGetValue(hash, out Transaction tx)) { if (!AddTransaction(tx, false))// 从缓存队列中读取添加到 context 中 { return; } } } // 添加分配字节费的交易 矿工手续费交易 if (!AddTransaction(message.MinerTransaction, true)) { return; } if (context.Transactions.Count < context.TransactionHashes.Length) { UInt256[] hashes = context.TransactionHashes.Where(i => !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); } } }