Skip to content

Commit

Permalink
[neox] consensus (neo-project#1719)
Browse files Browse the repository at this point in the history
* don't use snapshot directly in plugin

* recover

* consensus state root

* fix build error

* rename

* remerge

* rm GetMessage

* rename

* throw exception when no script hash in state root

* Clean double enter

* Clean big line

* format

* put root sig into request and response

* update consensus ut

* Check Json.Serialize map keys (neo-project#1705)

* don't use snapshot directly in plugin

* recover

* consensus state root

* fix build error

* rename

* remerge

* rm GetMessage

* rename

* throw exception when no script hash in state root

* Clean double enter

* Clean big line

* format

* put root sig into request and response

* update consensus ut

* fix some

* fix some

* format

* comment

* fix

* requst in recovery

* StateRootSignature fixed size

* no need ?

* format

* Update ConsensusContext.cs

* Check sc.Complete

Co-authored-by: Shargon <shargon@gmail.com>
  • Loading branch information
ZhangTao1596 and shargon committed Jul 17, 2020
1 parent 704ec10 commit 6879013
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 55 deletions.
50 changes: 41 additions & 9 deletions src/neo/Consensus/ConsensusContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal class ConsensusContext : IDisposable, ISerializable
private const byte ConsensusStatePrefix = 0xf4;

public Block Block;
public StateRoot PreviousBlockStateRoot;
public byte ViewNumber;
public ECPoint[] Validators;
public int MyIndex;
Expand Down Expand Up @@ -91,11 +92,31 @@ public Block CreateBlock()
sc.AddSignature(contract, Validators[i], CommitPayloads[i].GetDeserializedMessage<Commit>().Signature);
j++;
}
if (!sc.Completed) throw new Exception("There are not enough signatures for sign the Block.");
Block.Witness = sc.GetWitnesses()[0];
Block.Transactions = TransactionHashes.Select(p => Transactions[p]).ToArray();
return Block;
}

public StateRoot CreateStateRoot()
{
EnsureHeader();
Contract contract = Contract.CreateMultiSigContract(M, Validators);
ContractParametersContext sc = new ContractParametersContext(PreviousBlockStateRoot);
for (int i = 0, j = 0; i < Validators.Length && j < M; i++)
{
if (PreparationPayloads[i]?.ConsensusMessage.ViewNumber != ViewNumber) continue;
if (i == GetPrimaryIndex(ViewNumber))
sc.AddSignature(contract, Validators[i], PreparationPayloads[i].GetDeserializedMessage<PrepareRequest>().StateRootSignature);
else
sc.AddSignature(contract, Validators[i], PreparationPayloads[i].GetDeserializedMessage<PrepareResponse>().StateRootSignature);
j++;
}
if (!sc.Completed) throw new Exception("There are not enough signatures for sign the state root.");
PreviousBlockStateRoot.Witness = sc.GetWitnesses()[0];
return PreviousBlockStateRoot;
}

public void Deserialize(BinaryReader reader)
{
Reset(0);
Expand Down Expand Up @@ -137,6 +158,20 @@ public Block EnsureHeader()
return Block;
}

public StateRoot EnsureStateRoot()
{
if (PreviousBlockStateRoot is null)
{
PreviousBlockStateRoot = new StateRoot
{
Version = 0,
Index = Block.Index - 1,
RootHash = Blockchain.Singleton.GetLocalStateRoot(Block.Index - 1)
};
}
return PreviousBlockStateRoot;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetPrimaryIndex(byte viewNumber)
{
Expand Down Expand Up @@ -304,7 +339,8 @@ public ConsensusPayload MakePrepareRequest()
{
Timestamp = Block.Timestamp,
Nonce = Block.ConsensusData.Nonce,
TransactionHashes = TransactionHashes
TransactionHashes = TransactionHashes,
StateRootSignature = EnsureStateRoot().Sign(keyPair)
});
}

