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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Stratis.SmartContracts.CLR.Serialization;
using Stratis.SmartContracts.Core.Receipts;
using Stratis.SmartContracts.Core.State;
using Stratis.SmartContracts.Core.State.AccountAbstractionLayer;
using Stratis.SmartContracts.Networks;
using Xunit;

Expand Down Expand Up @@ -53,14 +54,14 @@ public void Deserialize_Basic_Log_Success()
var serializer = new ApiLogDeserializer(primitiveSerializer, network, Mock.Of<IStateRepositoryRoot>(), Mock.Of<IContractAssemblyCache>());
dynamic deserializedLog = serializer.DeserializeLogData(testBytes, typeof(TestLog));

Assert.Equal(testStruct.Id, deserializedLog.Id);
Assert.Equal(testStruct.Name, deserializedLog.Name);
Assert.Equal(testStruct.Data, deserializedLog.Data);
Assert.True(testStruct.Datas.SequenceEqual((byte[])deserializedLog.Datas));
Assert.Equal(testStruct.Truth, deserializedLog.Truth);
Assert.Equal(testStruct.Address.ToUint160().ToBase58Address(network), deserializedLog.Address);
Assert.Equal(testStruct.Value128.ToString(), deserializedLog.Value128.ToString());
Assert.Equal(testStruct.Value256.ToString(), deserializedLog.Value256.ToString());
Assert.Equal(testStruct.Id, deserializedLog.Data.Id);
Assert.Equal(testStruct.Name, deserializedLog.Data.Name);
Assert.Equal(testStruct.Data, deserializedLog.Data.Data);
Assert.True(testStruct.Datas.SequenceEqual((byte[])deserializedLog.Data.Datas));
Assert.Equal(testStruct.Truth, deserializedLog.Data.Truth);
Assert.Equal(testStruct.Address.ToUint160().ToBase58Address(network), deserializedLog.Data.Address);
Assert.Equal(testStruct.Value128.ToString(), deserializedLog.Data.Value128.ToString());
Assert.Equal(testStruct.Value256.ToString(), deserializedLog.Data.Value256.ToString());
}

[Fact]
Expand Down Expand Up @@ -103,15 +104,45 @@ public void Deserialize_Logs_With_Different_Addresses_From_Cache()

var serializer = new ApiLogDeserializer(primitiveSerializer, network, stateRoot.Object, assemblyCache.Object);

var responses = serializer.MapLogResponses(logs);
List<SmartContracts.Models.LogResponse> responses = serializer.MapLogResponses(logs);

// Verify that we deserialized the logs correctly.
Assert.Equal(testStruct0.Name, ((dynamic)responses[0].Log).Name);
Assert.Equal(testStruct1.Name, ((dynamic)responses[1].Log).Name);
Assert.Equal(testStruct0.Name, ((dynamic)responses[0].Log.Data).Name);
Assert.Equal(testStruct1.Name, ((dynamic)responses[1].Log.Data).Name);

// Verify that we got the code for both log assemblies.
stateRoot.Verify(s => s.GetCodeHash(logs[0].Address), Times.Once);
stateRoot.Verify(s => s.GetCodeHash(logs[1].Address), Times.Once);
}

[Fact]
public void MapTransferInfo_Success()
{
var network = new SmartContractsRegTest();
var primitiveSerializer = new ContractPrimitiveSerializer(network);

var stateRoot = new Mock<IStateRepositoryRoot>();
stateRoot.Setup(r => r.GetCodeHash(It.IsAny<uint160>())).Returns(uint256.Zero.ToBytes());

var assemblyCache = new Mock<IContractAssemblyCache>();
var contractAssembly = new Mock<IContractAssembly>();

var serializer = new ApiLogDeserializer(primitiveSerializer, network, stateRoot.Object, assemblyCache.Object);

var transferInfos = new List<TransferInfo>
{
new TransferInfo(uint160.Zero, uint160.One, 12345),
new TransferInfo(uint160.One, uint160.Zero, 12345),
};

List<SmartContracts.Models.TransferResponse> result = serializer.MapTransferInfo(transferInfos.ToArray());

for (var i = 0; i < transferInfos.Count; i++)
{
Assert.Equal(transferInfos[i].From.ToBase58Address(network), result[i].From);
Assert.Equal(transferInfos[i].To.ToBase58Address(network), result[i].To);
Assert.Equal(transferInfos[i].Value, result[i].Value);
}
}
}
}
26 changes: 18 additions & 8 deletions src/Stratis.Bitcoin.Features.SmartContracts/ApiLogDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Stratis.SmartContracts.CLR.Serialization;
using Stratis.SmartContracts.Core.Receipts;
using Stratis.SmartContracts.Core.State;
using Stratis.SmartContracts.Core.State.AccountAbstractionLayer;

