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

Feature external signer #6780

Merged
merged 38 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
18e9182
external signer
ak88 Feb 8, 2024
8a812ff
moved rpc clients to light project
ak88 Feb 8, 2024
3f38c73
external signer
ak88 Feb 8, 2024
0fc08ed
clique test for joc
ak88 Feb 9, 2024
1e5a569
remote sign tx
ak88 Feb 11, 2024
c535c1b
remote signer signs tx
ak88 Feb 12, 2024
b15fe43
Merge branch 'master' into feature/external-signer
ak88 Feb 12, 2024
5bf7c60
remote signer unittest
ak88 Feb 12, 2024
64becce
joc-sandbox
ak88 Feb 20, 2024
14284f2
Merge branch 'feature/joc-sandbox' into feature/external-signer
ak88 Feb 20, 2024
64dcddf
genesis correction
ak88 Feb 20, 2024
aee1fca
Clef signer for clique
ak88 Feb 23, 2024
5b69110
put clique producer on dispose stack
ak88 Feb 21, 2024
fedde62
Merge branch 'master' into feature/external-signer
ak88 Feb 23, 2024
1ab457c
Merge branch 'fix/clique-producer-dispose' into feature/external-signer
ak88 Feb 23, 2024
3294716
change from review
ak88 Feb 23, 2024
045a515
change from review
ak88 Feb 23, 2024
a245145
format whitespace
ak88 Feb 23, 2024
473927e
Merge branch 'fix/clique-producer-dispose' into feature/external-signer
ak88 Feb 23, 2024
1c2f94f
remove tx sign
ak88 Feb 23, 2024
6b60f76
Merge branch 'master' into feature/external-signer
ak88 Feb 26, 2024
2fbf68e
cleanup
ak88 Feb 26, 2024
bb05c1b
format whitespace
ak88 Feb 26, 2024
1012dd3
remove test cfg
ak88 Feb 26, 2024
48dcd4c
test fix
ak88 Feb 26, 2024
2a34f39
remove sandbox spec
ak88 Feb 26, 2024
54afa02
Merge branch 'master' into feature/external-signer
brbrr Mar 14, 2024
c99c5fa
Squashed commit of the following:
ak88 Mar 25, 2024
d06892a
format whitespace
ak88 Mar 25, 2024
79801f5
Merge branch 'master' into feature/external-signer
ak88 Mar 25, 2024
18f01ef
Update src/Nethermind/Nethermind.JsonRpc.Connectors/Client/BasicJsonR…
ak88 Apr 12, 2024
50fee7a
Merge branch 'master' into feature/external-signer
ak88 Apr 12, 2024
ece7b6f
code review comments
ak88 Apr 12, 2024
821117f
format whitespace
ak88 Apr 12, 2024
8d07596
removed Nethermind.JsonRpc.Clients.csproj
ak88 Apr 12, 2024
206a61b
one more unittest
ak88 Apr 15, 2024
536c290
Merge branch 'master' into feature/external-signer
ak88 Apr 15, 2024
7ebf203
Merge branch 'master' into feature/external-signer
ak88 May 27, 2024
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
101 changes: 101 additions & 0 deletions src/Nethermind/Nethermind.Blockchain.Test/Consensus/ClefSignerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Threading.Tasks;
using FluentAssertions;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Core.Test.Builders;
using Nethermind.JsonRpc;
using Nethermind.JsonRpc.Client;
using NSubstitute;
using NUnit.Framework;

