Skip to content

Commit

Permalink
Shutter: Fix validator info file validation (#7681)
Browse files Browse the repository at this point in the history
  • Loading branch information
Marchhill authored Oct 29, 2024
1 parent 6a08dd5 commit 2ec731d
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class ShutterApiSimulator(
IFileSystem fileSystem,
IKeyStoreConfig keyStoreConfig,
IShutterConfig cfg,
Dictionary<ulong, byte[]> validatorsInfo,
ShutterValidatorsInfo validatorsInfo,
Random rnd
) : ShutterApi(abiEncoder, blockTree, ecdsa, logFinder, receiptStorage,
logManager, specProvider, timestamper, worldStateManager, fileSystem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public void Can_decrypt_data(string cipherTextHex, string decryptionKeyHex, stri
public void Can_verify_validator_registration_signature(string msgHex, string sigHex, string pkHex)
{
Assert.That(ShutterCrypto.CheckValidatorRegistrySignature(
Convert.FromHexString(pkHex),
new(Convert.FromHexString(pkHex)),
Convert.FromHexString(sigHex),
Convert.FromHexString(msgHex)
));
Expand Down
4 changes: 2 additions & 2 deletions src/Nethermind/Nethermind.Shutter.Test/ShutterTestsCommon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public static ShutterApiSimulator InitApi(Random rnd, ITimestamper? timestamper
AbiEncoder, blockTree, Ecdsa, logFinder, receiptStorage,
LogManager, SpecProvider, timestamper ?? Substitute.For<ITimestamper>(),
worldStateManager, Substitute.For<IFileSystem>(),
Substitute.For<IKeyStoreConfig>(), Cfg, [], rnd
Substitute.For<IKeyStoreConfig>(), Cfg, new(), rnd
);
}

Expand All @@ -68,7 +68,7 @@ public static ShutterApiSimulator InitApi(Random rnd, MergeTestBlockchain chain,
eventSimulator ?? InitEventSimulator(rnd),
AbiEncoder, chain.BlockTree.AsReadOnly(), chain.EthereumEcdsa, chain.LogFinder, chain.ReceiptStorage,
chain.LogManager, chain.SpecProvider, timestamper ?? chain.Timestamper, chain.WorldStateManager,
Substitute.For<IFileSystem>(), Substitute.For<IKeyStoreConfig>(), Cfg, [], rnd
Substitute.For<IFileSystem>(), Substitute.For<IKeyStoreConfig>(), Cfg, new(), rnd
);

public static ShutterEventSimulator InitEventSimulator(Random rnd)
Expand Down
49 changes: 49 additions & 0 deletions src/Nethermind/Nethermind.Shutter/Config/ShutterValidatorsInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.IO;
using Nethermind.Crypto;
using Nethermind.Serialization.Json;

using G1Affine = Nethermind.Crypto.Bls.P1Affine;

namespace Nethermind.Shutter.Config;

public class ShutterValidatorsInfo
{
public bool IsEmpty { get => _indexToPubKeyBytes is null || _indexToPubKeyBytes.Count == 0; }
public IEnumerable<ulong> ValidatorIndices { get => _indexToPubKeyBytes!.Keys; }
public class ShutterValidatorsInfoException(string message) : Exception(message);

private Dictionary<ulong, byte[]>? _indexToPubKeyBytes;
private readonly Dictionary<ulong, long[]> _indexToPubKey = [];

public void Load(string fp)
{
FileStream fstream = new(fp, FileMode.Open, FileAccess.Read, FileShare.None);
_indexToPubKeyBytes = new EthereumJsonSerializer().Deserialize<Dictionary<ulong, byte[]>>(fstream);
}

public void Validate()
{
G1Affine pk = new(stackalloc long[G1Affine.Sz]);

foreach ((ulong index, byte[] pubkey) in _indexToPubKeyBytes!)
{
if (!pk.TryDecode(pubkey, out Bls.ERROR _))
{
throw new ShutterValidatorsInfoException($"Validator info file contains invalid public key with index {index}.");
}

_indexToPubKey.Add(index, pk.Point.ToArray());
}
}

public bool IsIndexRegistered(ulong index)
=> _indexToPubKeyBytes!.ContainsKey(index);

public G1Affine GetPubKey(ulong index)
=> new(_indexToPubKey[index]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using Nethermind.Core;
using Nethermind.Int256;
using Nethermind.Shutter.Config;
using Update = (byte[] Message, byte[] Signature);

namespace Nethermind.Shutter.Contracts;
Expand All @@ -15,7 +16,7 @@ public interface IValidatorRegistryContract
/// </summary>
/// <param name="message"></param>
/// <param name="signature"></param>
bool IsRegistered(BlockHeader header, in Dictionary<ulong, byte[]> validatorsInfo, out HashSet<ulong> unregistered);
bool IsRegistered(in BlockHeader header, in ShutterValidatorsInfo validatorsInfo, out HashSet<ulong> unregistered);

/// <summary>
/// Returns the number of previous updates to the registry.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Nethermind.Core.Extensions;
using Update = (byte[] Message, byte[] Signature);
using Nethermind.Crypto;
using Nethermind.Shutter.Config;

namespace Nethermind.Shutter.Contracts;

Expand All @@ -32,14 +33,14 @@ public class ValidatorRegistryContract(
public Update GetUpdate(BlockHeader header, in UInt256 i)
=> (Update)Call(header, nameof(GetUpdate), Address.Zero, [i])[0];

public bool IsRegistered(BlockHeader header, in Dictionary<ulong, byte[]> validatorsInfo, out HashSet<ulong> unregistered)
public bool IsRegistered(in BlockHeader header, in ShutterValidatorsInfo validatorsInfo, out HashSet<ulong> unregistered)
{
Dictionary<ulong, ulong?> nonces = [];
unregistered = [];
foreach (KeyValuePair<ulong, byte[]> validatorInfo in validatorsInfo)
foreach (ulong index in validatorsInfo.ValidatorIndices)
{
nonces.Add(validatorInfo.Key, null);
unregistered.Add(validatorInfo.Key);
nonces.Add(index, null);
unregistered.Add(index);
}

uint updates = (uint)GetNumUpdates(header);
Expand All @@ -56,7 +57,7 @@ public bool IsRegistered(BlockHeader header, in Dictionary<ulong, byte[]> valida
Message msg = new(update.Message.AsSpan());

// skip untracked validators
if (!validatorsInfo.ContainsKey(msg.ValidatorIndex))
if (!validatorsInfo.IsIndexRegistered(msg.ValidatorIndex))
{
continue;
}
Expand Down Expand Up @@ -85,7 +86,7 @@ public bool IsRegistered(BlockHeader header, in Dictionary<ulong, byte[]> valida
continue;
}

if (!ShutterCrypto.CheckValidatorRegistrySignature(validatorsInfo[msg.ValidatorIndex], update.Signature, update.Message))
if (!ShutterCrypto.CheckValidatorRegistrySignature(validatorsInfo.GetPubKey(msg.ValidatorIndex), update.Signature, update.Message))
{
if (_logger.IsDebug) _logger.Debug("Registration message has invalid signature.");
continue;
Expand Down
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Shutter/ShutterApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public ShutterApi(
IFileSystem fileSystem,
IKeyStoreConfig keyStoreConfig,
IShutterConfig cfg,
Dictionary<ulong, byte[]> validatorsInfo,
ShutterValidatorsInfo validatorsInfo,
TimeSpan slotLength,
IPAddress ip
)
Expand Down
8 changes: 4 additions & 4 deletions src/Nethermind/Nethermind.Shutter/ShutterBlockHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class ShutterBlockHandler : IShutterBlockHandler
private readonly IShutterEon _eon;
private readonly IReceiptFinder _receiptFinder;
private readonly ShutterTxLoader _txLoader;
private readonly Dictionary<ulong, byte[]> _validatorsInfo;
private readonly ShutterValidatorsInfo _validatorsInfo;
private readonly ILogManager _logManager;
private readonly IAbiEncoder _abiEncoder;
private readonly IBlockTree _blockTree;
Expand All @@ -50,7 +50,7 @@ public ShutterBlockHandler(
IBlockTree blockTree,
IAbiEncoder abiEncoder,
IReceiptFinder receiptFinder,
Dictionary<ulong, byte[]> validatorsInfo,
ShutterValidatorsInfo validatorsInfo,
IShutterEon eon,
ShutterTxLoader txLoader,
ShutterTime time,
Expand Down Expand Up @@ -190,9 +190,9 @@ private void OnNewHeadBlock(object? _, BlockEventArgs e)
}


private void CheckAllValidatorsRegistered(BlockHeader parent, Dictionary<ulong, byte[]> validatorsInfo)
private void CheckAllValidatorsRegistered(in BlockHeader parent, in ShutterValidatorsInfo validatorsInfo)
{
if (validatorsInfo.Count == 0)
if (validatorsInfo.IsEmpty)
{
return;
}
Expand Down
9 changes: 2 additions & 7 deletions src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,8 @@ public static bool CheckSlotDecryptionIdentitiesSignature(
}

[SkipLocalsInit]
public static bool CheckValidatorRegistrySignature(ReadOnlySpan<byte> pkBytes, ReadOnlySpan<byte> sigBytes, ReadOnlySpan<byte> msgBytes)
{
ValueHash256 h = ValueKeccak.Compute(msgBytes);
G1Affine pk = new(stackalloc long[G1Affine.Sz]);
pk.Decode(pkBytes);
return BlsSigner.Verify(pk, sigBytes, h.Bytes);
}
public static bool CheckValidatorRegistrySignature(G1Affine pk, ReadOnlySpan<byte> sigBytes, ReadOnlySpan<byte> msgBytes)
=> BlsSigner.Verify(pk, sigBytes, ValueKeccak.Compute(msgBytes).Bytes);

public static EncryptedMessage Encrypt(ReadOnlySpan<byte> msg, G1 identity, G2 eonKey, ReadOnlySpan<byte> sigma)
{
Expand Down
14 changes: 3 additions & 11 deletions src/Nethermind/Nethermind.Shutter/ShutterPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Nethermind.Api;
using Nethermind.Api.Extensions;
Expand All @@ -12,8 +11,6 @@
using Nethermind.Shutter.Config;
using Nethermind.Merge.Plugin;
using Nethermind.Logging;
using System.IO;
using Nethermind.Serialization.Json;
using System.Threading;
using Nethermind.Config;
using Multiformats.Address;
Expand Down Expand Up @@ -86,12 +83,13 @@ public IBlockProducer InitBlockProducer(IBlockProducerFactory consensusPlugin, I
throw new ShutterLoadingException("Invalid Shutter config", e);
}

Dictionary<ulong, byte[]> validatorsInfo = [];
ShutterValidatorsInfo validatorsInfo = new();
if (_shutterConfig!.ValidatorInfoFile is not null)
{
try
{
validatorsInfo = LoadValidatorInfo(_shutterConfig!.ValidatorInfoFile);
validatorsInfo.Load(_shutterConfig!.ValidatorInfoFile);
validatorsInfo.Validate();
}
catch (Exception e)
{
Expand Down Expand Up @@ -135,10 +133,4 @@ public async ValueTask DisposeAsync()
_cts.Dispose();
await (_shutterApi?.DisposeAsync() ?? default);
}

private static Dictionary<ulong, byte[]> LoadValidatorInfo(string fp)
{
FileStream fstream = new(fp, FileMode.Open, FileAccess.Read, FileShare.None);
return new EthereumJsonSerializer().Deserialize<Dictionary<ulong, byte[]>>(fstream);
}
}

0 comments on commit 2ec731d

Please sign in to comment.