namespace Stratis.Bitcoin.Features.SmartContracts
{
Expand All @@ -24,17 +25,28 @@ public class ApiLogDeserializer
{
private readonly IContractPrimitiveSerializer primitiveSerializer;
private readonly Network network;
private readonly IStateRepositoryRoot stateRepositoryRoot;
private readonly IStateRepository stateRepositoryRoot;
private readonly IContractAssemblyCache contractAssemblyCache;

public ApiLogDeserializer(IContractPrimitiveSerializer primitiveSerializer, Network network, IStateRepositoryRoot stateRepositoryRoot, IContractAssemblyCache contractAssemblyCache)
public ApiLogDeserializer(IContractPrimitiveSerializer primitiveSerializer, Network network, IStateRepository stateRepositoryRoot, IContractAssemblyCache contractAssemblyCache)
{
this.primitiveSerializer = primitiveSerializer;
this.network = network;
this.stateRepositoryRoot = stateRepositoryRoot;
this.contractAssemblyCache = contractAssemblyCache;
}

public List<TransferResponse> MapTransferInfo(TransferInfo[] transferInfos)
{
return transferInfos.Select(t => new TransferResponse
{
From = t.From.ToBase58Address(this.network),
To = t.To.ToBase58Address(this.network),
Value = t.Value
})
.ToList();
}

public List<LogResponse> MapLogResponses(Log[] logs)
{
var logResponses = new List<LogResponse>();
Expand Down Expand Up @@ -71,7 +83,7 @@ public List<LogResponse> MapLogResponses(Log[] logs)
}

// Deserialize it
dynamic deserialized = DeserializeLogData(log.Data, eventType);
LogData deserialized = DeserializeLogData(log.Data, eventType);

logResponse.Log = deserialized;
}
Expand All @@ -83,7 +95,7 @@ private Assembly GetAssembly(uint160 address)
{
var codeHashBytes = this.stateRepositoryRoot.GetCodeHash(address);

if (codeHashBytes == null)
if (codeHashBytes == null || codeHashBytes.Length != 32)
return null;

var codeHash = new uint256(codeHashBytes);
Expand Down Expand Up @@ -114,14 +126,12 @@ private Assembly GetAssembly(uint160 address)
/// <param name="bytes">The raw event log data.</param>
/// <param name="type">The type to attempt to deserialize.</param>
/// <returns>An <see cref="ExpandoObject"/> containing the fields of the Type and its deserialized values.</returns>
public dynamic DeserializeLogData(byte[] bytes, Type type)
public LogData DeserializeLogData(byte[] bytes, Type type)
{
RLPCollection collection = (RLPCollection)RLP.Decode(bytes);

var instance = new ExpandoObject() as IDictionary<string, object>;

instance["Event"] = type.Name;

FieldInfo[] fields = type.GetFields();

for (int i = 0; i < fields.Length; i++)
Expand All @@ -147,7 +157,7 @@ public dynamic DeserializeLogData(byte[] bytes, Type type)
}
}

return instance;
return new LogData(type.Name, instance);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class LogResponse
public string[] Topics { get; }
public string Data { get; }

public object Log { get; set; }
public LogData Log { get; set; }

public LogResponse(Log log, Network network)
{
Expand All @@ -55,4 +55,42 @@ public LogResponse(Log log, Network network)
this.Data = log.Data.ToHexString();
}
}

public class LogData
{
public LogData(string eventName, IDictionary<string, object> data)
{
this.@Event = eventName;

this.Data = data;
}

public string Event { get; }

public IDictionary<string, object> Data { get; }
}

