Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close #444: Add evm debugger #452

Open
wants to merge 53 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
bfe5c71
Fix tests #387
nevgeny Dec 11, 2018
0c25cfa
Fix tests №387
nevgeny Dec 11, 2018
bd8a9e2
Add test #387
nevgeny Dec 12, 2018
50a511c
Bump dotnet version and publish symbols #410
vovapolu Dec 10, 2018
d189efc
Include Pravda.pdb to nuget package #410
vovapolu Dec 10, 2018
ba31acb
Fix dotnet translator, analyse typedefs in each file separately #409
vovapolu Dec 10, 2018
c904358
[WIP] Add translation test, convert everything to bytes in simple ope…
vovapolu Dec 11, 2018
76db327
[WIP] Make opcodes to handle only Bytes
vovapolu Dec 12, 2018
89cbbee
Add memory emulation. Fix tests. Format sources #387
nevgeny Dec 13, 2018
dbc538e
[WIP] Continue to translate SimpleStorage
vovapolu Dec 13, 2018
fde9062
[WIP] Playing with calldata
vovapolu Dec 13, 2018
953cfc1
Remove unused imports #387
nevgeny Dec 14, 2018
f7f3ab6
[WIP] Working prototype, tests don't work
vovapolu Dec 14, 2018
4bdc66f
Fix tests
vovapolu Dec 17, 2018
a6f97b4
Fix all evm tests
vovapolu Dec 17, 2018
0fb8697
Fix all tests
vovapolu Dec 17, 2018
2e853e2
Merge remote-tracking branch 'origin/i245-evm-translator' into vovapo…
vovapolu Dec 17, 2018
faabeb1
Add new type in abi parser. Add Sha3 translation #417
nevgeny Dec 18, 2018
f96639f
Add SimpleToken.sl
vovapolu Dec 18, 2018
0d883ed
Add evm parser and abi parser tests, add sha3 stdlib func
vovapolu Dec 18, 2018
1d3d01e
[WIP] Add SimpleToken test
vovapolu Dec 18, 2018
5c48939
[WIP] Add evm cli command #418
nevgeny Dec 20, 2018
79cad98
Add tests for balanceOf, transfer in SimpleToken
vovapolu Dec 20, 2018
3acb30c
Convert result bytes to appropriate Pravda VM type, fix tests
vovapolu Dec 20, 2018
fc64733
Fix evm cli command #418
nevgeny Dec 20, 2018
447dd9e
Merge remote-tracking branch 'origin/i418-cli-evm' into vovapolu-evm
vovapolu Dec 20, 2018
3f18b2a
Refactor tests, move source files, recompile SimpleStorage
vovapolu Dec 24, 2018
2249da6
Add documentation #418
nevgeny Dec 24, 2018
95cca81
Remove unneccesary stdlib func, rename stdlib funcs, fix evm tests
vovapolu Dec 24, 2018
cbdc78f
Add file name checks #418
nevgeny Dec 27, 2018
13a66f2
Merge branch 'vovapolu-evm' into i418-cli-evm
nevgeny Dec 27, 2018
ecd4160
Format sources. Fix documentation #418
nevgeny Dec 27, 2018
676d08f
Add headers #418
nevgeny Jan 9, 2019
21002b4
Format sources #418
nevgeny Jan 9, 2019
b7f6e42
Merge pull request #428 from expload/i418-cli-evm
vovapolu Jan 10, 2019
bd510bd
Merge pull request #7 from expload/vovapolu-evm
nevgeny Jan 15, 2019
7ec9f56
Add evm debugger #444
nevgeny Jan 15, 2019
88f0800
Format sources. Move VmSandbox to compile. Fix bugs #444
nevgeny Jan 21, 2019
94db51e
Merge remote-tracking branch 'origin/i444-evm-debugger'
nevgeny Jan 22, 2019
3084b33
Merge branch 'master' into i444-evm-debugger
nevgeny Jan 22, 2019
b084bb9
Merge pull request #8 from expload/master
nevgeny Jan 22, 2019
6592554
Merge remote-tracking branch 'origin/master'
nevgeny Jan 22, 2019
f69a89f
Merge branch 'i444-evm-debugger'
nevgeny Jan 22, 2019
ac1065b
Merge branch 'master' into i444-evm-debugger
nevgeny Jan 22, 2019
f283e51
Format sources #444
nevgeny Jan 22, 2019
b7838fa
Format sources #444
nevgeny Jan 22, 2019
3ecd693
Try to Either #444
nevgeny Jan 23, 2019
ec9c9df
Format sources. List to ListBuffer #444
nevgeny Jan 28, 2019
40381ad
Format sources #444
nevgeny Jan 28, 2019
e71c635
Remove old docs #444
nevgeny Jan 28, 2019
ed5bd36
def to val #444
nevgeny Jan 28, 2019
44dd91a
Re-generate some docs #444
nevgeny Jan 28, 2019
2a48a6f
Add cli command #444
nevgeny Feb 1, 2019
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
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ lazy val `vm-asm` = (project in file("vm-asm"))