namespace Nethermind.Blockchain.Test.Consensus
{
[TestFixture]
public class ClefSignerTests
{
[Test]
public async Task Sign_SigningHash_RequestHasCorrectParameters()
{
IJsonRpcClient client = Substitute.For<IJsonRpcClient>();
client.Post<string[]>("account_list").Returns(Task.FromResult<string[]?>([TestItem.AddressA!.ToString()]));
Task<string?> postMethod = client.Post<string>("account_signData", "text/plain", Arg.Any<string>(), Keccak.Zero);
var returnValue = (new byte[65]).ToHexString();
postMethod.Returns(returnValue);
ClefSigner sut = await ClefSigner.Create(client);
Comment on lines +24 to +29
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly a lot of duplicate in setup


var result = sut.Sign(Keccak.Zero);

Assert.That(new Signature(returnValue).Bytes, Is.EqualTo(result.Bytes));
}

[Test]
public async Task Sign_SigningCliqueHeader_PassingCorrectClefParametersForRequest()
{
IJsonRpcClient client = Substitute.For<IJsonRpcClient>();
client.Post<string[]>("account_list").Returns(Task.FromResult<string[]?>([TestItem.AddressA!.ToString()]));
Task<string?> postMethod = client.Post<string>(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>());
var returnValue = (new byte[65]).ToHexString();
postMethod.Returns(returnValue);
BlockHeader blockHeader = Build.A.BlockHeader.TestObject;
ClefSigner sut = await ClefSigner.Create(client);

sut.Sign(blockHeader);

await client.Received().Post<string>("account_signData", "application/x-clique-header", Arg.Any<string>(), Arg.Any<string>());
}


[TestCase(0, 27)]
[TestCase(1, 28)]
public async Task Sign_RecoveryIdIsSetToCliqueValues_RecoveryIdIsAdjusted(byte recId, byte expected)
{
IJsonRpcClient client = Substitute.For<IJsonRpcClient>();
client.Post<string[]>("account_list").Returns(Task.FromResult<string[]?>([TestItem.AddressA!.ToString()]));
Task<string?> postMethod = client.Post<string>("account_signData", "application/x-clique-header", Arg.Any<string>(), Arg.Any<string>());
var returnValue = (new byte[65]);
returnValue[64] = recId;
postMethod.Returns(returnValue.ToHexString());
BlockHeader blockHeader = Build.A.BlockHeader.TestObject;
ClefSigner sut = await ClefSigner.Create(client);

var result = sut.Sign(blockHeader);

Assert.That(result.V, Is.EqualTo(expected));
}

[Test]
public async Task Create_SignerAddressSpecified_CorrectAddressIsSet()
{
IJsonRpcClient client = Substitute.For<IJsonRpcClient>();
client.Post<string[]>("account_list").Returns(Task.FromResult<string[]?>([TestItem.AddressA!.ToString(), TestItem.AddressB!.ToString()]));

ClefSigner sut = await ClefSigner.Create(client, TestItem.AddressB);

Assert.That(sut.Address, Is.EqualTo(TestItem.AddressB));
}

[Test]
public void Create_SignerAddressDoesNotExists_ThrowInvalidOperationException()
{
IJsonRpcClient client = Substitute.For<IJsonRpcClient>();
client.Post<string[]>("account_list").Returns(Task.FromResult<string[]?>([TestItem.AddressA!.ToString(), TestItem.AddressB!.ToString()]));

Assert.That(async () => await ClefSigner.Create(client, TestItem.AddressC), Throws.InstanceOf<InvalidOperationException>());
}

[Test]
public async Task SetSigner_TryingToASigner_ThrowInvalidOperationException()
{
IJsonRpcClient client = Substitute.For<IJsonRpcClient>();
client.Post<string[]>("account_list").Returns(Task.FromResult<string[]?>([TestItem.AddressA!.ToString()]));
ClefSigner sut = await ClefSigner.Create(client);

Assert.That(() => sut.SetSigner(Build.A.PrivateKey.TestObject), Throws.InstanceOf<InvalidOperationException>());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<ProjectReference Include="..\Nethermind.Consensus.Ethash\Nethermind.Consensus.Ethash.csproj" />
<ProjectReference Include="..\Nethermind.Core.Test\Nethermind.Core.Test.csproj" />
<ProjectReference Include="..\Nethermind.Db.Rocks\Nethermind.Db.Rocks.csproj" />
<ProjectReference Include="..\Nethermind.ExternalSigner.Plugin\Nethermind.ExternalSigner.Plugin.csproj" />
<ProjectReference Include="..\Nethermind.JsonRpc.Test\Nethermind.JsonRpc.Test.csproj" />
<ProjectReference Include="..\Nethermind.Specs.Test\Nethermind.Specs.Test.csproj" />
</ItemGroup>
Expand Down
18 changes: 18 additions & 0 deletions src/Nethermind/Nethermind.Clique.Test/CliqueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
using Nethermind.Int256;
using Nethermind.Logging;
using Nethermind.Serialization.Rlp;
using NSubstitute;
using NUnit.Framework;
using System.Threading.Tasks;
using BlockTree = Nethermind.Blockchain.BlockTree;

namespace Nethermind.Clique.Test
Expand Down Expand Up @@ -90,6 +92,22 @@ public void Test_no_signer_data_at_epoch_fails(string blockRlp)
Assert.True(validSeal);
}

[TestCase(Block4Rlp)]
public async Task SealBlock_SignerCanSignHeader_FullHeaderIsUsedToSign(string blockRlp)
{
ISigner signer = Substitute.For<ISigner>();
signer.CanSignHeader.Returns(true);
signer.CanSign.Returns(true);
signer.Address.Returns(new Address("0x7ffc57839b00206d1ad20c69a1981b489f772031"));
signer.Sign(Arg.Any<BlockHeader>()).Returns(new Signature(new byte[65]));
CliqueSealer sut = new CliqueSealer(signer, new CliqueConfig(), _snapshotManager, LimboLogs.Instance);
Block block = Rlp.Decode<Block>(new Rlp(Bytes.FromHexString(blockRlp)));

await sut.SealBlock(block, System.Threading.CancellationToken.None);

signer.Received().Sign(Arg.Any<BlockHeader>());
}

public static Block GetGenesis()
{
Hash256 parentHash = Keccak.Zero;
Expand Down
19 changes: 17 additions & 2 deletions src/Nethermind/Nethermind.Consensus.Clique/CliqueSealer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
using System.Threading.Tasks;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Crypto;
using Nethermind.JsonRpc;
using Nethermind.Logging;
using Nethermind.Serialization.Rlp;

[assembly: InternalsVisibleTo("Nethermind.Clique.Test")]

Expand All @@ -27,7 +30,6 @@ public CliqueSealer(ISigner signer, ICliqueConfig config, ISnapshotManager snaps
_snapshotManager = snapshotManager ?? throw new ArgumentNullException(nameof(snapshotManager));
_config = config ?? throw new ArgumentNullException(nameof(config));
_signer = signer ?? throw new ArgumentNullException(nameof(signer));

if (config.Epoch == 0) config.Epoch = Clique.DefaultEpochLength;
}

Expand Down Expand Up @@ -64,7 +66,20 @@ public CliqueSealer(ISigner signer, ICliqueConfig config, ISnapshotManager snaps

// Sign all the things!
Hash256 headerHash = SnapshotManager.CalculateCliqueHeaderHash(header);
Signature signature = _signer.Sign(headerHash);
Signature signature;
if (_signer.CanSignHeader)
{
BlockHeader clone = header.Clone();
int extraSeal = 65;
clone.ExtraData = clone.ExtraData.Slice(0, clone.ExtraData.Length - extraSeal);
clone.Hash = headerHash;
ak88 marked this conversation as resolved.
Show resolved Hide resolved
signature = _signer.Sign(clone);
}
else
{
signature = _signer.Sign(headerHash);
}

// Copy signature bytes (R and S)
byte[] signatureBytes = signature.Bytes;
Array.Copy(signatureBytes, 0, header.ExtraData, header.ExtraData.Length - Clique.ExtraSealLength, signatureBytes.Length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class SnapshotManager : ISnapshotManager
private readonly IDb _blocksDb;
private ulong _lastSignersCount = 0;
private readonly LruCache<ValueHash256, Snapshot> _snapshotCache = new(Clique.InMemorySnapshots, "clique snapshots");
private static readonly HeaderDecoder _headerDecoder = new HeaderDecoder();
ak88 marked this conversation as resolved.
Show resolved Hide resolved

public SnapshotManager(ICliqueConfig cliqueConfig, IDb blocksDb, IBlockTree blockTree, IEthereumEcdsa ecdsa, ILogManager logManager)
{
Expand Down
5 changes: 5 additions & 0 deletions src/Nethermind/Nethermind.Consensus/IMiningConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,9 @@ public interface IMiningConfig : IConfig

[ConfigItem(HiddenFromDocs = true, DisabledForCli = true, DefaultValue = "null")]
IBlocksConfig? BlocksConfig { get; }
[ConfigItem(
Description = "Url for an external signer like clef: https://github.com/ethereum/go-ethereum/blob/master/cmd/clef/tutorial.md",
HiddenFromDocs = false,
DefaultValue = "null")]
string Signer { get; set; }
}
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.Consensus/ISigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Nethermind.Core.Crypto;
using Nethermind.Crypto;
using Nethermind.TxPool;
using System;

namespace Nethermind.Consensus
{
Expand All @@ -14,6 +15,8 @@ public interface ISigner : ITxSigner
PrivateKey? Key { get; }
Address Address { get; }
Signature Sign(Hash256 message);
Signature Sign(BlockHeader header);
bool CanSign { get; }
bool CanSignHeader { get; }
ak88 marked this conversation as resolved.
Show resolved Hide resolved
}
}
2 changes: 2 additions & 0 deletions src/Nethermind/Nethermind.Consensus/MiningConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,6 @@ public IBlocksConfig? BlocksConfig
return _blocksConfig;
}
}

public string Signer { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is not possible, but could we make it an Uri? type?

Copy link
Contributor Author

@ak88 ak88 Apr 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not possible with the current JsonSerializer, but a custom one could be made.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should write one, is easy - here with JSON.NET: https://stackoverflow.com/a/8087049/1187056

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ProjectReference Include="..\Nethermind.Core\Nethermind.Core.csproj" />
<ProjectReference Include="..\Nethermind.Crypto\Nethermind.Crypto.csproj" />
<ProjectReference Include="..\Nethermind.Evm\Nethermind.Evm.csproj" />
<ProjectReference Include="..\Nethermind.JsonRpc.Connectors\Nethermind.JsonRpc.Clients.csproj" />
<ProjectReference Include="..\Nethermind.TxPool\Nethermind.TxPool.csproj" />
</ItemGroup>

Expand Down
4 changes: 4 additions & 0 deletions src/Nethermind/Nethermind.Consensus/NullSigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ public class NullSigner : ISigner, ISignerStore

public PrivateKey? Key { get; } = null;

public bool CanSignHeader => false;

public void SetSigner(PrivateKey key) { }

public void SetSigner(ProtectedPrivateKey key) { }

public Signature Sign(BlockHeader header) { return new(new byte[65]); }
}
}
7 changes: 7 additions & 0 deletions src/Nethermind/Nethermind.Consensus/Signer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class Signer : ISigner, ISignerStore

public bool CanSign => _key is not null;

public bool CanSignHeader => false;

public Signer(ulong chainId, PrivateKey? key, ILogManager logManager)
{
_chainId = chainId;
Expand All @@ -42,6 +44,11 @@ public Signature Sign(Hash256 message)
return new Signature(rs, v);
}

public Signature Sign(BlockHeader header)
{
return Sign(header.Hash);
}

public ValueTask Sign(Transaction tx)
{
Hash256 hash = Keccak.Compute(Rlp.Encode(tx, true, true, _chainId).Bytes);
Expand Down
Loading