public class LocalExecutionResponse
{
public IReadOnlyList<TransferResponse> InternalTransfers { get; set; }

public Stratis.SmartContracts.RuntimeObserver.Gas GasConsumed { get; set; }

public bool Revert { get; set; }

public ContractErrorMessage ErrorMessage { get; set; }

public object Return { get; set; }

public IReadOnlyList<LogResponse> Logs { get; set; }
}

public class TransferResponse
{
public string From { get; set; }

public string To { get; set; }

public ulong Value { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -576,10 +576,22 @@ public IActionResult LocalCallSmartContractTransaction([FromBody] LocalCallContr
ILocalExecutionResult result = this.localExecutor.Execute(
height,
request.Sender?.ToUint160(this.network) ?? new uint160(),
string.IsNullOrWhiteSpace(request.Amount) ? (Money)request.Amount : 0,
!string.IsNullOrWhiteSpace(request.Amount) ? (Money)request.Amount : 0,
txData);

return this.Json(result, new JsonSerializerSettings
var deserializer = new ApiLogDeserializer(this.primitiveSerializer, this.network, result.StateRoot, this.contractAssemblyCache);

var response = new LocalExecutionResponse
{
InternalTransfers = deserializer.MapTransferInfo(result.InternalTransfers.ToArray()),
Logs = deserializer.MapLogResponses(result.Logs.ToArray()),
GasConsumed = result.GasConsumed,
Revert = result.Revert,
ErrorMessage = result.ErrorMessage,
Return = result.Return // All return values should be primitives, let default serializer handle.
};

return this.Json(response, new JsonSerializerSettings
{
ContractResolver = new ContractParametersContractResolver(this.network)
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Text;
using NBitcoin;
using Newtonsoft.Json;
Expand Down Expand Up @@ -157,6 +158,21 @@ public void PersistentState_Nullable_Behaviour()
Assert.ThrowsAny<Exception>(() => this.serializer.Deserialize<ContainsNullInt>(serialized));
}

[Fact]
public void Deserialize_Empty_String()
{
var empty = this.serializer.Serialize(string.Empty);
Assert.Equal(string.Empty, this.serializer.Deserialize(typeof(string), empty));
}

[Fact]
public void Deserialize_Empty_Byte_Array()
{
var emptyByte = new byte[0];
var empty = this.serializer.Serialize(emptyByte);
Assert.True(emptyByte.SequenceEqual(this.serializer.Deserialize<byte[]>(empty)));
}

private TestValueType NewTestValueType()
{
var instance = new TestValueType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,15 @@ public void Deserialize_Garbage_Throws()
Assert.ThrowsAny<Exception>(() => this.Serializer.Deserialize(new byte[] { 0x00, 0x11, 0x22, 0x33 }));
}


public static IEnumerable<object[]> GetData(int numTests)
{
yield return new object[] { true }; // MethodParameterDataType.Bool
yield return new object[] { (byte)1 }; // MethodParameterDataType.Byte
yield return new object[] { Encoding.UTF8.GetBytes("test") }; // MethodParameterDataType.ByteArray
yield return new object[] { new byte[0] { } }; // MethodParameterDataType.ByteArray
yield return new object[] { 's' }; // MethodParameterDataType.Char
yield return new object[] { "test" }; // MethodParameterDataType.String
yield return new object[] { string.Empty }; // MethodParameterDataType.String
yield return new object[] { (uint)36 }; // MethodParameterDataType.UInt
yield return new object[] { (ulong)29 }; // MethodParameterDataType.ULong
yield return new object[] { "0x0000000000000000000000000000000000000001".HexToAddress() }; // MethodParameterDataType.Address
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ public static IEnumerable<object[]> GetData(int numTests)
yield return new object[] { true }; // MethodParameterDataType.Bool
yield return new object[] { (byte)1 }; // MethodParameterDataType.Byte
yield return new object[] { Encoding.UTF8.GetBytes("test") }; // MethodParameterDataType.ByteArray
yield return new object[] { new byte[0] }; // MethodParameterDataType.ByteArray
yield return new object[] { 's' }; // MethodParameterDataType.Char
yield return new object[] { "test" }; // MethodParameterDataType.String
yield return new object[] { "" }; // MethodParameterDataType.String
yield return new object[] { (uint)36 }; // MethodParameterDataType.UInt
yield return new object[] { (ulong)29 }; // MethodParameterDataType.ULong
yield return new object[] { new uint160("0x0000000000000000000000000000000000000001").ToBase58Address(Network) }; // MethodParameterDataType.Address
Expand Down
29 changes: 18 additions & 11 deletions src/Stratis.SmartContracts.CLR/Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,24 +89,31 @@ public static bool ConstructorExists(Type type, IReadOnlyList<object> parameters
/// <inheritdoc />
public IContractInvocationResult InvokeConstructor(IReadOnlyList<object> parameters)
{
// If it's a constructor we need to append the ISmartContractState to the start of the parameters array
object[] invokeParams = { this.State };
try
{
// If it's a constructor we need to append the ISmartContractState to the start of the parameters array
object[] invokeParams = { this.State };

if (parameters != null)
invokeParams = invokeParams.Concat(parameters).ToArray();
if (parameters != null)
invokeParams = invokeParams.Concat(parameters).ToArray();

Type[] types = invokeParams.Select(p => p.GetType()).ToArray();
Type[] types = invokeParams.Select(p => p.GetType()).ToArray();

ConstructorInfo methodToInvoke = this.Type.GetConstructor(types);
ConstructorInfo methodToInvoke = this.Type.GetConstructor(types);

if (methodToInvoke == null)
return ContractInvocationResult.Failure(ContractInvocationErrorType.MethodDoesNotExist);
if (methodToInvoke == null)
return ContractInvocationResult.Failure(ContractInvocationErrorType.MethodDoesNotExist);

IContractInvocationResult result = this.InvokeInternal(methodToInvoke, invokeParams);
IContractInvocationResult result = this.InvokeInternal(methodToInvoke, invokeParams);

this.initialized = result.IsSuccess;
this.initialized = result.IsSuccess;

return result;
return result;
}
catch (Exception)
{
return ContractInvocationResult.Failure(ContractInvocationErrorType.ParameterTypesDontMatch);
}
}

/// <inheritdoc />
Expand Down
2 changes: 2 additions & 0 deletions src/Stratis.SmartContracts.CLR/Local/ILocalExecutionResult.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Stratis.SmartContracts.Core;
using Stratis.SmartContracts.Core.Receipts;
using Stratis.SmartContracts.Core.State;
using Stratis.SmartContracts.Core.State.AccountAbstractionLayer;

namespace Stratis.SmartContracts.CLR.Local
Expand All @@ -13,5 +14,6 @@ public interface ILocalExecutionResult
ContractErrorMessage ErrorMessage { get; }
object Return { get; }
IList<Log> Logs { get; }
IStateRepository StateRoot { get; set; }
}
}
2 changes: 2 additions & 0 deletions src/Stratis.SmartContracts.CLR/Local/LocalExecutionResult.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Stratis.SmartContracts.Core;
using Stratis.SmartContracts.Core.Receipts;
using Stratis.SmartContracts.Core.State;
using Stratis.SmartContracts.Core.State.AccountAbstractionLayer;

namespace Stratis.SmartContracts.CLR.Local
Expand All @@ -13,5 +14,6 @@ public class LocalExecutionResult : ILocalExecutionResult
public ContractErrorMessage ErrorMessage { get; set; }
public object Return { get; set; }
public IList<Log> Logs { get; set; }
public IStateRepository StateRoot { get; set; }
}
}
3 changes: 2 additions & 1 deletion src/Stratis.SmartContracts.CLR/Local/LocalExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ public ILocalExecutionResult Execute(ulong blockHeight, uint160 sender, Money tx
GasConsumed = result.GasConsumed,
Return = result.Success?.ExecutionResult,
InternalTransfers = newState.InternalTransfers.ToList(),
Logs = newState.GetLogs(this.contractPrimitiveSerializer)
Logs = newState.GetLogs(this.contractPrimitiveSerializer),
StateRoot = newState.ContractState
};

return executionResult;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public T Deserialize<T>(byte[] stream)

public object Deserialize(Type type, byte[] stream)
{
if (stream == null || stream.Length == 0)
if (stream == null)
return null;

return Type.GetTypeCode(type) switch
Expand Down
Loading