Skip to content

Commit

Permalink
Allow MultiSig contracts in Wallet.Sign method (neo-project#1451)
Browse files Browse the repository at this point in the history
* Allow to sign multisignature account if you have some of them

* Update IsMultiSigContract()

* Fixes UT

* Check null

* Update Wallet.cs

* Change if

Co-authored-by: Erik Zhang <erik@neo.org>
  • Loading branch information
2 people authored and Tommo-L committed Jun 22, 2020
1 parent 8fd6ade commit 01cfd33
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 29 deletions.
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

0 comments on commit 01cfd33

Please sign in to comment.