lazy val evm = (project in file("evm")).
dependsOn(`vm-asm`).
dependsOn(vm % "test->test").
dependsOn(vm % "compile->compile;test->test").
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
dependsOn(vm % "compile->compile;test->test").
dependsOn(vm % "compile->compile;test->test").

settings(normalizedName := "pravda-evm").
settings( commonSettings: _* ).
settings(
Expand Down
15 changes: 15 additions & 0 deletions doc/standard-library/read-evm-word.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!--
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove these files, they are outdated

THIS FILE IS GENERATED. DO NOT EDIT MANUALLY!
-->
## ReadEvmWord

### Id

`0x06`
### Signature

`(index: bigint, array: array): bytes`

### Description

Takes byte array, index from stack. Returns 32 bytes, truncated if necessary, from given index in the given array.
15 changes: 15 additions & 0 deletions doc/standard-library/slice-evm-memory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!--
THIS FILE IS GENERATED. DO NOT EDIT MANUALLY!
-->
## SliceEvmMemory

### Id

`0x08`
### Signature

`(size: bigint, ind: bigint, array: array): bytes`

### Description

Takes byte array, index, size stack. Returns slice of the array between ind and ind + size.
15 changes: 15 additions & 0 deletions doc/standard-library/write-evm-word.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!--
THIS FILE IS GENERATED. DO NOT EDIT MANUALLY!
-->
## WriteEvmWord

### Id

`0x07`
### Signature

`(bytes: bytes, index: bigint, array: array): array`

### Description

Takes byte array, index from stack, bytes to write. Writes 32 bytes, fill with zeros if necessary, from given index in the given array. Returns reference to array
62 changes: 62 additions & 0 deletions evm/src/main/scala/pravda/evm/debug/DebugVm.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2018 Expload.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package pravda.evm.debug

import java.nio.ByteBuffer

import com.google.protobuf.ByteString
import pravda.common.domain
import pravda.common.domain.Address
import pravda.vm._
import pravda.vm.impl.MemoryImpl
import pravda.vm.sandbox.VmSandbox.StorageSandbox

//'extends Vm' is required for using 'this' in SystemOperation constructor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//'extends Vm' is required for using 'this' in SystemOperation constructor

trait DebugVm extends Vm {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use aggregation, not inheritance here

override def spawn(initialProgram: ByteString, environment: Environment, wattLimit: Long): ExecutionResult =
throw new Exception("It's debug vm")

override def run(programAddress: Address,
environment: Environment,
memory: Memory,
wattCounter: WattCounter,
pcallAllowed: Boolean): Unit =
throw new Exception("It's debug vm. You can't use pcall, lcall opcodes")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's bad, it's necessary feature


def debugBytes[S](program: ByteBuffer,
env: Environment,
mem: MemoryImpl,
counter: WattCounter,
maybeStorage: Option[StorageSandbox],
maybePA: Option[domain.Address],
pcallAllowed: Boolean)(implicit debugger: Debugger[S]): List[S]
}

object DebugVm {

sealed trait ExecutionResult

final case class UnitExecution(f: () => Unit) extends ExecutionResult {
f()
}

case object InterruptedExecution extends ExecutionResult

final case class MetaExecution(meta: Meta) extends ExecutionResult

}
30 changes: 30 additions & 0 deletions evm/src/main/scala/pravda/evm/debug/Debugger.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2018 Expload.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package pravda.evm.debug

import java.nio.ByteBuffer

import pravda.evm.debug.DebugVm.ExecutionResult
import pravda.vm.impl.MemoryImpl
import pravda.vm.sandbox.VmSandbox.StorageSandbox

trait Debugger[State] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
trait Debugger[State] {
trait Debugger[S] {


def debugOp(program: ByteBuffer, op: Int, mem: MemoryImpl, storage: StorageSandbox)(
execResult: Either[Throwable, ExecutionResult]): State
}
152 changes: 152 additions & 0 deletions evm/src/main/scala/pravda/evm/debug/VmImplDebug.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright (C) 2018 Expload.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package pravda.evm.debug

import java.nio.ByteBuffer

import pravda.common.domain
import pravda.vm.Error.PcallDenied
import pravda.vm.Opcodes._
import pravda.vm.WattCounter.CpuBasic
import pravda.vm._
import pravda.vm.impl.MemoryImpl
import pravda.vm.operations._

import scala.annotation.tailrec
import scala.util.Try
import pravda.evm.debug.DebugVm.{InterruptedExecution, MetaExecution, UnitExecution}
import pravda.vm.sandbox.VmSandbox.StorageSandbox

class VmImplDebug extends DebugVm {

def debugBytes[S](program: ByteBuffer,
env: Environment,
mem: MemoryImpl,
counter: WattCounter,
maybeStorage: Option[StorageSandbox],
maybePA: Option[domain.Address],
pcallAllowed: Boolean)(implicit debugger: Debugger[S]): List[S] = {

val logicalOperations = new LogicalOperations(mem, counter)
val arithmeticOperations = new ArithmeticOperations(mem, counter)
val storageOperations = new StorageOperations(mem, maybeStorage, counter)
val heapOperations = new HeapOperations(mem, program, counter)
val stackOperations = new StackOperations(mem, program, counter)
val controlOperations = new ControlOperations(program, mem, counter)
val nativeCoinOperations = new NativeCoinOperations(mem, env, counter, maybePA)
val systemOperations =
new SystemOperations(program, mem, maybeStorage, counter, env, maybePA, StandardLibrary.Index, this)
val dataOperations = new DataOperations(mem, counter)

val Some(storage) = maybeStorage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???


@tailrec def proc(acc: List[S]): List[S] = {
counter.cpuUsage(CpuBasic)
val op = program.get() & 0xff

import DebugVm.ExecutionResult
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move up

val executionResult = Try[ExecutionResult] {
mem.setCounter(program.position())
op match {
// Control operations
case CALL => UnitExecution(() => controlOperations.call())
case RET => UnitExecution(() => controlOperations.ret())
case JUMP => UnitExecution(() => controlOperations.jump())
case JUMPI => UnitExecution(() => controlOperations.jumpi())
// Native coin operations
case TRANSFER => UnitExecution(() => nativeCoinOperations.transfer())
case PTRANSFER => UnitExecution(() => nativeCoinOperations.ptransfer())
case BALANCE => UnitExecution(() => nativeCoinOperations.balance())
// Stack operations
case POP => UnitExecution(() => stackOperations.pop())
case PUSHX => UnitExecution(() => stackOperations.push())
case DUP => UnitExecution(() => stackOperations.dup())
case DUPN => UnitExecution(() => stackOperations.dupN())
case SWAP => UnitExecution(() => stackOperations.swap())
case SWAPN => UnitExecution(() => stackOperations.swapN())
// Heap operations
case NEW => UnitExecution(() => heapOperations.`new`())
case ARRAY_GET => UnitExecution(() => heapOperations.arrayGet())
case STRUCT_GET => UnitExecution(() => heapOperations.structGet())
case STRUCT_GET_STATIC => UnitExecution(() => heapOperations.structGetStatic())
case ARRAY_MUT => UnitExecution(() => heapOperations.arrayMut())
case STRUCT_MUT => UnitExecution(() => heapOperations.structMut())
case STRUCT_MUT_STATIC => UnitExecution(() => heapOperations.structMutStatic())
case PRIMITIVE_PUT => UnitExecution(() => heapOperations.primitivePut())
case PRIMITIVE_GET => UnitExecution(() => heapOperations.primitiveGet())
case NEW_ARRAY => UnitExecution(() => heapOperations.newArray())
case LENGTH => UnitExecution(() => heapOperations.length())
// Storage operations
case SPUT => UnitExecution(() => storageOperations.put())
case SGET => UnitExecution(() => storageOperations.get())
case SDROP => UnitExecution(() => storageOperations.drop())
case SEXIST => UnitExecution(() => storageOperations.exists())
// Arithmetic operations
case ADD => UnitExecution(() => arithmeticOperations.add())
case MUL => UnitExecution(() => arithmeticOperations.mul())
case DIV => UnitExecution(() => arithmeticOperations.div())
case MOD => UnitExecution(() => arithmeticOperations.mod())
// Logical operations
case NOT => UnitExecution(() => logicalOperations.not())
case AND => UnitExecution(() => logicalOperations.and())
case OR => UnitExecution(() => logicalOperations.or())
case XOR => UnitExecution(() => logicalOperations.xor())
case EQ => UnitExecution(() => logicalOperations.eq())
case LT => UnitExecution(() => logicalOperations.lt())
case GT => UnitExecution(() => logicalOperations.gt())
// Data operations
case CAST => UnitExecution(() => dataOperations.cast())
case CONCAT => UnitExecution(() => dataOperations.concat())
case SLICE => UnitExecution(() => dataOperations.slice())
// System operations
case STOP => InterruptedExecution
case FROM => UnitExecution(() => systemOperations.from())
case LCALL => UnitExecution(() => systemOperations.lcall())
case SCALL => UnitExecution(() => systemOperations.scall())
case PCREATE => UnitExecution(() => systemOperations.pcreate())
case SEAL => UnitExecution(() => systemOperations.seal())
case PUPDATE => UnitExecution(() => systemOperations.pupdate())
case PADDR => UnitExecution(() => systemOperations.paddr())
case PCALL =>
if (pcallAllowed) {
UnitExecution(() => systemOperations.pcall())
} else {
throw ThrowableVmError(PcallDenied)
}
case THROW => UnitExecution(() => systemOperations.`throw`())
case EVENT => UnitExecution(() => systemOperations.event())
case CALLERS => UnitExecution(() => systemOperations.callers())
case HEIGHT => UnitExecution(() => systemOperations.chainHeight())
case HASH => UnitExecution(() => systemOperations.lastBlockHash())
case CODE => UnitExecution(() => systemOperations.code())
case META =>
MetaExecution(Meta.readFromByteBuffer(program))
case _ => UnitExecution(() => ())
}
}.toEither
val state = debugger.debugOp(program, op, mem, storage)(executionResult)
val res = state :: acc
executionResult match {
case Right(InterruptedExecution) | Left(_) =>
res
case _ => if (program.hasRemaining) proc(res) else res
}
}
proc(Nil).reverse
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider ListBuffer instead, the whole vm module is quite mutable already

}
}
76 changes: 76 additions & 0 deletions evm/src/main/scala/pravda/evm/debug/VmSandboxDebug.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (C) 2018 Expload.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package pravda.evm.debug

import com.google.protobuf.ByteString
import pravda.common.domain.Address
import pravda.vm
import pravda.vm._
import pravda.vm.impl.{MemoryImpl, WattCounterImpl}
import pravda.vm.sandbox.VmSandbox.{EnvironmentSandbox, Preconditions, StorageSandbox}

import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer

object VmSandboxDebug {

def run[S](input: Preconditions, code: ByteString)(implicit debugger: Debugger[S]): List[S] = {
val sandboxVm = new VmImplDebug()
val heap = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smells like copy-paste

if (input.heap.nonEmpty) {
val length = input.heap.map(_._1.data).max + 1
val buffer = ArrayBuffer.fill[Data](length)(Data.Primitive.Null)
input.heap.foreach { case (ref, value) => buffer(ref.data) = value }
buffer
} else {
ArrayBuffer[Data]()
}
}
val memory = MemoryImpl(ArrayBuffer(input.stack: _*), heap)
val wattCounter = new WattCounterImpl(input.`watts-limit`)

val pExecutor = input.executor.getOrElse {
Address @@ ByteString.copyFrom((1 to 32).map(_.toByte).toArray)
}

val effects = mutable.Buffer[vm.Effect]()
val environment: Environment = new EnvironmentSandbox(
effects,
input.`program-storage`,
input.balances.toSeq,
input.programs.toSeq,
pExecutor,
input.`app-state-info`
)
val storage = new StorageSandbox(Address.Void, effects, input.storage.toSeq)

memory.enterProgram(Address.Void)
val res = sandboxVm.debugBytes(
code.asReadOnlyByteBuffer(),
environment,
memory,
wattCounter,
Some(storage),
Some(Address.Void),
pcallAllowed = true
)
memory.exitProgram()
res

}
}
Loading