diff --git a/src/Stratis.FullNode.sln b/src/Stratis.FullNode.sln
index 5c5e114540..875a8d413d 100644
--- a/src/Stratis.FullNode.sln
+++ b/src/Stratis.FullNode.sln
@@ -189,7 +189,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.Features.Unity3dApi
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.Bitcoin.Features.ExternalApi", "Stratis.Bitcoin.Features.ExternalAPI\Stratis.Bitcoin.Features.ExternalApi.csproj", "{21D41C53-62D8-4F68-A3D1-88BB2AB195E3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stratis.Interop.Contracts", "Stratis.Interop.Contracts\Stratis.Interop.Contracts.csproj", "{3A82CB5E-FB80-4A19-9223-AF25123F7288}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.Interop.Contracts", "Stratis.Interop.Contracts\Stratis.Interop.Contracts.csproj", "{3A82CB5E-FB80-4A19-9223-AF25123F7288}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stratis.SCL", "Stratis.SCL\Stratis.SCL.csproj", "{B80F392A-10CD-4A19-9B55-A7FA477533FC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -505,6 +507,10 @@ Global
{3A82CB5E-FB80-4A19-9223-AF25123F7288}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A82CB5E-FB80-4A19-9223-AF25123F7288}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A82CB5E-FB80-4A19-9223-AF25123F7288}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B80F392A-10CD-4A19-9B55-A7FA477533FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B80F392A-10CD-4A19-9B55-A7FA477533FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B80F392A-10CD-4A19-9B55-A7FA477533FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B80F392A-10CD-4A19-9B55-A7FA477533FC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -577,6 +583,7 @@ Global
{B08D2057-F48D-4E72-99F4-95A35E6E0DFD} = {15D29FFD-6142-4DC5-AFFD-10BA0CA55C45}
{21D41C53-62D8-4F68-A3D1-88BB2AB195E3} = {15D29FFD-6142-4DC5-AFFD-10BA0CA55C45}
{3A82CB5E-FB80-4A19-9223-AF25123F7288} = {1B9A916F-DDAC-4675-B424-EDEDC1A58231}
+ {B80F392A-10CD-4A19-9B55-A7FA477533FC} = {1B9A916F-DDAC-4675-B424-EDEDC1A58231}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6C780ABA-5872-4B83-AD3F-A5BD423AD907}
diff --git a/src/Stratis.SCL/ECRecover.cs b/src/Stratis.SCL/ECRecover.cs
new file mode 100644
index 0000000000..bbb74ee33b
--- /dev/null
+++ b/src/Stratis.SCL/ECRecover.cs
@@ -0,0 +1,56 @@
+using System;
+using NBitcoin;
+using Stratis.SmartContracts;
+
+namespace Stratis.SCL.Crypto
+{
+ public static class ECRecover
+ {
+ ///
+ /// Retrieves the address of the signer of an ECDSA signature.
+ ///
+ ///
+ /// The ECDSA signature prepended with header information specifying the correct value of recId.
+ /// The Address for the signer of a signature.
+ /// A bool representing whether or not the signer was retrieved successfully.
+ public static bool TryGetSigner(byte[] message, byte[] signature, out Address address)
+ {
+ address = Address.Zero;
+
+ if (message == null || signature == null)
+ return false;
+
+ // NBitcoin is very throwy
+ try
+ {
+ uint256 hashedUint256 = GetUint256FromMessage(message);
+
+ PubKey pubKey = PubKey.RecoverCompact(hashedUint256, signature);
+
+ address = CreateAddress(pubKey.Hash.ToBytes());
+
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private static uint256 GetUint256FromMessage(byte[] message)
+ {
+ return new uint256(SHA3.Keccak256(message));
+ }
+
+ private static Address CreateAddress(byte[] bytes)
+ {
+ uint pn0 = BitConverter.ToUInt32(bytes, 0);
+ uint pn1 = BitConverter.ToUInt32(bytes, 4);
+ uint pn2 = BitConverter.ToUInt32(bytes, 8);
+ uint pn3 = BitConverter.ToUInt32(bytes, 12);
+ uint pn4 = BitConverter.ToUInt32(bytes, 16);
+
+ return new Address(pn0, pn1, pn2, pn3, pn4);
+ }
+ }
+}
diff --git a/src/Stratis.SCL/Operations.cs b/src/Stratis.SCL/Operations.cs
new file mode 100644
index 0000000000..464fa45320
--- /dev/null
+++ b/src/Stratis.SCL/Operations.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Stratis.SCL.Base
+{
+ public static class Operations
+ {
+ public static void Noop() { }
+ }
+}
diff --git a/src/Stratis.SCL/SHA3.cs b/src/Stratis.SCL/SHA3.cs
new file mode 100644
index 0000000000..8e1071dc3d
--- /dev/null
+++ b/src/Stratis.SCL/SHA3.cs
@@ -0,0 +1,17 @@
+using HashLib;
+
+namespace Stratis.SCL.Crypto
+{
+ public static class SHA3
+ {
+ ///
+ /// Returns a 32-byte Keccak256 hash of the given bytes.
+ ///
+ ///
+ ///
+ public static byte[] Keccak256(byte[] input)
+ {
+ return HashFactory.Crypto.SHA3.CreateKeccak256().ComputeBytes(input).GetBytes();
+ }
+ }
+}
diff --git a/src/Stratis.SCL/SSAS.cs b/src/Stratis.SCL/SSAS.cs
new file mode 100644
index 0000000000..65da10ff7a
--- /dev/null
+++ b/src/Stratis.SCL/SSAS.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.Extensions.Primitives;
+using NBitcoin.DataEncoders;
+using Nethereum.RLP;
+using Stratis.SmartContracts;
+
+namespace Stratis.SCL.Crypto
+{
+ public static class SSAS
+ {
+ public static byte[] ValidateAndParse(Address address, string url, byte[] signature, string signatureTemplateMap)
+ {
+ // First verify the signature.
+ if (!ECRecover.TryGetSigner(Encoding.ASCII.GetBytes(url), signature, out Address signer) || signer != address)
+ return null;
+
+ try
+ {
+ // Create a mapping of available url arguments.
+ Dictionary argDict = QueryHelpers.ParseQuery(new Uri(url).Query);
+
+ // The "signatureTemplateMap" takes the following form: "uid#11,symbol#4,amount#12,targetAddres#4,targetNetwork#4"
+ var arguments = signatureTemplateMap
+ .Split(",", StringSplitOptions.RemoveEmptyEntries)
+ .Select(argName =>
+ {
+ var argNameSplit = argName.Split("#");
+ var argValue = argDict[argNameSplit[1]].ToString();
+ var fieldType = int.Parse(argNameSplit[0]);
+ var hexEncoder = new HexEncoder();
+ byte[] argumentBytes;
+ switch (fieldType)
+ {
+ case 4:
+ argumentBytes = Encoders.ASCII.DecodeData(argValue);
+ break;
+ case 11:
+ // URL's should contain UInt128 as 16 bytes encoded in hexadecimal.
+ if (argValue.Length != 32)
+ return null;
+
+ argumentBytes = hexEncoder.DecodeData(argValue);
+ break;
+ case 12:
+ // If the value contains a "." then its being passed as an amount.
+ // We assume 8 decimals for the conversion to UInt256.
+ if (argValue.Contains('.'))
+ {
+ decimal amt = decimal.Parse(argValue);
+
+ // Convert to UInt256.
+ argumentBytes = ((UInt256)((ulong)(amt * 100000000 /* 8 decimals */))).ToBytes();
+ }
+ else
+ {
+ // URL's should contain UInt256 as 32 bytes encoded in hexadecimal.
+ if (argValue.Length != 32)
+ return null;
+
+ argumentBytes = hexEncoder.DecodeData(argValue);
+ }
+ break;
+
+ // TODO: Handle more types as required.
+
+ default:
+ return null;
+ }
+ return argumentBytes;
+ })
+ .ToArray();
+
+ // Convert the list of objects to RLE.
+ return RLP.EncodeElementsAndList(arguments);
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/Stratis.SCL/Stratis.SCL.csproj b/src/Stratis.SCL/Stratis.SCL.csproj
new file mode 100644
index 0000000000..b17ad6f26d
--- /dev/null
+++ b/src/Stratis.SCL/Stratis.SCL.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net6.0
+
+ 2.0.2.0
+ 2.0.2.0
+ 2.0.2.0
+ Stratis Group Ltd.
+ Stratis.SCL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Stratis.SmartContracts.CLR.Tests/ContractCompilerTests.cs b/src/Stratis.SmartContracts.CLR.Tests/ContractCompilerTests.cs
index 5a30e83cc5..7976d4fadc 100644
--- a/src/Stratis.SmartContracts.CLR.Tests/ContractCompilerTests.cs
+++ b/src/Stratis.SmartContracts.CLR.Tests/ContractCompilerTests.cs
@@ -33,12 +33,13 @@ public void SmartContract_ReferenceResolver_HasCorrectAssemblies()
{
List allowedAssemblies = ReferencedAssemblyResolver.AllowedAssemblies.ToList();
- Assert.Equal(5, allowedAssemblies.Count);
+ Assert.Equal(6, allowedAssemblies.Count);
Assert.Contains(allowedAssemblies, a => a.GetName().Name == "System.Runtime");
Assert.Contains(allowedAssemblies, a => a.GetName().Name == "System.Private.CoreLib");
Assert.Contains(allowedAssemblies, a => a.GetName().Name == "Stratis.SmartContracts");
Assert.Contains(allowedAssemblies, a => a.GetName().Name == "System.Linq");
Assert.Contains(allowedAssemblies, a => a.GetName().Name == "Stratis.SmartContracts.Standards");
+ Assert.Contains(allowedAssemblies, a => a.GetName().Name == "Stratis.SCL");
}
[Fact]
diff --git a/src/Stratis.SmartContracts.CLR.Tests/ContractExecutorTests.cs b/src/Stratis.SmartContracts.CLR.Tests/ContractExecutorTests.cs
index 19ae959cdd..bbcee578ac 100644
--- a/src/Stratis.SmartContracts.CLR.Tests/ContractExecutorTests.cs
+++ b/src/Stratis.SmartContracts.CLR.Tests/ContractExecutorTests.cs
@@ -333,6 +333,12 @@ public void Execute_MultipleIfElseBlocks_ExecutionSucceeds()
AssertSuccessfulContractMethodExecution(nameof(MultipleIfElseBlocks), nameof(MultipleIfElseBlocks.PersistNormalizeValue), new object[] { "z" });
}
+ [Fact]
+ public void Execute_LibraryContract_ExecutionSucceeds()
+ {
+ AssertSuccessfulContractMethodExecution(nameof(LibraryTest), nameof(LibraryTest.Exists));
+ }
+
private void AssertSuccessfulContractMethodExecution(string contractName, string methodName, object[] methodParameters = null, string expectedReturn = null)
{
var transactionValue = (Money)100;
diff --git a/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/LibraryTest.cs b/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/LibraryTest.cs
new file mode 100644
index 0000000000..9b48635de2
--- /dev/null
+++ b/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/LibraryTest.cs
@@ -0,0 +1,16 @@
+using Stratis.SmartContracts;
+using Base = Stratis.SCL.Base;
+
+[Deploy]
+public class LibraryTest : SmartContract
+{
+ public LibraryTest(ISmartContractState state) : base(state)
+ {
+ Base.Operations.Noop();
+ }
+
+ public void Exists()
+ {
+ State.SetBool("Exists", true);
+ }
+}
diff --git a/src/Stratis.SmartContracts.CLR.Tests/Stratis.SmartContracts.CLR.Tests.csproj b/src/Stratis.SmartContracts.CLR.Tests/Stratis.SmartContracts.CLR.Tests.csproj
index 6da53f9e67..1b6a8a40d5 100644
--- a/src/Stratis.SmartContracts.CLR.Tests/Stratis.SmartContracts.CLR.Tests.csproj
+++ b/src/Stratis.SmartContracts.CLR.Tests/Stratis.SmartContracts.CLR.Tests.csproj
@@ -74,6 +74,9 @@
Always
+
+ Always
+
PreserveNewest
diff --git a/src/Stratis.SmartContracts.CLR.Validation.Tests/SmartContractDeterminismValidatorTests.cs b/src/Stratis.SmartContracts.CLR.Validation.Tests/SmartContractDeterminismValidatorTests.cs
index 5971b9029c..9ed64d361c 100644
--- a/src/Stratis.SmartContracts.CLR.Validation.Tests/SmartContractDeterminismValidatorTests.cs
+++ b/src/Stratis.SmartContracts.CLR.Validation.Tests/SmartContractDeterminismValidatorTests.cs
@@ -834,6 +834,44 @@ public Test(ISmartContractState state): base(state)
Assert.False(result.IsValid);
Assert.NotEmpty(result.Errors);
Assert.True(result.Errors.All(e => e is ModuleDefinitionValidationResult));
- }
+ }
+
+ [Fact]
+ public void SmartContractValidator_Allows_SCL()
+ {
+ var adjustedSource = @"
+using Stratis.SmartContracts;
+using Base = Stratis.SCL.Base;
+
+[Deploy]
+public class LibraryTest : SmartContract
+{
+ public LibraryTest(ISmartContractState state) : base(state)
+ {
+ Base.Operations.Noop();
+ }
+
+ public void Exists()
+ {
+ State.SetBool(""Exists"", true);
+ }
+}";
+ ContractCompilationResult compilationResult = ContractCompiler.Compile(adjustedSource);
+ Assert.True(compilationResult.Success);
+
+ byte[] assemblyBytes = compilationResult.Compilation;
+ IContractModuleDefinition decompilation = ContractDecompiler.GetModuleDefinition(assemblyBytes).Value;
+
+ // Add a module reference
+ decompilation.ModuleDefinition.ModuleReferences.Add(new ModuleReference("Test.dll"));
+
+ var moduleDefinition = decompilation.ModuleDefinition;
+
+ SmartContractValidationResult result = new SmartContractValidator().Validate(moduleDefinition);
+
+ Assert.False(result.IsValid);
+ Assert.NotEmpty(result.Errors);
+ Assert.True(result.Errors.All(e => e is ModuleDefinitionValidationResult));
+ }
}
}
\ No newline at end of file
diff --git a/src/Stratis.SmartContracts.CLR/Compilation/ReferencedAssemblyResolver.cs b/src/Stratis.SmartContracts.CLR/Compilation/ReferencedAssemblyResolver.cs
index 76a05075a0..ecf6621853 100644
--- a/src/Stratis.SmartContracts.CLR/Compilation/ReferencedAssemblyResolver.cs
+++ b/src/Stratis.SmartContracts.CLR/Compilation/ReferencedAssemblyResolver.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
+using Stratis.SCL;
using Stratis.SmartContracts.Standards;
namespace Stratis.SmartContracts.CLR.Compilation
@@ -23,7 +24,8 @@ public static class ReferencedAssemblyResolver
Core,
typeof(SmartContract).Assembly,
typeof(Enumerable).Assembly,
- typeof(IStandardToken).Assembly
+ typeof(IStandardToken).Assembly,
+ typeof(SCL.Base.Operations).Assembly
};
}
}
\ No newline at end of file
diff --git a/src/Stratis.SmartContracts.CLR/Stratis.SmartContracts.CLR.csproj b/src/Stratis.SmartContracts.CLR/Stratis.SmartContracts.CLR.csproj
index cf292140e1..9e5c4e1f00 100644
--- a/src/Stratis.SmartContracts.CLR/Stratis.SmartContracts.CLR.csproj
+++ b/src/Stratis.SmartContracts.CLR/Stratis.SmartContracts.CLR.csproj
@@ -18,6 +18,7 @@
+
diff --git a/src/Stratis.SmartContracts.IntegrationTests/ECRecoverTests.cs b/src/Stratis.SmartContracts.IntegrationTests/ECRecoverTests.cs
new file mode 100644
index 0000000000..28404a729f
--- /dev/null
+++ b/src/Stratis.SmartContracts.IntegrationTests/ECRecoverTests.cs
@@ -0,0 +1,179 @@
+using System.Threading.Tasks;
+using NBitcoin;
+using Stratis.Bitcoin.Features.SmartContracts.Models;
+using Stratis.SCL.Crypto;
+using Stratis.SmartContracts.CLR;
+using Stratis.SmartContracts.CLR.Compilation;
+using Stratis.SmartContracts.CLR.Serialization;
+using Stratis.SmartContracts.Core;
+using Stratis.SmartContracts.Networks;
+using Stratis.SmartContracts.Tests.Common.MockChain;
+using Xunit;
+using EcRecoverProvider = Stratis.SCL.Crypto.ECRecover;
+using Key = NBitcoin.Key;
+
+namespace Stratis.SmartContracts.IntegrationTests
+{
+ public class ECRecoverTests
+ {
+ // 2 things to test:
+
+ // 1) That we have the ECDSA code and can make it available.
+
+ [Fact]
+ public void CanSignAndRetrieveSender()
+ {
+ var network = new SmartContractsRegTest();
+ var privateKey = new Key();
+ Address address = privateKey.PubKey.GetAddress(network).ToString().ToAddress(network);
+ byte[] message = new byte[] { 0x69, 0x76, 0xAA };
+
+ // Sign a message
+ byte[] offChainSignature = SignMessage(privateKey, message);
+
+ // Get the address out of the signature
+ EcRecoverProvider.TryGetSigner(message, offChainSignature, out Address recoveredAddress);
+
+ // Check that the address matches that generated from the private key.
+ Assert.Equal(address, recoveredAddress);
+ }
+
+ [Fact]
+ public void GetSigner_Returns_Address_Zero_When_Message_Or_Signature_Null()
+ {
+ var network = new SmartContractsRegTest();
+ var privateKey = new Key();
+ Address address = privateKey.PubKey.GetAddress(network).ToString().ToAddress(network);
+ byte[] message = new byte[] { 0x69, 0x76, 0xAA };
+
+ // Sign a message
+ byte[] offChainSignature = SignMessage(privateKey, message);
+
+ // Get the address out of the signature
+ Assert.False(EcRecoverProvider.TryGetSigner(null, offChainSignature, out Address recoveredAddress));
+
+ Assert.Equal(Address.Zero, recoveredAddress);
+
+ Assert.False(EcRecoverProvider.TryGetSigner(message, null, out Address recoveredAddress2));
+
+ Assert.Equal(Address.Zero, recoveredAddress2);
+ }
+
+ ///
+ /// Signs a message, returning an ECDSA signature.
+ ///
+ /// The private key used to sign the message.
+ /// The complete message to be signed.
+ /// The ECDSA signature prepended with header information specifying the correct value of recId.
+ private static byte[] SignMessage(Key privateKey, byte[] message)
+ {
+ uint256 hashedUint256 = new uint256(SHA3.Keccak256(message));
+
+ return privateKey.SignCompact(hashedUint256);
+ }
+
+ [Fact]
+ public void CanCallEcRecoverContractWithValidSignatureAsync()
+ {
+ using (PoWMockChain chain = new PoWMockChain(2))
+ {
+ var node1 = chain.Nodes[0];
+
+ node1.MineBlocks(1);
+
+ var network = chain.Nodes[0].CoreNode.FullNode.Network;
+
+ var privateKey = new Key();
+ string address = privateKey.PubKey.GetAddress(network).ToString();
+ byte[] message = new byte[] { 0x69, 0x76, 0xAA };
+ byte[] signature = SignMessage(privateKey, message);
+
+ // TODO: If the incorrect parameters are passed to the constructor, the contract does not get properly created ('Method does not exist on contract'), but a success response is still returned?
+
+ byte[] contract = ContractCompiler.CompileFile("SmartContracts/EcRecoverContract.cs").Compilation;
+ string[] createParameters = new string[] { string.Format("{0}#{1}", (int)MethodParameterDataType.Address, address) };
+ BuildCreateContractTransactionResponse createResult = node1.SendCreateContractTransaction(contract, 1, createParameters);
+
+ Assert.NotNull(createResult);
+ Assert.True(createResult.Success);
+
+ node1.WaitMempoolCount(1);
+ node1.MineBlocks(1);
+
+ string[] callParameters = new string[]
+ {
+ string.Format("{0}#{1}", (int)MethodParameterDataType.ByteArray, message.ToHexString()),
+ string.Format("{0}#{1}", (int)MethodParameterDataType.ByteArray, signature.ToHexString())
+ };
+
+ BuildCallContractTransactionResponse response = node1.SendCallContractTransaction("CheckThirdPartySignature", createResult.NewContractAddress, 1, callParameters);
+ Assert.NotNull(response);
+ Assert.True(response.Success);
+
+ node1.WaitMempoolCount(1);
+ node1.MineBlocks(1);
+
+ ReceiptResponse receipt = node1.GetReceipt(response.TransactionId.ToString());
+
+ Assert.NotNull(receipt);
+ Assert.True(receipt.Success);
+ Assert.Equal("True", receipt.ReturnValue);
+ }
+ }
+
+ [Fact]
+ public void CanCallEcRecoverContractWithInvalidSignatureAsync()
+ {
+ using (PoWMockChain chain = new PoWMockChain(2))
+ {
+ var node1 = chain.Nodes[0];
+
+ node1.MineBlocks(1);
+
+ var network = chain.Nodes[0].CoreNode.FullNode.Network;
+
+ var privateKey = new Key();
+ string address = privateKey.PubKey.GetAddress(network).ToString();
+ byte[] message = new byte[] { 0x69, 0x76, 0xAA };
+
+ // Make the signature with a key unrelated to the third party signer for the contract.
+ byte[] signature = SignMessage(new Key(), message);
+
+ // TODO: If the incorrect parameters are passed to the constructor, the contract does not get properly created ('Method does not exist on contract'), but a success response is still returned?
+
+ byte[] contract = ContractCompiler.CompileFile("SmartContracts/EcRecoverContract.cs").Compilation;
+ string[] createParameters = new string[] { string.Format("{0}#{1}", (int)MethodParameterDataType.Address, address) };
+ BuildCreateContractTransactionResponse createResult = node1.SendCreateContractTransaction(contract, 1, createParameters);
+
+ Assert.NotNull(createResult);
+ Assert.True(createResult.Success);
+
+ node1.WaitMempoolCount(1);
+ node1.MineBlocks(1);
+
+ string[] callParameters = new string[]
+ {
+ string.Format("{0}#{1}", (int)MethodParameterDataType.ByteArray, message.ToHexString()),
+ string.Format("{0}#{1}", (int)MethodParameterDataType.ByteArray, signature.ToHexString())
+ };
+
+ BuildCallContractTransactionResponse response = node1.SendCallContractTransaction("CheckThirdPartySignature", createResult.NewContractAddress, 1, callParameters);
+ Assert.NotNull(response);
+ Assert.True(response.Success);
+
+ node1.WaitMempoolCount(1);
+ node1.MineBlocks(1);
+
+ ReceiptResponse receipt = node1.GetReceipt(response.TransactionId.ToString());
+
+ Assert.NotNull(receipt);
+ Assert.True(receipt.Success);
+ Assert.Equal("False", receipt.ReturnValue);
+ }
+ }
+
+ // 2) That we can enable the method in new contracts without affecting the older contracts
+
+ // TODO
+ }
+}
\ No newline at end of file
diff --git a/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/EcRecoverContract.cs b/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/EcRecoverContract.cs
new file mode 100644
index 0000000000..2e40d6ef2f
--- /dev/null
+++ b/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/EcRecoverContract.cs
@@ -0,0 +1,28 @@
+using Stratis.SmartContracts;
+using EcRecover = Stratis.SCL.Crypto.ECRecover;
+
+public class EcRecoverContract : SmartContract
+{
+ public Address ThirdPartySigner
+ {
+ get
+ {
+ return this.State.GetAddress(nameof(this.ThirdPartySigner));
+ }
+ set
+ {
+ this.State.SetAddress(nameof(this.ThirdPartySigner), value);
+ }
+ }
+
+ public EcRecoverContract(ISmartContractState state, Address thirdPartySigner) : base(state)
+ {
+ this.ThirdPartySigner = thirdPartySigner;
+ }
+
+ public bool CheckThirdPartySignature(byte[] message, byte[] signature)
+ {
+ EcRecover.TryGetSigner(message, signature, out Address signerOfMessage);
+ return (signerOfMessage == this.ThirdPartySigner);
+ }
+}
\ No newline at end of file
diff --git a/src/Stratis.SmartContracts.IntegrationTests/Stratis.SmartContracts.IntegrationTests.csproj b/src/Stratis.SmartContracts.IntegrationTests/Stratis.SmartContracts.IntegrationTests.csproj
index 9571e113b8..81e45e9e43 100644
--- a/src/Stratis.SmartContracts.IntegrationTests/Stratis.SmartContracts.IntegrationTests.csproj
+++ b/src/Stratis.SmartContracts.IntegrationTests/Stratis.SmartContracts.IntegrationTests.csproj
@@ -93,6 +93,9 @@
PreserveNewest
+
+ Always
+
PreserveNewest