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

Allow MultiSig contracts in Wallet.Sign method #1451

Merged
merged 8 commits into from
Feb 27, 2020
Merged
Show file tree
Hide file tree
Changes from 7 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
20 changes: 1 addition & 19 deletions src/neo/SmartContract/ContractParametersContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -139,24 +139,6 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature)
item.Signatures = new Dictionary<ECPoint, byte[]>();
else if (item.Signatures.ContainsKey(pubkey))
return false;
List<ECPoint> points = new List<ECPoint>();
{
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)
Expand Down
30 changes: 29 additions & 1 deletion src/neo/SmartContract/Helper.cs
Original file line number Diff line number Diff line change
@@ -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<ECPoint> list = new List<ECPoint>();
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<ECPoint> points)
{
m = 0; n = 0;
int i = 0;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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)
Expand Down
35 changes: 31 additions & 4 deletions src/neo/Wallets/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
15 changes: 10 additions & 5 deletions tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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++)
Expand All @@ -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++)
Expand All @@ -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++)
Expand All @@ -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]
Expand Down