Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 8 additions & 1 deletion src/Stratis.FullNode.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
56 changes: 56 additions & 0 deletions src/Stratis.SCL/ECRecover.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using NBitcoin;
using Stratis.SmartContracts;

namespace Stratis.SCL.Crypto
{
public static class ECRecover
{
/// <summary>
/// Retrieves the address of the signer of an ECDSA signature.
/// </summary>
/// <param name="message"></param>
/// <param name="signature">The ECDSA signature prepended with header information specifying the correct value of recId.</param>
/// <param name="address">The Address for the signer of a signature.</param>
/// <returns>A bool representing whether or not the signer was retrieved successfully.</returns>
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);
}
}
}
9 changes: 9 additions & 0 deletions src/Stratis.SCL/Operations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace Stratis.SCL.Base
{
public static class Operations
{
public static void Noop() { }
}
}
17 changes: 17 additions & 0 deletions src/Stratis.SCL/SHA3.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using HashLib;

namespace Stratis.SCL.Crypto
{
public static class SHA3
{
/// <summary>
/// Returns a 32-byte Keccak256 hash of the given bytes.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static byte[] Keccak256(byte[] input)
{
return HashFactory.Crypto.SHA3.CreateKeccak256().ComputeBytes(input).GetBytes();
}
}
}
86 changes: 86 additions & 0 deletions src/Stratis.SCL/SSAS.cs
Original file line number Diff line number Diff line change
@@ -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<string, StringValues> 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;
}
}
}
}
24 changes: 24 additions & 0 deletions src/Stratis.SCL/Stratis.SCL.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>

<AssemblyVersion>2.0.2.0</AssemblyVersion>
<FileVersion>2.0.2.0</FileVersion>
<Version>2.0.2.0</Version>
<Authors>Stratis Group Ltd.</Authors>
<Product>Stratis.SCL</Product>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CSharpFunctionalExtensions" Version="1.10.0" />
<PackageReference Include="ICSharpCode.Decompiler" Version="7.2.1.6856" />
<PackageReference Include="Stratis.SmartContracts.Standards" Version="2.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Stratis.Bitcoin.Features.PoA\Stratis.Bitcoin.Features.PoA.csproj" />
<ProjectReference Include="..\Stratis.SmartContracts.Core\Stratis.SmartContracts.Core.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ public void SmartContract_ReferenceResolver_HasCorrectAssemblies()
{
List<Assembly> 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]
Expand Down
6 changes: 6 additions & 0 deletions src/Stratis.SmartContracts.CLR.Tests/ContractExecutorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions src/Stratis.SmartContracts.CLR.Tests/SmartContracts/LibraryTest.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
<Compile Update="SmartContracts\InvalidParam.cs">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Compile>
<Compile Update="SmartContracts\LibraryTest.cs">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Compile>
<Compile Update="SmartContracts\MemoryLimit.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

<ItemGroup>
<ProjectReference Include="..\Stratis.Bitcoin.Features.PoA\Stratis.Bitcoin.Features.PoA.csproj" />
<ProjectReference Include="..\Stratis.SCL\Stratis.SCL.csproj" />
<ProjectReference Include="..\Stratis.SmartContracts.Core\Stratis.SmartContracts.Core.csproj" />
</ItemGroup>

Expand Down
Loading