Expand All @@ -321,13 +357,7 @@ public ConsensusPayload MakeRecoveryMessage()
PrepareRequest prepareRequestMessage = null;
if (TransactionHashes != null)
{
prepareRequestMessage = new PrepareRequest
{
ViewNumber = ViewNumber,
Timestamp = Block.Timestamp,
Nonce = Block.ConsensusData.Nonce,
TransactionHashes = TransactionHashes
};
prepareRequestMessage = PreparationPayloads[GetPrimaryIndex(ViewNumber)].GetDeserializedMessage<PrepareRequest>();
}
return MakeSignedPayload(new RecoveryMessage()
{
Expand All @@ -346,7 +376,8 @@ public ConsensusPayload MakePrepareResponse()
{
return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareResponse
{
PreparationHash = PreparationPayloads[Block.ConsensusData.PrimaryIndex].Hash
PreparationHash = PreparationPayloads[Block.ConsensusData.PrimaryIndex].Hash,
StateRootSignature = EnsureStateRoot().Sign(keyPair)
});
}

Expand Down Expand Up @@ -399,6 +430,7 @@ public void Reset(byte viewNumber)
keyPair = account.GetKey();
break;
}
PreviousBlockStateRoot = null;
}
else
{
Expand Down
16 changes: 16 additions & 0 deletions src/neo/Consensus/ConsensusService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ private void CheckPreparations()
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));

StateRoot stateRoot = context.CreateStateRoot();
Log($"relay state root, index={stateRoot.Index}, root_hash={stateRoot.RootHash}");
blockchain.Tell(stateRoot);
CheckCommits();
}
}
Expand Down Expand Up @@ -428,6 +432,12 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m
Log($"Invalid request: transaction already exists", LogLevel.Warning);
return;
}
var stateRootHashData = context.EnsureStateRoot().GetHashData();
if (!Crypto.VerifySignature(stateRootHashData, message.StateRootSignature, context.Validators[payload.ValidatorIndex]))
{
Log($"Invalid request: invalid state root signature", 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)
Expand Down Expand Up @@ -490,6 +500,12 @@ private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse
if (context.PreparationPayloads[payload.ValidatorIndex] != null || context.NotAcceptingPayloadsDueToViewChanging) return;
if (context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex] != null && !message.PreparationHash.Equals(context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex].Hash))
return;
byte[] stateRootHashData = context.EnsureStateRoot().GetHashData();
if (!Crypto.VerifySignature(stateRootHashData, message.StateRootSignature, context.Validators[payload.ValidatorIndex]))
{
Log($"Invalid response: invalid state root signature, height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}", LogLevel.Warning);
return;
}

// Timeout extension: prepare response has been received with success
// around 2*15/M=30.0/5 ~ 40% block time (for M=5)
Expand Down
8 changes: 6 additions & 2 deletions src/neo/Consensus/PrepareRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ public class PrepareRequest : ConsensusMessage
public ulong Timestamp;
public ulong Nonce;
public UInt256[] TransactionHashes;
public byte[] StateRootSignature;

public override int Size => base.Size
+ sizeof(ulong) //Timestamp
+ sizeof(ulong) //Timestamp
+ sizeof(ulong) //Nonce
+ TransactionHashes.GetVarSize(); //TransactionHashes
+ TransactionHashes.GetVarSize() //TransactionHashes
+ StateRootSignature.Length; //StateRootSignature

public PrepareRequest()
: base(ConsensusMessageType.PrepareRequest)
Expand All @@ -30,6 +32,7 @@ public override void Deserialize(BinaryReader reader)
TransactionHashes = reader.ReadSerializableArray<UInt256>(Block.MaxTransactionsPerBlock);
if (TransactionHashes.Distinct().Count() != TransactionHashes.Length)
throw new FormatException();
StateRootSignature = reader.ReadFixedBytes(64);
}

