Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[neox] Merge neox to master #1786

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 50 additions & 18 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 All @@ -40,7 +41,7 @@ internal class ConsensusContext : IDisposable, ISerializable
/// <summary>
/// Store all verified unsorted transactions' senders' fee currently in the consensus context.
/// </summary>
public SendersFeeMonitor SendersFeeMonitor = new SendersFeeMonitor();
public TransactionVerificationContext VerificationContext = new TransactionVerificationContext();

public SnapshotView Snapshot { get; private set; }
private KeyPair keyPair;
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 All @@ -109,18 +130,18 @@ public void Deserialize(BinaryReader reader)
ViewNumber = reader.ReadByte();
TransactionHashes = reader.ReadSerializableArray<UInt256>();
Transaction[] transactions = reader.ReadSerializableArray<Transaction>(Block.MaxTransactionsPerBlock);
PreparationPayloads = reader.ReadNullableArray<ConsensusPayload>(ProtocolSettings.Default.MaxValidatorsCount);
CommitPayloads = reader.ReadNullableArray<ConsensusPayload>(ProtocolSettings.Default.MaxValidatorsCount);
ChangeViewPayloads = reader.ReadNullableArray<ConsensusPayload>(ProtocolSettings.Default.MaxValidatorsCount);
LastChangeViewPayloads = reader.ReadNullableArray<ConsensusPayload>(ProtocolSettings.Default.MaxValidatorsCount);
PreparationPayloads = reader.ReadNullableArray<ConsensusPayload>(ProtocolSettings.Default.ValidatorsCount);
CommitPayloads = reader.ReadNullableArray<ConsensusPayload>(ProtocolSettings.Default.ValidatorsCount);
ChangeViewPayloads = reader.ReadNullableArray<ConsensusPayload>(ProtocolSettings.Default.ValidatorsCount);
LastChangeViewPayloads = reader.ReadNullableArray<ConsensusPayload>(ProtocolSettings.Default.ValidatorsCount);
if (TransactionHashes.Length == 0 && !RequestSentOrReceived)
TransactionHashes = null;
Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash);
SendersFeeMonitor = new SendersFeeMonitor();
VerificationContext = new TransactionVerificationContext();
if (Transactions != null)
{
foreach (Transaction tx in Transactions.Values)
SendersFeeMonitor.AddSenderFee(tx);
VerificationContext.AddTransaction(tx);
}
}

Expand All @@ -137,6 +158,20 @@ public Block EnsureHeader()
return Block;
}

public StateRoot EnsureStateRoot()
{
if (PreviousBlockStateRoot is null)
{
PreviousBlockStateRoot = new StateRoot
{
Version = StateRoot.CurrentVersion,
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 @@ -266,7 +301,7 @@ internal void EnsureMaxBlockLimitation(IEnumerable<Transaction> txs)
txs = txs.Take((int)maxTransactionsPerBlock);
List<UInt256> hashes = new List<UInt256>();
Transactions = new Dictionary<UInt256, Transaction>();
SendersFeeMonitor = new SendersFeeMonitor();
VerificationContext = new TransactionVerificationContext();

// Expected block size
var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count());
Expand All @@ -285,7 +320,7 @@ internal void EnsureMaxBlockLimitation(IEnumerable<Transaction> txs)

hashes.Add(tx.Hash);
Transactions.Add(tx.Hash, tx);
SendersFeeMonitor.AddSenderFee(tx);
VerificationContext.AddTransaction(tx);
}

TransactionHashes = hashes.ToArray();
Expand All @@ -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
44 changes: 32 additions & 12 deletions src/neo/Consensus/ConsensusService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,24 @@ internal ConsensusService(IActorRef localNode, IActorRef taskManager, IActorRef

private bool AddTransaction(Transaction tx, bool verify)
{
if (verify && tx.Verify(context.Snapshot, context.SendersFeeMonitor.GetSenderFee(tx.Sender)) != VerifyResult.Succeed)
if (verify)
{
Log($"Invalid transaction: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
RequestChangeView(ChangeViewReason.TxInvalid);
return false;
}
if (!NativeContract.Policy.CheckPolicy(tx, context.Snapshot))
{
Log($"reject tx: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
RequestChangeView(ChangeViewReason.TxRejectedByPolicy);
return false;
VerifyResult result = tx.Verify(context.Snapshot, context.VerificationContext);
if (result == VerifyResult.PolicyFail)
{
Log($"reject tx: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
RequestChangeView(ChangeViewReason.TxRejectedByPolicy);
return false;
}
else if (result != VerifyResult.Succeed)
{
Log($"Invalid transaction: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
RequestChangeView(ChangeViewReason.TxInvalid);
return false;
}
}
context.Transactions[tx.Hash] = tx;
context.SendersFeeMonitor.AddSenderFee(tx);
context.VerificationContext.AddTransaction(tx);
return CheckPrepareResponse();
}

Expand Down Expand Up @@ -165,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 @@ -424,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 All @@ -433,7 +447,7 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m
context.Block.ConsensusData.Nonce = message.Nonce;
context.TransactionHashes = message.TransactionHashes;
context.Transactions = new Dictionary<UInt256, Transaction>();
context.SendersFeeMonitor = new SendersFeeMonitor();
context.VerificationContext = new TransactionVerificationContext();
for (int i = 0; i < context.PreparationPayloads.Length; i++)
if (context.PreparationPayloads[i] != null)
if (!context.PreparationPayloads[i].GetDeserializedMessage<PrepareResponse>().PreparationHash.Equals(payload.Hash))
Expand Down Expand Up @@ -486,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
9 changes: 5 additions & 4 deletions src/neo/Consensus/RecoveryMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public RecoveryMessage() : base(ConsensusMessageType.RecoveryMessage)
public override void Deserialize(BinaryReader reader)
{
base.Deserialize(reader);
ChangeViewMessages = reader.ReadSerializableArray<ChangeViewPayloadCompact>(ProtocolSettings.Default.MaxValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex);
ChangeViewMessages = reader.ReadSerializableArray<ChangeViewPayloadCompact>(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex);
if (reader.ReadBoolean())
PrepareRequestMessage = reader.ReadSerializable<PrepareRequest>();
else
Expand All @@ -41,8 +41,8 @@ public override void Deserialize(BinaryReader reader)
PreparationHash = new UInt256(reader.ReadFixedBytes(preparationHashSize));
}

PreparationMessages = reader.ReadSerializableArray<PreparationPayloadCompact>(ProtocolSettings.Default.MaxValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex);
CommitMessages = reader.ReadSerializableArray<CommitPayloadCompact>(ProtocolSettings.Default.MaxValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex);
PreparationMessages = reader.ReadSerializableArray<PreparationPayloadCompact>(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex);
CommitMessages = reader.ReadSerializableArray<CommitPayloadCompact>(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex);
}

internal ConsensusPayload[] GetChangeViewPayloads(ConsensusContext context, ConsensusPayload payload)
Expand Down 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
2 changes: 1 addition & 1 deletion src/neo/Cryptography/MPT/MPTTrie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public partial class MPTTrie<TKey, TValue>
public MPTTrie(ISnapshot store, UInt256 root)
{
this.store = store ?? throw new ArgumentNullException();
this.root = root is null ? HashNode.EmptyNode : new HashNode(root);
this.root = root is null || root == UInt256.Zero ? HashNode.EmptyNode : new HashNode(root);
}

private MPTNode Resolve(HashNode n)
Expand Down
Loading