diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index f57f1f9679..d87ec74967 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -130,7 +130,7 @@ public bool Add(Contract contract, params object[] parameters) public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) { - if (contract.Script.IsMultiSigContract(out _, out _)) + if (contract.Script.IsMultiSigContract(out _, out ECPoint[] points)) { ContextItem item = CreateItem(contract); if (item == null) return false; @@ -139,24 +139,6 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) item.Signatures = new Dictionary(); else if (item.Signatures.ContainsKey(pubkey)) return false; - List points = new List(); - { - int i = 0; - switch (contract.Script[i++]) - { - case (byte)OpCode.PUSHINT8: - ++i; - break; - case (byte)OpCode.PUSHINT16: - i += 2; - break; - } - while (contract.Script[i++] == (byte)OpCode.PUSHDATA1) - { - points.Add(ECPoint.DecodePoint(contract.Script.AsSpan(++i, 33), ECCurve.Secp256r1)); - i += 33; - } - } if (!points.Contains(pubkey)) return false; item.Signatures.Add(pubkey, signature); if (item.Signatures.Count == contract.ParameterList.Length) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index faac766baf..4823832fab 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -1,17 +1,44 @@ using Neo.Cryptography; +using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.VM; using Neo.VM.Types; using System; using System.Buffers.Binary; +using System.Collections.Generic; using System.Text; namespace Neo.SmartContract { public static class Helper { + public static bool IsMultiSigContract(this byte[] script) + { + return IsMultiSigContract(script, out _, out _, null); + } + public static bool IsMultiSigContract(this byte[] script, out int m, out int n) + { + return IsMultiSigContract(script, out m, out n, null); + } + + public static bool IsMultiSigContract(this byte[] script, out int m, out ECPoint[] points) + { + List list = new List(); + if (IsMultiSigContract(script, out m, out _, list)) + { + points = list.ToArray(); + return true; + } + else + { + points = null; + return false; + } + } + + private static bool IsMultiSigContract(byte[] script, out int m, out int n, List points) { m = 0; n = 0; int i = 0; @@ -38,6 +65,7 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) { if (script.Length <= i + 35) return false; if (script[++i] != 33) return false; + points?.Add(ECPoint.DecodePoint(script.AsSpan(i + 1, 33), ECCurve.Secp256r1)); i += 34; ++n; } @@ -81,7 +109,7 @@ public static bool IsSignatureContract(this byte[] script) public static bool IsStandardContract(this byte[] script) { - return script.IsSignatureContract() || script.IsMultiSigContract(out _, out _); + return script.IsSignatureContract() || script.IsMultiSigContract(); } public static uint ToInteropMethodHash(this string method) diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 892cc40c1c..3fe4c61652 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -382,10 +382,37 @@ public bool Sign(ContractParametersContext context) foreach (UInt160 scriptHash in context.ScriptHashes) { WalletAccount account = GetAccount(scriptHash); - if (account?.HasKey != true) continue; - KeyPair key = account.GetKey(); - byte[] signature = context.Verifiable.Sign(key); - fSuccess |= context.AddSignature(account.Contract, key.PublicKey, signature); + if (account is null) continue; + + // Try to sign self-contained multiSig + + Contract multiSigContract = account.Contract; + + if (multiSigContract != null && + multiSigContract.Script.IsMultiSigContract(out int m, out ECPoint[] points)) + { + foreach (var point in points) + { + account = GetAccount(point); + if (account?.HasKey != true) continue; + KeyPair key = account.GetKey(); + byte[] signature = context.Verifiable.Sign(key); + fSuccess |= context.AddSignature(multiSigContract, key.PublicKey, signature); + if (fSuccess) m--; + if (context.Completed || m <= 0) break; + } + } + else + { + // Try to sign with regular accounts + + if (account.HasKey) + { + KeyPair key = account.GetKey(); + byte[] signature = context.Verifiable.Sign(key); + fSuccess |= context.AddSignature(account.Contract, key.PublicKey, signature); + } + } } return fSuccess; } diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index dc7729f626..8be6866885 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -4,8 +4,10 @@ using Neo.SmartContract; using Neo.Wallets; using System; +using System.Linq; using System.Security.Cryptography; using System.Text; +using ECPoint = Neo.Cryptography.ECC.ECPoint; namespace Neo.UnitTests.SmartContract { @@ -25,7 +27,8 @@ public void TestIsMultiSigContract() publicKeys1[i] = key1.PublicKey; } byte[] script1 = Contract.CreateMultiSigRedeemScript(20, publicKeys1); - Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script1, out int m1, out int n1)); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script1, out _, out ECPoint[] p1)); + CollectionAssert.AreEqual(publicKeys1.OrderBy(p => p).ToArray(), p1); Neo.Cryptography.ECC.ECPoint[] publicKeys2 = new Neo.Cryptography.ECC.ECPoint[256]; for (int i = 0; i < 256; i++) @@ -37,7 +40,8 @@ public void TestIsMultiSigContract() publicKeys2[i] = key2.PublicKey; } byte[] script2 = Contract.CreateMultiSigRedeemScript(256, publicKeys2); - Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script2, out int m2, out int n2)); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script2, out _, out ECPoint[] p2)); + CollectionAssert.AreEqual(publicKeys2.OrderBy(p => p).ToArray(), p2); Neo.Cryptography.ECC.ECPoint[] publicKeys3 = new Neo.Cryptography.ECC.ECPoint[3]; for (int i = 0; i < 3; i++) @@ -49,7 +53,8 @@ public void TestIsMultiSigContract() publicKeys3[i] = key3.PublicKey; } byte[] script3 = Contract.CreateMultiSigRedeemScript(3, publicKeys3); - Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script3, out int m3, out int n3)); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script3, out _, out ECPoint[] p3)); + CollectionAssert.AreEqual(publicKeys3.OrderBy(p => p).ToArray(), p3); Neo.Cryptography.ECC.ECPoint[] publicKeys4 = new Neo.Cryptography.ECC.ECPoint[3]; for (int i = 0; i < 3; i++) @@ -62,8 +67,8 @@ public void TestIsMultiSigContract() } byte[] script4 = Contract.CreateMultiSigRedeemScript(3, publicKeys4); script4[script4.Length - 1] = 0x00; - Assert.AreEqual(false, Neo.SmartContract.Helper.IsMultiSigContract(script4, out int m4, out int n4)); - + Assert.AreEqual(false, Neo.SmartContract.Helper.IsMultiSigContract(script4, out _, out ECPoint[] p4)); + Assert.IsNull(p4); } [TestMethod]