public override void Serialize(BinaryWriter writer)
Expand All @@ -38,6 +41,7 @@ public override void Serialize(BinaryWriter writer)
writer.Write(Timestamp);
writer.Write(Nonce);
writer.Write(TransactionHashes);
writer.Write(StateRootSignature);
}
}
}
5 changes: 4 additions & 1 deletion src/neo/Consensus/PrepareResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ namespace Neo.Consensus
public class PrepareResponse : ConsensusMessage
{
public UInt256 PreparationHash;
public byte[] StateRootSignature;

public override int Size => base.Size + PreparationHash.Size;
public override int Size => base.Size + PreparationHash.Size + StateRootSignature.Length;

public PrepareResponse()
: base(ConsensusMessageType.PrepareResponse)
Expand All @@ -18,12 +19,14 @@ public override void Deserialize(BinaryReader reader)
{
base.Deserialize(reader);
PreparationHash = reader.ReadSerializable<UInt256>();
StateRootSignature = reader.ReadFixedBytes(64);
}

public override void Serialize(BinaryWriter writer)
{
base.Serialize(writer);
writer.Write(PreparationHash);
writer.Write(StateRootSignature);
}
}
}
17 changes: 14 additions & 3 deletions src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Neo.IO;
using Neo.Network.P2P.Payloads;
using System;
using System.IO;

namespace Neo.Consensus
Expand All @@ -10,30 +11,40 @@ public class PreparationPayloadCompact : ISerializable
{
public ushort ValidatorIndex;
public byte[] InvocationScript;
public byte[] StateRootSignature;

int ISerializable.Size =>
sizeof(ushort) + //ValidatorIndex
InvocationScript.GetVarSize(); //InvocationScript
sizeof(ushort) + //ValidatorIndex
InvocationScript.GetVarSize() + //InvocationScript
StateRootSignature.Length; //StateRootSignature

void ISerializable.Deserialize(BinaryReader reader)
{
ValidatorIndex = reader.ReadUInt16();
InvocationScript = reader.ReadVarBytes(1024);
StateRootSignature = reader.ReadFixedBytes(64);
}

public static PreparationPayloadCompact FromPayload(ConsensusPayload payload)
{
byte[] state_root_sig = Array.Empty<byte>();
if (payload.ConsensusMessage is PrepareResponse req)
state_root_sig = req.StateRootSignature;
else if (payload.ConsensusMessage is PrepareResponse resp)
state_root_sig = resp.StateRootSignature;
return new PreparationPayloadCompact
{
ValidatorIndex = payload.ValidatorIndex,
InvocationScript = payload.Witness.InvocationScript
InvocationScript = payload.Witness.InvocationScript,
StateRootSignature = state_root_sig
};
}

void ISerializable.Serialize(BinaryWriter writer)
{
writer.Write(ValidatorIndex);
writer.WriteVarBytes(InvocationScript);
writer.Write(StateRootSignature);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/neo/Consensus/RecoveryMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ internal ConsensusPayload[] GetPrepareResponsePayloads(ConsensusContext context,
ConsensusMessage = new PrepareResponse
{
ViewNumber = ViewNumber,
PreparationHash = preparationHash
PreparationHash = preparationHash,
StateRootSignature = p.StateRootSignature
},
Witness = new Witness
{
Expand Down
12 changes: 12 additions & 0 deletions src/neo/Ledger/Blockchain.state.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Akka.Actor;

namespace Neo.Ledger
{
public sealed partial class Blockchain : UntypedActor
{
public UInt256 GetLocalStateRoot(uint index)
{
return UInt256.Zero;
}
}
}
8 changes: 4 additions & 4 deletions src/neo/Network/P2P/Payloads/StateRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ Witness[] IVerifiable.Witnesses
}

public int Size =>
sizeof(byte) + //Version
sizeof(uint) + //Index
UInt256.Length + //Root
Witness.Size; //Witness
sizeof(byte) + //Version
sizeof(uint) + //Index
UInt256.Length + //RootHash
Witness.Size; //Witness

StateRoot ICloneable<StateRoot>.Clone()
{
Expand Down
Loading

0 comments on commit 6879013

Please sign in to comment.