diff --git a/neo.sln b/neo.sln
index 11ee10a080..54e97affc4 100644
--- a/neo.sln
+++ b/neo.sln
@@ -1,4 +1,4 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32516.85
MinimumVisualStudioVersion = 10.0.40219.1
@@ -80,6 +80,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RpcClient", "src\Plugins\Rp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Plugins.ApplicationLogs.Tests", "tests\Neo.Plugins.ApplicationLogs.Tests\Neo.Plugins.ApplicationLogs.Tests.csproj", "{8C866DC8-2E55-4399-9563-2F47FD4602EC}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Plugins.DBFTPlugin.Tests", "tests\Neo.Plugins.DBFTPlugin.Tests\Neo.Plugins.DBFTPlugin.Tests.csproj", "{72997EAB-9B0C-4BC8-B797-955C219C2C97}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.Extensions.Tests", "tests\Neo.Extensions.Tests\Neo.Extensions.Tests.csproj", "{77FDEE2E-9381-4BFC-B9E6-741EDBD6B90F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.Extensions.Benchmarks", "benchmarks\Neo.Extensions.Benchmarks\Neo.Extensions.Benchmarks.csproj", "{B6CB2559-10F9-41AC-8D58-364BFEF9688B}"
@@ -240,6 +242,10 @@ Global
{5F984D2B-793F-4683-B53A-80050E6E0286}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5F984D2B-793F-4683-B53A-80050E6E0286}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5F984D2B-793F-4683-B53A-80050E6E0286}.Release|Any CPU.Build.0 = Release|Any CPU
+ {72997EAB-9B0C-4BC8-B797-955C219C2C97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {72997EAB-9B0C-4BC8-B797-955C219C2C97}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {72997EAB-9B0C-4BC8-B797-955C219C2C97}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {72997EAB-9B0C-4BC8-B797-955C219C2C97}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -283,6 +289,7 @@ Global
{77FDEE2E-9381-4BFC-B9E6-741EDBD6B90F} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1}
{B6CB2559-10F9-41AC-8D58-364BFEF9688B} = {C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC}
{5F984D2B-793F-4683-B53A-80050E6E0286} = {C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC}
+ {72997EAB-9B0C-4BC8-B797-955C219C2C97} = {7F257712-D033-47FF-B439-9D4320D06599}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BCBA19D9-F868-4C6D-8061-A2B91E06E3EC}
diff --git a/src/Neo/Wallets/SignException.cs b/src/Neo/Wallets/SignException.cs
new file mode 100644
index 0000000000..20ab39f1d3
--- /dev/null
+++ b/src/Neo/Wallets/SignException.cs
@@ -0,0 +1,27 @@
+// Copyright (C) 2015-2025 The Neo Project.
+//
+// SignException.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System;
+
+namespace Neo.Wallets
+{
+ ///
+ /// The exception that is thrown when `Sign` fails.
+ ///
+ public class SignException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message that describes the error.
+ public SignException(string message) : base(message) { }
+ }
+}
diff --git a/src/Neo/Wallets/Wallet.cs b/src/Neo/Wallets/Wallet.cs
index 917d0ec6af..031cf6adf7 100644
--- a/src/Neo/Wallets/Wallet.cs
+++ b/src/Neo/Wallets/Wallet.cs
@@ -594,7 +594,10 @@ private Transaction MakeTransaction(DataCache snapshot, ReadOnlyMemory scr
/// Signs the in the specified with the wallet.
///
/// The to be used.
- /// if the signature is successfully added to the context; otherwise, .
+ ///
+ /// if any signature is successfully added to the context;
+ /// otherwise, .
+ ///
public bool Sign(ContractParametersContext context)
{
if (context.Network != ProtocolSettings.Network) return false;
@@ -657,6 +660,49 @@ public bool Sign(ContractParametersContext context)
return fSuccess;
}
+ ///
+ /// Signs the specified data with the corresponding private key of the specified public key.
+ ///
+ /// The data to sign.
+ /// The public key.
+ ///
+ /// Thrown when or is .
+ ///
+ ///
+ /// Thrown when no account is found for the given public key or no private key is found for the given public key.
+ ///
+ /// The signature
+ public byte[] Sign(byte[] signData, ECPoint publicKey)
+ {
+ if (signData is null) throw new ArgumentNullException(nameof(signData));
+ if (publicKey is null) throw new ArgumentNullException(nameof(publicKey));
+
+ var account = GetAccount(publicKey);
+ if (account is null)
+ throw new SignException("No such account found");
+
+ var privateKey = account.GetKey()?.PrivateKey;
+ if (privateKey is null)
+ throw new SignException("No private key found for the given public key");
+
+ return Crypto.Sign(signData, privateKey);
+ }
+
+ ///
+ /// Checks if the wallet contains the specified public key and the corresponding private key.
+ /// If the wallet has the public key but not the private key, it will return .
+ ///
+ /// The public key.
+ ///
+ /// if the wallet contains the specified public key and the corresponding private key;
+ /// otherwise, .
+ ///
+ public bool ContainsKeyPair(ECPoint publicKey)
+ {
+ var account = GetAccount(publicKey);
+ return account != null && account.HasKey;
+ }
+
///
/// Checks that the specified password is correct for the wallet.
///
diff --git a/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs b/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs
index d15c56a649..4cb0b1bfe5 100644
--- a/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs
+++ b/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs
@@ -11,11 +11,11 @@
using Neo.Extensions;
using Neo.Ledger;
+using Neo.Network.P2P;
using Neo.Network.P2P.Payloads;
using Neo.Plugins.DBFTPlugin.Messages;
using Neo.Plugins.DBFTPlugin.Types;
using Neo.SmartContract;
-using Neo.Wallets;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
@@ -37,10 +37,15 @@ public ExtensiblePayload MakeChangeView(ChangeViewReason reason)
public ExtensiblePayload MakeCommit()
{
- return CommitPayloads[MyIndex] ?? (CommitPayloads[MyIndex] = MakeSignedPayload(new Commit
+ if (CommitPayloads[MyIndex] is not null)
+ return CommitPayloads[MyIndex];
+
+ var signData = EnsureHeader().GetSignData(dbftSettings.Network);
+ CommitPayloads[MyIndex] = MakeSignedPayload(new Commit
{
- Signature = EnsureHeader().Sign(keyPair, neoSystem.Settings.Network)
- }));
+ Signature = _wallet.Sign(signData, _myPublicKey)
+ });
+ return CommitPayloads[MyIndex];
}
private ExtensiblePayload MakeSignedPayload(ConsensusMessage message)
@@ -59,7 +64,7 @@ private void SignPayload(ExtensiblePayload payload)
try
{
sc = new ContractParametersContext(neoSystem.StoreView, payload, dbftSettings.Network);
- wallet.Sign(sc);
+ _wallet.Sign(sc);
}
catch (InvalidOperationException exception)
{
@@ -149,11 +154,22 @@ public ExtensiblePayload MakeRecoveryMessage()
}
return MakeSignedPayload(new RecoveryMessage
{
- ChangeViewMessages = LastChangeViewPayloads.Where(p => p != null).Select(p => GetChangeViewPayloadCompact(p)).Take(M).ToDictionary(p => p.ValidatorIndex),
+ ChangeViewMessages = LastChangeViewPayloads.Where(p => p != null)
+ .Select(p => GetChangeViewPayloadCompact(p))
+ .Take(M)
+ .ToDictionary(p => p.ValidatorIndex),
PrepareRequestMessage = prepareRequestMessage,
// We only need a PreparationHash set if we don't have the PrepareRequest information.
- PreparationHash = TransactionHashes == null ? PreparationPayloads.Where(p => p != null).GroupBy(p => GetMessage(p).PreparationHash, (k, g) => new { Hash = k, Count = g.Count() }).OrderByDescending(p => p.Count).Select(p => p.Hash).FirstOrDefault() : null,
- PreparationMessages = PreparationPayloads.Where(p => p != null).Select(p => GetPreparationPayloadCompact(p)).ToDictionary(p => p.ValidatorIndex),
+ PreparationHash = TransactionHashes == null
+ ? PreparationPayloads.Where(p => p != null)
+ .GroupBy(p => GetMessage(p).PreparationHash, (k, g) => new { Hash = k, Count = g.Count() })
+ .OrderByDescending(p => p.Count)
+ .Select(p => p.Hash)
+ .FirstOrDefault()
+ : null,
+ PreparationMessages = PreparationPayloads.Where(p => p != null)
+ .Select(p => GetPreparationPayloadCompact(p))
+ .ToDictionary(p => p.ValidatorIndex),
CommitMessages = CommitSent
? CommitPayloads.Where(p => p != null).Select(p => GetCommitPayloadCompact(p)).ToDictionary(p => p.ValidatorIndex)
: new Dictionary()
diff --git a/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.cs b/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.cs
index 9434fd010b..aaefb33b7b 100644
--- a/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.cs
+++ b/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.cs
@@ -55,11 +55,11 @@ public partial class ConsensusContext : IDisposable, ISerializable
public TransactionVerificationContext VerificationContext = new();
public StoreCache Snapshot { get; private set; }
- private KeyPair keyPair;
+ private ECPoint _myPublicKey;
private int _witnessSize;
private readonly NeoSystem neoSystem;
private readonly Settings dbftSettings;
- private readonly Wallet wallet;
+ private readonly Wallet _wallet;
private readonly IStore store;
private Dictionary cachedMessages;
@@ -114,7 +114,7 @@ public bool ValidatorsChanged
public ConsensusContext(NeoSystem neoSystem, Settings settings, Wallet wallet)
{
- this.wallet = wallet;
+ _wallet = wallet;
this.neoSystem = neoSystem;
dbftSettings = settings;
@@ -245,13 +245,14 @@ public void Reset(byte viewNumber)
LastSeenMessage[validator] = height;
}
}
- keyPair = null;
+
+ _myPublicKey = null;
for (int i = 0; i < Validators.Length; i++)
{
- WalletAccount account = wallet?.GetAccount(Validators[i]);
- if (account?.HasKey != true) continue;
+ // ContainsKeyPair may be called multiple times
+ if (!_wallet.ContainsKeyPair(Validators[i])) continue;
MyIndex = i;
- keyPair = account.GetKey();
+ _myPublicKey = Validators[MyIndex];
break;
}
cachedMessages = new Dictionary();
diff --git a/tests/Neo.Plugins.DBFTPlugin.Tests/Neo.Plugins.DBFTPlugin.Tests.csproj b/tests/Neo.Plugins.DBFTPlugin.Tests/Neo.Plugins.DBFTPlugin.Tests.csproj
new file mode 100644
index 0000000000..8fce097ebd
--- /dev/null
+++ b/tests/Neo.Plugins.DBFTPlugin.Tests/Neo.Plugins.DBFTPlugin.Tests.csproj
@@ -0,0 +1,19 @@
+
+
+
+ true
+ Exe
+ net9.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Neo.Plugins.DBFTPlugin.Tests/UT_ConsensusContext.cs b/tests/Neo.Plugins.DBFTPlugin.Tests/UT_ConsensusContext.cs
new file mode 100644
index 0000000000..6ae6e283a1
--- /dev/null
+++ b/tests/Neo.Plugins.DBFTPlugin.Tests/UT_ConsensusContext.cs
@@ -0,0 +1,118 @@
+// Copyright (C) 2015-2025 The Neo Project.
+//
+// UT_ConsensusContext.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Microsoft.Extensions.Configuration;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.Cryptography;
+using Neo.Cryptography.ECC;
+using Neo.IO;
+using Neo.Network.P2P;
+using Neo.Plugins.DBFTPlugin.Consensus;
+using Neo.Plugins.DBFTPlugin.Messages;
+using Neo.SmartContract.Native;
+using Neo.UnitTests;
+using Neo.UnitTests.Persistence;
+using System;
+using System.Collections.Generic;
+
+namespace Neo.Plugins.DBFTPlugin.Tests
+{
+ [TestClass]
+ public class UT_ConsensusContext
+ {
+ static readonly ProtocolSettings ProtocolSettings = ProtocolSettings.Default with
+ {
+ Network = 0x334F454Eu,
+ StandbyCommittee =
+ [
+ // private key: [0] => 0x01 * 32, [1] => 0x02 * 32, [2] => 0x03 * 32, [3] => 0x04 * 32
+ ECPoint.Parse("026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca16", ECCurve.Secp256r1),
+ ECPoint.Parse("02550f471003f3df97c3df506ac797f6721fb1a1fb7b8f6f83d224498a65c88e24", ECCurve.Secp256r1),
+ ECPoint.Parse("02591ab771ebbcfd6d9cb9094d106528add1a69d44c2c1f627f089ec58b9c61adf", ECCurve.Secp256r1),
+ ECPoint.Parse("0273103ec30b3ccf57daae08e93534aef144a35940cf6bbba12a0cf7cbd5d65a64", ECCurve.Secp256r1),
+ ],
+ ValidatorsCount = 4,
+ SeedList = ["seed1.neo.org:10333"],
+ };
+
+ private static IConfigurationSection MockConfig()
+ {
+ return new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary {
+ { "PluginConfiguration:IgnoreRecoveryLogs", "true" },
+ { "PluginConfiguration:Network", "0x334F454E" },
+ })
+ .Build()
+ .GetSection("PluginConfiguration");
+ }
+
+ [TestMethod]
+ public void TestReset()
+ {
+ var config = MockConfig();
+ var wallet = TestUtils.GenerateTestWallet("123");
+ var system = new NeoSystem(ProtocolSettings, new TestMemoryStoreProvider(new()));
+ var context = new ConsensusContext(system, new Settings(config), wallet);
+ context.Reset(0);
+ Assert.AreEqual(-1, context.MyIndex);
+
+ var validators = NativeContract.NEO.GetNextBlockValidators(system.GetSnapshotCache(), 4);
+ Assert.AreEqual(4, validators.Length);
+
+ var privateKey = new byte[32];
+ Array.Fill(privateKey, (byte)1);
+ wallet.CreateAccount(privateKey);
+
+ context = new ConsensusContext(system, new Settings(config), wallet);
+ context.Reset(0);
+ Assert.AreEqual(2, context.MyIndex);
+ }
+
+ [TestMethod]
+ public void TestMakeCommit()
+ {
+ var config = MockConfig();
+ var wallet = TestUtils.GenerateTestWallet("123");
+ var system = new NeoSystem(ProtocolSettings, new TestMemoryStoreProvider(new()));
+
+ var privateKey = new byte[32];
+ Array.Fill(privateKey, (byte)1);
+ wallet.CreateAccount(privateKey);
+
+ var context = new ConsensusContext(system, new Settings(config), wallet);
+ context.Reset(0);
+
+ context.Block = new()
+ {
+ Header = new() { PrevHash = UInt256.Zero, Index = 1, NextConsensus = UInt160.Zero },
+ Transactions = []
+ };
+ context.TransactionHashes = [];
+
+ var payload = context.MakeCommit();
+ Assert.IsNotNull(payload);
+ Assert.IsTrue(ReferenceEquals(payload, context.MakeCommit()));
+ Assert.IsNotNull(payload.Witness);
+
+ var data = context.CommitPayloads[context.MyIndex].Data;
+ var commit = new Commit();
+ var reader = new MemoryReader(data);
+ ((ISerializable)commit).Deserialize(ref reader);
+ Assert.AreEqual(1u, commit.BlockIndex);
+ Assert.AreEqual(2, commit.ValidatorIndex);
+ Assert.AreEqual(0, commit.ViewNumber);
+ Assert.AreEqual(64, commit.Signature.Length);
+
+ var signData = context.EnsureHeader().GetSignData(ProtocolSettings.Network);
+ Assert.IsTrue(Crypto.VerifySignature(signData, commit.Signature.Span, context.Validators[context.MyIndex]));
+ }
+ }
+}
diff --git a/tests/Neo.UnitTests/TestProtocolSettings.cs b/tests/Neo.UnitTests/TestProtocolSettings.cs
index 2757e6f9b5..a12c4345ea 100644
--- a/tests/Neo.UnitTests/TestProtocolSettings.cs
+++ b/tests/Neo.UnitTests/TestProtocolSettings.cs
@@ -15,10 +15,9 @@ namespace Neo.UnitTests
{
public static class TestProtocolSettings
{
- public static readonly ProtocolSettings Default = new()
+ public static readonly ProtocolSettings Default = ProtocolSettings.Default with
{
Network = 0x334F454Eu,
- AddressVersion = ProtocolSettings.Default.AddressVersion,
StandbyCommittee =
[
//Validators
@@ -54,18 +53,11 @@ public static class TestProtocolSettings
"seed4.neo.org:10333",
"seed5.neo.org:10333"
],
- MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock,
- MaxTransactionsPerBlock = ProtocolSettings.Default.MaxTransactionsPerBlock,
- MemoryPoolMaxTransactions = ProtocolSettings.Default.MemoryPoolMaxTransactions,
- MaxTraceableBlocks = ProtocolSettings.Default.MaxTraceableBlocks,
- InitialGasDistribution = ProtocolSettings.Default.InitialGasDistribution,
- Hardforks = ProtocolSettings.Default.Hardforks
};
- public static readonly ProtocolSettings SoleNode = new()
+ public static readonly ProtocolSettings SoleNode = ProtocolSettings.Default with
{
Network = 0x334F454Eu,
- AddressVersion = ProtocolSettings.Default.AddressVersion,
StandbyCommittee =
[
//Validators
@@ -80,12 +72,6 @@ public static class TestProtocolSettings
"seed4.neo.org:10333",
"seed5.neo.org:10333"
],
- MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock,
- MaxTransactionsPerBlock = ProtocolSettings.Default.MaxTransactionsPerBlock,
- MemoryPoolMaxTransactions = ProtocolSettings.Default.MemoryPoolMaxTransactions,
- MaxTraceableBlocks = ProtocolSettings.Default.MaxTraceableBlocks,
- InitialGasDistribution = ProtocolSettings.Default.InitialGasDistribution,
- Hardforks = ProtocolSettings.Default.Hardforks
};
}
}
diff --git a/tests/Neo.UnitTests/Wallets/UT_Wallet.cs b/tests/Neo.UnitTests/Wallets/UT_Wallet.cs
index 84fbb4cf6c..59aefc8264 100644
--- a/tests/Neo.UnitTests/Wallets/UT_Wallet.cs
+++ b/tests/Neo.UnitTests/Wallets/UT_Wallet.cs
@@ -10,7 +10,9 @@
// modifications are permitted.
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.Cryptography;
using Neo.Cryptography.ECC;
+using Neo.Extensions;
using Neo.Network.P2P.Payloads;
using Neo.SmartContract;
using Neo.SmartContract.Native;
@@ -82,9 +84,7 @@ public override WalletAccount CreateAccount(UInt160 scriptHash)
return account;
}
- public override void Delete()
- {
- }
+ public override void Delete() { }
public override bool DeleteAccount(UInt160 scriptHash)
{
@@ -107,9 +107,7 @@ public override bool VerifyPassword(string password)
return true;
}
- public override void Save()
- {
- }
+ public override void Save() { }
}
[TestClass]
@@ -150,7 +148,7 @@ public void TestCreateAccount1()
public void TestCreateAccount2()
{
MyWallet wallet = new();
- Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 });
+ Contract contract = Contract.Create([ContractParameterType.Boolean], [1]);
WalletAccount account = wallet.CreateAccount(contract, UT_Crypto.GenerateCertainKey(32).PrivateKey);
Assert.IsNotNull(account);
@@ -163,7 +161,7 @@ public void TestCreateAccount2()
public void TestCreateAccount3()
{
MyWallet wallet = new();
- Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 });
+ Contract contract = Contract.Create([ContractParameterType.Boolean], [1]);
Assert.IsNotNull(wallet.CreateAccount(contract, glkey));
}
@@ -230,7 +228,7 @@ public void TestGetAccounts()
public void TestGetAvailable()
{
MyWallet wallet = new();
- Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 });
+ Contract contract = Contract.Create([ContractParameterType.Boolean], [1]);
WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey);
account.Lock = false;
@@ -250,7 +248,7 @@ public void TestGetAvailable()
public void TestGetBalance()
{
MyWallet wallet = new();
- Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 });
+ Contract contract = Contract.Create([ContractParameterType.Boolean], [1]);
WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey);
account.Lock = false;
@@ -260,8 +258,10 @@ public void TestGetBalance()
var entry = snapshotCache.GetAndChange(key, () => new StorageItem(new AccountState()));
entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor;
- Assert.AreEqual(new BigDecimal(BigInteger.Zero, 0), wallet.GetBalance(snapshotCache, UInt160.Zero, new UInt160[] { account.ScriptHash }));
- Assert.AreEqual(new BigDecimal(new BigInteger(1000000000000M), 8), wallet.GetBalance(snapshotCache, NativeContract.GAS.Hash, new UInt160[] { account.ScriptHash }));
+ Assert.AreEqual(new BigDecimal(BigInteger.Zero, 0),
+ wallet.GetBalance(snapshotCache, UInt160.Zero, [account.ScriptHash]));
+ Assert.AreEqual(new BigDecimal(new BigInteger(1000000000000M), 8),
+ wallet.GetBalance(snapshotCache, NativeContract.GAS.Hash, [account.ScriptHash]));
entry = snapshotCache.GetAndChange(key, () => new StorageItem(new AccountState()));
entry.GetInteroperable().Balance = 0;
@@ -270,13 +270,15 @@ public void TestGetBalance()
[TestMethod]
public void TestGetPrivateKeyFromNEP2()
{
- Action action = () => Wallet.GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2", ProtocolSettings.Default.AddressVersion, 2, 1, 1);
+ Action action = () => Wallet.GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2",
+ ProtocolSettings.Default.AddressVersion, 2, 1, 1);
Assert.ThrowsExactly(action);
action = () => Wallet.GetPrivateKeyFromNEP2(nep2Key, "Test", ProtocolSettings.Default.AddressVersion, 2, 1, 1);
Assert.ThrowsExactly(action);
- CollectionAssert.AreEqual(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, Wallet.GetPrivateKeyFromNEP2(nep2Key, "pwd", ProtocolSettings.Default.AddressVersion, 2, 1, 1));
+ CollectionAssert.AreEqual("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f".HexToBytes(),
+ Wallet.GetPrivateKeyFromNEP2(nep2Key, "pwd", ProtocolSettings.Default.AddressVersion, 2, 1, 1));
}
[TestMethod]
@@ -288,7 +290,8 @@ public void TestGetPrivateKeyFromWIF()
action = () => Wallet.GetPrivateKeyFromWIF("3vQB7B6MrGQZaxCuFg4oh");
Assert.ThrowsExactly(action);
- CollectionAssert.AreEqual(new byte[] { 199, 19, 77, 111, 216, 231, 61, 129, 158, 130, 117, 92, 100, 201, 55, 136, 216, 219, 9, 97, 146, 158, 2, 90, 83, 54, 60, 76, 192, 42, 105, 98 }, Wallet.GetPrivateKeyFromWIF("L3tgppXLgdaeqSGSFw1Go3skBiy8vQAM7YMXvTHsKQtE16PBncSU"));
+ CollectionAssert.AreEqual("c7134d6fd8e73d819e82755c64c93788d8db0961929e025a53363c4cc02a6962".HexToBytes(),
+ Wallet.GetPrivateKeyFromWIF("L3tgppXLgdaeqSGSFw1Go3skBiy8vQAM7YMXvTHsKQtE16PBncSU"));
}
[TestMethod]
@@ -310,44 +313,41 @@ public void TestMakeTransaction1()
{
var snapshotCache = TestBlockchain.GetTestSnapshotCache();
MyWallet wallet = new();
- Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 });
+ Contract contract = Contract.Create([ContractParameterType.Boolean], [1]);
WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey);
account.Lock = false;
- Action action = () => wallet.MakeTransaction(snapshotCache, new TransferOutput[]
- {
- new TransferOutput()
+ Action action = () => wallet.MakeTransaction(snapshotCache, [
+ new()
{
- AssetId = NativeContract.GAS.Hash,
- ScriptHash = account.ScriptHash,
- Value = new BigDecimal(BigInteger.One,8),
- Data = "Dec 12th"
+ AssetId = NativeContract.GAS.Hash,
+ ScriptHash = account.ScriptHash,
+ Value = new BigDecimal(BigInteger.One, 8),
+ Data = "Dec 12th"
}
- }, UInt160.Zero);
+ ], UInt160.Zero);
Assert.ThrowsExactly(action);
- action = () => wallet.MakeTransaction(snapshotCache, new TransferOutput[]
- {
- new TransferOutput()
+ action = () => wallet.MakeTransaction(snapshotCache, [
+ new()
{
- AssetId = NativeContract.GAS.Hash,
- ScriptHash = account.ScriptHash,
- Value = new BigDecimal(BigInteger.One,8),
- Data = "Dec 12th"
+ AssetId = NativeContract.GAS.Hash,
+ ScriptHash = account.ScriptHash,
+ Value = new BigDecimal(BigInteger.One, 8),
+ Data = "Dec 12th"
}
- }, account.ScriptHash);
+ ], account.ScriptHash);
Assert.ThrowsExactly(action);
- action = () => wallet.MakeTransaction(snapshotCache, new TransferOutput[]
- {
- new TransferOutput()
+ action = () => wallet.MakeTransaction(snapshotCache, [
+ new()
{
AssetId = UInt160.Zero,
ScriptHash = account.ScriptHash,
Value = new BigDecimal(BigInteger.One,8),
Data = "Dec 12th"
}
- }, account.ScriptHash);
+ ], account.ScriptHash);
Assert.ThrowsExactly(action);
// Fake balance
@@ -359,27 +359,25 @@ public void TestMakeTransaction1()
var entry2 = snapshotCache.GetAndChange(key, () => new StorageItem(new NeoToken.NeoAccountState()));
entry2.GetInteroperable().Balance = 10000 * NativeContract.NEO.Factor;
- var tx = wallet.MakeTransaction(snapshotCache, new TransferOutput[]
- {
- new TransferOutput()
+ var tx = wallet.MakeTransaction(snapshotCache, [
+ new()
{
AssetId = NativeContract.GAS.Hash,
ScriptHash = account.ScriptHash,
Value = new BigDecimal(BigInteger.One,8)
}
- });
+ ]);
Assert.IsNotNull(tx);
- tx = wallet.MakeTransaction(snapshotCache, new TransferOutput[]
- {
- new TransferOutput()
+ tx = wallet.MakeTransaction(snapshotCache, [
+ new()
{
AssetId = NativeContract.NEO.Hash,
ScriptHash = account.ScriptHash,
Value = new BigDecimal(BigInteger.One,8),
Data = "Dec 12th"
}
- });
+ ]);
Assert.IsNotNull(tx);
entry1 = snapshotCache.GetAndChange(key, () => new StorageItem(new AccountState()));
@@ -393,10 +391,10 @@ public void TestMakeTransaction2()
{
var snapshotCache = TestBlockchain.GetTestSnapshotCache();
MyWallet wallet = new();
- Action action = () => wallet.MakeTransaction(snapshotCache, Array.Empty(), null, null, Array.Empty());
+ Action action = () => wallet.MakeTransaction(snapshotCache, Array.Empty(), null, null, []);
Assert.ThrowsExactly(action);
- Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 });
+ Contract contract = Contract.Create([ContractParameterType.Boolean], [1]);
WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey);
account.Lock = false;
@@ -405,15 +403,17 @@ public void TestMakeTransaction2()
var entry = snapshotCache.GetAndChange(key, () => new StorageItem(new AccountState()));
entry.GetInteroperable().Balance = 1000000 * NativeContract.GAS.Factor;
- var tx = wallet.MakeTransaction(snapshotCache, Array.Empty(), account.ScriptHash, new[]{ new Signer()
- {
- Account = account.ScriptHash,
- Scopes = WitnessScope.CalledByEntry
- }}, Array.Empty());
+ var tx = wallet.MakeTransaction(snapshotCache, Array.Empty(), account.ScriptHash, [
+ new()
+ {
+ Account = account.ScriptHash,
+ Scopes = WitnessScope.CalledByEntry
+ }
+ ], []);
Assert.IsNotNull(tx);
- tx = wallet.MakeTransaction(snapshotCache, Array.Empty(), null, null, Array.Empty());
+ tx = wallet.MakeTransaction(snapshotCache, Array.Empty(), null, null, []);
Assert.IsNotNull(tx);
entry = snapshotCache.GetAndChange(key, () => new StorageItem(new AccountState()));
@@ -433,5 +433,70 @@ public void TestVerifyPassword()
Assert.Fail();
}
}
+
+ [TestMethod]
+ public void TestSign()
+ {
+ MyWallet wallet = new();
+ Action action = () => wallet.Sign([0xa, 0xb, 0xc, 0xd], glkey.PublicKey);
+ Assert.ThrowsExactly(action); // no account
+
+ wallet.CreateAccount(glkey.PrivateKey);
+
+ var signature = wallet.Sign([0xa, 0xb, 0xc, 0xd], glkey.PublicKey);
+ Assert.IsNotNull(signature);
+ Assert.AreEqual(signature.Length, 64);
+
+ var isValid = Crypto.VerifySignature([0xa, 0xb, 0xc, 0xd], signature, glkey.PublicKey);
+ Assert.IsTrue(isValid);
+
+ var key = new byte[32];
+ Array.Fill(key, (byte)0x02);
+
+ var pair = new KeyPair(key);
+ var scriptHash = Contract.CreateSignatureRedeemScript(pair.PublicKey).ToScriptHash();
+ wallet.CreateAccount(scriptHash);
+ Assert.IsNotNull(pair.PublicKey);
+
+ action = () => wallet.Sign([0xa, 0xb, 0xc, 0xd], pair.PublicKey);
+ Assert.ThrowsExactly(action); // no private key
+ }
+
+ [TestMethod]
+ public void TestContainsKeyPair()
+ {
+ MyWallet wallet = new();
+ var contains = wallet.ContainsKeyPair(glkey.PublicKey);
+ Assert.IsFalse(contains);
+
+ wallet.CreateAccount(glkey.PrivateKey);
+
+ contains = wallet.ContainsKeyPair(glkey.PublicKey);
+ Assert.IsTrue(contains);
+
+ var key = new byte[32];
+ Array.Fill(key, (byte)0x01);
+
+ var pair = new KeyPair(key);
+ contains = wallet.ContainsKeyPair(pair.PublicKey);
+ Assert.IsFalse(contains);
+
+ wallet.CreateAccount(pair.PrivateKey);
+ contains = wallet.ContainsKeyPair(pair.PublicKey);
+ Assert.IsTrue(contains);
+
+ contains = wallet.ContainsKeyPair(glkey.PublicKey);
+ Assert.IsTrue(contains);
+
+ key = new byte[32];
+ Array.Fill(key, (byte)0x02);
+
+ pair = new KeyPair(key);
+ var scriptHash = Contract.CreateSignatureRedeemScript(pair.PublicKey).ToScriptHash();
+ wallet.CreateAccount(scriptHash);
+
+ contains = wallet.ContainsKeyPair(pair.PublicKey);
+ Assert.IsFalse(contains); // no private key
+ }
}
}