Skip to content

Commit

Permalink
EVM update first pass
Browse files Browse the repository at this point in the history
EVM simulator with internal/external storage, to allow the simulation and validation of state of expected interaction with any smart contract.
Current implementation working with a single contract, Note, some opcodes are not implemented and overall requires further testing.

Also small utilities to identify if a contract implements specific function selectors, simple approach.
  • Loading branch information
juanfranblanco committed Oct 24, 2022
1 parent ba7c7cc commit f576f66
Show file tree
Hide file tree
Showing 23 changed files with 2,835 additions and 985 deletions.
7 changes: 5 additions & 2 deletions src/Nethereum.ABI/Encoders/IntTypeEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,12 @@ public byte[] EncodeInt(int value)
return EncodeInt(new BigInteger(value));
}

public byte[] EncodeInt(BigInteger value, uint numberOfBytesArray)
public byte[] EncodeInt(BigInteger value, uint numberOfBytesArray, bool validate = true)
{
ValidateValue(value);
if (validate)
{
ValidateValue(value);
}
//It should always be Big Endian.
var bytes = BitConverter.IsLittleEndian
? value.ToByteArray().Reverse().ToArray()
Expand Down
2 changes: 1 addition & 1 deletion src/Nethereum.ABI/IntType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class IntType : ABIType
{
public static BigInteger MAX_INT256_VALUE =
BigInteger.Parse("57896044618658097711785492504343953926634992332820282019728792003956564819967");

public static BigInteger MIN_INT256_VALUE =
BigInteger.Parse("-57896044618658097711785492504343953926634992332820282019728792003956564819968");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Nethereum.BlockchainProcessing.BlockStorage.Entities
 namespace Nethereum.BlockchainProcessing.BlockStorage.Entities
{
public class TransactionLog : TableRow, ITransactionLogView
{
Expand Down
382 changes: 382 additions & 0 deletions src/Nethereum.EVM/EVMSimulator.cs

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions src/Nethereum.EVM/INodeDataService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Numerics;
using System.Threading.Tasks;

namespace Nethereum.EVM
{
public interface INodeDataService
{
Task<BigInteger> GetBalanceAsync(byte[] address);
Task<byte[]> GetCodeAsync(byte[] address);
Task<byte[]> GetBlockHashAsync(BigInteger blockNumber);
Task<byte[]> GetStorageAtAsync(byte[] address, BigInteger position);
}
}
6 changes: 3 additions & 3 deletions src/Nethereum.EVM/Instruction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ public enum Instruction
SAR,

///<summary>compute SHA3-256 hash</summary>///
KECCAK256 = 0x20,
KECCAK256 = 0x20,

///<summary>get address of currently executing account</summary>///
///<summary>address of executing contract (account)</summary>///
ADDRESS = 0x30,
///<summary>get balance of the given account</summary>///
///<summary>get balance of the given contract</summary>///
BALANCE,
///<summary>get execution origination address</summary>///
ORIGIN,
Expand Down
71 changes: 71 additions & 0 deletions src/Nethereum.EVM/InternalStorageState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Nethereum.Hex.HexConvertors.Extensions;
using System.Collections.Generic;
using System.Numerics;

namespace Nethereum.EVM
{
public class InternalStorageState
{
public Dictionary<string, Dictionary<BigInteger, byte[]>> Storage { get; protected set; }

public InternalStorageState()
{
Storage = new Dictionary<string, Dictionary<BigInteger, byte[]>>();
}
public bool ContainsKey(string address, BigInteger key)
{
address = address.ToLower();
return Storage.ContainsKey(address) && Storage[address].ContainsKey(key);
}

public void UpsertValue(string address, BigInteger key, byte[] value)
{
address = address.ToLower();
if(!Storage.ContainsKey(address))
{
Storage.Add(address, new Dictionary<BigInteger, byte[]>());
}

if (!Storage[address].ContainsKey(key))
{
Storage[address].Add(key, value);
}
else
{
Storage[address][key] = value;
}
}

public byte[] GetValue(string address, BigInteger key)
{
address = address.ToLower();
if (ContainsKey(address, key))
{
return Storage[address][key];
}
return null;
}

public Dictionary<BigInteger, byte[]> GetStorage(string address)
{
address = address.ToLower();
if (!Storage.ContainsKey(address.ToLower())) return null;
return Storage[address];
}

public Dictionary<string, string> GetContractStorageAsHex(string address)
{
address = address.ToLower();
var storage = GetStorage(address);
if (storage == null) return null;
var dictionary = new Dictionary<string, string>();
foreach (var item in storage)
{
if (item.Value != null) {
dictionary.Add(item.Key.ToString(), item.Value.ToHex());
}
}
return dictionary;
}
}
}
23 changes: 12 additions & 11 deletions src/Nethereum.EVM/Nethereum.EVM.csproj
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<AssemblyName>Nethereum.EVM</AssemblyName>
<PackageId>Nethereum.EVM</PackageId>
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<Description>Nethereum.EVM EVM simulator (WIP) and Bytecode utils</Description>
<AssemblyTitle>Nethereum.EVM</AssemblyTitle>
<Version>0.2.0</Version>
<TargetFrameworks>$(DefaultFrameworks)</TargetFrameworks>
<AssemblyName>Nethereum.EVM</AssemblyName>
<PackageId>Nethereum.EVM</PackageId>
<PackageTags>Netherum;Ethereum;Blockchain;</PackageTags>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Nethereum.ABI\Nethereum.ABI.csproj" />
<ProjectReference Include="..\Nethereum.Hex\Nethereum.Hex.csproj" />
<ProjectReference Include="..\Nethereum.RPC\Nethereum.RPC.csproj" />
</ItemGroup>

</Project>
141 changes: 87 additions & 54 deletions src/Nethereum.EVM/Program.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,90 @@
using System;
using Nethereum.ABI;
using Nethereum.Hex.HexConvertors.Extensions;
using Nethereum.Hex.HexTypes;
using Nethereum.RPC;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.RPC.Shh.KeyPair;
using Nethereum.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace Nethereum.EVM
{

public class Program
{
public Stack<byte[]> Stack { get; set; }
public byte[] Instructions { get; private set; }
private List<byte[]> stack { get; set; }
public List<byte> Memory { get; set; }
public List<ProgramInstruction> Instructions { get; private set; }

public ProgramResult ProgramResult { get; private set; }


public List<string> GetCurrentStackAsHex()
{
return stack.Select(x => x.ToHex()).ToList();
}

public string GetCurrentMemoryAsHex()
{
return Memory.ToArray().ToHex();
}



private int currentInstructionIndex = 0;

public Program(byte[] instructions)
public Program(byte[] bytecode, ProgramContext programContext = null)
{
this.Instructions = instructions;
this.Stack = new Stack<byte[]>();
this.Instructions = ProgramInstructionsUtils.GetProgramInstructions(bytecode);
this.stack = new List<byte[]>();
this.Memory = new List<byte>();
ByteCode = bytecode;
ProgramContext = programContext;
ProgramResult = new ProgramResult();
}

public byte GetCurrentInstruction()
public ProgramInstruction GetCurrentInstruction()
{
return Instructions?[currentInstructionIndex] ?? 0;
return Instructions[currentInstructionIndex];
}

public void GoToJumpDestination(int step)
{
var jump = this.Instructions.Where(x => x.Step == step).FirstOrDefault();
if (jump != null && jump.Instruction == Instruction.JUMPDEST)
{
SetInstrunctionIndex(this.Instructions.IndexOf(jump));
}
else
{
throw new Exception("Invalid jump destination");
}

}

public int GetProgramCounter()
{
return GetCurrentInstruction().Step;
}

public virtual void SetInstrunctionIndex(int index)
{
currentInstructionIndex = index;

if (currentInstructionIndex >= Instructions.Length)
if (currentInstructionIndex >= Instructions.Count)
{
Stop();
}
}

public bool Stopped { get; private set; } = false;
public byte[] ByteCode { get; }
public ProgramContext ProgramContext { get; }

public void Stop()
{
Expand All @@ -48,25 +101,35 @@ public void Step(int steps)
SetInstrunctionIndex(currentInstructionIndex + steps);
}

public byte[] Sweep(int number)
public void StackPush(byte[] stackWord)
{
var lastInstrunction = currentInstructionIndex + number;
if (lastInstrunction > Instructions.Length)
{
Stop();
}
ThrowWhenPushStackOverflows();
stack.Insert(0, stackWord);
}

var data = new byte[number];
Array.Copy(Instructions, currentInstructionIndex, data, 0, number);
Step(number);
public byte[] StackPeek()
{
return stack[0];
}

return data;
public void StackSwap(int index)
{
var swapTemp = stack[0];
stack[0] = stack[index];
stack[index] = swapTemp;
}

public void StackPush(byte[] stackWord)
public void StackDup(int index)
{
ThrowWhenPushStackOverflows();
Stack.Push(stackWord);
var dup = stack[index - 1];
StackPush(dup);
}

public byte[] StackPop()
{
var popItem = stack[0];
stack.RemoveAt(0);
return popItem;
}

private void ThrowWhenPushStackOverflows()
Expand All @@ -76,44 +139,14 @@ private void ThrowWhenPushStackOverflows()

public void VerifyStackOverflow(int args, int returns)
{
if (Stack.Count - args + returns > MAX_STACKSIZE)

if (stack.Count - args + returns > MAX_STACKSIZE)
{
throw new Exception("Stack overflow, maximum size is " + MAX_STACKSIZE);
}
}

public const int MAX_STACKSIZE = 1024;

public byte[] StackPop()
{
return Stack.Pop();
}



/*
public virtual void Precompile()
{
for (int i = 0; i < Ops.Length; ++i)
{
OpCode op = OpCode.code(Ops[i]);
if (op == null)
{
continue;
}
if (op.Equals(OpCode.JUMPDEST))
{
Jumpdest.Add(i);
}
if (op.asInt() >= OpCode.PUSH1.asInt() && op.asInt() <= OpCode.PUSH32.asInt())
{
i += op.asInt() - OpCode.PUSH1.asInt() + 1;
}
}
} */
}
}

0 comments on commit f576f66

Please sign in to comment.